I began using Azure Keyvault to store private keys for my application.
I have a use case where I need to sign a JWT token with an RSA private key.
When I had the private key in my application memory, it was easy, I would just do that
var token = new JwtSecurityToken(
issuer,
...,
claims,
...,
...,
signingCredentials_PrivateKey);
Now that I began to use Azure Keyvault, I want to see if it's possible to sign JWT tokens via the KeyVaultClient.SignAsync
method.
Something along the lines of
KeyVaultClient client = ...;
var token = new JwtSecurityToken(
issuer,
...,
claims,
...,
...);
var tokenString = client.SignAsync(myKeyIdentifier, token);
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
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
KeyVaultClient
with this solution..GetAwaiter().GetResult()
on KeyVaultClient.SignAsync
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With