I would like to use the Microsft Graph API to get informations from the azure active directory. For this I need an access token, which is issued based on a secret or certificate.
In the azure portal the use of a certificate is recommended.
What are the reasons for using a certificate? Is the use of a certificate more secure than a secret?
What type of certificate can and should be stored? How can it be generated?
Many thanks in advance!
What are the reasons for using a certificate? Is the use of a
certificate more secure than a secret?
Yes, Certificate is more secure than Secret, thus it is more complex and more expensive.
You could refer to the link in silent's comment.
What type of certificate can and should be stored? How can it be
generated?
File Type: .cer, .pem, .crt
Read details about the format of certificate. Here is how to craft a signed client assertion:
string Encode(byte[] arg)
{
char Base64PadCharacter = '=';
char Base64Character62 = '+';
char Base64Character63 = '/';
char Base64UrlCharacter62 = '-';
char Base64UrlCharacter63 = '_';
string s = Convert.ToBase64String(arg);
s = s.Split(Base64PadCharacter)[0]; // RemoveAccount any trailing padding
s = s.Replace(Base64Character62, Base64UrlCharacter62); // 62nd char of encoding
s = s.Replace(Base64Character63, Base64UrlCharacter63); // 63rd char of encoding
return s;
}
string GetSignedClientAssertion()
{
//Signing with SHA-256
string rsaSha256Signature = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256";
X509Certificate2 certificate = new X509Certificate2("Certificate.pfx", "Password", X509KeyStorageFlags.EphemeralKeySet);
//Create RSACryptoServiceProvider
var x509Key = new X509AsymmetricSecurityKey(certificate);
var privateKeyXmlParams = certificate.PrivateKey.ToXmlString(true);
var rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(privateKeyXmlParams);
//alg represents the desired signing algorithm, which is SHA-256 in this case
//kid represents the certificate thumbprint
var header = new Dictionary<string, string>()
{
{ "alg", "RS256"},
{ "kid", Encode(Certificate.GetCertHash()) }
};
//Please see the previous code snippet on how to craft claims for the GetClaims() method
string token = Encode(Encoding.UTF8.GetBytes(JObject.FromObject(header).ToString())) + "." + Encode(Encoding.UTF8.GetBytes(JObject.FromObject(GetClaims())));
string signature = Encode(rsa.SignData(Encoding.UTF8.GetBytes(token), new SHA256Cng()));
string signedClientAssertion = string.Concat(token, ".", signature);
return signedClientAssertion;
}
private static IDictionary<string, string> GetClaims()
{
//aud = https://login.microsoftonline.com/ + Tenant ID + /v2.0
string aud = $"https://login.microsoftonline.com/{tenantId}/v2.0";
string ConfidentialClientID = "00000000-0000-0000-0000-000000000000" //client id
const uint JwtToAadLifetimeInSeconds = 60 * 10; // Ten minutes
DateTime validFrom = DateTime.UtcNow;
var nbf = ConvertToTimeT(validFrom);
var exp = ConvertToTimeT(validFrom + TimeSpan.FromSeconds(JwtToAadLifetimeInSeconds));
return new Dictionary<string, string>()
{
{ "aud", aud },
{ "exp", exp.ToString() },
{ "iss", ConfidentialClientID },
{ "jti", Guid.NewGuid().ToString() },
{ "nbf", nbf.ToString() },
{ "sub", ConfidentialClientID }
};
}
Or using Powershell: https://learn.microsoft.com/en-us/azure/cosmos-db/certificate-based-authentication#create-a-self-signed-certificate
Related
I am trying to support DPS for my ESP32 firmware through HTTPS REST API using SAS.
My device registration ID is: xx-xx-8c4b14149ff4
I took the group enrollment from DPS primary key to generate the symmetric key from the registration ID.
I created the related SAS from that and forged the request, but the server returns "Unauthorized" with error code being 401002.
Here is my request (a curl version is provided for handiness ):
curl -L -i -X PUT \
-H 'Content-Type: application/json' \
-H 'Content-Encoding: utf-8' \
-H 'Authorization: SharedAccessSignature sr=0neXXXXXX22%2Fregistrations%xx-xx-8c4b14149ff4&sig=XXXXXXXXXXXXXXXXXXX%3D&skn=registration&se=1651482003' \
-d '{"registrationId": "xx-xx-8c4b14149ff4"}' \
https://global.azure-devices-provisioning.net/0neXXXXXX22/registrations/xx-xx-8c4b14149ff4/register?api-version=2021-06-01
Note that I replaced secret information with "xx".
The response body is as follows:
{
"errorCode": 401002,
"trackingId": "9fecada7-4e51-455e-9392-68522654a64a",
"message": "Unauthorized",
"timestampUtc": "2022-05-02T08:04:03.5761437Z"
}
Is there anything I must tweak to use the HTTPS REST API from the portal?
What should I look at beside the information themselves (which I already double-checked)?
References:
About SAS: https://learn.microsoft.com/en-us/azure/iot-dps/how-to-control-access
About the REST API: https://learn.microsoft.com/en-us/rest/api/iot-dps/device/runtime-registration/register-device#provisioningserviceerrordetails
Consider the following and note how I calculate a deviceKey from the DPS key and then generate the SAS Token from the device key. Some people overlook that. :)
from base64 import b64encode, b64decode
from hashlib import sha256
from time import time
from urllib.parse import urlencode, quote_plus
from hmac import HMAC
import requests
def generate_sas_token(uri, key, policy_name, expiry=3600):
ttl = time() + expiry
sign_key = "%s\n%d" % ((quote_plus(uri)), int(ttl))
sign_key = sign_key.encode('utf-8')
signature = b64encode(HMAC(b64decode(key), sign_key, sha256).digest())
rawtoken = {
'sr' : uri,
'sig': signature,
'se' : str(int(ttl))
}
if policy_name:
rawtoken['skn'] = policy_name
return 'SharedAccessSignature ' + urlencode(rawtoken)
device_id = "3c71bfa95f7c"
scope_id = "0neYOURSCOPEHERE39"
dpskey = 'CsBYOURSASCODEHEREXWyfUMl/OA=='
deviceKey = b64encode(HMAC(b64decode(dpskey), device_id.encode('utf-8'), sha256).digest())
uri = scope_id + '/registrations/' + device_id
policy= 'registration'
url = "https://global.azure-devices-provisioning.net/" + scope_id + "/registrations/" + device_id + "/register?api-version=2021-06-01"
headers = {'Authorization': generate_sas_token(uri=uri, key=deviceKey, policy_name=policy), 'User-Agent': 'MicroPython', 'content-type': 'application/json', 'Content-Encoding': 'utf-8'}
data = '{"registrationId" : "' + device_id + '"}'
print(generate_sas_token(uri=uri, key=deviceKey, policy_name=policy))
print(requests.request("PUT", url=url, data=data, headers=headers).json())
The response shows "assigning", which is success. Hope this helps.
C:/Python38-64/python.exe h:/test/sastoken.py
SharedAccessSignature sr=0ne00223A39%2Fregistrations%2F3c71bfa95f7c&sig=UH5KKeREMOVED0vcs%3D&se=1651880438&skn=registration
{'operationId': '4.4cb788ae24922c84.6856a3d0-857f-4c79-a90c-360fdf3355f8', 'status': 'assigning'}
Can you provide a code sample of how you are generating the individual key? Did you try the samples at https://learn.microsoft.com/en-us/azure/iot-dps/how-to-legacy-device-symm-key?tabs=linux%22%20%5Cl%20%22derive-a-device-key#derive-a-device-key ?
Here's an example using C#
private string GenerateDeviceSas(string enrollmentGroupName, string dpsIdScope, string registrationId, string key)
{
if(String.IsNullOrEmpty(dpsIdScope) || String.IsNullOrEmpty(registrationId) || String.IsNullOrEmpty(key))
{
Console.WriteLine("Error: Missing required values in settings.json");
return null;
}
if (String.IsNullOrEmpty(enrollmentGroupName))
{
// Generate Device API SAS key for individual enrollment
return GenerateSasToken($"{dpsIdScope}/registrations/{registrationId}", key, "registration");
}
else
{
// Generate derived Device API SAS key for group enrollment
// See https://learn.microsoft.com/en-us/azure/iot-dps/how-to-legacy-device-symm-key?tabs=linux#create-a-symmetric-key-enrollment-group
HMACSHA256 hmacsha256 = new HMACSHA256();
hmacsha256.Key = Convert.FromBase64String(key);
var sig = hmacsha256.ComputeHash(ASCIIEncoding.ASCII.GetBytes(registrationId));
var derivedkey = Convert.ToBase64String(sig);
return GenerateSasToken($"{dpsIdScope}/registrations/{registrationId}", derivedkey, "registration");
}
}
private static string GenerateSasToken(string resourceUri, string key, string policyName, int expiryInSeconds = 3600)
{
TimeSpan fromEpochStart = DateTime.UtcNow - new DateTime(1970, 1, 1);
string expiry = Convert.ToString((int)fromEpochStart.TotalSeconds + expiryInSeconds);
string stringToSign = WebUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(key));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
string token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}", WebUtility.UrlEncode(resourceUri), WebUtility.UrlEncode(signature), expiry);
if (!String.IsNullOrEmpty(policyName))
{
token += "&skn=" + policyName;
}
return token;
}
Actually my signature code had a small bug. I fixed it and now it works.
Here is the (working) code for signing:
#include <mbedtls/md.h> // mbed tls lib used to sign SHA-256
#include <base64.hpp> // Densaugeo Base64 version 1.2.0 or 1.2.1
/// Returns the SHA-256 signature of [dataToSign] with the key [enrollmentPrimaryKey]
/// params[in]: dataToSign The data to sign (for our purpose, it is the registration ID (or the device ID if it is different)
/// params[in]: enrollmentPrimaryKey The group enrollment primary key.
/// returns The SHA-256 base-64 signature to present to DPS.
/// Note: I use mbed to SHA-256 sign.
String Sha256Sign(String dataToSign, String enrollmentPrimaryKey){
/// Length of the dataToSign string
const unsigned dataToSignLength = dataToSign.length();
/// Buffer to hold the dataToSign as a char[] buffer from String.
char dataToSignChar[dataToSignLength + 1];
/// String to c-style string (char[])
dataToSign.toCharArray(dataToSignChar, dataToSignLength + 1);
/// The binary decoded key (from the base 64 definition)
unsigned char decodedPSK[32];
/// Encrypted binary signature
unsigned char encryptedSignature[32];
/// Base 64 encoded signature
unsigned char encodedSignature[100];
Serial.printf("Sha256Sign(): Registration Id to sign is: (%d bytes) %s\n", dataToSignLength, dataToSignChar);
Serial.printf("Sha256Sign(): DPS group enrollment primary key is: (%d bytes) %s\n", enrollmentPrimaryKey.length(), enrollmentPrimaryKey.c_str());
// Need to base64 decode the Preshared key and the length
const unsigned base64DecodedDeviceLength = decode_base64((unsigned char*)enrollmentPrimaryKey.c_str(), decodedPSK);
Serial.printf("Sha256Sign(): Decoded primary key is: (%d bytes) ", base64DecodedDeviceLength);
for(int i= 0; i<base64DecodedDeviceLength; i++) {
Serial.printf("%02x ", (int)decodedPSK[i]);
}
Serial.println();
// Use mbed to sign
mbedtls_md_type_t mdType = MBEDTLS_MD_SHA256;
mbedtls_md_context_t hmacKeyContext;
mbedtls_md_init(&hmacKeyContext);
mbedtls_md_setup(&hmacKeyContext, mbedtls_md_info_from_type(mdType), 1);
mbedtls_md_hmac_starts(&hmacKeyContext, (const unsigned char *) decodedPSK, base64DecodedDeviceLength);
mbedtls_md_hmac_update(&hmacKeyContext, (const unsigned char *) dataToSignChar, dataToSignLength);
mbedtls_md_hmac_finish(&hmacKeyContext, encryptedSignature);
mbedtls_md_free(&hmacKeyContext);
Serial.print("Sha256Sign(): Computed hash is: ");
for(int i= 0; i<sizeof(encryptedSignature); i++) {
Serial.printf("%02x ", (int)encryptedSignature[i]);
}
Serial.println();
// base64 decode the HMAC to a char
encode_base64(encryptedSignature, sizeof(encryptedSignature), encodedSignature);
Serial.printf("Sha256Sign(): Computed hash as base64: %s\n", encodedSignature);
// creating the real SAS Token
return String((char*)encodedSignature);
}
See it there: How to generate a symmetric key in C or C++ the same way this script does?
//This is how I am calling from controller
var signature = Request.Headers["X-Docusign-Signature-1"];
var message = Request.Body;
StreamReader reader1 = new StreamReader(message);
string text = reader1.ReadToEnd();
var isValid = docuSignHMACKeyValidation.HashIsValid(text, signature);
// My Methods
public string ComputeHash(string secret, string payload)
{
byte[] bytes = Encoding.UTF8.GetBytes(secret);
HMAC hmac = new HMACSHA256(bytes);
bytes = Encoding.UTF8.GetBytes(payload);
return Convert.ToBase64String(hmac.ComputeHash(bytes));
}
public bool HashIsValid(string payload, string verify)
{
string secret = "secretkey";
ReadOnlySpan<byte> hashBytes =
Convert.FromBase64String(ComputeHash(secret, payload));
ReadOnlySpan<byte> verifyBytes = Convert.FromBase64String(verify);
return CryptographicOperations.FixedTimeEquals(hashBytes, verifyBytes);
}
I'm a little suspicious of your line
bytes = Encoding.UTF8.GetBytes(payload);
I'd try it without an encoding conversion.
Also:
Check that the hmac signature is in X-Docusign-Signature-1 (check that you only have one hmac secret set in DocuSign.)
Use your debugger to check each step and compare with the results from a HMAC checking web site such as https://www.freeformatter.com/hmac-generator.html In other words, first check that you can use the online tool to verify the hmac. Then debug your code to determine why your getting a false response.
I am using this great method, found in this article https://codingstill.com/2016/01/verify-jwt-token-signed-with-rs256-using-the-public-key/#comment-3232, to validate my Azure idToken, which is signed with RS256.
I noticed that Azure AD changes the public key in a period of time. By reading this article, https://nicksnettravels.builttoroam.com/post-2017-01-24-verifying-azure-active-directory-jwt-tokens-aspx. Therefore, I used HttpClient (c#) to obtain the public key from the URL below, every time when I need to validate the token.
https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}
I did get the result of the public key, which is a string array in x5c key. Below is my code:
public static string GetPublicKeyAsync(string kid)
{
using (var client = new HttpClient())
{
HttpResponseMessage response = client.GetAsync("https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}").Result;
if (response.IsSuccessStatusCode)
{
var responseContent = response.Content;
// by calling .Result you are synchronously reading the result
string responseBody = responseContent.ReadAsStringAsync().Result;
JObject json = JObject.Parse(responseBody);
string c = json["keys"].ToString();
List<ADPublic> allPublicKeys = JsonConvert.DeserializeObject<List<ADPublic>>(json["keys"].ToString());
foreach (ADPublic key in allPublicKeys)
{
if (key.kid == kid)
{
string certificateString = key.x5c[0];
var certificate = new X509Certificate2(Convert.FromBase64String(certificateString));
string pkey = Convert.ToBase64String(certificate.PublicKey.EncodedKeyValue.RawData);
var x509SecurityKey = new X509SecurityKey(certificate)
{
KeyId = key.kid
};
return pkey;
}
}
}
return null;
}
}
When I pass my public key over to the validation method. I always get an error at the line (PublicKeyFactory.CreateKey(keyBytes) method ):
Please refer to the verify method I mentioned in the beginning of this question for the code below:
// call my get public key function...
string key = GetPublicKeyAsync(headerData["kid"].ToString());
var keyBytes = Convert.FromBase64String(key); // your key here
**AsymmetricKeyParameter asymmetricKeyParameter = PublicKeyFactory.CreateKey(keyBytes);**
Unknown object in GetInstance: Org.BouncyCastle.Asn1.DerInteger Parameter name: obj
I think I am almost there, but missing the last part, could you please help? Thank you very much!
I got the same error when using certificate.PublicKey.EncodedKeyValue.RawData to get the public key. I referred to this issue and finally succeeded.
The code shows getting AsymmetricKeyParameter from x5c string. Decode(...) function is referred to in the article.
string token = "<the token that you want to verify>";
string certificateString = "<the first x5c of the first key from https://login.microsoftonline.com/{TenantId}/discovery/v2.0/keys?appid={AppId}>";
byte[] buffer = Convert.FromBase64String(certificateString);
X509CertificateParser parser = new X509CertificateParser();
var _certificate = parser.ReadCertificate(buffer);
AsymmetricKeyParameter publicKey = _certificate.GetPublicKey();
string result = Decode(token, publicKey);
Console.WriteLine("result: " + result);
I have to register a device on IoT hub using DPS service. I cannot use the .net SDK as device firmware does not support so we decided to use the REST based API's to do the same.
With C# SDK all I need are the .PFX file with password, DPS_IDSCOPE and device endpoint something like this (xyz.azure-devices-provisioning.net).
Now How I can use above information to do the same with azure rest API.For Authentication I have seen below link which says I have to use SAS token for the same as Azure AD access token wont work.
https://social.msdn.microsoft.com/Forums/en-US/19183e82-437e-4d6f-8498-ed33ba18a3fa/creating-iot-device-with-azure-dps-via-rest?forum=azureiothub
Now If I trust on above link (However I do not think it will work ) then where is the use of certificate .PFX file ?
I have found this official API to register the device .
https://learn.microsoft.com/en-us/rest/api/iot-dps/runtimeregistration/registerdevice
I have not understand how to pass the body information like structure of JSON.I know I have to use x509 as Attestation type but how I will form is it like
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("registrationId", "device1"),
new KeyValuePair<string, string>("type", "x509"),
};
Or if its a json then what will be the name of attribute ?
Now below are the sample code that I tried to use and getting same error.
Way-1 (Used .PFX as authentication)
public static void RegisterDeviceWithEnrollementGroup()
{
try
{
var handler = new WebRequestHandler();
var certFile = Path.Combine(#"C:\IoT\", "device1.pfx");
handler.ClientCertificates.Add(new X509Certificate2(certFile, "certificatepassword"));
HttpClient client4 = new HttpClient(handler);
client4.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client4.BaseAddress = new Uri("https://XYZ.azure-devices-provisioning.net/scopeid/registrations/device1/register?api-version=2018-11-01");
string content = Newtonsoft.Json.JsonConvert.SerializeObject(null);
var httpContent3 = new StringContent(content, Encoding.UTF8, "application/json");
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("registrationId", "device1"),
new KeyValuePair<string, string>("type", "x509"),
};
var content2 = new FormUrlEncodedContent(pairs);
HttpResponseMessage response4 = client4.PutAsync(client4.BaseAddress.ToString(), content2).Result;
var commandResult = string.Empty;
if (response4.IsSuccessStatusCode)
{
commandResult = response4.Content.ReadAsStringAsync().Result;
}
else
{
commandResult = response4.Content.ReadAsStringAsync().Result;
}
Console.WriteLine("IoT hub API call result - " + commandResult);
}
catch (Exception)
{
throw;
}
}
Way-2 - Using SAS token :
public static void RegisterDeviceWithEnrollementGroup()
{
try
{
HttpClient client4 = new HttpClient();
var sas = generateSasToken("XYZ.azure-devices-provisioning.net", "key", "provisioningserviceowner");
client4.DefaultRequestHeaders.Add("Authorization", sas);
client4.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client4.BaseAddress = new Uri("https://XYZ.azure-devices-provisioning.net/scopeid/registrations/device1/register?api-version=2018-11-01");
string content = Newtonsoft.Json.JsonConvert.SerializeObject(null);
var httpContent3 = new StringContent(content, Encoding.UTF8, "application/json");
var pairs = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("registrationId", "device1"),
new KeyValuePair<string, string>("type", "x509"),
};
var content2 = new FormUrlEncodedContent(pairs);
HttpResponseMessage response4 = client4.PutAsync(client4.BaseAddress.ToString(), content2).Result;
var commandResult = string.Empty;
if (response4.IsSuccessStatusCode)
{
commandResult = response4.Content.ReadAsStringAsync().Result;
}
else
{
commandResult = response4.Content.ReadAsStringAsync().Result;
}
Console.WriteLine("IoT hub API call result - " + commandResult);
}
catch (Exception)
{
throw;
}
}
Helper method :
public static string generateSasToken(string resourceUri, string key, string policyName, int expiryInSeconds = 3600)
{
TimeSpan fromEpochStart = DateTime.UtcNow - new DateTime(1970, 1, 1);
string expiry = Convert.ToString((int)fromEpochStart.TotalSeconds + expiryInSeconds);
string stringToSign = WebUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Convert.FromBase64String(key));
string signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
string token = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}", WebUtility.UrlEncode(resourceUri), WebUtility.UrlEncode(signature), expiry);
if (!String.IsNullOrEmpty(policyName))
{
token += "&skn=" + policyName;
}
return token;
}
Now Please answer some body whether I am doing correct or wrong here as I am getting exception.
{StatusCode: 415, ReasonPhrase: 'Unsupported Media Type', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
x-ms-request-id: 6475343d-5a2e-407a-9e7f-896e0c489307
Strict-Transport-Security: max-age=31536000; includeSubDomains
Date: Thu, 28 Feb 2019 11:42:59 GMT
Content-Length: 0
}}
Looking forward for the help ...
Please follow the steps outlined here:
https://learn.microsoft.com/en-us/azure/iot-dps/tutorial-net-provision-device-to-hub
to first create an Enrollment in DPS for this device using X.509 (no need to use EnrollmentGroup for a single device).
When registering a device with DPS, use the global endpoint - global.azure-devices-provisioning.net. Configure the HTTP client to include the device client certificate. Do not provide a SAStoken from the device.
You can setup the HTTP message content for device registration as follows:
httpRequest.Content = new StringContent("{\"registrationId\": \"device1\"}", Encoding.UTF8);
httpRequest.Content.Headers.ContentType=MediaTypeHeaderValue.Parse("application/json; charset=utf-8");
Note that the JSON content does NOT include "type" : "x509".
The device endpoint would be as follows:
PUT https://global.azure-devices-provisioning.net/{idScope}/registrations/device1/register?api-version=2018-11-01 (replace idScope with the one from your DPS. A sample idScope would be 0ne00000012)
This POST explains it quite well: http://busbyland.com/azure-iot-device-provisioning-service-via-rest-part-1/
Using the endpoint documented here: https://learn.microsoft.com/es-es/rest/api/iot-dps/runtimeregistration/registerdevice
You can manage to register your devices with a HTTPRequest see the CURL Example above:
curl -L -i -X PUT --cert ./chain.pem --key ./iot-device-1.key.pem -H 'Content-Type: application/json' -H 'Content-Encoding: utf-8' -d '{"registrationId": "iot-device-1", "payload": {"CustomProperty": "CustomValue"}}' https://global.azure-devices-provisioning.net/XXXXXXXXXXX/registrations/iot-device-1/register?api-version=2019-03-31
You could replicate that request in your development environment.
Note that the global.azure-devices-provisioning.net is used which is public and you do not need any authentication/authorization.
I am trying to generate Shared Access signature for the Azure service bus queue. Here is the code snippnet through which I am generating SAS token.
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Web;
namespace SASTokenGeneration
{
class Program
{
static void Main(string[] args)
{
string resourceUri = "sb://xxxservicequeue.servicebus.windows.net/;SharedAccessKeyName=xxxservicequeue;SharedAccessKey=xxxxxx";
string key = "token";
string keyName = "xxxservicequeue";
try
{
TimeSpan sinceEpoch = DateTime.UtcNow - new DateTime(1970, 1, 1);
var week = 60 * 60 * 24 * 7;
var expiry = Convert.ToString((int)sinceEpoch.TotalSeconds + week);
string stringToSign = HttpUtility.UrlEncode(resourceUri) + "\n" + expiry;
HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
var signature = Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)));
var sasToken = String.Format(CultureInfo.InvariantCulture, "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}",
HttpUtility.UrlEncode(resourceUri), HttpUtility.UrlEncode(signature), expiry, keyName);
}
catch (Exception ex)
{
}
}
}
}
When I use this token in postman then sometime it gives
MalformedToken: Failed to parse simple web token. azure sevice Bus
and Now I am getting
401 40103: Invalid authorization token signature
From error, I know that I am doing something wrong with connection string but cannot figure out what. Can you please point me in right direction.
Thanks
Problems may locate at
string resourceUri = "sb://xxxservicequeue.servicebus.windows.net/;SharedAccessKeyName=xxxservicequeue;SharedAccessKey=xxxxxx";
string key = "token";
string keyName = "xxxservicequeue";
The resourceUri is the full URI of the Service Bus resource to which access is claimed, in the format of sb://xxxservicequeue.servicebus.windows.net/ or with specific entity you need to operate like sb://xxxservicequeue.servicebus.windows.net/queueName.
The key should be the value of SharedAccessKeyKey and keyName is SAS policy name like the default RootManageSharedAccessKey.
Have a look at doc for more details.