I\'m trying to read the private key of a certificate which has been shared with me by a third-party service provider, so I can use it to encrypt some XML before sending it t
In my case, the following code worked fine in localhost (both NET 3.5 and NET 4.7):
var certificate = new X509Certificate2(certificateBytes, password);
string xml = "....";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(xml);
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.SigningKey = certificate.PrivateKey;
//etc...
But it failed when deployed to an Azure Web App, at certificate.PrivateKey
It worked by changing the code as follows:
var certificate = new X509Certificate2(certificateBytes, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
//^ Here
string xml = "....";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.PreserveWhitespace = true;
xmlDocument.LoadXml(xml);
SignedXml signedXml = new SignedXml(xmlDocument);
signedXml.SigningKey = certificate.GetRSAPrivateKey();
// ^ Here too
//etc...
A whole day of work lost thanks to Microsoft Azure, once again in my life.
In my case, I was trying to use a self-signed certificate with PowerShell's New-SelfSignedCertificate command. By default, it will generate a certificate using the CNG (Crypto-Next Generation) API instead of the older/classic crypto CAPI. Some older pieces of code will have trouble with this; in my case it was an older version of the IdentityServer STS provider.
By adding this at the end of my New-SelfSignedCertificate command, I got past the issue:
-KeySpec KeyExchange
Reference on the switch for the powershell command:
https://docs.microsoft.com/en-us/powershell/module/pkiclient/new-selfsignedcertificate?view=win10-ps
After following the accepted answer (specifying KeySpec), the exception changed to System.Security.Cryptography.CryptographicException: Invalid provider type specified.
. I resolved that exception by giving my web application access to the private key (IIS_IUSRS). I discovered that this would also fix the problem with my original certificate. So before generating and deploying new certificates, check the private key's permissions as well.
Using Visual Studio 2019 and IISExpress, I was able to correct this problem by removing the following flag when loading the .pfx|.p12
file:
X509KeyStorageFlags.MachineKeySet
Before:
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable
After:
X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable
Normally, I'll load the cert this way:
var myCert = new X509Certificate2("mykey.pfx", "mypassword", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
This does NOT raise an Exception, instead the Exception is raised when attempting to use the certificate (or in my case, when trying to obtain the .PrivateKey
). I've found this issue can be caused when the invoking user has insufficient permissions.
Since I rely on the MachineKeySet
flag for some environments, my current solution is to swallow the Exception, change the flags, try again. :/
A more organic solution would be to test the permissions level and set this flag dynamically, however I'm not aware of a simple way to do this, hence the fallback.
NOTE: The .pfx
file I'm using was created from a website that uses a JavaScript library (digitalbazaar/forge
) which does NOT use CNG (Cryptography Next Generation) Key Storage Providers. here are many common causes for this same error (the most common fixes related to CNG
extensions, which have -- unfortunately even changed namespaces in .NET versions), which throw the same error. Microsoft should ultimately be more verbose when throwing these types of errors.
I also had this issue and after attempting the suggestions in this post without success. I was able to resolve my issue by reloading the certificate with the Digicert certificate utility https://www.digicert.com/util/. This allows one to select the provider to load the certificate into. In my case loading the certificate into the Microsoft RSA Schannel Cryptographic Provider provider where I had expected it to be in the first place resolved the issue.
I faced the same problem in our IIS app:
System.Security.Cryptography.Pkcs.PkcsUtils.CreateSignerEncodeInfo(CmsSigner signer, Boolean silent)
System.Security.Cryptography.Pkcs.SignedCms.Sign(CmsSigner signer, Boolean silent)
System.Security.Cryptography.Pkcs.SignedCms.ComputeSignature(CmsSigner signer, Boolean silent)
Regenerating certs as mentioned here didnt help. I also noticed that test console app works fine under pool user.
Problem disappeared after clearing "Enable 32-bit Applications" setting for IIS app pool.