Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I load an OpenSSL ECDSA key into C#?

I need to load an OpenSSL private key into a C# based application.

The commands I used to generate the key are:

$ openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem
$ openssl ec -in eckey.pem
read EC key
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIMiuwhV+yI0od5E5pSU6ZGuUcflskYD4urONi1g3G7EPoAoGCCqGSM49
AwEHoUQDQgAEe+C/M6u171u5CcL2SQKuFEb+OIEibjw1rx+S5LK4gNNePlDV/bqu
Ofjwc5JDqXA07shbfHNIPUn6Hum7qdiUKg==
-----END EC PRIVATE KEY-----

openssl pkcs8 -topk8 -nocrypt -in eckey.pem -out ec2.pem
cat ec2.pem
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgyK7CFX7IjSh3kTml
JTpka5Rx+WyRgPi6s42LWDcbsQ+hRANCAAR74L8zq7XvW7kJwvZJAq4URv44gSJu
PDWvH5LksriA014+UNX9uq45+PBzkkOpcDTuyFt8c0g9Sfoe6bup2JQq
-----END PRIVATE KEY-----

The C# code I'm using

   string privKeyPKCS8 = @"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgyK7CFX7IjSh3kTmlJTpka5Rx+WyRgPi6s42LWDcbsQ+hRANCAAR74L8zq7XvW7kJwvZJAq4URv44gSJuPDWvH5LksriA014+UNX9uq45+PBzkkOpcDTuyFt8c0g9Sfoe6bup2JQq";
  byte[] privKeyBytes8 = Convert.FromBase64String(privKeyPKCS8);//Encoding.UTF8.GetBytes(privKeyEcc);

 var pubCNG = CngKey.Import(privKeyBytes, CngKeyBlobFormat.EccPrivateBlob);

What is the correct way to load the EC based key into CngKey?


EDIT

The key within the base 64 encoding adheres to the following format:

ECPrivateKey ::= SEQUENCE {
    version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
    privateKey     OCTET STRING,
    parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
    publicKey  [1] BIT STRING OPTIONAL
}

Using the secp256r1 curve and a public key in uncompressed point format.

like image 795
makerofthings7 Avatar asked Oct 17 '22 14:10

makerofthings7


1 Answers

Your key PEM / ASCII armor (the header, footer and base 64) is encoded using the format described in RFC 5915: Elliptic Curve Private Key Structure. This was first specified by Standards for Efficient Cryptography Group (SECG) which is also where the named curve secp256r1 got it's name from. The curve is supported by Microsoft CNG.

ECPrivateKey ::= SEQUENCE {
    version        INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
    privateKey     OCTET STRING,
    parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
    publicKey  [1] BIT STRING OPTIONAL
}

You first need to convert this "raw" EC private key structure to a PKCS#8 structure using the command in the (updated) question:

openssl pkcs8 -topk8 -nocrypt -in eckey.pem -out ec2.pem

to get:

SEQUENCE(3 elem)
  INTEGER 0 # version of PKCS#8 structure
  SEQUENCE (2 elem)
    OBJECT IDENTIFIER 1.2.840.10045.2.1 # it's an EC key)
    OBJECT IDENTIFIER 1.2.840.10045.3.1.7 # it's secp256r1
  OCTET STRING (1 elem) # the key structure
    SEQUENCE (3 elem)
      INTEGER 1 # version 
      OCTET STRING (32 byte) # private key value (removed)
      [1] (1 elem)
        BIT STRING (520 bit) # public key value (removed)

The resulting structure isn't that different, what you are seeing is actually the same as the initial structure. Except that the PKCS#8 structure has an Object Identifier (OID) for designating the key type and an OID for the curve itself in front while your key has just the OID afterwards as parameter. Both also carry the (optional) public key value in the BIT STRING.

So the decoder recognizes this type and returns the EC private key.


The EccPrivateBlob that you were using requires a Microsoft specific structure. See also my question here. It won't work with the structures mentioned above.

like image 158
Maarten Bodewes Avatar answered Oct 21 '22 08:10

Maarten Bodewes