I’ve already viewed these questions and their answers:
And this blog that was referenced by the second question.
I suspect there is a problem with the certificate issuer, but I could be wrong.
Preface
I’m new at authentication (you can read that as idiot). The current project is to upgrade an existing web site and web application written in Visual Studio 2013 .Net 4.5.1 to Visual Studio 2017 2017 .Net version 4.6.1 to meet requirements for a new message broker.
The environment
Problem Description
The web server written in C# is throwing this error during authentication:
{"IDX10614: AsymmetricSecurityKey.GetSignatureFormater( 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256' ) threw an exception.
Key:
'System.IdentityModel.Tokens.X509AsymmetricSecurityKey'\nSignatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', check to make sure the SignatureAlgorithm is supported.\nException:'System.Security.Cryptography.CryptographicException: Invalid provider type specified.
at System.Security.Cryptography.Utils.CreateProvHandle(CspParameters parameters, Boolean randomKeyContainer)
at System.Security.Cryptography.Utils.GetKeyPairHelper(CspAlgorithmType keyType, CspParameters parameters, Boolean randomKeyContainer, Int32 dwKeySize, SafeProvHandle& safeProvHandle, SafeKeyHandle& safeKeyHandle)\r\n at System.Security.Cryptography.RSACryptoServiceProvider.GetKeyPair()
at System.Security.Cryptography.RSACryptoServiceProvider..ctor(Int32 dwKeySize, CspParameters parameters, Boolean useDefaultKeySize)
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.get_PrivateKey()\r\n at System.IdentityModel.Tokens.X509AsymmetricSecurityKey.GetSignatureFormatter(String algorithm)
at System.IdentityModel.Tokens.AsymmetricSignatureProvider..ctor(AsymmetricSecurityKey key, String algorithm, Boolean willCreateSignatures)'.
If you only need to verify signatures the parameter 'willBeUseForSigning' should be false if the private key is not be available."}
Steps taken
Initially there was no certificate to check and that generated a different error. In PowerShell running as Administrator:
New-SelfSignedCertificate -Subject "CN= XxxxxxxXXCA" -DnsName "localhost" -FriendlyName "XxxxxxxXXCA" -KeyUsage DigitalSignature -KeyUsageProperty ALL -KeyAlgorithm RSA -KeyLength 2048 -CertStoreLocation "Cert:\CurrentUser\My"
Then:
Walk through the authentication code in Visual Studio 2017 debugger in the code below:
The exception is thrown in the return statement.
public string GenerateToken(string email)
{
X509Store store = new X509Store(StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates;
X509Certificate2 signingCert =
certs.Cast<X509Certificate2>().FirstOrDefault(cert => cert.FriendlyName == "XxxxxxxXXCA");
SigningCredentials signingCredentials = new X509SigningCredentials(signingCert);
var tokenHandler = new JwtSecurityTokenHandler();
var now = DateTime.UtcNow;
var customer = _customerService.GetCustomerByEmail(email);
var emailClaim = new Claim(ClaimTypes.Email, customer.Email, ClaimValueTypes.String);
var userIdClaim = new Claim(ClaimTypes.NameIdentifier, customer.Id.ToString(), ClaimValueTypes.Integer);
var roleClaim = new Claim(ClaimTypes.Role, "customer", ClaimValueTypes.String);
var claimsList = new List<Claim> { emailClaim, userIdClaim, roleClaim };
var tokenDescriptor = new SecurityTokenDescriptor()
{
AppliesToAddress = "http://localhost/api",
SigningCredentials = signingCredentials,
TokenIssuerName = "http://localhost",
Lifetime = new Lifetime(now, now.AddDays(30)),
//Lifetime = new Lifetime(now, now.AddDays(1)),
Subject = new ClaimsIdentity(claimsList)
};
store.Close();
return tokenHandler.WriteToken(tokenHandler.CreateToken(tokenDescriptor));
}
New-SelfSignedCertificate
cmdlet uses key storage provider by default. Most of .NET Framework (X509Certificate2
specifically) do not support CNG keys. As the result, when you create X509Certificate2
instance from certificate with private key stored in CNG, get
accessor on PrivateKey
property throws exception:
at System.Security.Cryptography.X509Certificates.X509Certificate2.get_PrivateKey()
I believe, you don't own the code that calls getter on PrivateKey
, therefore, you need to re-create your certificate by explicitly providing legacy provider name in the -Provider
parameter in New-SelfSignedCertificate
cmdlet call. For example, you can use microsoft enhanced rsa and aes cryptographic provider
provider as a parameter value.