Reputation: 8354
Given a newly generated exportable public/private key pair CngKey
and a SecureString
, what API calls do I need to make to create a PFX file containing the public/private contents secured with the password? And given a PFX file and a SecureString
password, what API calls are needed to load the contents into a CngKey
?
It looks like the base class library does not provide this, but I'd prefer p/invoke over taking a dependency on a third party library. I also want to avoid storing the private key decrypted in memory even momentarily if there is a way (similar to SecureString
) to keep it secure between the PFX and the CngKey
.
While trying to wrap my head around these concepts, all the examples I can find involve self-signed certificates. This isn't a certificate, just a public/private blob I want to password protect. I don't want to import the blob into a store, I want to use it from a PFX file.
This is as far as I get on my own:
using (var key = CngKey.Create(CngAlgorithm.ECDsaP521, null, new CngKeyCreationParameters { ExportPolicy = CngExportPolicies.AllowExport, KeyUsage = CngKeyUsages.Signing }))
using (var algorithm = new ECDsaCng(key))
{
var container = new X509Certificate2();
container.PrivateKey = algorithm; // Exception: m_safeCertContext is an invalid handle
var pfxFileContents = container.Export(X509ContentType.Pkcs12, password);
using (var pfxFile = File.Create("text.pfx"))
pfxFile.Write(pfxFileContents, 0, pfxFileContents.Length);
}
Upvotes: 3
Views: 1692
Reputation: 8354
The best solution in this case is to reference Security.Cryptography.dll. This open source library is stable and perhaps would have been merged into the .NET Framework if I understand correctly; the dev behind it is from the team.
To store a CngKey
in a SecureString
protected PFX stream:
// NB! X509Certificate2 does not implement IDisposable in .NET 4.5.2, but it does in 4.6.1.
using (var cert = key.CreateSelfSignedCertificate(new X509CertificateCreationParameters(new X500DistinguishedName("CN=Example Name"))
{
StartTime = DateTime.Now,
EndTime = DateTime.MaxValue,
TakeOwnershipOfKey = true,
SignatureAlgorithm = X509CertificateSignatureAlgorithm.ECDsaSha256 // Manually match your CngKey type (RSA/ECDSA)
}))
{
File.WriteAllBytes(pfxPath, cert.Export(X509ContentType.Pkcs12, pfxPassword));
}
To load a CngKey
from a SecureString
protected PFX stream:
// NB! X509Certificate2 does not implement IDisposable in .NET 4.5.2, but it does in 4.6.1.
using (var cert = new X509Certificate2(pfxPath, pfxPassword))
return cert.GetCngPrivateKey();
Upvotes: 3