Why does PEM Base64 created by `openssl pgen` not match the DER file passed in?
In experimenting with openssl
on the Linux command line with elliptic curve secp256k1
I encountered a strange situation where on converting a DER private key file to PEM format using openssl pgen
the Base64 in the PEM file does not match the original DER file.
The DER private key file priv.der
is generated with :
ubuntu3@ubuntu3:~/OpenSSL Test$ openssl ecparam -name secp256k1 -genkey -noout -outform DER -out priv.der
(recreate present example with :
ubuntu3@ubuntu3:~/OpenSSL Test$ echo "3074020101042090f4a50b8dd639597c01b56bb97eb0a5063ec56326da72c7acbd303354df393ba00706052b8104000aa14403420004cb452cb3662eb70920eb8532040f807853928a5d13c4331e822334a60aa04ea115e7ce0c8648416e6bc22ef33f55d75057442b2a66c5c95ef652df9e6a306c57" | xxd -r -ps > priv.der
)
which looks like the following under asn1parse
:
ubuntu3@ubuntu3:~/OpenSSL Test$ openssl asn1parse -inform DER -in priv.der
0:d=0 hl=2 l= 116 cons: SEQUENCE
2:d=1 hl=2 l= 1 prim: INTEGER :01
5:d=1 hl=2 l= 32 prim: OCTET STRING [HEX DUMP]:90F4A50B8DD639597C01B56BB97EB0A5063EC56326DA72C7ACBD303354DF393B
39:d=1 hl=2 l= 7 cons: cont [ 0 ]
41:d=2 hl=2 l= 5 prim: OBJECT :secp256k1
48:d=1 hl=2 l= 68 cons: cont [ 1 ]
50:d=2 hl=2 l= 66 prim: BIT STRING
The conversion of the DER file to a PEM file using openssl pkey
:
ubuntu3@ubuntu3:~/OpenSSL Test$ openssl pkey -inform DER -in priv.der > priv.pem
ubuntu3@ubuntu3:~/OpenSSL Test$ cat priv.pem
-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgkPSlC43WOVl8AbVruX6w
pQY+xWMm2nLHrL0wM1TfOTuhRANCAATLRSyzZi63CSDrhTIED4B4U5KKXRPEMx6C
IzSmCqBOoRXnzgyGSEFua8Iu8z9V11BXRCsqZsXJXvZS355qMGxX
-----END PRIVATE KEY-----
Decoding the Base64 text in the PEM file :
ubuntu3@ubuntu3:~/OpenSSL Test$ echo "MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgkPSlC43WOVl8AbVruX6wpQY+xWMm2nLHrL0wM1TfOTuhRANCAATLRSyzZi63CSDrhTIED4B4U5KKXRPEMx6CIzSmCqBOoRXnzgyGSEFua8Iu8z9V11BXRCsqZsXJXvZS355qMGxX" | base64 --decode > priv2.der
Comparing priv2.der with priv.der :
ubuntu3@ubuntu3:~/OpenSSL Test$ ll priv.der priv2.der
-rw-r--r-- 1 ubuntu3 ubuntu3 135 Jan 29 17:00 priv2.der
-rw-r--r-- 1 ubuntu3 ubuntu3 118 Jan 29 16:42 priv.der
=> file sizes are different and ASN.1 structure is different :
ubuntu3@ubuntu3:~/OpenSSL Test$ openssl asn1parse -inform DER -in priv2.der
0:d=0 hl=3 l= 132 cons: SEQUENCE
3:d=1 hl=2 l= 1 prim: INTEGER :00
6:d=1 hl=2 l= 16 cons: SEQUENCE
8:d=2 hl=2 l= 7 prim: OBJECT :id-ecPublicKey
17:d=2 hl=2 l= 5 prim: OBJECT :secp256k1
24:d=1 hl=2 l= 109 prim: OCTET STRING [HEX DUMP]:306B020101042090F4A50B8DD639597C01B56BB97EB0A5063EC56326DA72C7ACBD303354DF393BA14403420004CB452CB3662EB70920EB8532040F807853928A5D13C4331E822334A60AA04EA115E7CE0C8648416E6BC22EF33F55D75057442B2A66C5C95EF652DF9E6A306C57
The ASN.1 data for priv2.der
contains 2 OID's, whereas the original priv.der
only contains only 1 OID.
So why did the openssl pkey
command not just Base64 encode the priv.der
file passed in and place that between the
-----BEGIN PRIVATE KEY-----
-----END PRIVATE KEY-----
lines in the priv.pem
file ?
Why did it encode to Base64 a different DER structure than priv.der
?
The byte by byte breakdowns of priv.der
and priv2.der
are shown below :
Breakdown of bytes of DER file priv.der
---------------------------------------
30 sequence type
74 length = 116 bytes
02 integer type
01
01
04 octet string type
20 length = 32 bytes
90f4a50b8dd639597c01b56bb97eb0a5063ec56326da72c7acbd303354df393b private key
a0
07 length = 7 bytes
06 OID type
05 length = 5 bytes
2b8104000a OID for secp256k1 = 1.3.132.0.10
a1
44 length = 68 bytes
03 bit string type
42 length = 66 bytes
00 no of unused bits added to right (to make a multiple of 8 bits)
04 pubkey prefix
cb452cb3662eb70920eb8532040f807853928a5d13c4331e822334a60aa04ea1 pubkey x
15e7ce0c8648416e6bc22ef33f55d75057442b2a66c5c95ef652df9e6a306c57 pubkey y
Breakdown of bytes of DER file priv2.der
----------------------------------------
30 sequence type
81
84 length = 132 bytes
02 integer type
01
00
30 sequence type
10 length = 16 bytes (sum of 2 OID lengths below)
06 OID type
07 length = 7 bytes
2a8648ce3d0201 object identifier = 1.2.840.10045.2.1 ecPublicKey (ANSI X9.62 public key type)
06 OID type
05 length = 5 bytes
2b8104000a object identifier = 1.3.132.0.10 secp256k1 (SECG (Certicom) named elliptic curve)
04 octet string type
6d length = 109 bytes
30 sequence type
6b length = 107 bytes
02 integer type
01
01
04 octet string type
20 length = 32 bytes
90f4a50b8dd639597c01b56bb97eb0a5063ec56326da72c7acbd303354df393b private key
a1
44 length = 68 bytes
03 bit string type
42 length = 66 bytes
00 no of unused bits added to right (to make a multiple of 8 bits)
04 pubkey_prefix
cb452cb3662eb70920eb8532040f807853928a5d13c4331e822334a60aa04ea1 pubkey_x
15e7ce0c8648416e6bc22ef33f55d75057442b2a66c5c95ef652df9e6a306c57 pubkey_y