$epoch = Get-Date -Date "1970-01-01 00:00:00Z" $epoch.ToUniversalTime() | Out-Null $utcNow = Get-Date $utcNow.ToUniversalTime() | Out-Null $sinceEpoch = New-TimeSpan -Start $epoch -End $utcNow $expiry = [System.Convert]::ToString([int32]$sinceEpoch.TotalSeconds + 3600) $stringToSign = [System.Web.HttpUtility]::UrlEncode($resourceUri) + "`n" + $expiry $hamcsha = New-Object System.Security.Cryptography.HMACSHA256 $hamcsha.Key = [Text.Encoding]::UTF8.GetBytes($key); $signature = [System.Convert]::ToBase64String($hamcsha.ComputeHash([Text.Encoding]::UTF8.GetBytes($stringToSign))) $token = [System.String]::Format([System.Globalization.CultureInfo]::InvariantCulture,"SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",[System.Web.HttpUtility]::UrlEncode($resourceUri),[System.Web.HttpUtility]::UrlEncode($signature),$expiry,$keyName);
Epoch := CREATEDATETIME(DMY2DATE(1,1,1970),000000T); SinceEpoch := ( CURRENTDATETIME - Epoch ) / 1000; Expiry := Convert.ToString(ROUND((SinceEpoch + 3600),1,'<')); StringToSign := HttpUtility.UrlEncode(AzureServiceBusQueue.URL) + Environment.NewLine + Expiry; HMACSHA256 := HMACSHA256.HMACSHA256(Encoding.UTF8.GetBytes(AzureServiceBusQueue.Key)); Signature := Convert.ToBase64String(HMACSHA256.ComputeHash(Encoding.UTF8.GetBytes(StringToSign))); Token := Convert.ToString(STRSUBSTNO('SharedAccessSignature sr=%1&sig=%2&se=%3&skn=%4', HttpUtility.UrlEncode(AzureServiceBusQueue.URL), HttpUtility.UrlEncode(Signature), Expiry, AzureServiceBusQueue."Key Name"), CultureInfo.InvariantCulture);
AzureServiceBusQueue@1000000001 : Record 54000; HttpUtility@1000000005 : DotNet "'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.System.Web.HttpUtility"; HMACSHA256@1000000007 : DotNet "'mscorlib'.System.Security.Cryptography.HMACSHA256"; Convert@1000000008 : DotNet "'mscorlib'.System.Convert"; Encoding@1000000009 : DotNet "'mscorlib'.System.Text.Encoding"; CultureInfo@1000000012 : DotNet "'mscorlib'.System.Globalization.CultureInfo"; Environment@1000000014 : DotNet "'mscorlib'.System.Environment";
Epoch := CREATEDATETIME(DMY2DATE(1,1,1970),000000T); SinceEpoch := ( CURRENTDATETIME - Epoch ) / 1000; Expiry := Convert.ToString(ROUND((SinceEpoch + 3600),1,'<')); newline[1] := 10; StringToSign := HttpUtility.UrlEncode(namespace) + newline + Expiry; HMACSHA256 := HMACSHA256.HMACSHA256(Encoding.UTF8.GetBytes(key)); Signature := Convert.ToBase64String(HMACSHA256.ComputeHash(Encoding.UTF8.GetBytes(StringToSign))); Token := Convert.ToString(STRSUBSTNO('SharedAccessSignature sr=%1&sig=%2&se=%3&skn=%4', HttpUtility.UrlEncode(namespace), HttpUtility.UrlEncode(Signature), Expiry, keyname), CultureInfo.InvariantCulture);
Answers
I´ve debugged your code and it seems to me that the expiry timestamp actually is in the year 1970 (I created a Date object in javascript with the expiry timestamp).
If that is the case, you get a 401 response simply because your token expired in 1970.
However, something is wrong with the generated timestamp in the C/AL code. If you generate an expiry timestamp in powershell, 'hard code' it into you c/al codeunit it will probably work for you. It did for me.
I was able to generate a valid sas-token with your c/al code by using an expiry data generated from your powershell script
cool, thanks for having a look.
So you mean that the Epoch in the C/AL code is the wrong timestamp?
And how can I hardcode it? I have to calculate it anyway to get the unix timestamp...
But it's a good idea anyway to separate the code bit which is generating the unix timestamp and check that that it's valid.
My first suspicion was the NewLine character...
I have a workout btw - I'm simply running the Powershell Script from NAV using waldo's solution. I know it's not "elegant" but...
The Environment.NewLine property in C# is just "\r\n" and it isn´t a new line in a utf8-buffer.
I created a text variable with a length of 1, and assigned the value 10 to it, and used that as a new line char wich seems to work!
Here is my working code: