aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/ssl_certificate.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_certificate.erl')
-rw-r--r--lib/ssl/src/ssl_certificate.erl156
1 files changed, 156 insertions, 0 deletions
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
new file mode 100644
index 0000000000..d97b61a5ce
--- /dev/null
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -0,0 +1,156 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. 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%
+%%
+
+%%----------------------------------------------------------------------
+%% Purpose: Help funtions for handling certificat verification.
+%% The path validation defined in ssl_handshake.erl that mainly
+%% calls functions in this module is described in RFC 3280.
+%%----------------------------------------------------------------------
+
+-module(ssl_certificate).
+
+-include("ssl_handshake.hrl").
+-include("ssl_alert.hrl").
+-include("ssl_internal.hrl").
+-include("ssl_debug.hrl").
+
+-export([trusted_cert_and_path/3,
+ certificate_chain/2,
+ file_to_certificats/1]).
+
+%%====================================================================
+%% Internal application API
+%%====================================================================
+
+trusted_cert_and_path(CertChain, CertDbRef, Verify) ->
+ [Cert | RestPath] = lists:reverse(CertChain),
+ {ok, OtpCert} = public_key:pkix_decode_cert(Cert, otp),
+ IssuerAnPath =
+ case public_key:pkix_is_self_signed(OtpCert) of
+ true ->
+ {ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
+ {IssuerId, RestPath};
+ false ->
+ case public_key:pkix_issuer_id(OtpCert, other) of
+ {ok, IssuerId} ->
+ {IssuerId, [Cert | RestPath]};
+ {error, issuer_not_found} ->
+ case find_issuer(OtpCert, no_candidate) of
+ {ok, IssuerId} ->
+ {IssuerId, [Cert | RestPath]};
+ Other ->
+ {Other, RestPath}
+ end
+ end
+ end,
+
+ case IssuerAnPath of
+ {{error, issuer_not_found}, _ } ->
+ %% The root CA was not sent and can not be found, we fail if verify = true
+ not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA), Verify, {Cert, RestPath});
+ {{SerialNr, Issuer}, Path} ->
+ case ssl_certificate_db:lookup_trusted_cert(CertDbRef,
+ SerialNr, Issuer) of
+ {ok, {BinCert,_}} ->
+ {BinCert, Path, []};
+ _ ->
+ %% Fail if verify = true
+ not_valid(?ALERT_REC(?FATAL, ?UNKNOWN_CA),
+ Verify, {Cert, RestPath})
+ end
+ end.
+
+
+certificate_chain(undefined, _CertsDbRef) ->
+ {error, no_cert};
+certificate_chain(OwnCert, CertsDbRef) ->
+ {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp),
+ certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]).
+
+file_to_certificats(File) ->
+ {ok, List} = ssl_manager:cache_pem_file(File),
+ [Bin || {cert, Bin, not_encrypted} <- List].
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) ->
+ IssuerAndSelfSigned =
+ case public_key:pkix_is_self_signed(OtpCert) of
+ true ->
+ {public_key:pkix_issuer_id(OtpCert, self), true};
+ false ->
+ {public_key:pkix_issuer_id(OtpCert, other), false}
+ end,
+
+ case IssuerAndSelfSigned of
+ {_, true = SelfSigned} ->
+ certificate_chain(CertsDbRef, Chain, ignore, ignore, SelfSigned);
+ {{error, issuer_not_found}, SelfSigned} ->
+ case find_issuer(OtpCert, no_candidate) of
+ {ok, {SerialNr, Issuer}} ->
+ certificate_chain(CertsDbRef, Chain,
+ SerialNr, Issuer, SelfSigned);
+ _ ->
+ %% Guess the the issuer must be the root
+ %% certificate. The verification of the
+ %% cert chain will fail if guess is
+ %% incorrect.
+ {ok, lists:reverse(Chain)}
+ end;
+ {{ok, {SerialNr, Issuer}}, SelfSigned} ->
+ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, SelfSigned)
+ end.
+
+certificate_chain(_CertsDbRef, Chain, _SerialNr, _Issuer, true) ->
+ {ok, lists:reverse(Chain)};
+
+certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) ->
+ case ssl_certificate_db:lookup_trusted_cert(CertsDbRef,
+ SerialNr, Issuer) of
+ {ok, {IssuerCert, ErlCert}} ->
+ {ok, ErlCert} = public_key:pkix_decode_cert(IssuerCert, otp),
+ certificate_chain(ErlCert, IssuerCert,
+ CertsDbRef, [IssuerCert | Chain]);
+ _ ->
+ %% The trusted cert may be obmitted from the chain as the
+ %% counter part needs to have it anyway to be able to
+ %% verify it. This will be the normal case for servers
+ %% that does not verify the clients and hence have not
+ %% specified the cacertfile.
+ {ok, lists:reverse(Chain)}
+ end.
+
+find_issuer(OtpCert, PrevCandidateKey) ->
+ case ssl_certificate_db:issuer_candidate(PrevCandidateKey) of
+ no_more_candidates ->
+ {error, issuer_not_found};
+ {Key, {_Cert, ErlCertCandidate}} ->
+ case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
+ true ->
+ public_key:pkix_issuer_id(ErlCertCandidate, self);
+ false ->
+ find_issuer(OtpCert, Key)
+ end
+ end.
+
+not_valid(Alert, true, _) ->
+ throw(Alert);
+not_valid(_, false, {ErlCert, Path}) ->
+ {ErlCert, Path, [{bad_cert, unknown_ca}]}.