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
- UTF-8 encode the signature string the next section will discuss
- Base64 decode the Azure storage account shared key (Access Key)
- Call HMAC-SHA256 on the results of step 1 and 2
- 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
});