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
I tried everything in this post, but for me the solution was:
Read the new certificate using this flag options from this post solution:
var certificate = new X509Certificate2(certKeyFilePath, passCode,
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet );
It seems on Windows 10, if I run my program without administrative privileges (whether I use the PrivateKey property or the GetRSAPrivateKey() extension method), I will see this exception (see the title of this discussion thread for the exception). If I run my program with administrative privileges and use the PrivateKey property, I will also see this exception. Only if I run my program with administrative privileges and use the GetRSAPrivateKey() extension method, I will not see this exception.
The link to Alejandro's blog is key.
I believe this is because the certificate is stored on your machine with the CNG ("Crypto Next-Generation") API. The old .NET API is not compatible with it, so it doesn't work.
You can use the Security.Cryptography wrapper for this API (available on Codeplex). This adds extension methods to X509Certificate/X509Certificate2
, so your code will look something like:
using Security.Cryptography.X509Certificates; // Get extension methods
X509Certificate cert; // Populate from somewhere else...
if (cert.HasCngKey())
{
var privateKey = cert.GetCngPrivateKey();
}
else
{
var privateKey = cert.PrivateKey;
}
Unfortunately the object model for CNG private keys is quite a bit different. I'm not sure if you can export them to XML like in your original code sample...in my case I just needed to sign some data with the private key.
Powershell version of the answer from @berend-engelbrecht, assuming openssl
installed via chocolatey
function Fix-Certificates($certPasswordPlain)
{
$certs = Get-ChildItem -path "*.pfx" -Exclude "*.converted.pfx"
$certs | ForEach-Object{
$certFile = $_
$shortName = [io.path]::GetFileNameWithoutExtension($certFile.Name)
Write-Host "Importing $shortName"
$finalPfx = "$shortName.converted.pfx"
Set-Alias openssl "C:\Program Files\OpenSSL\bin\openssl.exe"
# Extract public key
OpenSSL pkcs12 -in $certFile.Fullname -nokeys -out "$shortName.cer" -passin "pass:$certPasswordPlain"
# Extract private key
OpenSSL pkcs12 -in $certFile.Fullname -nocerts -out "$shortName.pem" -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain"
# Convert private key to RSA format
OpenSSL rsa -inform PEM -in "$shortName.pem" -out "$shortName.rsa" -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain" 2>$null
# Merge public keys with RSA private key to new PFX
OpenSSL pkcs12 -export -in "$shortName.cer" -inkey "$shortName.rsa" -out $finalPfx -passin "pass:$certPasswordPlain" -passout "pass:$certPasswordPlain"
# Clean up
Remove-Item "$shortName.pem"
Remove-Item "$shortName.cer"
Remove-Item "$shortName.rsa"
Write-Host "$finalPfx created"
}
}
# Execute in cert folder
Fix-Certificates password
For a .net 4.8 WCF service I was getting this error because i hadn't (also) set the httpRuntime version.
Without the httpRuntime set i was getting the error whenever a secured .net tcp binding was present.
<system.web>
<compilation targetFramework="4.8" />
<httpRuntime targetFramework="4.8" />
<!--<customErrors mode="Off" />-->
</system.web>
I had already setup the required permissions in mmc.exe -> add snap-in -> certificates -> personal -> all tasks -> manage private keys -> add [computer name]\IIS_IUSRS (all my application pools are ApplicationPoolIdentity [default] - which is covered by IIS_IUSRS)
the PrivateKey and PublicKey properties are outdated beginning with NET 4.6. Please use:
Regards