4

I have an ECC private and a certificate file which includes the public key. I can get them in either PEM or DER formats.

I can read the certificate into an X509Certificate with this code:

var certbytes = File.ReadAllBytes("certificate.pem");
var cert = new X509Certificate2(certbytes);

But I'm unable to load the private key. I've tried this code:

var keyContent = File.ReadAllBytes("certificate_private_key.pem");
var key = CngKey.Import(keyContent, CngKeyBlobFormat.EccPrivateBlob);

It throws Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'An error occurred during encode or decode operation'.

I've also tried other values of the CngKeyBlobFormat parameter. The Import method fails with those as well.

openssl can read the file, and it outputs the following information about it:

openssl ec -in certificate_private_key.pem -text
read EC key
Private-Key: (256 bit)
priv:
    44:<cut>:68
pub:
    04:<cut>:13
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcC <cut here>
-----END EC PRIVATE KEY-----

Is there built-in API in .NET or .NET Core which can do this? Or are there 3rd party libraries that can do it, and how?

RasmusW
  • 3,355
  • 3
  • 28
  • 46
  • Thank you for the tip. `openssl pkcs8` can't understand the file, only `openssl ec`. I already have the file in both DER and PEM formats, neither work. Depending on the `CngKeyBlobFormat` parameter, the exception is either `An error occurred during encode or decode operation` or `The parameter is incorrect`. I've also tried converting the DER file to an explicit EC file with `openssl ec -in certificate_private_key.der -inform DER -param_enc explicit -out explicit.der -outform der`, and using `CngKeyBlobFormat.EccFullPrivateBlob` as the format, but it still fails when importing. – RasmusW May 15 '19 at 10:59
  • Ah, it is in X9.62 format in that case. Should have seen that from the `-----BEGIN EC PRIVATE KEY-----` header really. X9.62 private key is what is *within* a PKCS#8 file. – Maarten Bodewes May 15 '19 at 10:59
  • 1
    Try `openssl pkcs8 -topk8 -nocrypt -in oldfile.pem -out newfile.pem` - probably works as well with `-outform DER` I guess. – Maarten Bodewes May 15 '19 at 11:06
  • Any luck with that? First converting to binary PKCS#8 format and then using `Pkcs8PrivateBlob` should really work. – Maarten Bodewes May 15 '19 at 14:45
  • Yes, that works. (Sorry for the late reply - other work and sleep got in the way). Now the DER file can be read using `var privatekey = File.ReadAllBytes("newfile.der"); var privkey = CngKey.Import(privatekey, CngKeyBlobFormat.Pkcs8PrivateBlob); var privkeyecdsa = new ECDsaCng(privkey);` – RasmusW May 16 '19 at 06:35

1 Answers1

6

.NET Core 3.0 (currently in preview) has ECDsa.ImportECPrivateKey, AsymmetricAlgorithm.ImportPkcs8PrivateKey, and AsymmetricAlgorithm.ImportEncryptedPkcs8PrivateKey, (and RSA has RSAPublicKey and RSAPrivateKey) and for the current file (BEGIN EC PRIVATE KEY) you'd want the first one.

  • The good news of those methods is: they exist.
  • The bad news is: They're part of the next version, not the current one.
  • Good: The next version should be the current version soonishly.
  • Bad: They only understand BER/DER data, not PEM.

The last point means that you currently would have to find the base64 content between the -----BEGIN EC PRIVATE KEY-----\n and \n-----END EC PRIVATE KEY----- and de-base64-it, then pass that into the methods.


The only private key formats that I know that are supported by CNG import are PKCS8, encrypted PKCS8, and CNG private formats. To use CngKey.Import you'd first need to convert the keyfile to PKCS#8 then specify that the format is Pkcs8PrivateBlob, as suggested in the comments.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Are there any PEM decoding utilities in .NET or would you have to use e.g. Bouncy? – Maarten Bodewes May 15 '19 at 16:13
  • @MaartenBodewes There isn't one currently. Either hand-rolled or something like BouncyCastle's PEMParser are the way to go for now. Maybe next version I can get one in that's RFC 7468-compliant (e.g. ignore the headers section from RFC 1421). – bartonjs May 15 '19 at 16:22
  • 3
    Thank you, @bartonjs, this works. Using the DER encoded version of the original file that was described in the question, I can do `var ecdsa = ECDsa.Create(); ecdsa.ImportECPrivateKey(privatekey, out _); ecdsa.VerifyHash(dataToVerify, signature);` I'm pretty sure that .NET Core 3.0 will be out before this needs to go in production, so I think I'll go down this road. – RasmusW May 16 '19 at 06:42