Public CngKey from an X509Certificate2 in the CertStore

Mar 28, 2014 at 11:32 PM
Hey guys,

So using OpenSSL I've managed to create myself an X509 certificate with ECC keys, signed with ECDSA.
  1. openssl ecparam -out ec_key.pem -name prime256v1 -genkey
  2. openssl req -new -key ec_key.pem -x509 -nodes -days 365 -out cert.cer
  3. openssl pkcs12 -export -in cert.cer -inkey ec_key.pem -out cert.pfx
I then imported said certificate in to the cert store, and using C# code I can successfully retrieve an instance of X509Certificate2. Where I initially got stuck was how do I get these keys in to stuff like CngKey and ultimately ECDiffieHellmanCng. The GetGngPrivateKey extension method provided in this library works perfectly to give me a private/public key pair, but I cannot for the life of me work out how to get a CngKey for a certificate for which I only have a public key.

Why is there no GetCngPublicKey? Is there something I'm missing?
Feb 23, 2016 at 4:37 PM
In the .NET Framework 4.6.1 we have GetECDsaPublicKey() and GetECDsaPrivateKey(). If what you're after is ECDH, though; it's a little trickier. You can do something like the following:
ECDsa ecdsa = cert.GetECDsaPublicKey();
ECDsaCng ecdsaCng = ecdsa as ECDsaCng;
ECDiffieHellmanPublicKey ecdhPublicKey = null;

if (ecdsaCng != null)
    byte[] blob = ecdsaCng.Key.Export(CngKeyBlobFormat.EccPublicBlob);

    // Blob should start with 0x45, 0x43, 0x53 (ECDSA).
    // We want to change it to 0x45, 0x43, 0x4B (ECDH).
    // If it happens to already say 0x4B, that's just fine!
    Debug.Assert(blob[2] == 0x53 || blob[2] == 0x4B);
    blob[2] = 0x4B;

    ecdhPublicKey = ECDiffieHellmanCngPublicKey.FromByteArray(blob, CngKeyBlobFormat.EccPublicBlob);

if (ecdhPublicKey == null)
    throw new InvalidOperationException($"Cannot convert an ECDsa of type {ecdsa.GetType().FullName}");
Though I'm not sure how long-term stable that is... which is partly why it hasn't made it into the .NET Framework yet :).