diff options
Diffstat (limited to 'lib/ssl/src/ssl_certificate.erl')
-rw-r--r-- | lib/ssl/src/ssl_certificate.erl | 97 |
1 files changed, 64 insertions, 33 deletions
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index c15e8a2138..549e557beb 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -33,6 +33,7 @@ -export([trusted_cert_and_path/4, certificate_chain/3, + certificate_chain/4, file_to_certificats/2, file_to_crls/2, validate/3, @@ -40,7 +41,8 @@ is_valid_key_usage/2, select_extension/2, extensions_list/1, - public_key_type/1 + public_key_type/1, + foldl_db/3 ]). %%==================================================================== @@ -79,7 +81,8 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) - %% Trusted must be selfsigned or it is an incomplete chain handle_path(Trusted, Path, PartialChainHandler); _ -> - %% Root CA could not be verified + %% Root CA could not be verified, but partial + %% chain handler may trusted a cert that we got handle_incomplete_chain(Path, PartialChainHandler) end end. @@ -94,10 +97,23 @@ certificate_chain(undefined, _, _) -> {error, no_cert}; certificate_chain(OwnCert, CertDbHandle, CertsDbRef) when is_binary(OwnCert) -> ErlCert = public_key:pkix_decode_cert(OwnCert, otp), - certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]); + certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert], []); certificate_chain(OwnCert, CertDbHandle, CertsDbRef) -> DerCert = public_key:pkix_encode('OTPCertificate', OwnCert, otp), - certificate_chain(OwnCert, DerCert, CertDbHandle, CertsDbRef, [DerCert]). + certificate_chain(OwnCert, DerCert, CertDbHandle, CertsDbRef, [DerCert], []). + +%%-------------------------------------------------------------------- +-spec certificate_chain(undefined | binary() | #'OTPCertificate'{} , db_handle(), certdb_ref(), [der_cert()]) -> + {error, no_cert} | {ok, #'OTPCertificate'{} | undefined, [der_cert()]}. +%% +%% Description: Create certificate chain with certs from +%%-------------------------------------------------------------------- +certificate_chain(Cert, CertDbHandle, CertsDbRef, Candidates) when is_binary(Cert) -> + ErlCert = public_key:pkix_decode_cert(Cert, otp), + certificate_chain(ErlCert, Cert, CertDbHandle, CertsDbRef, [Cert], Candidates); +certificate_chain(Cert, CertDbHandle, CertsDbRef, Candidates) -> + DerCert = public_key:pkix_encode('OTPCertificate', Cert, otp), + certificate_chain(Cert, DerCert, CertDbHandle, CertsDbRef, [DerCert], Candidates). %%-------------------------------------------------------------------- -spec file_to_certificats(binary(), term()) -> [der_cert()]. %% @@ -187,9 +203,20 @@ public_key_type(?'id-ecPublicKey') -> ec. %%-------------------------------------------------------------------- +-spec foldl_db(fun(), db_handle() | {extracted, list()}, list()) -> + {ok, term()} | issuer_not_found. +%% +%% Description: +%%-------------------------------------------------------------------- +foldl_db(IsIssuerFun, CertDbHandle, []) -> + ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle); +foldl_db(IsIssuerFun, _, [_|_] = ListDb) -> + lists:foldl(IsIssuerFun, issuer_not_found, ListDb). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) -> +certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain, ListDb) -> IssuerAndSelfSigned = case public_key:pkix_is_self_signed(OtpCert) of true -> @@ -200,12 +227,12 @@ certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) -> case IssuerAndSelfSigned of {_, true = SelfSigned} -> - certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned); + do_certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned, ListDb); {{error, issuer_not_found}, SelfSigned} -> - case find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) of + case find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef, ListDb) of {ok, {SerialNr, Issuer}} -> - certificate_chain(CertDbHandle, CertsDbRef, Chain, - SerialNr, Issuer, SelfSigned); + do_certificate_chain(CertDbHandle, CertsDbRef, Chain, + SerialNr, Issuer, SelfSigned, ListDb); _ -> %% Guess the the issuer must be the root %% certificate. The verification of the @@ -214,19 +241,19 @@ certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) -> {ok, undefined, lists:reverse(Chain)} end; {{ok, {SerialNr, Issuer}}, SelfSigned} -> - certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned) + do_certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned, ListDb) end. -certificate_chain(_, _, [RootCert | _] = Chain, _, _, true) -> +do_certificate_chain(_, _, [RootCert | _] = Chain, _, _, true, _) -> {ok, RootCert, lists:reverse(Chain)}; -certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> +do_certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _, ListDb) -> case ssl_manager:lookup_trusted_cert(CertDbHandle, CertsDbRef, SerialNr, Issuer) of {ok, {IssuerCert, ErlCert}} -> ErlCert = public_key:pkix_decode_cert(IssuerCert, otp), certificate_chain(ErlCert, IssuerCert, - CertDbHandle, CertsDbRef, [IssuerCert | Chain]); + CertDbHandle, CertsDbRef, [IssuerCert | Chain], ListDb); _ -> %% The trusted cert may be obmitted from the chain as the %% counter part needs to have it anyway to be able to @@ -234,7 +261,8 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned {ok, undefined, lists:reverse(Chain)} end. -find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) -> + +find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef, ListDb) -> IsIssuerFun = fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) -> case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of @@ -252,26 +280,29 @@ find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) -> Acc end, - if is_reference(CertsDbRef) -> % actual DB exists - try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of - issuer_not_found -> - {error, issuer_not_found} - catch - {ok, _IssuerId} = Return -> - Return - end; - is_tuple(CertsDbRef), element(1,CertsDbRef) =:= extracted -> % cache bypass byproduct - {extracted, CertsData} = CertsDbRef, - DB = [Entry || {decoded, Entry} <- CertsData], - try lists:foldl(IsIssuerFun, issuer_not_found, DB) of - issuer_not_found -> - {error, issuer_not_found} - catch - {ok, _IssuerId} = Return -> - Return - end + Result = case is_reference(CertsDbRef) of + true -> + do_find_issuer(IsIssuerFun, CertDbHandle, ListDb); + false -> + {extracted, CertsData} = CertsDbRef, + DB = [Entry || {decoded, Entry} <- CertsData], + do_find_issuer(IsIssuerFun, CertDbHandle, DB) + end, + case Result of + issuer_not_found -> + {error, issuer_not_found}; + Result -> + Result end. +do_find_issuer(IssuerFun, CertDbHandle, CertDb) -> + try + foldl_db(IssuerFun, CertDbHandle, CertDb) + catch + throw:{ok, _} = Return -> + Return + end. + is_valid_extkey_usage(KeyUse, client) -> %% Client wants to verify server is_valid_key_usage(KeyUse,?'id-kp-serverAuth'); @@ -300,7 +331,7 @@ other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) -> {ok, IssuerId} -> {other, IssuerId}; {error, issuer_not_found} -> - case find_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) of + case find_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef, []) of {ok, IssuerId} -> {other, IssuerId}; Other -> |