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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With