Generate Azure Blob Storage Shared Key Signature using Google Apps Script

Requests of Azure Blob Storage need to be authorized. To authorize with shared key (Access Key of your Azure storage account) , requests require at least two headers: Date (or x-ms-date) header and Authorization header. This article will discuss the formats of these headers and how to generate the shared key signature by using Google Apps Script.

Format of Date Header

  • “Date” or “x-ms-date” can be used in the header key field.
  • If both “Date” and “x-ms-date” headers are on the requests, “x-ms-date” will be used.
  • Value of the header should be UTC timestamp format. For example: Fri, 26 Jun 2015 23:39:12 GMT. You can generate it with:
var now = new Date().toUTCString();

Format of Authorization Header

Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"
  • SharedKey or SharedKeyLite: authorization scheme.
  • AccountName: Azure storage account name
  • Signature: signed signature string with HMAC-SHA256 and Base64 encoding

How to sign and encode the signature

  1. UTF-8 encode the signature string the next section will discuss
  2. Base64 decode the Azure storage account shared key (Access Key)
  3. Call HMAC-SHA256 on the results of step 1 and 2
  4. Base64 encode the result of step 3

Base64(HMAC-SHA256(UTF8(StringToSign), Base64.decode(<shared_key>)))

Format of signature string

StringToSign = VERB + "\n" +  
               Content-Encoding + "\n" +  
               Content-Language + "\n" +  
               Content-Length + "\n" +  
               Content-MD5 + "\n" +  
               Content-Type + "\n" +  
               Date + "\n" +  
               If-Modified-Since + "\n" +  
               If-Match + "\n" +  
               If-None-Match + "\n" +  
               If-Unmodified-Since + "\n" +  
               Range + "\n" +  
               CanonicalizedHeaders +   
               CanonicalizedResource;

Take “Get Blob” (download file) as an example:

GET\n /*HTTP Verb*/  
\n    /*Content-Encoding*/  
\n    /*Content-Language*/  
\n    /*Content-Length (empty string when zero)*/  
\n    /*Content-MD5*/  
\n    /*Content-Type*/  
\n    /*Date*/  
\n    /*If-Modified-Since */  
\n    /*If-Match*/  
\n    /*If-None-Match*/  
\n    /*If-Unmodified-Since*/  
\n    /*Range*/  
x-ms-date:Fri, 26 Jun 2015 23:39:12 GMT\nx-ms-version:2015-02-21\n    /*CanonicalizedHeaders*/  
/myaccount /mycontainer\ncomp:metadata\nrestype:container\ntimeout:20    /*CanonicalizedResource*/

“Put Blob” (upload/update file) example

PUT\n /*HTTP Verb*/  
\n    /*Content-Encoding*/  
\n    /*Content-Language*/  
6729182\n    /*Content-Length (empty string when zero)*/  
\n    /*Content-MD5*/  
\n    /*Content-Type*/  
\n    /*Date*/  
\n    /*If-Modified-Since */  
\n    /*If-Match*/  
\n    /*If-None-Match*/  
\n    /*If-Unmodified-Since*/  
\n    /*Range*/  
x-ms-blob-type:BlockBlob\nx-ms-date:Fri, 26 Jun 2015 23:39:12 GMT\nx-ms-version:2015-02-21\n
/myaccount /mycontainer\ncomp:metadata\nrestype:container\ntimeout:20

Generate the Signature

In the procedure of sign and encode the signature, the first step is to encode the signature string with UTF-8. We can do that in Google Apps Script by using:

Utilities.newBlob("").setDataFromString(string).getBytes()

We can use the following code is to generate the the signature (using “Get Blob” as the example):

function signAzureBlobKeyGET(){

  var verb = 'GET';
  var headers = '\n\n\n\n\n\n\n\n\n\n\n\n';
  var msDate = 'x-ms-date:';
  var msVersion = 'x-ms-version:2020-04-08';
  var now = new Date().toUTCString();
  msDate = msDate + now;
  var cHeaders = msDate + '\n' + msVersion + '\n';
  var cResource = '/'+ azureStorageAccount + '/' + azureContainer + '/' + azureBlob;
  var string = verb + headers + cHeaders + cResource;
  var utf8bytes = Utilities.newBlob("").setDataFromString(string).getBytes();
  var decodeKey = Utilities.base64Decode(azureAccessKey);
  var shaSig = Utilities.computeHmacSha256Signature(utf8bytes, decodeKey);
  var signature = Utilities.base64Encode(shaSig);
  return signature;

}

Example original signature string:

GET











x-ms-date:Sat, 03 Jul 2021 06:01:43 GMT
x-ms-version:2020-04-08
/stgaccountname/examplecontainer/signature.mp3

And the signed signature will be something like this:

PK4b/Hye7QtXDpy8Ub8+LQ6Zu3aOpV9qQvzORuPkxm0=

So your Authorization Header will be (JSON format):

"Authorization": "SharedKey stgaccountname:PK4b/Hye7QtXDpy8Ub8+LQ6Zu3aOpV9qQvzORuPkxm0="

Full GET HTTP Header example

Remember to add “x-ms-date” and “x-ms-version” to HTTP header. So the complete HTTP header you should add is:

{
    'x-ms-date': "Sat, 03 Jul 2021 06:01:43 GMT",
    'x-ms-version': "2020-04-08",
    'Authorization': "SharedKey stgaccountname:PK4b/Hye7QtXDpy8Ub8+LQ6Zu3aOpV9qQvzORuPkxm0="
}

Full PUT HTTP Header example

In additional to the header content shown above, we need to add ‘x-ms-blob-type’ and ‘Content-Length’ to the header if you want to add/update the files. Note that ‘Content-Length’ should be placed in the option portion of the UrlFetchApp.fetch() call.

Here is the example:

var res = UrlFetchApp.fetch(azureBlobURL, {
      'headers': {
      'x-ms-blob-type': 'BlockBlob',
      'Authorization': 'SharedKey accntname:PK4b/Hye7QtXDpy8Ub8+LQ6Zu3aOpV9qQvzORuPkxm0=',
      'x-ms-date': 'Sat, 03 Jul 2021 06:01:43 GMT',
      'x-ms-version': '2020-04-08'
    },
    'contentLength': content.length,
    'method': 'put',
    'muteHttpExceptions': true,
    'payload': content
});

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料