[erlang-questions] public_key & OpenSSL format weirdness

zxq9 <>
Thu Feb 26 02:56:32 CET 2015


I have encountered a strange issue with the public_key and crypto modules 
regarding RSA key formats generated by OpenSSL.

If I generate a private key in PEM format, public_key:pem_entry_decode/1 will 
not return an #'RSAPrivateKey'{} record, but if I generate the private key in 
DER format public_key:der_decode('RSAPrivateKey', KeyFile) will return the 
correct #'RSAPrivateKey'{}.

Conversely, if I generate the public key in DER format 
public_key:der_decode('RSAPublicKey', PubFile) will give me an asn1 error, but 
if I generate the public key in PEM format public_key:pem_entry_decode/1 
returns the correct #'RSAPublicKey'{} record.

If I go one step further and decode the public key from a PEM file, then use 
public_key:der_encode/2 and write the resulting binary to a file as DER, then 
I can use the Erlang-generated DER of the public key with no problem.

The I'm using "OpenSSL 1.0.1f 6 Jan 2014" on Linux.

The following is a screen dump illustrating the problem, then another dump of 
some code illustrating the workaround:

%%%%%%%%%%%%%%%%
:~/Code/erlang/keytest$ cat keygen-der 
#! /bin/bash

prefix=${1:?"Cannot proceed without a file prefix."}

keyfile="$prefix"".key.der"
pubfile="$prefix"".pub.der"

openssl genpkey \
    -algorithm rsa \
    -out $keyfile \
    -outform DER \
    -pkeyopt rsa_keygen_bits:8192

openssl rsa \
    -inform DER \
    -in $keyfile \
    -outform DER \
    -pubout \
    -out $pubfile
:~/Code/erlang/keytest$ cat keygen-pem 
#! /bin/bash

prefix=${1:?"Cannot proceed without a file prefix."}

keyfile="$prefix"".key.pem"
pubfile="$prefix"".pub.pem"

openssl genpkey \
    -algorithm rsa \
    -out $keyfile \
    -outform PEM \
    -pkeyopt rsa_keygen_bits:8192

openssl rsa \
    -inform PEM \
    -in $keyfile \
    -outform PEM \
    -pubout \
    -out $pubfile
:~/Code/erlang/keytest$ ./keygen-der rsa1
.....................................................................................................................................................................................................................................................................................................................
++
.............................................................................................................................................................................................................................................................................
++
writing RSA key
:~/Code/erlang/keytest$ ./keygen-pem rsa2
..........................................................................................................................................................................................................................................................................................................................................................................................................................
++
........................................................++
writing RSA key
:~/Code/erlang/keytest$ erl
Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:2:2] [async-threads:10] 
[kernel-poll:false]

Eshell V6.3  (abort with ^G)
1> rr("/usr/lib/erlang/lib/public_key-0.22.1/include/public_key.hrl").
['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, DerKeyBin} = file:read_file("rsa1.key.der").
{ok,<<48,130,18,41,2,1,0,2,130,4,1,0,189,228,172,204,135,
      175,97,84,205,254,58,197,47,41,196,...>>}
3> DerKey = public_key:der_decode('RSAPrivateKey', DerKeyBin).
#'RSAPrivateKey'{version = 'two-prime',
                 modulus = 8090848...,
                 publicExponent = 65537,
                 privateExponent = 7670114...,
                 prime1 = 9729823...,
                 prime2 = 8315513...,
                 exponent1 = 4754084...,
                 exponent2 = 6031370...,
                 coefficient = 4497486...,
                 otherPrimeInfos = asn1_NOVALUE}
4> {ok, DerPubBin} = file:read_file("rsa1.pub.der").
{ok,<<48,130,4,34,48,13,6,9,42,134,72,134,247,13,1,1,1,5,
      0,3,130,4,15,0,48,130,4,...>>}
5> DerPub = public_key:der_decode('RSAPublicKey', DerPubBin). 
** exception error: no match of right hand side value {error,{asn1,{wrong_tag,
{{expected,2},
                                                                               {got,16,
                                                                                    {16,
[{6,<<42,134,72,134,247,13,1,1,1>>},{5,<<>>}]}}}}}}
     in function  public_key:der_decode/2 (public_key.erl, line 219)
6> {ok, PemKeyBin} = file:read_file("rsa2.key.pem").
{ok,<<"-----BEGIN PRIVATE 
KEY-----\nMIISQwIBADANBgkqhkiG9w0BAQEFAASCEi0wghIpAgEAAoIEAQCs758ufZpw6gVn\nlcb3GDri0nWovuJ"...>>}
7> [PemKeyData] = public_key:pem_decode(PemKeyBin).
[{'PrivateKeyInfo',<<48,130,18,67,2,1,0,48,13,6,9,42,134,
                     72,134,247,13,1,1,1,5,0,4,130,18,45,
                     ...>>,
                   not_encrypted}]
8> PemKey = public_key:pem_entry_decode(PemKeyData).
#'PrivateKeyInfo'{version = v1,
    privateKeyAlgorithm = #'PrivateKeyInfo_privateKeyAlgorithm'{
        algorithm = {1,2,840,113549,1,1,1},
        parameters = <<5,0>>},
    privateKey = [48,130,18,41,2,1,0,2,130,4,1,0,172,239,159,46,
                  125,154,112,234,5,103,149,198,247|...],
    attributes = asn1_NOVALUE}
9> {ok, PemPubBin} = file:read_file("rsa2.pub.pem").
{ok,<<"-----BEGIN PUBLIC 
KEY-----\nMIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEArO+fLn2acOoFZ5XG9xg6\n4tJ1qL7iUiVxLzWM"...>>}
10> [PemPubData] = public_key:pem_decode(PemPubBin).
[#'SubjectPublicKeyInfo'{algorithm = <<48,130,4,34,48,13,
                                       6,9,42,134,72,134,
                                       247,13,1,1,1,5,0,3,
                                       130,4,15,0,48,130,
                                       ...>>,
                         subjectPublicKey = not_encrypted}]
11> PemPub = public_key:pem_entry_decode(PemPubData).
#'RSAPublicKey'{modulus = 7368345...,
                publicExponent = 65537}
%%%%%%%%%%%%%%%%


The PEM -> DER workaround for the public key:

%%%%%%%%%%%%%%%%
:~/Code/erlang/keytest$ cat keygen-asym 
#! /bin/bash

prefix=${1:?"Cannot proceed without a file prefix."}

keyfile="$prefix"".key.der"
pubfile="$prefix"".pub.pem"

openssl genpkey \
    -algorithm rsa \
    -out $keyfile \
    -outform DER \
    -pkeyopt rsa_keygen_bits:8192

openssl rsa \
    -inform DER \
    -in $keyfile \
    -outform PEM \
    -pubout \
    -out $pubfile
:~/Code/erlang/keytest$ ./keygen-asym rsa3
............++
..............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
++
writing RSA key
:~/Code/erlang/keytest$ erl
Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:2:2] [async-threads:10] 
[kernel-poll:false]

Eshell V6.3  (abort with ^G)
1> {ok, PubPemBin} = file:read_file("rsa3.pub.pem").
{ok,<<"-----BEGIN PUBLIC 
KEY-----\nMIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAyqeCmfJNxPynjp8RDUWU\nKaEyLIrwv1WLfkmy"...>>}
2> [PubPemData] = public_key:pem_decode(PubPemBin).
[{'SubjectPublicKeyInfo',<<48,130,4,34,48,13,6,9,42,134,
                           72,134,247,13,1,1,1,5,0,3,130,
                           4,15,0,48,130,...>>,
                         not_encrypted}]
3> Pub1 = public_key:pem_entry_decode(PubPemData).
{'RSAPublicKey',8634564...,65537}
4> PubDer = public_key:der_encode('RSAPublicKey', Pub1).
<<48,130,4,10,2,130,4,1,0,202,167,130,153,242,77,196,252,
  167,142,159,17,13,69,148,41,161,50,44,138,...>>
5> file:write_file("rsa3.pub.der", PubDer).
ok
6> {ok, PubDerBin} = file:read_file("rsa3.pub.der").
{ok,<<48,130,4,10,2,130,4,1,0,202,167,130,153,242,77,196,
      252,167,142,159,17,13,69,148,41,161,50,...>>}
7> Pub2 = public_key:der_decode('RSAPublicKey', PubDerBin).
{'RSAPublicKey',8634564...,65537}
8> Pub1 = Pub2.
{'RSAPublicKey',8634564...,65537}
9> {ok, KeyDerBin} = file:read_file("rsa3.key.der").
{ok,<<48,130,18,40,2,1,0,2,130,4,1,0,202,167,130,153,242,
      77,196,252,167,142,159,17,13,69,148,...>>}
10> Key = public_key:der_decode('RSAPrivateKey', KeyDerBin).
{'RSAPrivateKey','two-prime',
                 8634564...,
                 65537,
                 5179789...,
                 9406113...,
                 9179736...,
                 8502631...,
                 7664604...,
                 4453412...,
                 asn1_NOVALUE}
11> TestMessage = <<"A test message.">>.
<<"A test message.">>
12> Sig = public_key:sign(TestMessage, sha512, Key).
<<129,61,138,16,14,168,75,66,38,125,157,33,233,114,150,
  135,45,97,237,181,2,255,162,180,242,124,36,223,208,...>>
13> public_key:verify(TestMessage, sha512, Sig, Pub2).
true
14> public_key:verify(TestMessage, sha512, Sig, Pub1).
true
%%%%%%%%%%%%%%%%


What is going on here?


More information about the erlang-questions mailing list