From 9539125a8ee97855e7cca4a8060cbf91957d0d98 Mon Sep 17 00:00:00 2001 From: Maxim Fedorov Date: Thu, 10 May 2018 12:48:15 -0700 Subject: PKCS8 handling improvements: * added PKCS8 encoder for DSA, RSA and EC private keys * added tests (full loop, PKCS8 decode/encode operations) * rewritten private key decoder to be more Erlang-y --- lib/public_key/src/pubkey_pem.erl | 2 ++ lib/public_key/src/public_key.erl | 51 +++++++++++++++++++++----------- lib/public_key/test/public_key_SUITE.erl | 29 +++++++++++------- 3 files changed, 54 insertions(+), 28 deletions(-) (limited to 'lib/public_key') diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index 06a4455b3f..bacc9ec600 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -209,6 +209,8 @@ pem_start('DSAPrivateKey') -> <<"-----BEGIN DSA PRIVATE KEY-----">>; pem_start('DHParameter') -> <<"-----BEGIN DH PARAMETERS-----">>; +pem_start('PrivateKeyInfo') -> + <<"-----BEGIN PRIVATE KEY-----">>; pem_start('EncryptedPrivateKeyInfo') -> <<"-----BEGIN ENCRYPTED PRIVATE KEY-----">>; pem_start('CertificationRequest') -> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 6900ede946..42fbd59e3c 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -204,6 +204,24 @@ pem_entry_encode('SubjectPublicKeyInfo', {'AlgorithmIdentifier', ?'id-ecPublicKey', Params}, Key}, pem_entry_encode('SubjectPublicKeyInfo', Spki); +pem_entry_encode('PrivateKeyInfo', #'DSAPrivateKey'{p=P, q=Q, g=G, x=X}) -> + pem_entry_encode('PrivateKeyInfo', + {'PrivateKeyInfo', v1, + {'PrivateKeyInfo_privateKeyAlgorithm', ?'id-dsa', + {asn1_OPENTYPE, der_encode('Dss-Parms', #'Dss-Parms'{p=P, q=Q, g=G})}}, + der_encode('Prime-p', X), asn1_NOVALUE}); +pem_entry_encode('PrivateKeyInfo', #'RSAPrivateKey'{} = PrivKey) -> + pem_entry_encode('PrivateKeyInfo', + {'PrivateKeyInfo', v1, + {'PrivateKeyInfo_privateKeyAlgorithm', ?'rsaEncryption', + {asn1_OPENTYPE, <<5, 0>>}}, + der_encode('RSAPrivateKey', PrivKey), asn1_NOVALUE}); +pem_entry_encode('PrivateKeyInfo', #'ECPrivateKey'{parameters = Parameters} = PrivKey) -> + pem_entry_encode('PrivateKeyInfo', + {'PrivateKeyInfo', v1, + {'PrivateKeyInfo_privateKeyAlgorithm', ?'id-ecPublicKey', + {asn1_OPENTYPE, der_encode('EcpkParameters', Parameters)}}, + der_encode('ECPrivateKey', PrivKey#'ECPrivateKey'{parameters = asn1_NOVALUE}), asn1_NOVALUE}); pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> Der = der_encode(Asn1Type, Entity), {Asn1Type, Der, not_encrypted}. @@ -252,6 +270,21 @@ der_decode(Asn1Type, Der) when is_atom(Asn1Type), is_binary(Der) -> erlang:error(Error) end. +der_priv_key_decode({'PrivateKeyInfo', v1, + {'PrivateKeyInfo_privateKeyAlgorithm', ?'id-ecPublicKey', {asn1_OPENTYPE, Parameters}}, PrivKey, _}) -> + EcPrivKey = der_decode('ECPrivateKey', PrivKey), + EcPrivKey#'ECPrivateKey'{parameters = der_decode('EcpkParameters', Parameters)}; +der_priv_key_decode({'PrivateKeyInfo', v1, + {'PrivateKeyInfo_privateKeyAlgorithm', ?'rsaEncryption', _}, PrivKey, _}) -> + der_decode('RSAPrivateKey', PrivKey); +der_priv_key_decode({'PrivateKeyInfo', v1, + {'PrivateKeyInfo_privateKeyAlgorithm', ?'id-dsa', {asn1_OPENTYPE, Parameters}}, PrivKey, _}) -> + {params, #'Dss-Parms'{p=P, q=Q, g=G}} = der_decode('DSAParams', Parameters), + X = der_decode('Prime-p', PrivKey), + #'DSAPrivateKey'{p=P, q=Q, g=G, x=X}; +der_priv_key_decode(PKCS8Key) -> + PKCS8Key. + %%-------------------------------------------------------------------- -spec der_encode(asn1_type(), term()) -> Der::binary(). %% @@ -276,24 +309,6 @@ der_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> erlang:error(Error) end. -der_priv_key_decode({'PrivateKeyInfo', v1, PKAlgo, PrivKey, _} = PKCS8Key) -> - % do actual decoding - {'PrivateKeyInfo_privateKeyAlgorithm', Algorithm, {asn1_OPENTYPE, Parameters}} = PKAlgo, - case Algorithm of - ?'id-ecPublicKey' -> - EcpkParameters = public_key:der_decode('EcpkParameters', Parameters), - EcPrivKey = public_key:der_decode('ECPrivateKey', PrivKey), - EcPrivKey#'ECPrivateKey'{parameters = EcpkParameters}; - ?'rsaEncryption' -> - public_key:der_decode('RSAPrivateKey', PrivKey); - ?'id-dsa' -> - {params, #'Dss-Parms'{p=P, q=Q, g=G}} = public_key:der_decode('DSAParams', Parameters), - X = public_key:der_decode('Prime-p', PrivKey), - #'DSAPrivateKey'{p=P, q=Q, g=G, x=X}; - _ -> - PKCS8Key - end. - %%-------------------------------------------------------------------- -spec pkix_decode_cert(Cert::binary(), plain | otp) -> #'Certificate'{} | #'OTPCertificate'{}. diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index f5a6f79136..572748edc9 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -183,14 +183,17 @@ dsa_pem(Config) when is_list(Config) -> DSAPubPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PubEntry0])). dsa_priv_pkcs8() -> - [{doc, "DSA PKCS8 private key decode"}]. + [{doc, "DSA PKCS8 private key decode/encode"}]. dsa_priv_pkcs8(Config) when is_list(Config) -> Datadir = proplists:get_value(data_dir, Config), - [{'PrivateKeyInfo', DerDSAKey, not_encrypted} = Entry0 ] = - erl_make_certs:pem_to_der(filename:join(Datadir, "dsa_key_pkcs8.pem")), + {ok, DsaPem} = file:read_file(filename:join(Datadir, "dsa_key_pkcs8.pem")), + [{'PrivateKeyInfo', DerDSAKey, not_encrypted} = Entry0 ] = public_key:pem_decode(DsaPem), DSAKey = public_key:der_decode('PrivateKeyInfo', DerDSAKey), DSAKey = public_key:pem_entry_decode(Entry0), - true = check_entry_type(DSAKey, 'DSAPrivateKey'). + true = check_entry_type(DSAKey, 'DSAPrivateKey'), + PrivEntry0 = public_key:pem_entry_encode('PrivateKeyInfo', DSAKey), + DSAPemNoEndNewLines = strip_superfluous_newlines(DsaPem), + DSAPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PrivEntry0])). %%-------------------------------------------------------------------- @@ -228,14 +231,17 @@ rsa_pem(Config) when is_list(Config) -> RSARawPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PubEntry1])). rsa_priv_pkcs8() -> - [{doc, "RSA PKCS8 private key decode"}]. + [{doc, "RSA PKCS8 private key decode/encode"}]. rsa_priv_pkcs8(Config) when is_list(Config) -> Datadir = proplists:get_value(data_dir, Config), - [{'PrivateKeyInfo', DerRSAKey, not_encrypted} = Entry0 ] = - erl_make_certs:pem_to_der(filename:join(Datadir, "rsa_key_pkcs8.pem")), + {ok, RsaPem} = file:read_file(filename:join(Datadir, "rsa_key_pkcs8.pem")), + [{'PrivateKeyInfo', DerRSAKey, not_encrypted} = Entry0 ] = public_key:pem_decode(RsaPem), RSAKey = public_key:der_decode('PrivateKeyInfo', DerRSAKey), RSAKey = public_key:pem_entry_decode(Entry0), - true = check_entry_type(RSAKey, 'RSAPrivateKey'). + true = check_entry_type(RSAKey, 'RSAPrivateKey'), + PrivEntry0 = public_key:pem_entry_encode('PrivateKeyInfo', RSAKey), + RSAPemNoEndNewLines = strip_superfluous_newlines(RsaPem), + RSAPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PrivEntry0])). %%-------------------------------------------------------------------- @@ -284,14 +290,17 @@ ec_pem2(Config) when is_list(Config) -> ECPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([Entry1, Entry2])). ec_priv_pkcs8() -> - [{doc, "EC PKCS8 private key decode"}]. + [{doc, "EC PKCS8 private key decode/encode"}]. ec_priv_pkcs8(Config) when is_list(Config) -> Datadir = proplists:get_value(data_dir, Config), {ok, ECPrivPem} = file:read_file(filename:join(Datadir, "ec_key_pkcs8.pem")), [{'PrivateKeyInfo', _, not_encrypted} = PKCS8Key] = public_key:pem_decode(ECPrivPem), ECPrivKey = public_key:pem_entry_decode(PKCS8Key), true = check_entry_type(ECPrivKey, 'ECPrivateKey'), - true = check_entry_type(ECPrivKey#'ECPrivateKey'.parameters, 'EcpkParameters'). + true = check_entry_type(ECPrivKey#'ECPrivateKey'.parameters, 'EcpkParameters'), + PrivEntry0 = public_key:pem_entry_encode('PrivateKeyInfo', ECPrivKey), + ECPemNoEndNewLines = strip_superfluous_newlines(ECPrivPem), + ECPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PrivEntry0])). init_ec_pem_encode_generated(Config) -> case catch true = lists:member('secp384r1', crypto:ec_curves()) of -- cgit v1.2.3