Azure KeyVault - Sign JWT Token
I ended up using Jack Jia's answer
var token = new JwtSecurityToken(
issuer,
appId,
claims,
signDate,
expiryDate);
var header = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(new Dictionary<string, string>()
{
{ JwtHeaderParameterNames.Alg, "RS256" },
{ JwtHeaderParameterNames.Kid, "https://myvault.vault.azure.net/keys/mykey/keyid" },
{ JwtHeaderParameterNames.Typ, "JWT" }
}));
var byteData = Encoding.UTF8.GetBytes(header + "." + token.EncodedPayload);
var hasher = new SHA256CryptoServiceProvider();
var digest = hasher.ComputeHash(byteData);
var signature = await _keyVault.SignAsync("https://myvault.vault.azure.net/keys/mykey/keyid", "RS256", digest);
return $"{header}.{token.EncodedPayload}.{Base64UrlEncoder.Encode(signature.Result)}";
I found another solution, which I didn't like as much but it "integrates" better with the JWT libraries.
var token = new JwtSecurityToken(
issuer,
appId,
claims,
signDate,
expiryDate,
new SigningCredentials(new KeyVaultSecurityKey("https://myvault.vault.azure.net/keys/mykey/keyid", new KeyVaultSecurityKey.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback)), "RS256")
{
CryptoProviderFactory = new CryptoProviderFactory() { CustomCryptoProvider = new KeyVaultCryptoProvider() }
});
var handler = new JwtSecurityTokenHandler();
return handler.WriteToken(token);
Turns out that there is a library Microsoft.IdentityModel.KeyVaultExtensions
with extensions to SecurityToken
and ICryptoProvider
which support KeyVault.
My problems with it are
- I can't reuse an existing instance of
KeyVaultClient
with this solution. - It's blocking (Behind the scenes, it calls
.GetAwaiter().GetResult()
onKeyVaultClient.SignAsync
First, a JWT token consists of three parts: Header, Payload and Signature. All of them are Base64UrlEncoded.
You can get the signature as following:
HMAC-SHA256(
base64urlEncoding(header) + '.' + base64urlEncoding(payload),
secret
)
So, you need to generate the header and payload, combine them by dot, compute the hash, and then you can get the signature.
Here is a sample for your reference:
var byteData = Encoding.Unicode.GetBytes(base64urlEncoding(header) + "." + base64urlEncoding(payload));
var hasher = new SHA256CryptoServiceProvider();
var digest = hasher.ComputeHash(byteData);
var signature = await keyClient.SignAsync(keyIdentifier, "RS256", digest);
var token = base64urlEncoding(header) + "." + base64urlEncoding(payload) + "." + base64urlEncoding(signature)
The official SDK documentation for SignAsync
Wiki for JWT