IDX10638: Cannot created the SignatureProvider, ‘key.HasPrivateKey’ is false, cannot create signatures. Key: Microsoft.IdentityModel.Tokens.RsaSecurityKey.

After updating the Microsoft.IdentityModel.Tokens library we were getting the following error message when creating JWT tokens:

System.InvalidOperationException
HResult=0x80131509
Message=IDX10638: Cannot created the SignatureProvider, ‘key.HasPrivateKey’ is false, cannot create signatures. Key: Microsoft.IdentityModel.Tokens.RsaSecurityKey.
Source=Microsoft.IdentityModel.Tokens
StackTrace:
at Microsoft.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(SecurityKey key, String algorithm, Boolean willCreateSignatures)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateSignatureProvider(SecurityKey key, String algorithm, Boolean willCreateSignatures)
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.CreateForSigning(SecurityKey key, String algorithm)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateEncodedSignature(String input, SigningCredentials signingCredentials)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.WriteToken(SecurityToken token)

The code where this happened looked like this and was working fine before the update:

var buffer = Convert.FromBase64String(Base64Cert);
var signingCertificate = new X509Certificate2(buffer, CertificatePassword);

var identity = new ClaimsIdentity(Claims);
var data = new AuthenticationTicket(identity, null);

if (signingCertificate.PrivateKey is RSACryptoServiceProvider rsaProvider)
{
	var key = new RsaSecurityKey(rsaProvider);
	var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature);

	var token = new JwtSecurityToken(
		issuer: TokenIssuer,
		audience: TokenAudience,
		claims: data.Identity.Claims,
		notBefore: DateTime.UtcNow,
		expires: DateTime.UtcNow.AddMinutes(TokenValidityInMinutes),
		signingCredentials: signingCredentials
	);

	var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
	Console.WriteLine(tokenString);
}
else
{
	Console.Error.WriteLine("signingCertificate.PrivateKey is not an RSACryptoServiceProvider");
}

Debugging the code, I saw that signingCertificate.HasPrivateKey was true but key.HasPrivateKey was false

In order to solve it, two small changes were required:

  1. Add a keyStorageFlags parameter to the X509Certificate2 constructor so that the imported keys are marked as exportable
  2. Use the ExportParameters method to retrieve the raw RSA key in the form of an RSAParameters structure including private parameters.

So using the following code worked without exception:

var buffer = Convert.FromBase64String(Base64Cert);
var signingCertificate = new X509Certificate2(buffer, CertificatePassword, X509KeyStorageFlags.Exportable);

var identity = new ClaimsIdentity(Claims);
var data = new AuthenticationTicket(identity, null);

if (signingCertificate.PrivateKey is RSACryptoServiceProvider rsaProvider)
{
	var key = new RsaSecurityKey(rsaProvider.ExportParameters(true));
	var signingCredentials = new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature);

	var token = new JwtSecurityToken(
		issuer: TokenIssuer,
		audience: TokenAudience,
		claims: data.Identity.Claims,
		notBefore: DateTime.UtcNow,
		expires: DateTime.UtcNow.AddMinutes(TokenValidityInMinutes),
		signingCredentials: signingCredentials
	);

	var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
	Console.WriteLine(tokenString);
}
else
{
	Console.Error.WriteLine("signingCertificate.PrivateKey is not an RSACryptoServiceProvider");
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *