diff options
Diffstat (limited to 'lib/public_key')
-rw-r--r-- | lib/public_key/asn1/OTP-PKIX.asn1 | 2 | ||||
-rw-r--r-- | lib/public_key/src/pubkey_cert.erl | 33 | ||||
-rw-r--r-- | lib/public_key/src/pubkey_cert_records.erl | 398 | ||||
-rw-r--r-- | lib/public_key/src/pubkey_pem.erl | 16 | ||||
-rw-r--r-- | lib/public_key/src/public_key.erl | 4 | ||||
-rw-r--r-- | lib/public_key/test/Makefile | 6 | ||||
-rw-r--r-- | lib/public_key/test/pkey_test.erl | 412 | ||||
-rw-r--r-- | lib/public_key/test/public_key.cover | 2 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE.erl | 238 | ||||
-rw-r--r-- | lib/public_key/vsn.mk | 11 |
10 files changed, 646 insertions, 476 deletions
diff --git a/lib/public_key/asn1/OTP-PKIX.asn1 b/lib/public_key/asn1/OTP-PKIX.asn1 index 2bcacc0990..c0cf440496 100644 --- a/lib/public_key/asn1/OTP-PKIX.asn1 +++ b/lib/public_key/asn1/OTP-PKIX.asn1 @@ -313,7 +313,7 @@ SupportedPublicKeyAlgorithms PUBLIC-KEY-ALGORITHM-CLASS ::= { dsa-with-sha1 SIGNATURE-ALGORITHM-CLASS ::= { ID id-dsa-with-sha1 - TYPE NULL } -- XXX Must be empty and not NULL + TYPE Dss-Parms } -- -- RSA Keys and Signatures diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl index 8f7dfa8352..0651dcec29 100644 --- a/lib/public_key/src/pubkey_cert.erl +++ b/lib/public_key/src/pubkey_cert.erl @@ -29,7 +29,7 @@ validate_issuer/4, validate_names/6, validate_revoked_status/3, validate_extensions/4, validate_unknown_extensions/3, - normalize_general_name/1, digest_type/1, digest/2, is_self_signed/1, + normalize_general_name/1, digest_type/1, is_self_signed/1, is_issuer/2, issuer_id/2, is_fixed_dh_cert/1]). -define(NULL, 0). @@ -130,13 +130,14 @@ validate_signature(OtpCert, DerCert, Key, KeyParams, validate_names(OtpCert, Permit, Exclude, Last, AccErr, Verify) -> case is_self_signed(OtpCert) andalso (not Last) of true -> - ok; + AccErr; false -> TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, Subject = TBSCert#'OTPTBSCertificate'.subject, + Extensions = + extensions_list(TBSCert#'OTPTBSCertificate'.extensions), AltSubject = - select_extension(?'id-ce-subjectAltName', - TBSCert#'OTPTBSCertificate'.extensions), + select_extension(?'id-ce-subjectAltName', Extensions), EmailAddress = extract_email(Subject), Name = [{directoryName, Subject}|EmailAddress], @@ -196,7 +197,7 @@ normalize_general_name({rdnSequence, Issuer}) -> normalize_general_name(Issuer) -> Normalize = fun([{Description, Type, {printableString, Value}}]) -> NewValue = string:to_lower(strip_spaces(Value)), - {Description, Type, {printableString, NewValue}}; + [{Description, Type, {printableString, NewValue}}]; (Atter) -> Atter end, @@ -212,7 +213,7 @@ is_issuer({rdnSequence, Issuer}, {rdnSequence, Candidate}) -> issuer_id(Otpcert, other) -> TBSCert = Otpcert#'OTPCertificate'.tbsCertificate, - Extensions = TBSCert#'OTPTBSCertificate'.extensions, + Extensions = extensions_list(TBSCert#'OTPTBSCertificate'.extensions), case select_extension(?'id-ce-authorityKeyIdentifier', Extensions) of undefined -> {error, issuer_not_found}; @@ -232,12 +233,17 @@ is_fixed_dh_cert(#'OTPCertificate'{tbsCertificate = SubjectPublicKeyInfo, extensions = Extensions}}) -> - is_fixed_dh_cert(SubjectPublicKeyInfo, Extensions). + is_fixed_dh_cert(SubjectPublicKeyInfo, extensions_list(Extensions)). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +extensions_list(asn1_NOVALUE) -> + []; +extensions_list(Extensions) -> + Extensions. + not_valid(Error, true, _) -> throw(Error); not_valid(Error, false, AccErrors) -> @@ -269,13 +275,6 @@ digest_type(?md5WithRSAEncryption) -> digest_type(?'id-dsa-with-sha1') -> sha. -digest(?sha1WithRSAEncryption, Msg) -> - crypto:sha(Msg); -digest(?md5WithRSAEncryption, Msg) -> - crypto:md5(Msg); -digest(?'id-dsa-with-sha1', Msg) -> - crypto:sha(Msg). - public_key_info(PublicKeyInfo, #path_validation_state{working_public_key_algorithm = WorkingAlgorithm, @@ -326,12 +325,6 @@ is_dir_name([[{'AttributeTypeAndValue', Type, What1}]|Rest1], true -> is_dir_name(Rest1,Rest2,Exact); false -> false end; -is_dir_name([{'AttributeTypeAndValue', Type, What1}|Rest1], - [{'AttributeTypeAndValue', Type, What2}|Rest2], Exact) -> - case is_dir_name2(What1,What2) of - true -> is_dir_name(Rest1,Rest2,Exact); - false -> false - end; is_dir_name(_,[],false) -> true; is_dir_name(_,_,_) -> diff --git a/lib/public_key/src/pubkey_cert_records.erl b/lib/public_key/src/pubkey_cert_records.erl index c7d4080adb..ac04e1c2cb 100644 --- a/lib/public_key/src/pubkey_cert_records.erl +++ b/lib/public_key/src/pubkey_cert_records.erl @@ -25,8 +25,6 @@ -export([decode_cert/2, encode_cert/1, encode_tbs_cert/1, transform/2]). --export([old_decode_cert/2, old_encode_cert/1]). %% Debugging and testing new code. - %%==================================================================== %% Internal application API %%==================================================================== @@ -35,77 +33,25 @@ decode_cert(DerCert, plain) -> 'OTP-PUB-KEY':decode('Certificate', DerCert); decode_cert(DerCert, otp) -> {ok, Cert} = 'OTP-PUB-KEY':decode('OTPCertificate', DerCert), - {ok, decode_all_otp(Cert)}. - -old_decode_cert(DerCert, otp) -> - {ok, Cert} = 'OTP-PUB-KEY':decode('Certificate', DerCert), - {ok, plain_to_otp(Cert)}. - -old_encode_cert(Cert) -> - PlainCert = otp_to_plain(Cert), - {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', PlainCert), - list_to_binary(EncCert). - + #'OTPCertificate'{tbsCertificate = TBS} = Cert, + {ok, Cert#'OTPCertificate'{tbsCertificate = decode_tbs(TBS)}}. encode_cert(Cert = #'Certificate'{}) -> {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', Cert), list_to_binary(EncCert); -encode_cert(C = #'OTPCertificate'{tbsCertificate = TBS = - #'OTPTBSCertificate'{ - issuer=Issuer0, - subject=Subject0, - subjectPublicKeyInfo=Spki0, - extensions=Exts0} - }) -> - Issuer = transform(Issuer0,encode), - Subject = transform(Subject0,encode), - Spki = encode_supportedPublicKey(Spki0), - Exts = encode_extensions(Exts0), - %% io:format("Extensions ~p~n",[Exts]), - Cert = C#'OTPCertificate'{tbsCertificate= - TBS#'OTPTBSCertificate'{ - issuer=Issuer, subject=Subject, - subjectPublicKeyInfo=Spki, - extensions=Exts}}, +encode_cert(C = #'OTPCertificate'{tbsCertificate = TBS}) -> + Cert = C#'OTPCertificate'{tbsCertificate=encode_tbs(TBS)}, {ok, EncCert} = 'OTP-PUB-KEY':encode('OTPCertificate', Cert), list_to_binary(EncCert). -encode_tbs_cert(TBS = #'OTPTBSCertificate'{ - issuer=Issuer0, - subject=Subject0, - subjectPublicKeyInfo=Spki0, - extensions=Exts0}) -> - Issuer = transform(Issuer0,encode), - Subject = transform(Subject0,encode), - Spki = encode_supportedPublicKey(Spki0), - Exts = encode_extensions(Exts0), - TBSCert = TBS#'OTPTBSCertificate'{issuer=Issuer,subject=Subject, - subjectPublicKeyInfo=Spki,extensions=Exts}, - {ok, EncTBSCert} = 'OTP-PUB-KEY':encode('OTPTBSCertificate', TBSCert), +encode_tbs_cert(TBS) -> + {ok, EncTBSCert} = 'OTP-PUB-KEY':encode('OTPTBSCertificate', encode_tbs(TBS)), list_to_binary(EncTBSCert). %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -decode_all_otp(C = #'OTPCertificate'{tbsCertificate = TBS = - #'OTPTBSCertificate'{ - issuer=Issuer0, - subject=Subject0, - subjectPublicKeyInfo=Spki0, - extensions=Exts0} - }) -> - Issuer = transform(Issuer0,decode), - Subject = transform(Subject0,decode), - Spki = decode_supportedPublicKey(Spki0), - Exts = decode_extensions(Exts0), - %% io:format("Extensions ~p~n",[Exts]), - C#'OTPCertificate'{tbsCertificate= - TBS#'OTPTBSCertificate'{ - issuer=Issuer, subject=Subject, - subjectPublicKeyInfo=Spki,extensions=Exts}}. - - %%% SubjectPublicKey supportedPublicKeyAlgorithms(?'rsaEncryption') -> 'RSAPublicKey'; supportedPublicKeyAlgorithms(?'id-dsa') -> 'DSAPublicKey'; @@ -204,15 +150,35 @@ transform({rdnSequence, SeqList},Func) when is_list(SeqList) -> lists:map(fun(Seq) -> lists:map(fun(Element) -> transform(Element,Func) end, Seq) end, SeqList)}; -%% transform(List = [{rdnSequence, _}|_],Func) -> -%% lists:map(fun(Element) -> transform(Element,Func) end, List); transform(#'NameConstraints'{permittedSubtrees=Permitted, excludedSubtrees=Excluded}, Func) -> - Res = #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func), - excludedSubtrees=transform_sub_tree(Excluded,Func)}, -%% io:format("~p~n",[Res]), - Res; + #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func), + excludedSubtrees=transform_sub_tree(Excluded,Func)}; + transform(Other,_) -> Other. + +encode_tbs(TBS=#'OTPTBSCertificate'{issuer=Issuer0, + subject=Subject0, + subjectPublicKeyInfo=Spki0, + extensions=Exts0}) -> + Issuer = transform(Issuer0,encode), + Subject = transform(Subject0,encode), + Spki = encode_supportedPublicKey(Spki0), + Exts = encode_extensions(Exts0), + TBS#'OTPTBSCertificate'{issuer=Issuer, subject=Subject, + subjectPublicKeyInfo=Spki,extensions=Exts}. + +decode_tbs(TBS = #'OTPTBSCertificate'{issuer=Issuer0, + subject=Subject0, + subjectPublicKeyInfo=Spki0, + extensions=Exts0}) -> + Issuer = transform(Issuer0,decode), + Subject = transform(Subject0,decode), + Spki = decode_supportedPublicKey(Spki0), + Exts = decode_extensions(Exts0), + TBS#'OTPTBSCertificate'{issuer=Issuer, subject=Subject, + subjectPublicKeyInfo=Spki,extensions=Exts}. + transform_sub_tree(asn1_NOVALUE,_) -> asn1_NOVALUE; transform_sub_tree(TreeList,Func) -> [Tree#'GeneralSubtree'{base=transform(Name,Func)} || @@ -236,303 +202,3 @@ attribute_type(?'id-at-pseudonym') -> 'X520Pseudonym'; attribute_type(?'id-domainComponent') -> 'DomainComponent'; attribute_type(?'id-emailAddress') -> 'EmailAddress'; attribute_type(Type) -> Type. - -%%% Old code transforms - -plain_to_otp(#'Certificate'{tbsCertificate = TBSCert, - signatureAlgorithm = SigAlg, - signature = Signature} = Cert) -> - Cert#'Certificate'{tbsCertificate = plain_to_otp(TBSCert), - signatureAlgorithm = plain_to_otp(SigAlg), - signature = plain_to_otp(Signature)}; - -plain_to_otp(#'TBSCertificate'{signature = Signature, - issuer = Issuer, - subject = Subject, - subjectPublicKeyInfo = SPubKeyInfo, - extensions = Extensions} = TBSCert) -> - - TBSCert#'TBSCertificate'{signature = plain_to_otp(Signature), - issuer = plain_to_otp(Issuer), - subject = - plain_to_otp(Subject), - subjectPublicKeyInfo = - plain_to_otp(SPubKeyInfo), - extensions = - plain_to_otp_extensions(Extensions) - }; - -plain_to_otp(#'AlgorithmIdentifier'{algorithm = Algorithm, - parameters = Params}) -> - SignAlgAny = - #'SignatureAlgorithm-Any'{algorithm = Algorithm, - parameters = Params}, - {ok, AnyEnc} = 'OTP-PUB-KEY':encode('SignatureAlgorithm-Any', - SignAlgAny), - {ok, SignAlg} = 'OTP-PUB-KEY':decode('SignatureAlgorithm', - list_to_binary(AnyEnc)), - SignAlg; - -plain_to_otp({rdnSequence, SeqList}) when is_list(SeqList) -> - {rdnSequence, - lists:map(fun(Seq) -> - lists:map(fun(Element) -> - plain_to_otp(Element) - end, - Seq) - end, SeqList)}; - -plain_to_otp(#'AttributeTypeAndValue'{} = ATAV) -> - {ok, ATAVEnc} = - 'OTP-PUB-KEY':encode('AttributeTypeAndValue', ATAV), - {ok, ATAVDec} = 'OTP-PUB-KEY':decode('OTPAttributeTypeAndValue', - list_to_binary(ATAVEnc)), - #'AttributeTypeAndValue'{type = ATAVDec#'OTPAttributeTypeAndValue'.type, - value = - ATAVDec#'OTPAttributeTypeAndValue'.value}; - -plain_to_otp(#'SubjectPublicKeyInfo'{algorithm = - #'AlgorithmIdentifier'{algorithm - = Algo, - parameters = - Params}, - subjectPublicKey = PublicKey}) -> - - AnyAlgo = #'PublicKeyAlgorithm'{algorithm = Algo, - parameters = Params}, - {0, AnyKey} = PublicKey, - AnyDec = #'OTPSubjectPublicKeyInfo-Any'{algorithm = AnyAlgo, - subjectPublicKey = AnyKey}, - {ok, AnyEnc} = - 'OTP-PUB-KEY':encode('OTPSubjectPublicKeyInfo-Any', AnyDec), - {ok, InfoDec} = 'OTP-PUB-KEY':decode('OTPOLDSubjectPublicKeyInfo', - list_to_binary(AnyEnc)), - - AlgorithmDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.algorithm, - AlgoDec = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.algo, - NewParams = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.parameters, - PublicKeyDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.subjectPublicKey, - NewAlgorithmDec = - #'SubjectPublicKeyInfoAlgorithm'{algorithm = AlgoDec, - parameters = NewParams}, - #'SubjectPublicKeyInfo'{algorithm = NewAlgorithmDec, - subjectPublicKey = PublicKeyDec - }; - -plain_to_otp(#'Extension'{extnID = ExtID, - critical = Critical, - extnValue = Value}) - when ExtID == ?'id-ce-authorityKeyIdentifier'; - ExtID == ?'id-ce-subjectKeyIdentifier'; - ExtID == ?'id-ce-keyUsage'; - ExtID == ?'id-ce-privateKeyUsagePeriod'; - ExtID == ?'id-ce-certificatePolicies'; - ExtID == ?'id-ce-policyMappings'; - ExtID == ?'id-ce-subjectAltName'; - ExtID == ?'id-ce-issuerAltName'; - ExtID == ?'id-ce-subjectDirectoryAttributes'; - ExtID == ?'id-ce-basicConstraints'; - ExtID == ?'id-ce-nameConstraints'; - ExtID == ?'id-ce-policyConstraints'; - ExtID == ?'id-ce-extKeyUsage'; - ExtID == ?'id-ce-cRLDistributionPoints'; - ExtID == ?'id-ce-inhibitAnyPolicy'; - ExtID == ?'id-ce-freshestCRL' -> - ExtAny = #'Extension-Any'{extnID = ExtID, - critical = Critical, - extnValue = Value}, - {ok, AnyEnc} = 'OTP-PUB-KEY':encode('Extension-Any', ExtAny), - {ok, ExtDec} = 'OTP-PUB-KEY':decode('OTPExtension', - list_to_binary(AnyEnc)), - - ExtValue = plain_to_otp_extension_value(ExtID, - ExtDec#'OTPExtension'.extnValue), - #'Extension'{extnID = ExtID, - critical = ExtDec#'OTPExtension'.critical, - extnValue = ExtValue}; - -plain_to_otp(#'Extension'{} = Ext) -> - Ext; - -plain_to_otp(#'AuthorityKeyIdentifier'{} = Ext) -> - CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer, - Ext#'AuthorityKeyIdentifier'{authorityCertIssuer = - plain_to_otp(CertIssuer)}; - - -plain_to_otp([{directoryName, Value}]) -> - [{directoryName, plain_to_otp(Value)}]; - -plain_to_otp(Value) -> - Value. - -otp_to_plain(#'Certificate'{tbsCertificate = TBSCert, - signatureAlgorithm = SigAlg, - signature = Signature} = Cert) -> - Cert#'Certificate'{tbsCertificate = otp_to_plain(TBSCert), - signatureAlgorithm = - otp_to_plain(SigAlg), - signature = otp_to_plain(Signature)}; - -otp_to_plain(#'TBSCertificate'{signature = Signature, - issuer = Issuer, - subject = Subject, - subjectPublicKeyInfo = SPubKeyInfo, - extensions = Extensions} = TBSCert) -> - - TBSCert#'TBSCertificate'{signature = otp_to_plain(Signature), - issuer = otp_to_plain(Issuer), - subject = - otp_to_plain(Subject), - subjectPublicKeyInfo = - otp_to_plain(SPubKeyInfo), - extensions = otp_to_plain_extensions(Extensions) - }; - -otp_to_plain(#'SignatureAlgorithm'{} = SignAlg) -> - {ok, EncSignAlg} = 'OTP-PUB-KEY':encode('SignatureAlgorithm', SignAlg), - {ok, #'SignatureAlgorithm-Any'{algorithm = Algorithm, - parameters = Params}} = - 'OTP-PUB-KEY':decode('SignatureAlgorithm-Any', - list_to_binary(EncSignAlg)), - #'AlgorithmIdentifier'{algorithm = Algorithm, - parameters = Params}; - -otp_to_plain({rdnSequence, SeqList}) when is_list(SeqList) -> - {rdnSequence, - lists:map(fun(Seq) -> - lists:map(fun(Element) -> - otp_to_plain(Element) - end, - Seq) - end, SeqList)}; - -otp_to_plain(#'AttributeTypeAndValue'{type = Type, value = Value}) -> - {ok, ATAVEnc} = - 'OTP-PUB-KEY':encode('OTPAttributeTypeAndValue', - #'OTPAttributeTypeAndValue'{type = Type, - value = Value}), - {ok, ATAVDec} = 'OTP-PUB-KEY':decode('AttributeTypeAndValue', - list_to_binary(ATAVEnc)), - ATAVDec; - -otp_to_plain(#'SubjectPublicKeyInfo'{algorithm = - #'SubjectPublicKeyInfoAlgorithm'{ - algorithm = Algo, - parameters = - Params}, - subjectPublicKey = PublicKey}) -> - - OtpAlgo = #'OTPOLDSubjectPublicKeyInfo_algorithm'{algo = Algo, - parameters = Params}, - OtpDec = #'OTPOLDSubjectPublicKeyInfo'{algorithm = OtpAlgo, - subjectPublicKey = PublicKey}, - {ok, OtpEnc} = - 'OTP-PUB-KEY':encode('OTPOLDSubjectPublicKeyInfo', OtpDec), - - {ok, AnyDec} = 'OTP-PUB-KEY':decode('OTPSubjectPublicKeyInfo-Any', - list_to_binary(OtpEnc)), - - #'OTPSubjectPublicKeyInfo-Any'{algorithm = #'PublicKeyAlgorithm'{ - algorithm = NewAlgo, - parameters = NewParams}, - subjectPublicKey = Bin} = AnyDec, - - #'SubjectPublicKeyInfo'{algorithm = - #'AlgorithmIdentifier'{ - algorithm = NewAlgo, - parameters = plain_key_params(NewParams)}, - subjectPublicKey = - {0, Bin} - }; - -otp_to_plain(#'Extension'{extnID = ExtID, - extnValue = Value} = Ext) -> - ExtValue = - otp_to_plain_extension_value(ExtID, Value), - - Ext#'Extension'{extnValue = ExtValue}; - -otp_to_plain(#'AuthorityKeyIdentifier'{} = Ext) -> - CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer, - Ext#'AuthorityKeyIdentifier'{authorityCertIssuer = - otp_to_plain(CertIssuer)}; - -otp_to_plain([{directoryName, Value}]) -> - [{directoryName, otp_to_plain(Value)}]; - -otp_to_plain(Value) -> - Value. - -plain_key_params('NULL') -> - <<5,0>>; -plain_key_params(Value) -> - Value. - -plain_to_otp_extension_value(?'id-ce-authorityKeyIdentifier', Value) -> - plain_to_otp(Value); -plain_to_otp_extension_value(_, Value) -> - Value. - -plain_to_otp_extensions(Exts) when is_list(Exts) -> - lists:map(fun(Ext) -> plain_to_otp(Ext) end, Exts). - -otp_to_plain_extension_value(?'id-ce-authorityKeyIdentifier', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('AuthorityKeyIdentifier', - otp_to_plain(Value)), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-subjectKeyIdentifier', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectKeyIdentifier', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-keyUsage', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('KeyUsage', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-privateKeyUsagePeriod', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('PrivateKeyUsagePeriod', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-certificatePolicies', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('CertificatePolicies', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-policyMappings', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyMappings', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-subjectAltName', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectAltName', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-issuerAltName', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('IssuerAltName', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-subjectDirectoryAttributes', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectDirectoryAttributes', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-basicConstraints', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('BasicConstraints', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-nameConstraints', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('NameConstraints', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-policyConstraints', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyConstraints', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-extKeyUsage', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('ExtKeyUsage', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-cRLDistributionPoints', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('CRLDistributionPoints', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-inhibitAnyPolicy', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('InhibitAnyPolicy', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(?'id-ce-freshestCRL', Value) -> - {ok, Enc} = 'OTP-PUB-KEY':encode('FreshestCRL', Value), - otp_to_plain_extension_value_format(Enc); -otp_to_plain_extension_value(_Id, Value) -> - Value. - -otp_to_plain_extension_value_format(Value) -> - list_to_binary(Value). - -otp_to_plain_extensions(Exts) when is_list(Exts) -> - lists:map(fun(Ext) -> - otp_to_plain(Ext) - end, Exts). diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl index 9fc17b6f73..65879f1bbe 100644 --- a/lib/public_key/src/pubkey_pem.erl +++ b/lib/public_key/src/pubkey_pem.erl @@ -124,25 +124,31 @@ decode_file2([L|Rest], RLs, Ens, Tag, Info0) -> decode_file2([], _, Ens, _, _) -> {ok, lists:reverse(Ens)}. -%% TODO Support same as decode_file +%% Support same as decode_file encode_file(Ds) -> lists:map( - fun({cert, Bin}) -> + fun({cert, Bin, not_encrypted}) -> %% PKIX (X.509) ["-----BEGIN CERTIFICATE-----\n", b64encode_and_split(Bin), "-----END CERTIFICATE-----\n\n"]; - ({cert_req, Bin}) -> + ({cert_req, Bin, not_encrypted}) -> %% PKCS#10 ["-----BEGIN CERTIFICATE REQUEST-----\n", b64encode_and_split(Bin), "-----END CERTIFICATE REQUEST-----\n\n"]; - ({rsa_private_key, Bin}) -> + ({rsa_private_key, Bin, not_encrypted}) -> %% PKCS#? ["XXX Following key assumed not encrypted\n", "-----BEGIN RSA PRIVATE KEY-----\n", b64encode_and_split(Bin), - "-----END RSA PRIVATE KEY-----\n\n"] + "-----END RSA PRIVATE KEY-----\n\n"]; + ({dsa_private_key, Bin, not_encrypted}) -> + %% PKCS#? + ["XXX Following key assumed not encrypted\n", + "-----BEGIN DSA PRIVATE KEY-----\n", + b64encode_and_split(Bin), + "-----END DSA PRIVATE KEY-----\n\n"] end, Ds). dek_info(Line0, Info) -> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 157e76bb21..d1d45f21a0 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -113,13 +113,13 @@ decrypt_public(CipherText, Key, Options) -> encrypt_public(PlainText, Key) -> encrypt_public(PlainText, Key, []). encrypt_public(PlainText, Key, Options) -> - Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_padding), + Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), pubkey_crypto:encrypt_public(PlainText, Key, Padding). encrypt_private(PlainText, Key) -> encrypt_private(PlainText, Key, []). encrypt_private(PlainText, Key, Options) -> - Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_padding), + Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), pubkey_crypto:encrypt_private(PlainText, Key, Padding). %%-------------------------------------------------------------------- diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile index c7215020c7..5544339ff2 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/Makefile @@ -28,6 +28,7 @@ INCLUDES= -I. -I ../include # ---------------------------------------------------- MODULES= \ + pkey_test \ public_key_SUITE \ pkits_SUITE @@ -40,6 +41,9 @@ TARGET_FILES= \ SPEC_FILES = public_key.spec +COVER_FILE = public_key.cover + + # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- @@ -74,7 +78,7 @@ release_spec: opt release_tests_spec: opt $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(HRL_FILES)$(RELSYSDIR) + $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR) $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR) chmod -f -R u+w $(RELSYSDIR) @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) diff --git a/lib/public_key/test/pkey_test.erl b/lib/public_key/test/pkey_test.erl new file mode 100644 index 0000000000..9d596eee4f --- /dev/null +++ b/lib/public_key/test/pkey_test.erl @@ -0,0 +1,412 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% Create test certificates + +-module(pkey_test). +-include_lib("public_key/include/public_key.hrl"). + +-export([make_cert/1, gen_rsa/1, verify_signature/3, write_pem/3]). +-compile(export_all). + +%%-------------------------------------------------------------------- +%% @doc Create and return a der encoded certificate +%% Option Default +%% ------------------------------------------------------- +%% digest sha1 +%% validity {date(), date() + week()} +%% version 3 +%% subject [] list of the following content +%% {name, Name} +%% {email, Email} +%% {city, City} +%% {state, State} +%% {org, Org} +%% {org_unit, OrgUnit} +%% {country, Country} +%% {serial, Serial} +%% {title, Title} +%% {dnQualifer, DnQ} +%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created) +%% (obs IssuerKey migth be {Key, Password} +%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key +%% +%% +%% (OBS: The generated keys are for testing only) +%% @spec ([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()} +%% @end +%%-------------------------------------------------------------------- + +make_cert(Opts) -> + SubjectPrivateKey = get_key(Opts), + {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts), + Cert = public_key:sign(TBSCert, IssuerKey), + true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok + {Cert, encode_key(SubjectPrivateKey)}. + +%%-------------------------------------------------------------------- +%% @doc Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem" +%% @spec (::string(), ::string(), {Cert,Key}) -> ok +%% @end +%%-------------------------------------------------------------------- +write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) -> + ok = public_key:der_to_pem(filename:join(Dir, FileName ++ ".pem"), [{cert, Cert, not_encrypted}]), + ok = public_key:der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]). + +%%-------------------------------------------------------------------- +%% @doc Creates a rsa key (OBS: for testing only) +%% the size are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_rsa(Size) when is_integer(Size) -> + Key = gen_rsa2(Size), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% @doc Creates a dsa key (OBS: for testing only) +%% the sizes are in bytes +%% @spec (::integer()) -> {::atom(), ::binary(), ::opaque()} +%% @end +%%-------------------------------------------------------------------- +gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> + Key = gen_dsa2(LSize, NSize), + {Key, encode_key(Key)}. + +%%-------------------------------------------------------------------- +%% @doc Verifies cert signatures +%% @spec (::binary(), ::tuple()) -> ::boolean() +%% @end +%%-------------------------------------------------------------------- +verify_signature(DerEncodedCert, DerKey, KeyParams) -> + Key = decode_key(DerKey), + case Key of + #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} -> + public_key:verify_signature(DerEncodedCert, + #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, + 'NULL'); + #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} -> + public_key:verify_signature(DerEncodedCert, Y, #'Dss-Parms'{p=P, q=Q, g=G}); + + _ -> + public_key:verify_signature(DerEncodedCert, Key, KeyParams) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_key(Opts) -> + case proplists:get_value(key, Opts) of + undefined -> make_key(rsa, Opts); + rsa -> make_key(rsa, Opts); + dsa -> make_key(dsa, Opts); + Key -> + Password = proplists:get_value(password, Opts, no_passwd), + decode_key(Key, Password) + end. + +decode_key({Key, Pw}) -> + decode_key(Key, Pw); +decode_key(Key) -> + decode_key(Key, no_passwd). + + +decode_key(#'RSAPublicKey'{} = Key,_) -> + Key; +decode_key(#'RSAPrivateKey'{} = Key,_) -> + Key; +decode_key(#'DSAPrivateKey'{} = Key,_) -> + Key; +decode_key(Der = {_,_,_}, Pw) -> + {ok, Key} = public_key:decode_private_key(Der, Pw), + Key; +decode_key(FileOrDer, Pw) -> + {ok, [KeyInfo]} = public_key:pem_to_der(FileOrDer), + decode_key(KeyInfo, Pw). + +encode_key(Key = #'RSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), + {rsa_private_key, list_to_binary(Der), not_encrypted}; +encode_key(Key = #'DSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), + {dsa_private_key, list_to_binary(Der), not_encrypted}. + +make_tbs(SubjectKey, Opts) -> + Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), + {Issuer, IssuerKey} = issuer(Opts, SubjectKey), + + {Algo, Parameters} = sign_algorithm(IssuerKey, Opts), + + SignAlgo = #'SignatureAlgorithm'{algorithm = Algo, + parameters = Parameters}, + + {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1, + signature = SignAlgo, + issuer = Issuer, + validity = validity(Opts), + subject = subject(proplists:get_value(subject, Opts),false), + subjectPublicKeyInfo = publickey(SubjectKey), + version = Version, + extensions = extensions(Opts) + }, IssuerKey}. + +issuer(Opts, SubjectKey) -> + IssuerProp = proplists:get_value(issuer, Opts, true), + case IssuerProp of + true -> %% Self signed + {subject(proplists:get_value(subject, Opts), true), SubjectKey}; + {Issuer, IssuerKey} when is_binary(Issuer) -> + {issuer_der(Issuer), decode_key(IssuerKey)}; + {File, IssuerKey} when is_list(File) -> + {ok, [{cert, Cert, _}|_]} = public_key:pem_to_der(File), + {issuer_der(Cert), decode_key(IssuerKey)} + end. + +issuer_der(Issuer) -> + {ok, Decoded} = public_key:pkix_decode_cert(Issuer, otp), + #'OTPCertificate'{tbsCertificate=Tbs} = Decoded, + #'OTPTBSCertificate'{subject=Subject} = Tbs, + Subject. + +subject(undefined, IsCA) -> + User = if IsCA -> "CA"; true -> os:getenv("USER") end, + Opts = [{email, User ++ "@erlang.org"}, + {name, User}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "testing dep"}], + subject(Opts); +subject(Opts, _) -> + subject(Opts). + +subject(SubjectOpts) when is_list(SubjectOpts) -> + Encode = fun(Opt) -> + {Type,Value} = subject_enc(Opt), + [#'AttributeTypeAndValue'{type=Type, value=Value}] + end, + {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}. + +%% Fill in the blanks +subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}}; +subject_enc({email, Email}) -> {?'id-emailAddress', Email}; +subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}}; +subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}}; +subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}}; +subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}}; +subject_enc({country, Country}) -> {?'id-at-countryName', Country}; +subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial}; +subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}}; +subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ}; +subject_enc(Other) -> Other. + + +extensions(Opts) -> + case proplists:get_value(extensions, Opts, []) of + false -> + asn1_NOVALUE; + Exts -> + lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)]) + end. + +default_extensions(Exts) -> + Def = [{key_usage,undefined}, + {subject_altname, undefined}, + {issuer_altname, undefined}, + {basic_constraints, default}, + {name_constraints, undefined}, + {policy_constraints, undefined}, + {ext_key_usage, undefined}, + {inhibit_any, undefined}, + {auth_key_id, undefined}, + {subject_key_id, undefined}, + {policy_mapping, undefined}], + Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end, + Exts ++ lists:foldl(Filter, Def, Exts). + +extension({_, undefined}) -> []; +extension({basic_constraints, Data}) -> + case Data of + default -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true}, + critical=true}; + false -> + []; + Len when is_integer(Len) -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len}, + critical=true}; + _ -> + #'Extension'{extnID = ?'id-ce-basicConstraints', + extnValue = Data} + end; +extension({Id, Data, Critical}) -> + #'Extension'{extnID = Id, extnValue = Data, critical = Critical}. + + +publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) -> + Public = #'RSAPublicKey'{modulus=N, publicExponent=E}, + Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, + subjectPublicKey = Public}; +publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) -> + Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa', + parameters=#'Dss-Parms'{p=P, q=Q, g=G}}, + #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}. + +validity(Opts) -> + DefFrom0 = date(), + DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7), + {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}), + Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end, + #'Validity'{notBefore={generalTime, Format(DefFrom)}, + notAfter ={generalTime, Format(DefTo)}}. + +sign_algorithm(#'RSAPrivateKey'{}, Opts) -> + Type = case proplists:get_value(digest, Opts, sha1) of + sha1 -> ?'sha1WithRSAEncryption'; + sha512 -> ?'sha512WithRSAEncryption'; + sha384 -> ?'sha384WithRSAEncryption'; + sha256 -> ?'sha256WithRSAEncryption'; + md5 -> ?'md5WithRSAEncryption'; + md2 -> ?'md2WithRSAEncryption' + end, + {Type, 'NULL'}; +sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) -> + {?'id-dsa-with-sha1', #'Dss-Parms'{p=P, q=Q, g=G}}. + +make_key(rsa, _Opts) -> + %% (OBS: for testing only) + gen_rsa2(64); +make_key(dsa, _Opts) -> + gen_dsa2(128, 20). %% Bytes i.e. {1024, 160} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% RSA key generation (OBS: for testing only) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53, + 47,43,41,37,31,29,23,19,17,13,11,7,5,3]). + +gen_rsa2(Size) -> + P = prime(Size), + Q = prime(Size), + N = P*Q, + Tot = (P - 1) * (Q - 1), + [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES), + {D1,D2} = extended_gcd(E, Tot), + D = erlang:max(D1,D2), + case D < E of + true -> + gen_rsa2(Size); + false -> + {Co1,Co2} = extended_gcd(Q, P), + Co = erlang:max(Co1,Co2), + #'RSAPrivateKey'{version = 'two-prime', + modulus = N, + publicExponent = E, + privateExponent = D, + prime1 = P, + prime2 = Q, + exponent1 = D rem (P-1), + exponent2 = D rem (Q-1), + coefficient = Co + } + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% DSA key generation (OBS: for testing only) +%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +%% and the fips_186-3.pdf +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_dsa2(LSize, NSize) -> + Q = prime(NSize), %% Choose N-bit prime Q + X0 = prime(LSize), + P0 = prime((LSize div 2) +1), + + %% Choose L-bit prime modulus P such that p–1 is a multiple of q. + case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of + error -> + gen_dsa2(LSize, NSize); + P -> + G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p–1)/q mod p, commonly h=2 is used. + + X = prime(20), %% Choose x by some random method, where 0 < x < q. + Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + + #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + end. + +%% See fips_186-3.pdf +dsa_search(T, P0, Q, Iter) when Iter > 0 -> + P = 2*T*Q*P0 + 1, + case is_prime(crypto:mpint(P), 50) of + true -> P; + false -> dsa_search(T+1, P0, Q, Iter-1) + end; +dsa_search(_,_,_,_) -> + error. + + +%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prime(ByteSize) -> + Rand = odd_rand(ByteSize), + crypto:erlint(prime_odd(Rand, 0)). + +prime_odd(Rand, N) -> + case is_prime(Rand, 50) of + true -> + Rand; + false -> + NotPrime = crypto:erlint(Rand), + prime_odd(crypto:mpint(NotPrime+2), N+1) + end. + +%% see http://en.wikipedia.org/wiki/Fermat_primality_test +is_prime(_, 0) -> true; +is_prime(Candidate, Test) -> + CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), + case crypto:mod_exp(CoPrime, Candidate, Candidate) of + CoPrime -> is_prime(Candidate, Test-1); + _ -> false + end. + +odd_rand(Size) -> + Min = 1 bsl (Size*8-1), + Max = (1 bsl (Size*8))-1, + odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + +odd_rand(Min,Max) -> + Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), + BitSkip = (Sz+4)*8-1, + case Rand of + Odd = <<_:BitSkip, 1:1>> -> Odd; + Even = <<_:BitSkip, 0:1>> -> + crypto:mpint(crypto:erlint(Even)+1) + end. + +extended_gcd(A, B) -> + case A rem B of + 0 -> + {0, 1}; + N -> + {X, Y} = extended_gcd(B, N), + {Y, X-Y*(A div B)} + end. diff --git a/lib/public_key/test/public_key.cover b/lib/public_key/test/public_key.cover new file mode 100644 index 0000000000..8477c76ef6 --- /dev/null +++ b/lib/public_key/test/public_key.cover @@ -0,0 +1,2 @@ + +{exclude, ['OTP-PUB-KEY']}.
\ No newline at end of file diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 8cc36e490d..6a3d6bfcf5 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -101,14 +101,13 @@ all(doc) -> all(suite) -> [app, + dh, pem_to_der, - decode_private_key -%% encrypt_decrypt, -%% rsa_verify -%% dsa_verify_sign, -%% pkix_encode_decode, -%% pkix_verify_sign, -%% pkix_path_validation + decode_private_key, + encrypt_decrypt, + sign_verify, + pkix, + pkix_path_validation ]. %% Test cases starts here. @@ -118,20 +117,35 @@ app(doc) -> "Test that the public_key app file is ok"; app(suite) -> []; -app(Config) when list(Config) -> +app(Config) when is_list(Config) -> ok = test_server:app_test(public_key). +dh(doc) -> + "Test diffie-hellman functions file is ok"; +dh(suite) -> + []; +dh(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + {ok,[DerDHparams = {dh_params, _, _}]} = + public_key:pem_to_der(filename:join(Datadir, "dh.pem")), + {ok, DHps = #'DHParameter'{prime=P,base=G}} = public_key:decode_dhparams(DerDHparams), + DHKeys = {Private,_Public} = public_key:gen_key(DHps), + test_server:format("DHparams = ~p~nDH Keys~p~n", [DHps, DHKeys]), + {_Private,_Public2} = pubkey_crypto:gen_key(diffie_hellman, [crypto:erlint(Private), P, G]), + ok. + + pem_to_der(doc) -> ["Check that supported PEM files are decoded into the expected entry type"]; pem_to_der(suite) -> []; pem_to_der(Config) when is_list(Config) -> Datadir = ?config(data_dir, Config), - {ok,[{dsa_private_key, _, not_encrypted}]} = + {ok,DSAKey =[{dsa_private_key, _, not_encrypted}]} = public_key:pem_to_der(filename:join(Datadir, "dsa.pem")), {ok,[{rsa_private_key, _, _}]} = public_key:pem_to_der(filename:join(Datadir, "client_key.pem")), - {ok,[{rsa_private_key, _, _}]} = + {ok, [{rsa_private_key, _, _}]} = public_key:pem_to_der(filename:join(Datadir, "rsa.pem")), {ok,[{rsa_private_key, _, _}]} = public_key:pem_to_der(filename:join(Datadir, "rsa.pem"), "abcd1234"), @@ -144,12 +158,18 @@ pem_to_der(Config) when is_list(Config) -> public_key:pem_to_der(filename:join(Datadir, "client_cert.pem")), {ok,[{cert_req, _, _}]} = public_key:pem_to_der(filename:join(Datadir, "req.pem")), - {ok,[{cert, _, _}, {cert, _, _}]} = + {ok, Certs = [{cert, _, _}, {cert, _, _}]} = public_key:pem_to_der(filename:join(Datadir, "cacerts.pem")), - {ok, Bin1} = file:read_file(filename:join(Datadir, "cacerts.pem")), + {ok, Bin1} = file:read_file(filename:join(Datadir, "cacerts.pem")), {ok, [{cert, _, _}, {cert, _, _}]} = public_key:pem_to_der(Bin1), + + ok = public_key:der_to_pem(filename:join(Datadir, "wcacerts.pem"), Certs), + ok = public_key:der_to_pem(filename:join(Datadir, "wdsa.pem"), DSAKey), + {ok, Certs} = public_key:pem_to_der(filename:join(Datadir, "wcacerts.pem")), + {ok, DSAKey} = public_key:pem_to_der(filename:join(Datadir, "wdsa.pem")), + ok. %%-------------------------------------------------------------------- decode_private_key(doc) -> @@ -178,84 +198,148 @@ encrypt_decrypt(doc) -> encrypt_decrypt(suite) -> []; encrypt_decrypt(Config) when is_list(Config) -> - RSAPrivateKey = #'RSAPrivateKey'{publicExponent = 17, - modulus = 3233, - privateExponent = 2753, - prime1 = 61, - prime2 = 53, - version = 'two-prime'}, - Msg = <<0,123>>, - {ok, Encrypted} = public_key:encrypt(Msg, RSAPrivateKey, [{block_type, 2}]), - test_server:format("Expected 855, Encrypted ~p ~n", [Encrypted]), + {PrivateKey, _DerKey} = pkey_test:gen_rsa(64), + #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} = PrivateKey, + PublicKey = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, + Msg = list_to_binary(lists:duplicate(5, "Foo bar 100")), + RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey), + Msg = public_key:decrypt_public(RsaEncrypted, PublicKey), + Msg = public_key:decrypt_public(RsaEncrypted, PrivateKey), + RsaEncrypted2 = public_key:encrypt_public(Msg, PublicKey), + RsaEncrypted3 = public_key:encrypt_public(Msg, PrivateKey), + Msg = public_key:decrypt_private(RsaEncrypted2, PrivateKey), + Msg = public_key:decrypt_private(RsaEncrypted3, PrivateKey), + ok. + +%%-------------------------------------------------------------------- +sign_verify(doc) -> + ["Checks that we can sign and verify signatures."]; +sign_verify(suite) -> + []; +sign_verify(Config) when is_list(Config) -> + %% Make cert signs and validates the signature using RSA and DSA + Ca = {_, CaKey} = pkey_test:make_cert([]), + {ok, PrivateRSA = #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp}} = + public_key:decode_private_key(CaKey), + + CertInfo = {Cert1,CertKey1} = pkey_test:make_cert([{key, dsa}, {issuer, Ca}]), + + PublicRSA = #'RSAPublicKey'{modulus=Mod, publicExponent=Exp}, + true = public_key:verify_signature(Cert1, PublicRSA, undefined), + + {Cert2,_CertKey} = pkey_test:make_cert([{issuer, CertInfo}]), + + {ok, #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y, x=_X}} = + public_key:decode_private_key(CertKey1), + true = public_key:verify_signature(Cert2, Y, #'Dss-Parms'{p=P, q=Q, g=G}), + %% RSA sign + Msg0 = lists:duplicate(5, "Foo bar 100"), + Msg = list_to_binary(Msg0), + RSASign = public_key:sign(sha, Msg0, PrivateRSA), + RSASign = public_key:sign(Msg, PrivateRSA), + true = public_key:verify_signature(Msg, sha, RSASign, PublicRSA), + false = public_key:verify_signature(<<1:8, Msg/binary>>, sha, RSASign, PublicRSA), + false = public_key:verify_signature(Msg, sha, <<1:8, RSASign/binary>>, PublicRSA), + RSASign = public_key:sign(sha, Msg, PrivateRSA), + RSASign1 = public_key:sign(md5, Msg, PrivateRSA), + true = public_key:verify_signature(Msg, md5, RSASign1, PublicRSA), + + %% DSA sign + Datadir = ?config(data_dir, Config), + {ok,[DsaKey = {dsa_private_key, _, _}]} = + public_key:pem_to_der(filename:join(Datadir, "dsa.pem")), + {ok, DSAPrivateKey} = public_key:decode_private_key(DsaKey), + #'DSAPrivateKey'{p=P1, q=Q1, g=G1, y=Y1, x=_X1} = DSAPrivateKey, + DSASign = public_key:sign(Msg, DSAPrivateKey), + DSAPublicKey = Y1, + DSAParams = #'Dss-Parms'{p=P1, q=Q1, g=G1}, + true = public_key:verify_signature(Msg, sha, DSASign, DSAPublicKey, DSAParams), + false = public_key:verify_signature(<<1:8, Msg/binary>>, sha, DSASign, DSAPublicKey, DSAParams), + false = public_key:verify_signature(Msg, sha, <<1:8, DSASign/binary>>, DSAPublicKey, DSAParams), + + ok. +pkix(doc) -> + "Misc pkix tests not covered elsewhere"; +pkix(suite) -> + []; +pkix(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + {ok,Certs0} = public_key:pem_to_der(filename:join(Datadir, "cacerts.pem")), + {ok,Certs1} = public_key:pem_to_der(filename:join(Datadir, "client_cert.pem")), + TestTransform = fun({cert, CertDer, not_encrypted}) -> + {ok, PlainCert} = public_key:pkix_decode_cert(CertDer, plain), + {ok, OtpCert} = public_key:pkix_decode_cert(CertDer, otp), + CertDer = public_key:pkix_encode_cert(OtpCert), + CertDer = public_key:pkix_encode_cert(PlainCert), + OTPSubj = (OtpCert#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subject, + Subj = public_key:pkix_transform(OTPSubj, encode), + {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj), + Subj2 = (PlainCert#'Certificate'.tbsCertificate)#'TBSCertificate'.subject, + {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj2), + OTPSubj = public_key:pkix_transform(Subj2, decode), + false = public_key:pkix_is_fixed_dh_cert(CertDer) + end, + [TestTransform(Cert) || Cert <- Certs0 ++ Certs1], + true = public_key:pkix_is_self_signed(element(2,hd(Certs0))), + false = public_key:pkix_is_self_signed(element(2,hd(Certs1))), + CaIds = [element(2, public_key:pkix_issuer_id(Cert, self)) || {cert, Cert, _} <- Certs0], + {ok, IssuerId = {_, IssuerName}} = public_key:pkix_issuer_id(element(2,hd(Certs1)), other), + true = lists:member(IssuerId, CaIds), + %% Should be normalized allready + TestStr = {rdnSequence, [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"ERLANGCA"}}], + [{'AttributeTypeAndValue', {2,5,4,3},{printableString," erlang ca "}}]]}, + VerifyStr = {rdnSequence, [[{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlang ca"}}], + [{'AttributeTypeAndValue', {2,5,4,3},{printableString,"erlangca"}}]]}, + VerifyStr = public_key:pkix_normalize_general_name(TestStr), -%% Datadir = ?config(data_dir, Config), -%% {ok,[{rsa_private_key, EncKey}]} = -%% public_key:pem_to_der(filename:join(Datadir, "server_key.pem")), -%% {ok, Key} = public_key:decode_private_key(EncKey, rsa), -%% RSAPublicKey = #'RSAPublicKey'{publicExponent = -%% Key#'RSAPrivateKey'.publicExponent, -%% modulus = Key#'RSAPrivateKey'.modulus}, -%% {ok, Msg} = file:read_file(filename:join(Datadir, "msg.txt")), -%% Hash = crypto:sha(Msg), -%% {ok, Encrypted} = public_key:encrypt(Hash, Key, [{block_type, 2}]), -%% test_server:format("Encrypted ~p", [Encrypted]), -%% {ok, Decrypted} = public_key:decrypt(Encrypted, -%% RSAPublicKey, [{block_type, 1}]), -%% test_server:format("Encrypted ~p", [Decrypted]), -%% true = Encrypted == Decrypted. - -%%-------------------------------------------------------------------- -rsa_verify(doc) -> - ["Cheks that we can verify an rsa signature."]; -rsa_verify(suite) -> + ok. + +pkix_path_validation(doc) -> + "Misc pkix tests not covered elsewhere"; +pkix_path_validation(suite) -> []; -rsa_verify(Config) when is_list(Config) -> - Datadir = ?config(data_dir, Config), - - {ok,[{cert, DerCert}]} = - public_key:pem_to_der(filename:join(Datadir, "server_cert.pem")), +pkix_path_validation(Config) when is_list(Config) -> + CaK = {Trusted,_} = + pkey_test:make_cert([{key, dsa}, + {subject, [ + {name, "Public Key"}, + {?'id-at-name', {printableString, "public_key"}}, + {?'id-at-pseudonym', {printableString, "pubkey"}}, + {city, "Stockholm"}, + {country, "SE"}, + {org, "erlang"}, + {org_unit, "testing dep"} + ]} + ]), + ok = pkey_test:write_pem("/tmp", "cacert", CaK), + + CertK1 = {Cert1, _} = pkey_test:make_cert([{issuer, CaK}]), + CertK2 = {Cert2,_} = pkey_test:make_cert([{issuer, CertK1}, {digest, md5}, {extensions, false}]), + ok = pkey_test:write_pem("/tmp", "cert", CertK2), + + {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1], []), - {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp), + {error, {bad_cert,invalid_issuer}} = public_key:pkix_path_validation(Trusted, [Cert2], []), + %%{error, {bad_cert,invalid_issuer}} = public_key:pkix_path_validation(Trusted, [Cert2], [{verify,false}]), - {0, Signature} = OTPCert#'Certificate'.signature, - TBSCert = OTPCert#'Certificate'.tbsCertificate, + {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1, Cert2], []), + {error, issuer_not_found} = public_key:pkix_issuer_id(Cert2, other), - #'TBSCertificate'{subjectPublicKeyInfo = Info} = TBSCert, + CertK3 = {Cert3,_} = pkey_test:make_cert([{issuer, CertK1}, {extensions, [{basic_constraints, false}]}]), + {Cert4,_} = pkey_test:make_cert([{issuer, CertK3}]), + {error, E={bad_cert,missing_basic_constraint}} = + public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], []), - #'SubjectPublicKeyInfo'{subjectPublicKey = RSAPublicKey} = Info, - - EncTBSCert = encoded_tbs_cert(DerCert), - Digest = crypto:sha(EncTBSCert), - - public_key:verify_signature(Digest, Signature, RSAPublicKey). - - -%% Signature is generated in the following way (in datadir): -%% openssl dgst -sha1 -binary -out rsa_signature -sign server_key.pem msg.txt -%%{ok, Signature} = file:read_file(filename:join(Datadir, "rsa_signature")), -%%{ok, Signature} = file:read_file(filename:join(Datadir, "rsa_signature")), -%% {ok, Msg} = file:read_file(filename:join(Datadir, "msg.txt")), -%% Digest = crypto:sha(Msg), -%% {ok,[{rsa_private_key, EncKey}]} = -%% public_key:pem_to_der(filename:join(Datadir, "server_key.pem")), -%% {ok, Key} = public_key:decode_private_key(EncKey, rsa), -%% RSAPublicKey = #'RSAPublicKey'{publicExponent = -%% Key#'RSAPrivateKey'.publicExponent, -%% modulus = Key#'RSAPrivateKey'.modulus}, - -encoded_tbs_cert(Cert) -> - {ok, PKIXCert} = - 'OTP-PUB-KEY':decode_TBSCert_exclusive(Cert), - {'Certificate', - {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert, - EncodedTBSCert. + {ok, {_,_,[E]}} = public_key:pkix_path_validation(Trusted, [Cert1, Cert3,Cert4], [{verify,false}]), + % test_server:format("PV ~p ~n", [Result]), + ok. diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index da1465d538..8c4e4127b2 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1,7 +1,10 @@ -PUBLIC_KEY_VSN = 0.6 -TICKETS = OTP-7046 \ - OTP-8553 -#TICKETS_o.5 = OTP-8372 +PUBLIC_KEY_VSN = 0.7 + +TICKETS = OTP-8626 + +#TICKETS_0.6 = OTP-7046 \ +# OTP-8553 +#TICKETS_0.5 = OTP-8372 #TICKETS_0.4 = OTP-8250 #TICKETS_0.3 = OTP-8100 OTP-8142 #TICKETS_0.2 = OTP-7860 |