Search code examples
opensslprivate-keyencryption-asymmetricder

Return to binary private key secp256k1 from hex DER


I want to get private_key.pem from foo_priv.key

$ openssl ecparam -genkey -name secp256k1 -rand /dev/urandom -out private_key.pem   
$ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32 |xxd -p -c 32 > foo_priv.key

I tried with

$ openssl ec -noout -text -inform DER -in foo_priv.key
read EC key
unable to load Key

$ openssl x509 -in foo_priv.key -inform DER -outform PEM
unable to load certificate
4486393452:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1220:
4486393452:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:386:Type=X509

$ cat foo_priv.key | xxd -r -p  > test.bin  
$ openssl ec -in test.bin -inform DER -pubin -text -noout                
read EC key
unable to load Key
4456304236:error:0D06B08E:asn1 encoding routines:ASN1_D2I_READ_BIO:not enough data:a_d2i_fp.c:247:

Solution

  • Your tail and head mechanisms to extract the private part of your key gives you just its bytes. However, OpenSSL needs more than that to store the private key information. If you are using OpenSSL 1.1.1, you can extract the required bytes using the -no_public option of the ec tool, like this:

    $ openssl ec -in private_key.pem -outform DER -no_public | xxd -p -c 32
    read EC key
    writing EC key
    302e020101042031792710388085aaec53a04072a231116dc102e63cccdf5e85
    ddd875cab6be6da00706052b8104000a
    

    Comparing this to you original approach, you can see that information was lost there:

    $ openssl ec -in private_key.pem -outform DER | tail -c +8 | head -c 32 | xxd -p -c 32
    read EC key
    writing EC key
    31792710388085aaec53a04072a231116dc102e63cccdf5e85ddd875cab6be6d
    

    In general, these files are stored in ASN.1 format which you can not easily modify without breaking it. To get a feel for the additional information that you removed, you could use

    $ openssl ec -in private_key.pem -no_public | openssl asn1parse
        0:d=0  hl=2 l=  46 cons: SEQUENCE          
        2:d=1  hl=2 l=   1 prim: INTEGER           :01
        5:d=1  hl=2 l=  32 prim: OCTET STRING      [HEX DUMP]:31792710388085AAEC53A04072A231116DC102E63CCCDF5E85DDD875CAB6BE6D
       39:d=1  hl=2 l=   7 cons: cont [ 0 ]        
       41:d=2  hl=2 l=   5 prim: OBJECT            :secp256k1
    

    Note the 32 bytes in the OCTET STRING section, which are exactly the ones you were extracting. But there is additional information about the curve type as well as a version number of the key format. This follows the private EC key format as outlined in RFC 5915: Elliptic Curve Private Key Structure, section 3. Without that information, OpenSSL can not reconstruct the private key.


    If you still want to reconstruct your private key file from the 32 key bytes after reading all this, you could achieve this with the (brittle) mechanism of prepending 302e0201010420 and appending a00706052b8104000a and converting the result to its binary form. Something like

    $ cat <(echo 302e0201010420) foo_priv.key <(echo a00706052b8104000a) | xxd -r -p | openssl ec -inform DER
    read EC key
    writing EC key
    -----BEGIN EC PRIVATE KEY-----
    MC4CAQEEIDF5JxA4gIWq7FOgQHKiMRFtwQLmPMzfXoXd2HXKtr5toAcGBSuBBAAK
    -----END EC PRIVATE KEY-----