Authentication Schemes
[This is prerelease documentation and is subject to change in future releases.]

Every request made against a storage service must be authenticated, unless the request is for a resource that has been made available for public or signed access.

noteNote
A blob resource may be made available for public access by setting its container's access control list (ACL). A blob may be available for signed access via a Shared Access Signature; a Shared Access Signature is authenticated through a different mechanism. For more information, see Managing Access to Containers and Blobs.

Storage services support one of two Shared Key authentication schemes.

An authenticated request requires two headers: the Date or x-ms-Date header and the Authorization header. The following sections describe how to construct these headers.

This topic contains the following subtopics:

Specifying the Date Header

Specifying the Authorization Header

Example: Shared Key Authentication (Blob and Queue)

Example: Shared Key Lite Authentication

Specifying the Date Header

All authenticated requests must include the Coordinated Universal Time (UTC) timestamp for the request. You can specify the timestamp either in the standard HTTP/HTTPS Date header, or in the x-ms-Date header.

The x-ms-Date header is provided because some HTTP client libraries automatically set the Date header, and do not give the developer an opportunity to read its value in order to include it in the authenticated request. If you set x-ms-Date, construct the signature with an empty value for the Date header.

Note that the storage services reject requests where the specified timestamp is skewed by more than 15 minutes in order to guard against certain security attacks, including replay attacks. When this check fails, the server returns response code 403 (Forbidden).

Specifying the Authorization Header

An authenticated request must include the Authorization header. If this header is not included, the request is anonymous and may only succeed against a blob container that is marked for public access. Public access is not permitted for tables and queues.

To authenticate a request, you must sign the request with the key for the account that is making the request and pass that signature as part of the request.

The format for the Authorization header is as follows:

Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"

where SharedKey or SharedKeyLite is the name of the authorization scheme, AccountName is the name of the account requesting the resource, and Signature is a Hash-based Message Authentication Code (HMAC) constructed from the request and computed by using the SHA256 algorithm, and then encoded by using Base64 encoding.

noteNote
It is possible to request a resource that resides beneath a different account, if that resource is publicly accessible.

The following sections describe how to construct the Authorization header, and provide examples drawn from the StorageClient sample that ships with the Windows® Azure™ SDK.

Constructing the Signature String

How you construct the signature string depends on which service you are authenticating against and which authentication scheme you are using. Depending on the authentication scheme, you must include the HTTP verb, the Content-MD5 and Content-Type headers, the Date or x-ms-date header, and canonicalized strings that include the targeted resource and the request headers.

When constructing the signature string, keep in mind the following:

  • The VERB portion of the string is the HTTP verb, such as GET or PUT, and must be uppercase.

  • The Content-MD5, Content-Type, and Date headers are described in the Header Field Definitions of the HTTP/1.1 Protocol Specification. The values of these headers must be included in the string in the order shown in the signature format, without the header names. These headers may be empty; in that case, only the new line character is required.

  • If the x-ms-Date header is specified, you may ignore the Date header, regardless of whether it exists, and simply specify an empty line for the Date portion of the signature string. In this case, follow the instructions in the Constructing the CanonicalizedHeaders Element section for adding the x-ms-Date header.

  • If the x-ms-Date header is not specified, specify the Date header in the signature string, without including the header name.

  • All new line characters (\n) shown are required within the signature string.

  • For detailed information on constructing the CanonicalizedHeaders and CanonicalizedResource strings that make up part of the signature string, see the appropriate sections later in this topic.

Blob and Queue Services (Shared Key Authentication)

To encode the signature string for a request against the Blob or Queue service, use the following format:

StringToSign = VERB + "\n" +
               Content-MD5 + "\n" +
               Content-Type + "\n" +
               Date + "\n" +
               CanonicalizedHeaders + 
               CanonicalizedResource;

The following example shows a signature string for a Put Blob operation. Note that the Content-MD5 header line is empty. The headers shown in the string are name-value pairs that specify custom metadata values for the new blob.

"PUT\n\ntext/plain; charset=UTF-8\n\nx-ms-Date:Wed, 17 Sep 2008 20:36:40 GMT\nx-ms-meta-m1:v1\nx-ms-meta-m2:v2\n/testaccount1/storageclientcontainer/hello.txt"

Next, encode this string by using the HMAC-SHA256 algorithm, construct the Authorization header, and add the header to the request. The following example shows the Authorization header for the same operation:

Authorization: SharedKey myaccount:ctzMq410TV3wS7upTBcunJTDLEJwMAZuFPfr0mrrA08=

Table Service (Shared Key Authentication)

You can use Shared Key authentication to authenticate a request made against the Table service if your service is using the REST API to make the request. Note that the Shared Key signature string for a request against the Table service differs slightly from that for a request against the Blob or Queue service, in that it does not include the CanonicalizedHeaders portion of the string. Additionally, the Date header in this case is never empty even if the request sets the x-ms-Date header. If the request sets x-ms-Date, that value is also used for the value of the Date header.

To encode the signature string for a request against the Table service made using the REST API, use the following format:

StringToSign = VERB + "\n" + 
               Content-MD5 + "\n" + 
               Content-Type + "\n" +
               Date + "\n" +
               CanonicalizedResource;

Table Service (Shared Key Lite Authentication)

You may use Shared Key Lite authentication to authenticate a request made against the Table service if your service is using the .NET Client Library for ADO.NET Data Services Framework to make the request.

When using Shared Key Lite authentication, you must add the signature to the request within a callback method that is fired when the request is created. Assign the callback method to the SendingRequest event on the DataServiceContext class defined by the .NET Client Library.

To encode the signature string for a request against the Table service made using the client library, use the following format:

StringToSign = Date + "\n" 
               CanonicalizedResource

The following example shows a signature string for a Create Table operation.

"Wed, 17 Sep 2008 19:52:39 GMT\n/testaccount1/Tables"

Next, encode this string by using the HMAC-SHA256 algorithm, construct the Authorization header, and then add the header to the request. The following example shows the Authorization header for the same operation:

Authorization: SharedKeyLite testaccount1:uay+rilMVayH/SVI8X+a3fL8k/NxCnIePdyZSkqvydM=

Constructing the Canonicalized Headers String

To construct the CanonicalizedHeaders portion of the signature string, follow these steps:

  1. Retrieve all headers for the resource that begin with x-ms-, including the x-ms-date header.

  2. Convert each HTTP header name to lowercase.

  3. Sort the container of headers lexicographically by header name, in ascending order.

  4. Combine headers with the same name into one header. The resulting header should be a name-value pair of the format "header-name:comma-separated-value-list", without any white space between values.

    Important   The comma-separated list of headers is not ordered by the header values but by the order in which the headers appear in the request. The list of headers must be in the correct order to properly authenticate the request.

  5. Replace any breaking white space with a single space.

  6. Trim any white space around the colon in the header.

  7. Finally, append a new line character to each canonicalized header in the resulting list. Construct the CanonicalizedHeaders string by concatenating all headers in this list into a single string.

Constructing the Canonicalized Resource String

The CanonicalizedResource part of the signature string represents the storage services resource targeted by the request. Any portion of the CanonicalizedResource string that is derived from the resource's URI should be encoded exactly as it is in the URI.

You can construct the CanonicalizedResource string as follows:

  1. Beginning with an empty string (""), append a forward slash (/), followed by the name of the account that owns the resource being accessed.

  2. Append the resource's encoded URI path. If the request URI addresses a component of the resource, append the appropriate query string. The query string should include the question mark and the comp parameter (for example, ?comp=metadata). No other parameters should be included on the query string.

    For help constructing the URI for the resource you are accessing, see one of the following topics:

noteNote
If you are authenticating against development storage, the account name will appear twice in the CanonicalizedResource string. This is expected. If you are authenticating against Windows Azure storage services, the account name will appear only one time in the CanonicalizedResource string.

Encoding the Signature

To encode the signature, call the HMAC-SHA256 algorithm on the signature string and encode the result as Base64. Use the following format (shown as pseudocode):

Signature=Base64(HMAC-SHA256(UTF8(StringToSign)))

Example: Shared Key Authentication (Blob and Queue)

This example, adapted from the StorageClient Sample, shows the SharedKeyCredentials class, which constructs the signature string for Shared Key authentication, passes it through the HMAC-SHA256, encodes it as Base64, and adds the Authorization header to the request.

To view and run the complete code example, see the Authorization.cs class in the StorageClient sample.

public class SharedKeyCredentials
{
    public SharedKeyCredentials(string accountName, byte[] key)
    {
        this.accountName = accountName;
        this.key = key;
    }

    public void SignRequest(HttpWebRequest request, ResourceUriComponents uriComponents)
    {
        if (request == null)
            throw new ArgumentNullException("request");
        string message = MessageCanonicalizer.CanonicalizeHttpRequest(request, uriComponents);
        string computedBase64Signature = ComputeMacSha(message);
        request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization,
                            string.Format(CultureInfo.InvariantCulture,
                                          "{0} {1}:{2}",
                                          StorageHttpConstants.AuthenticationSchemeNames.SharedKeyAuthSchemeName,
                                          accountName,
                                          computedBase64Signature));
    }

    private string ComputeMacSha(string canonicalizedString)
    {
        byte[] dataToMAC = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);

        using (HMACSHA256 hmacsha1 = new HMACSHA256(key))
        {
            return System.Convert.ToBase64String(hmacsha1.ComputeHash(dataToMAC));
        }
    }

    private string accountName;
    private byte[] key;
}

Example: Shared Key Lite Authentication

This example, adapted from the StorageClient Sample, shows the callback method that signs the request. This method calls the SignRequestForSharedKeyLite method to add the Authorization header to the request.

private void DataContextSendingRequest(object sender, SendingRequestEventArgs e)
{
    HttpWebRequest request = e.Request as HttpWebRequest;
    // Do the authentication.
    byte[] key;
    SharedKeyCredentials credentials;

    Debug.Assert(_sharedKey != null);
    key = Convert.FromBase64String(_sharedKey);
    credentials = new SharedKeyCredentials(_accountName, key);
    credentials.SignRequestForSharedKeyLite(request, new ResourceUriComponents(_accountName, _accountName));
}

The following code shows the SharedKeyCredentials class that constructs the signature string for Shared Key Lite authentication, encodes it with HMAC-SHA256, and adds the Authorization header to the request. To view and run the code, see the Authorization.cs class in the StorageClient sample.

public class SharedKeyCredentials
{
    public SharedKeyCredentials(string accountName, byte[] key)
    {
        this.accountName = accountName;
        this.key = key;
    }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lite", 
                                                      Justification = "Name of the authentication scheme in the REST protocol")]
    public void SignRequestForSharedKeyLite(HttpWebRequest request, ResourceUriComponents uriComponents)
    {
        if (request == null)
        {
            throw new ArgumentNullException("request");
        }

        // Add the date header to the request.
        string dateString = MessageCanonicalizer.ConvertDateTimeToHttpString(DateTime.UtcNow);
        request.Headers.Add(StorageHttpConstants.HeaderNames.StorageDateTime, dateString);
request.Headers.Add("x-ms-version", "2009-04-14"); //include the x-ms-version header when calling the 2009-04-14 version of an operation

        // Compute the signature and add the authentication scheme.
        string message = MessageCanonicalizer.CanonicalizeHttpRequestForSharedKeyLite(request, uriComponents, dateString);
        string computedBase64Signature = ComputeMacSha(message);
        request.Headers.Add(StorageHttpConstants.HeaderNames.Authorization,
                            string.Format(CultureInfo.InvariantCulture,
                                          "{0} {1}:{2}",
                                          StorageHttpConstants.AuthenticationSchemeNames.SharedKeyLiteAuthSchemeName,
                                          accountName,
                                          computedBase64Signature));
    }

    private string ComputeMacSha(string canonicalizedString)
    {
        byte[] dataToMAC = System.Text.Encoding.UTF8.GetBytes(canonicalizedString);

        using (HMACSHA256 hmacsha1 = new HMACSHA256(key))
        {
            return System.Convert.ToBase64String(hmacsha1.ComputeHash(dataToMAC));
        }
    }

    private string accountName;
    private byte[] key;
}

See Also

Page view tracker