Search code examples
.netpowershellazureazureservicebusdynamics-nav

Generate Azure Service Bus SAS Token from Dynamics NAV


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:

$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?


Solution

  • Let me answer my own question...the problem is with the Environment.NewLine because it's CRLF but \n is only LF. Therefore I've replaced it, here is the right code (NewLine is Text[1]):

    Epoch := CREATEDATETIME(DMY2DATE(1,1,1970),000000T);
    SinceEpoch := ( CURRENTDATETIME - Epoch ) / 1000;
    Expiry := Convert.ToString(ROUND((SinceEpoch + 3600),1,'<'));
    NewLine = 10;
    StringToSign := HttpUtility.UrlEncode(AzureServiceBusQueue.URL) + 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);