-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot export certificate data even with X509KeyStorageFlags.Exportable #77590
Comments
Tagging subscribers to this area: @dotnet/area-system-security, @vcsjones Issue DetailsDescriptionCome from #26031
Reproduction StepsX509Certificate2 certificate = new X509Certificate2(pfxFileName, pfxPassword, X509KeyStorageFlags.Exportable); Expected behaviorexport parameters sucessfully Actual behaviorI received "The requested operation is not supported." Regression?No response Known WorkaroundsNo response Configurationdotnet standard 2.0.3 Other informationNo response
|
This is a duplicate of #26031 (comment). The would around should be to do an encrypted export, import, and export unencrypted. Stealing @bartonjs's solution... using (RSA tmp = RSA.Create())
using (RSA key = certificate.GetRSAPrivateKey())
{
cont string pwd = "TempPassword";
PbeParameters pbeParameters = new PbeParameters(PbeEncryptionAlgorithm.Aes128Cbc, HashAlgorithmName.SHA256, 1);
tmp.ImportPkcs8PrivateKey(key.ExportPkcs8PrivateKey(pwd, pbeParameters), pwd);
return tmp.ExportParameters(true);
} |
Thanks for your response, @vcsjones . I can't find class PbeParameters and method ImportPkcs8PrivateKey and ExportPkcs8PrivateKey on dotnet standard 2.0. How can I include them? We need to support user on windows powershell so that we can't upgrade our runtime version, do you have any advice for our case? |
Why do you need to use |
Hi @bartonjs , thanks for your response. We need to use many RSA parameters, including rsaParameters.Modulus, rsaParameters.Exponent, rsaParameters.DP and so on in PFX file, so that we can pass data to our backend . You can find details on our open source repo if you are interested: https://github.com/Azure/azure-powershell/blob/a440db085c71a9ceae797bddcf6450dbc01835db/src/KeyVault/KeyVault/Models/PfxWebKeyConverter.cs#L125-L138. Please feel free to let me know if you need further information. |
OK, so you need the parameters because you're exporting the private key in a different format. That makes sense. The best advice I have for .NET Standard 2.0 or .NET Framework is to do something like private const CngExportPolicies FullyExportable = CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport;
private static readonly s_fullyExportableProperty = new CngProperty(
"Export Policy",
BitConverter.GetBytes((int)FullyExportable),
CngPropertyOptions.Persist));
...
X509Certificate2 cert = new X509Certificate2(pfx, pwd, flags);
using (RSA rsa = cert.GetRSAPrivateKey())
{
if (rsa != null)
{
if (rsa is RSACng rsaCng)
{
rsaCng.Key.SetProperty(s_fullyExportableProperty);
}
return CreateTrack2SdkJWK(rsa, extraInfo);
}
}
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())
{
if (ecdsa != null)
{
if (ecdsa is ECDsaCng ecdsaCng)
{
ecdsaCng.Key.SetProperty(s_fullyExportableProperty);
}
return CreateTrack2SdkJWK(ecdsa, extraInfo);
}
}
... Note also that X509Certificate2.PrivateKey is deprecated, and will never return an ECDSA key, so |
Hi @vcsjones , thanks for reminding me about the deprecation of X509Certificate2.PrivateKey. Seems like CngProperty is not available on dotnet standard 2.0, could you help? |
It is, but it's part of the System.Security.Cryptography.Cng package that you might have to reference explicitly (though if you can see RSACng/ECDsaCng you're already referencing it). I do have a syntax error in my snippet, the static-readonly field doesn't have a type; so you could also be seeing that? |
I think the correct snippet should be private const CngExportPolicies FullyExportable = CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport;
private static readonly CngProperty s_fullyExportableProperty = new CngProperty("ExportPolicy",
BitConverter.GetBytes((int)FullyExportable),
CngPropertyOptions.Persist);
...
var rsaKey = certificate.GetRSAPrivateKey();
if (rsaKey != null)
{
if(rsaKey is RSACng rsaCng)
{
rsaCng.Key.SetProperty(s_fullyExportableProperty);
}
return CreateTrack2SdkJWK(rsaKey, extraInfo);
} I got an exception when code goes to |
Hi, I am having the same issue. Tried with 6 and .net framework 4.7.2. As soon as you export the certificate as bytes, the .ExportParameters(true) will fail.
|
No, it's the import, not the export. The certificate instance that you exported from can still export its key, because it's an ephemeral key and ephemeral keys are plaintext exportable. The property sets you did are on the ephemeral key, don't get saved into the PFX, and thus have been discarded when the key is re-imported. While I recall being surprised that the exportable property could be changed between import and first use, it looks like I'm now surprised that it doesn't (as just tested in net48 on Win11). So now looks like I get to fall back on answers I've given on StackOverflow. Namely, https://stackoverflow.com/questions/57269726/x509certificate2-import-with-ncrypt-allow-plaintext-export-flag/57330499#57330499 |
Sorry, I got lost. How can I export certificate data in my case? |
@BethanyZhou The easiest way is to fully move onto .NET Core/.NET 5+ and use the new encrypted exports. Failing that, you'd need to copy/paste all of the code from my StackOverflow answer (https://stackoverflow.com/questions/57269726/x509certificate2-import-with-ncrypt-allow-plaintext-export-flag/57330499#57330499), which will overwrite the key with a plaintext-exportable version of itself (and then calls to |
Thanks for your patient. I use following snippet to let certificate support exportParameters(true) on dotnet standard 2.0 byte[] certificateBytes = File.ReadAllBytes(fileName);
// ImportExportable() is from your SO answer
X509Certificate2 certificate = ImportExportable(certificateBytes, password, machineScope)
...
var rsa= certificate.GetRSAPrivateKey();
...
RSAParameters rsaParameters = rsa.ExportParameters(true);
... Current solution works for me now. Just want to confirm two points:
|
No. This issue is specific to Windows only. The work around makes use of APIs that are only on Windows and will fail on other platforms. You need to avoid the work around when not on Windows.
Yes. The issue applies to both RSA and EC keys. Probably DSA keys too, but DSA is largely irrelevant. Since Jeremy's fix uses PKCS8 at first glance the work around should work for all keys types. |
Much thanks for your quick responses @vcsjones . Could you help me understand the basic principles of Jeremy's fix? Specially, what is |
The general problem is, when the PFX was imported, Windows likes to put it in a situation where the private CNG key can only be exported encrypted. So the work around for this in .NET Core / .NET is, export the key encrypted. Then import the encrypted key again, but this time with a special bit on it that means "it's okay to export this key unencrypted". Then we can finally export the key unencrypted. The problem with .NET Standard 2.0, is there isn't an API than can export a CNG encrypted. The So Jeremy basically implemented the PKCS8 export and import again. .NET relies heavily on CNG, a set of native APIs available only available on Windows. The work around starts by constructing an Then it uses the WinCrypt Once it has a So now we have a the private key exported. But it's encrypted, so we can't do what we want with it, yet. The next step is to import it again, expect this time, specifically saying "it's fine to export this unencrypted". This is done in This basically does everything in reverse. It uses When the private key gets re-imported, it does so with the same name as the unexportable key. So it replaces the unexportable key with an exportable one. After it has been replaced with the same name, the |
Description
Come from #26031
Reproduction Steps
X509Certificate2 certificate = new X509Certificate2(pfxFileName, pfxPassword, X509KeyStorageFlags.Exportable);
var key = certificate.PrivateKey as RSA;
RSAParameters rSAParameters = key.ExportParameters(true);
Expected behavior
export parameters sucessfully
Actual behavior
I received "The requested operation is not supported."
Regression?
No response
Known Workarounds
No response
Configuration
dotnet standard 2.0.3
Other information
No response
The text was updated successfully, but these errors were encountered: