Generate Azure Service Bus SAS Token
martonn
Member Posts: 29
I'm trying to generate a SAS Token from Dynamics NAV 2018 using DotNet. I have a working code in PowerShell what I've been using for testing:
here is the C/AL code but I get (401) Unauthorized and the token looks a bit different
the DotNet variables are:
If I try the token generated by the C/AL code in the PowerShell script I get the same result : (401) Unauthorized
Any ideas?
$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);
here is the C/AL code but I get (401) Unauthorized and the token looks a bit different
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);
the DotNet variables are:
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";
If I try the token generated by the C/AL code in the PowerShell script I get the same result : (401) Unauthorized
Any ideas?
0
Best Answer
-
I´m sorry, I was way to quick to jump to conclusions. It was not the timestamp that was the missing part, it was indeed the new line character as you suspected.
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: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);
1
Answers
-
Hi, I´m trying to solve the same problem.
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.
1 -
Hmm, the powershell script also is a timestamp in then 1970´s when using the javascript Date object to parse the timestamp.
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 script1 -
Hi andnil,
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...0 -
I´m sorry, I was way to quick to jump to conclusions. It was not the timestamp that was the missing part, it was indeed the new line character as you suspected.
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: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);
1 -
Many thanks! I've tried with CRLF but haven't tried with LF only!0
Categories
- All Categories
- 75 General
- 75 Announcements
- 66.7K Microsoft Dynamics NAV
- 18.8K NAV Three Tier
- 38.4K NAV/Navision Classic Client
- 3.6K Navision Attain
- 2.4K Navision Financials
- 116 Navision DOS
- 851 Navision e-Commerce
- 1K NAV Tips & Tricks
- 772 NAV Dutch speaking only
- 610 NAV Courses, Exams & Certification
- 1.9K Microsoft Dynamics-Other
- 1.5K Dynamics AX
- 251 Dynamics CRM
- 103 Dynamics GP
- 6 Dynamics SL
- 1.5K Other
- 991 SQL General
- 383 SQL Performance
- 34 SQL Tips & Tricks
- 28 Design Patterns (General & Best Practices)
- Architectural Patterns
- 9 Design Patterns
- 4 Implementation Patterns
- 53 3rd Party Products, Services & Events
- 1.6K General
- 1K General Chat
- 1.6K Website
- 77 Testing
- 1.2K Download section
- 23 How Tos section
- 249 Feedback
- 12 NAV TechDays 2013 Sessions
- 13 NAV TechDays 2012 Sessions