jnm2
jnm2

Reputation: 8354

What's the simplest way to save and load a CngKey from a PFX file, given a SecureString password?

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

Answers (1)

jnm2
jnm2

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

Related Questions