Search code examples
erlangpublic-keypemecdsa

(Erlang) Got error while parsing ecsda public key pem file


I made private and public keys files as

openssl ecparam -name secp256k1 -genkey -out gen.pem

openssl ec -in gen.pem -pubout -out gen.pub

then I checked .pub file

openssl pkey -in gen.pub  -pubin -text -noout

Public-Key: (256 bit)
pub: 
    04:fc:de:4f:2a:77:bd:c4:f2:74:2b:ba:b5:fc:85:
    e4:aa:96:a7:8e:86:14:bc:0d:fa:8e:d2:dd:50:3c:
    5d:fa:f4:07:f4:17:80:49:06:19:0c:72:03:63:4e:
    07:37:e9:10:64:c8:33:a4:a3:7e:26:d8:df:79:21:
    d7:2d:a6:01:80
ASN1 OID: secp256k1

After that start erl

erl
Erlang/OTP 18 [erts-7.2] [source-e6dd627] [64-bit] [smp:3:3] [async-threads:10] [hipe] [kernel-poll:false]

After execution of commands

1>rr(public_key),
  {ok,RawData} = file:read_file("gen.pub"),
  Decoded = public_key:pem_decode(RawData),
  [public_key:pem_entry_decode(X) || X <- Decoded ].

I got exception

** exception error: no match of right hand side value 
                    {error,{asn1,{invalid_length,1}}}
     in function  public_key:der_decode/2 (public_key.erl, line 229)

I know that there are workarounds, but I'm wondering if I do something wrong or there are errors in public key modules ?

UPD 1.

When we look at result

{ok,#'SubjectPublicKeyInfo'{algorithm = #'AlgorithmIdentifier'{algorithm = {1,2,840,10045,2,1},
                                                           parameters = <<6,5,43,129,4,0,10>>},
                        subjectPublicKey = <<4,241,200,19,168,25,25,81,43,216,
                                             89,201,37,62,66,39,166,231,161,98,
                                             223,133,119,12,...>>}}

we can see next :

{1,2,840,10045,2,1} is oid of ecPublicKey http://oid-info.com/get/1.2.840.10045.2.1

parameters = <<6,5,43,129,4,0,10>>

'OTP-PUB-KEY':decode('EcpkParameters',<<6,5,43,129,4,0,10>>).

gives us

{ok,{namedCurve,{1,3,132,0,10}}}

and

pubkey_cert_records:namedCurves({1,3,132,0,10}).

gives us our source curve - secp256k1, and

 subjectPublicKey = <<4,241,200,19,168,25,25,81,43,216,
                    89,201,37,62,66,39,166,231,161,98,
                    223,133,119,12,...>>

is public key itself. But this is workaround how I said before.


Solution

  • I tried your steps and it worked for me straight away:

    g@crayon2:~/test % openssl ecparam -name secp256k1 -genkey -out gen.pem
    
    g@crayon2:~/test % openssl ec -in gen.pem -pubout -out gen.pub
    read EC key
    writing EC key
    
    g@crayon2:~/test % openssl pkey -in gen.pub -pubin -text -noout
    Public-Key: (256 bit)
    pub: 
        04:f1:c8:13:a8:19:19:51:2b:d8:59:c9:25:3e:42:
        27:a6:e7:a1:62:df:85:77:0c:ef:f7:0e:0e:19:93:
        df:e6:f5:42:66:7e:ee:02:07:76:85:19:a4:a8:2d:
        03:11:73:0c:b2:d4:4c:c7:0e:42:d3:30:b5:51:e3:
        97:45:f8:b5:6a
    ASN1 OID: secp256k1
    
    g@crayon2:~/test % erl
    Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
    
    Eshell V7.2.1  (abort with ^G)
    
    1> rr(public_key).
    ['AAControls','ACClearAttrs','AccessDescription',
     'Algorithm','AlgorithmIdentifier',
     'AlgorithmIdentifierPKCS-10','AlgorithmIdentifierPKCS-8',
     'AlgorithmIdentifierPKCS5v2-0','AlgorithmIdentifierPKSC-7',
     'AlgorithmNull','AnotherName','AttCertValidityPeriod',
     'Attribute','AttributeCertificate',
     'AttributeCertificateInfo','AttributePKCS-10',
     'AttributePKCS-7','AttributeTypeAndValue',
     'Attributes_SETOF',
     'Attributes_SETOF_valuesWithContext_SETOF',
     'AuthorityKeyIdentifier','BasicConstraints',
     'BuiltInDomainDefinedAttribute','BuiltInStandardAttributes',
     'Certificate','CertificateList','CertificationRequest',
     'CertificationRequestInfo',
     'CertificationRequestInfo_attributes_SETOF'|...]
    
    2> {ok,Data}=file:read_file("gen.pub").
    {ok,<<"-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE8cgTqBkZUSvYWcklPkInpuehYt+Fdwzv\n9w4OGZPf5vVCZn7u"...>>}
    
    3> Dec=public_key:pem_decode(Data).
    [#'SubjectPublicKeyInfo'{algorithm = <<48,86,48,16,6,7,42,
                                           134,72,206,61,2,1,
                                           6,5,43,129,4,0,10,
                                           3,66,0,4,241,200,
                                           ...>>,
                             subjectPublicKey = not_encrypted}]
    

    My OpenSSL version:

    g@crayon2:~/test % openssl version
    OpenSSL 1.0.1p-freebsd 9 Jul 2015
    

    System is FreeBSD 10.2-RELEASE. Erlang version is in the shell output above. The gen.pub key that it generated for me (if you want to try to load it) is:

    g@crayon2:~/test % cat gen.pub
    -----BEGIN PUBLIC KEY-----
    MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE8cgTqBkZUSvYWcklPkInpuehYt+Fdwzv
    9w4OGZPf5vVCZn7uAgd2hRmkqC0DEXMMstRMxw5C0zC1UeOXRfi1ag==
    -----END PUBLIC KEY-----
    

    Do you maybe want to paste your key so that I can try to load it?

    Edit:

    Sorry, I missed the last step. This seems to be a bug in the OTP code. The der_decode/2 function is called with KeyType set to ECPoint, see the pem_entry_decode/1 function. But der_decode/2 expects Asn1Type. Calling the 'OTP-PUB-KEY' module directly seems to be working:

    8> Bin = hd(Dec)#'SubjectPublicKeyInfo'.algorithm.
    32> 'OTP-PUB-KEY':decode('SubjectPublicKeyInfo', Bin).
    {ok,#'SubjectPublicKeyInfo'{algorithm = #'AlgorithmIdentifier'{algorithm = {1,
                                                                                2,840,10045,2,1},
                                                                   parameters = <<6,5,43,129,4,0,10>>},
                                subjectPublicKey = <<4,241,200,19,168,25,25,81,43,216,
                                                     89,201,37,62,66,39,166,231,161,98,
                                                     223,133,119,12,...>>}}
    

    However I am not sure if it returns anything sensible. Maybe worth raising a bug in OTP with the code that you posted in this question as steps to reproduce. Then at least someone competent would verify why it doesn't work. I am sure the steps you followed are fine and the types returned and passed to further functions are OK as far as the documentation is concerned.