Azure KeyVault - Sign JWT Token

a 夏天 提交于 2020-01-01 06:43:06

问题


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);

回答1:


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




回答2:


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

  1. I can't reuse an existing instance of KeyVaultClient with this solution.
  2. It's blocking (Behind the scenes, it calls .GetAwaiter().GetResult() on KeyVaultClient.SignAsync


来源:https://stackoverflow.com/questions/56929205/azure-keyvault-sign-jwt-token

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!