Extract raw (octet) private key from PKSC8 EC with OpenSSL CLI

Viewed 411

Is there a way to extract a raw/octet private key from EC PKCS#8?

Here is an example file (private.pem):

-----BEGIN PRIVATE KEY-----
MIH3AgEAMBAGByqGSM49AgEGBSuBBAAjBIHfMIHcAgEBBEIA1tZ6QFxLWMJyp7vO
pDNj2Wbu2or9QaxJ3ehpi1qaVF/otjrx3Q/AMso4W9a6YQ4heDCH1rned0C2VdyK
f8n0bcugBwYFK4EEACOhgYkDgYYABAGi+uY7a67sTbwOAK/+aNUewZ3haLUV4INx
Fnk6E1iNee0YvyQ5XJrowSWjW6YfBTjYKKKYeaV5s2QTbzhvgvqL3gD1EgXNbfB9
27lO2Luy0EYxOPLxtBhCEgGnlkzHVwZaKK3+qJpR+D6oVe7l0hgBfoIYzkJgpQPC
1lblIG8qAtQEGg==
-----END PRIVATE KEY-----

If I run:

# openssl ec -in private.pem -text -noout

I am getting:

read EC key
Private-Key: (521 bit)
priv:
    00:d6:d6:7a:40:5c:4b:58:c2:72:a7:bb:ce:a4:33:
    63:d9:66:ee:da:8a:fd:41:ac:49:dd:e8:69:8b:5a:
    9a:54:5f:e8:b6:3a:f1:dd:0f:c0:32:ca:38:5b:d6:
    ba:61:0e:21:78:30:87:d6:b9:de:77:40:b6:55:dc:
    8a:7f:c9:f4:6d:cb
pub:
    04:01:a2:fa:e6:3b:6b:ae:ec:4d:bc:0e:00:af:fe:
    68:d5:1e:c1:9d:e1:68:b5:15:e0:83:71:16:79:3a:
    13:58:8d:79:ed:18:bf:24:39:5c:9a:e8:c1:25:a3:
    5b:a6:1f:05:38:d8:28:a2:98:79:a5:79:b3:64:13:
    6f:38:6f:82:fa:8b:de:00:f5:12:05:cd:6d:f0:7d:
    db:b9:4e:d8:bb:b2:d0:46:31:38:f2:f1:b4:18:42:
    12:01:a7:96:4c:c7:57:06:5a:28:ad:fe:a8:9a:51:
    f8:3e:a8:55:ee:e5:d2:18:01:7e:82:18:ce:42:60:
    a5:03:c2:d6:56:e5:20:6f:2a:02:d4:04:1a
ASN1 OID: secp521r1
NIST CURVE: P-521

I need the "priv" value in a binary format or at least as a string in hex: 00d6d67a405c4b58c272a7bbcea43363d966eeda8afd41ac49dde8698b5a9a545fe8b63af1dd0fc032ca385bd6ba610e21783087d6b9de7740b655dc8a7fc9f46dcb so I can convert it to binary with xxd.

How can I do that?

I can always do something like this:

openssl ec -in private.pem -text -noout | tr '\n' ' ' | grep -Po '(?<=priv:).*(?=pub:)' | tr -cd '[0-9a-f]'

but it's a terrible approach, imho.

1 Answers

Still not pretty but an alternative to your solution can be achieved with asn1parse. Inspecting the output for the entire keyfile:

$ openssl asn1parse -in private.pem
    0:d=0  hl=3 l= 247 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            :secp521r1
   24:d=1  hl=3 l= 223 prim: OCTET STRING      [HEX DUMP]:3081DC020101044200D6D67A405C4B58C272A7BBCEA43363D966EEDA8AFD41AC49DDE8698B5A9A545FE8B63AF1DD0FC032CA385BD6BA610E21783087D6B9DE7740B655DC8A7FC9F46DCBA00706052B81040023A18189038186000401A2FAE63B6BAEEC4DBC0E00AFFE68D51EC19DE168B515E0837116793A13588D79ED18BF24395C9AE8C125A35BA61F0538D828A29879A579B364136F386F82FA8BDE00F51205CD6DF07DDBB94ED8BBB2D0463138F2F1B418421201A7964CC757065A28ADFEA89A51F83EA855EEE5D218017E8218CE4260A503C2D656E5206F2A02D4041A

The actual key(pair) information starts at offset 24, an octet string that itself is an ASN.1 object. Zooming in to that location:

$ openssl asn1parse -in private.pem -strparse 24
    0:d=0  hl=3 l= 220 cons: SEQUENCE          
    3:d=1  hl=2 l=   1 prim: INTEGER           :01
    6:d=1  hl=2 l=  66 prim: OCTET STRING      [HEX DUMP]:00D6D67A405C4B58C272A7BBCEA43363D966EEDA8AFD41AC49DDE8698B5A9A545FE8B63AF1DD0FC032CA385BD6BA610E21783087D6B9DE7740B655DC8A7FC9F46DCB
   74:d=1  hl=2 l=   7 cons: cont [ 0 ]        
   76:d=2  hl=2 l=   5 prim: OBJECT            :secp521r1
   83:d=1  hl=3 l= 137 cons: cont [ 1 ]        
   86:d=2  hl=3 l= 134 prim: BIT STRING        

The OCTET STRING is the private component and can be extracted and converted to binary using the awk and xxd tools as follows:

$ openssl asn1parse -in private.pem -strparse 24 | awk -F ":" '/OCTET STRING/ {print $4}' | xxd -r -p > private.der

After writing this initial approach, I realized that the ec tool in combination with a simpler ans1parse invocation works as well:

$ openssl ec -in private.pem | openssl asn1parse |  awk -F ":" '/OCTET STRING/ {print $4}' | xxd -r -p > private.der
Related