%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2015-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %---------------------------------------------------------------------- %% Purpose: CRL handling %%---------------------------------------------------------------------- -module(ssl_crl). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([trusted_cert_and_path/3]). trusted_cert_and_path(CRL, {SerialNumber, Issuer},{_, {Db, DbRef}} = DbHandle) -> case ssl_pkix_db:lookup_trusted_cert(Db, DbRef, SerialNumber, Issuer) of undefined -> trusted_cert_and_path(CRL, issuer_not_found, DbHandle); {ok, {_, OtpCert}} -> {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef), {ok, Root, lists:reverse(Chain)} end; trusted_cert_and_path(CRL, issuer_not_found, {CertPath, {Db, DbRef}}) -> case find_issuer(CRL, {certpath, [{Der, public_key:pkix_decode_cert(Der,otp)} || Der <- CertPath]}) of {ok, OtpCert} -> {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef), {ok, Root, lists:reverse(Chain)}; {error, issuer_not_found} -> trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef}) end; trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbInfo) -> case find_issuer(CRL, DbInfo) of {ok, OtpCert} -> {ok, Root, Chain} = ssl_certificate:certificate_chain(OtpCert, Db, DbRef), {ok, Root, lists:reverse(Chain)}; {error, issuer_not_found} -> {error, unknown_ca} end. find_issuer(CRL, {certpath = Db, DbRef}) -> Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)), IsIssuerFun = fun({_Der,ErlCertCandidate}, Acc) -> verify_crl_issuer(CRL, ErlCertCandidate, Issuer, Acc); (_, Acc) -> Acc end, find_issuer(IsIssuerFun, Db, DbRef); find_issuer(CRL, {Db, DbRef}) -> Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)), IsIssuerFun = fun({_Key, {_Der,ErlCertCandidate}}, Acc) -> verify_crl_issuer(CRL, ErlCertCandidate, Issuer, Acc); (_, Acc) -> Acc end, find_issuer(IsIssuerFun, Db, DbRef). find_issuer(IsIssuerFun, certpath, Certs) -> try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of issuer_not_found -> {error, issuer_not_found} catch {ok, _} = Result -> Result end; find_issuer(IsIssuerFun, extracted, CertsData) -> Certs = [Entry || {decoded, Entry} <- CertsData], try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of issuer_not_found -> {error, issuer_not_found} catch {ok, _} = Result -> Result end; find_issuer(IsIssuerFun, Db, _) -> try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of issuer_not_found -> {error, issuer_not_found} catch {ok, _} = Result -> Result end. verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) -> TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate, case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of Issuer -> case public_key:pkix_crl_verify(CRL, ErlCertCandidate) of true -> throw({ok, ErlCertCandidate}); false -> NotIssuer end; _ -> NotIssuer end.