5
votes

I would like to generate the SAS token for Azure table service from C# code. I generated the one from the portal which looks like

?sv=2016-05-31&ss=t&srt=sco&sp=rwdlacu&se=2017-03-23T20:05:14Z&st=2017-03-23T12:05:14Z&sip={MY_IP}&spr=https&sig=fL9GNAZqybSlQKWvaspwr%2FrFFtWO%2F5jVgFu1Ayu94Ic%3D

How to generate such kind of token from c# code? If there is any tutorial please redirect me to it. I tried with a method below, but the token generated is invalid.

UPDATED CODE

I am still getting an error 403 Forbidden. Is my code to compute the signature correct?

var StringToSign = "{Storage_account_name}" + "\n" +
                            "rwdlacu" + "\n" +
                            "t" + "\n" +
                            "sco" + "\n" +
                            "2017-03-24T12:05:14Z" + "\n" +
                            "2017-03-24T20:05:14Z" + "\n" +
                            "{IP}" + "\n" +
                            "https" + "\n" +
                            "2016-05-31" + "\n";
string encodedString = HttpUtility.UrlEncode(StringToSign);
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String("accountkey"));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(encodedString)));
3

3 Answers

6
votes

The reason you're running into the issue is because you're calculating signature for SAS based on the logic for calculating Authorization header. StringToSign is different in both cases.

For SAS, this should be (for Service SAS):

StringToSign = signedpermissions + "\n" +  
               signedstart + "\n" +  
               signedexpiry + "\n" +  
               canonicalizedresource + "\n" +  
               signedidentifier + "\n" +  
               signedIP + "\n" +  
               signedProtocol + "\n" +  
               signedversion + "\n" +  
               startingPartitionKey + "\n"  
               startingRowKey + "\n"  
               endingPartitionKey + "\n"  
               endingRowKey  

If you want to use Account SAS (which is what Portal does), it should be:

StringToSign = accountname + "\n" +  
    signedpermissions + "\n" +  
    signedservice + "\n" +  
    signedresourcetype + "\n" +  
    signedstart + "\n" +  
    signedexpiry + "\n" +  
    signedIP + "\n" +  
    signedProtocol + "\n" +  
    signedversion + "\n"  

So based on your parameters, the StringToSign for Account SAS would be:

StringToSign = {youraccountname} + "\n" +  
    "rwdlacu" + "\n" +  
    "t" + "\n" +  
    "sco" + "\n" +  
    "2017-03-23T12:05:14Z" + "\n" +  
    "2017-03-23T20:05:14Z" + "\n" +  
    {yourip} + "\n" +  
    "https" + "\n" +  
    "2016-05-31 + "\n"  

The computation for signature is correct.

You may find these links helpful to learn more about computing SAS: Account SAS and Service SAS.

UPDATE

There's an issue with hmac calculation as well. It should be using your account key and also it should use Convert.FromBase64String.

HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(accountKey));

Also, you should not URLEncode StringToSign. Elements there should be URL decoded.

Lastly the SAS token should look like what you're getting back from the portal.

Code Sample

    static void AccountSasSample()
    {
        var accountName = "your-account-name";
        var accountKey = "your-account-key";
        var start = DateTime.UtcNow.AddHours(-1).ToString("yyyy-MM-ddTHH:mm:ssZ");
        var end = DateTime.UtcNow.AddHours(1).ToString("yyyy-MM-ddTHH:mm:ssZ");
        var permission = "rwdlacu";
        var serviceType = "t";
        var resourceTypes = "sco";
        var ipAddress = "your-ip-address";
        var protocol = "https";
        var serviceVersion = "2016-05-31";
        var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}\n{4}\n{5}\n{6}\n{7}\n{8}\n", accountName, permission, serviceType, resourceTypes, start, end, ipAddress, protocol, serviceVersion);
        Console.WriteLine(stringToSign);
        HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(accountKey));
        string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
        var sasToken = string.Format("?sv={0}&ss={1}&srt={2}&sp={3}&se={4}&st={5}&sip={6}&spr={7}&sig={8}", serviceVersion,
            serviceType, resourceTypes, permission, end, start, ipAddress, protocol, HttpUtility.UrlEncode(signature));
        Console.WriteLine(sasToken);
        var urlToListTables = string.Format("https://{0}.table.core.windows.net/Tables{1}", accountName, sasToken);
        //Copy this urlToListTables & paste it in browser's address bar. You should be able to see the list of tables in your storage account.
    }
4
votes
//account name               
var storageAccountName = ConfigProvider.AccountName;
// your storage account access key here
var accessKey = ConfigProvider.BlobKey;

// connect to our storage account and create a blob client
var connectionString = String.Format("DefaultEndpointsProtocol=https;AccountName={0};AccountKey={1}",
    storageAccountName,
    accessKey);
var storageAccount = CloudStorageAccount.Parse(connectionString);
var blobClient = storageAccount.CreateCloudBlobClient();

SharedAccessAccountPolicy policy = new SharedAccessAccountPolicy()
{
    Permissions = SharedAccessAccountPermissions.Write | SharedAccessAccountPermissions.Create,
    Services = SharedAccessAccountServices.Blob,
    ResourceTypes = SharedAccessAccountResourceTypes.Container | SharedAccessAccountResourceTypes.Object,
    SharedAccessExpiryTime = DateTime.UtcNow.AddMonths(1),
    Protocols = SharedAccessProtocol.HttpsOnly,
};

string sasToken = storageAccount.GetSharedAccessSignature(policy);
2
votes