aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public_key/src/public_key.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/public_key/src/public_key.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/public_key/src/public_key.erl')
-rw-r--r--lib/public_key/src/public_key.erl411
1 files changed, 411 insertions, 0 deletions
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
new file mode 100644
index 0000000000..b0b0b7a832
--- /dev/null
+++ b/lib/public_key/src/public_key.erl
@@ -0,0 +1,411 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-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%
+%%
+
+%%
+
+-module(public_key).
+
+-include("public_key.hrl").
+
+-export([decode_private_key/1, decode_private_key/2,
+ decrypt_private/2, decrypt_private/3, encrypt_public/2,
+ encrypt_public/3, decrypt_public/2, decrypt_public/3,
+ encrypt_private/2, encrypt_private/3,
+ sign/2, sign/3,
+ verify_signature/3, verify_signature/4, verify_signature/5,
+ pem_to_der/1, pem_to_der/2,
+ pkix_decode_cert/2, pkix_encode_cert/1,
+ pkix_is_self_signed/1, pkix_is_fixed_dh_cert/1,
+ pkix_issuer_id/2,
+ pkix_is_issuer/2, pkix_normalize_general_name/1,
+ pkix_path_validation/3
+ ]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% Function: decode_private_key(KeyInfo [,Password]) ->
+%% {ok, PrivateKey} | {error, Reason}
+%%
+%% KeyInfo = {Type, der_bin(), ChipherInfo} - as returned from
+%% pem_to_der/[1,2] for private keys
+%% Type = rsa_private_key | dsa_private_key
+%% ChipherInfo = opaque() | no_encryption
+%%
+%% Description: Decodes an asn1 der encoded private key.
+%%--------------------------------------------------------------------
+decode_private_key(KeyInfo) ->
+ decode_private_key(KeyInfo, no_passwd).
+
+decode_private_key(KeyInfo = {rsa_private_key, _, _}, Password) ->
+ DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
+ 'OTP-PUB-KEY':decode('RSAPrivateKey', DerEncoded);
+decode_private_key(KeyInfo = {dsa_private_key, _, _}, Password) ->
+ DerEncoded = pubkey_pem:decode_key(KeyInfo, Password),
+ 'OTP-PUB-KEY':decode('DSAPrivateKey', DerEncoded).
+
+%%--------------------------------------------------------------------
+%% Function: decrypt_private(CipherText, Key) ->
+%% decrypt_private(CipherText, Key, Options) -> PlainTex
+%% decrypt_public(CipherText, Key) ->
+%% decrypt_public(CipherText, Key, Options) -> PlainTex
+%%
+%% CipherText = binary()
+%% Key = rsa_key()
+%% PlainText = binary()
+%%
+%% Description: Decrypts <CipherText>.
+%%--------------------------------------------------------------------
+decrypt_private(CipherText, Key) ->
+ decrypt_private(CipherText, Key, []).
+decrypt_private(CipherText, Key, Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ pubkey_crypto:decrypt_private(CipherText, Key, Padding).
+
+decrypt_public(CipherText, Key) ->
+ decrypt_public(CipherText, Key, []).
+decrypt_public(CipherText, Key, Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding),
+ pubkey_crypto:decrypt_public(CipherText, Key, Padding).
+
+%%--------------------------------------------------------------------
+%% Function: encrypt_public(PlainText, Key, Options) -> CipherText
+%% encrypt_private(PlainText, Key, Options) -> CipherText
+%%
+%% PlainText = iolist()
+%% Key = rsa_private_key()
+%% CipherText = binary()
+%%
+%% Description: Encrypts <Plain>
+%%--------------------------------------------------------------------
+encrypt_public(PlainText, Key) ->
+ encrypt_public(PlainText, Key, []).
+encrypt_public(PlainText, Key, Options) ->
+ Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_oaep_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),
+ pubkey_crypto:encrypt_private(PlainText, Key, Padding).
+
+%%--------------------------------------------------------------------
+%% Function: pem_to_der(CertSource) ->
+%% pem_to_der(CertSource, Password) -> {ok, [Entry]} |
+%% {error, Reason}
+%%
+%% CertSource = File | CertData
+%% CertData = binary()
+%% File = path()
+%% Password = string()
+%% Entry = {entry_type(), der_bin(), ChipherInfo}
+%% ChipherInfo = opague() | no_encryption
+%% der_bin() = binary()
+%% entry_type() = cert | cert_req | rsa_private_key | dsa_private_key
+%% dh_params
+%%
+%% Description: decode PEM binary data or a PEM file and return
+%% entries as asn1 der encoded entities. Currently supported entry
+%% types are certificates, certificate requests, rsa private keys and
+%% dsa private keys. In the case of a key entry ChipherInfo will be
+%% used by decode_private_key/2 if the key is protected by a password.
+%%--------------------------------------------------------------------
+pem_to_der(CertSource) ->
+ pem_to_der(CertSource, no_passwd).
+
+pem_to_der(File, Password) when is_list(File) ->
+ pubkey_pem:read_file(File, Password);
+pem_to_der(PemBin, Password) when is_binary(PemBin) ->
+ pubkey_pem:decode(PemBin, Password).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_decode_cert(BerCert, Type) -> {ok, Cert} | {error, Reason}
+%%
+%% BerCert = binary()
+%% Type = plain | otp
+%% Cert = certificate()
+%%
+%% Description: Decodes an asn1 ber encoded pkix certificate.
+%% otp - Uses OTP-PKIX.asn1 to decode known extensions and
+%% enhance the signature field in #'Certificate'{} and '#TBSCertificate'{}.
+%%--------------------------------------------------------------------
+pkix_decode_cert(BinCert, Type) ->
+ pubkey_cert_records:decode_cert(BinCert, Type).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_encode_cert(Cert) -> {ok, binary()} | {error, Reason}
+%%
+%% Cert = #'Certificate'{}
+%%
+%% Description: Encodes a certificate record using asn1.
+%%--------------------------------------------------------------------
+pkix_encode_cert(Cert) ->
+ pubkey_cert_records:encode_cert(Cert).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_path_validation(TrustedCert, CertChain, Options) ->
+%% {ok, {{algorithm(), public_key(), public_key_params()} policy_tree()}} |
+%% {error, Reason}
+%%
+%% Description: Performs a bacis path validation according to RFC 3280.
+%%--------------------------------------------------------------------
+pkix_path_validation(TrustedCert, CertChain, Options)
+ when is_binary(TrustedCert) ->
+ {ok, OtpCert} = pkix_decode_cert(TrustedCert, otp),
+ pkix_path_validation(OtpCert, CertChain, Options);
+
+pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options)
+ when is_list(CertChain), is_list(Options) ->
+ MaxPathDefault = length(CertChain),
+ ValidationState = pubkey_cert:init_validation_state(TrustedCert,
+ MaxPathDefault,
+ Options),
+ Fun = proplists:get_value(validate_extensions_fun, Options,
+ fun(Extensions, State, _, AccError) ->
+ {Extensions, State, AccError}
+ end),
+ Verify = proplists:get_value(verify, Options, true),
+ path_validation(CertChain, ValidationState, Fun, Verify).
+%%--------------------------------------------------------------------
+%% Function: pkix_is_fixed_dh_cert(Cert) -> true | false
+%%
+%% Description: Checks if a Certificate is a fixed Diffie-Hellman Cert
+%%--------------------------------------------------------------------
+pkix_is_fixed_dh_cert(#'OTPCertificate'{} = OTPCert) ->
+ pubkey_cert:is_fixed_dh_cert(OTPCert);
+pkix_is_fixed_dh_cert(Cert) when is_binary(Cert) ->
+ {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ pkix_is_fixed_dh_cert(OtpCert).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_is_self_signed(Cert) -> true | false
+%%
+%% Description: Checks if a Certificate is self signed.
+%%--------------------------------------------------------------------
+pkix_is_self_signed(#'OTPCertificate'{} = OTPCert) ->
+ pubkey_cert:is_self_signed(OTPCert);
+pkix_is_self_signed(Cert) when is_binary(Cert) ->
+ {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ pkix_is_self_signed(OtpCert).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_issuer_id(Cert) -> {ok, {SerialNr, Issuer}} | {error, Reason}
+%%
+%% Cert = asn1_der_encoded() | 'OTPCertificate'{}
+%%
+%% Description: Returns the issuer id.
+%%--------------------------------------------------------------------
+pkix_issuer_id(#'OTPCertificate'{} = OtpCert, self) ->
+ pubkey_cert:issuer_id(OtpCert, self);
+
+pkix_issuer_id(#'OTPCertificate'{} = OtpCert, other) ->
+ pubkey_cert:issuer_id(OtpCert, other);
+
+pkix_issuer_id(Cert, Signed) when is_binary(Cert) ->
+ {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ pkix_issuer_id(OtpCert, Signed).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_is_issuer(Cert, IssuerCert) -> true | false
+%%
+%% Cert = asn1_der_encoded() | 'OTPCertificate'{}
+%% IssuerCert = asn1_der_encoded() | 'OTPCertificate'{}
+%%
+%% Description: Checks if <IssuerCert> issued <Cert>.
+%%--------------------------------------------------------------------
+pkix_is_issuer(Cert, IssuerCert) when is_binary(Cert) ->
+ {ok, OtpCert} = pkix_decode_cert(Cert, otp),
+ pkix_is_issuer(OtpCert, IssuerCert);
+
+pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) ->
+ {ok, OtpIssuerCert} = pkix_decode_cert(IssuerCert, otp),
+ pkix_is_issuer(Cert, OtpIssuerCert);
+
+pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert},
+ #'OTPCertificate'{tbsCertificate = Candidate}) ->
+ pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer,
+ Candidate#'OTPTBSCertificate'.subject).
+
+%%--------------------------------------------------------------------
+%% Function: pkix_normalize_general_name(Issuer) ->
+%%
+%% Issuer = general_name() - see PKIX
+%%
+%% Description: Normalizes a general name so that it can be easily
+%% compared to another genral name.
+%%--------------------------------------------------------------------
+pkix_normalize_general_name(Issuer) ->
+ pubkey_cert:normalize_general_name(Issuer).
+
+%%--------------------------------------------------------------------
+%% Function:sign(Msg, Key) -> {ok, Signature}
+%% sign(Msg, Key, KeyParams) -> {ok, Signature}
+%%
+%% Msg = binary() | #'TBSCertificate'{}
+%% Key = private_key()
+%% KeyParams = key_params()
+%% Signature = binary()
+%%
+%% Description: Signs plaintext Msg or #TBSCertificate{}, in the later
+%% case a der encoded "#Certificate{}" will be returned.
+%%--------------------------------------------------------------------
+sign(Msg, #'RSAPrivateKey'{} = Key) when is_binary(Msg) ->
+ pubkey_crypto:sign(Msg, Key);
+
+sign(Msg, #'DSAPrivateKey'{} = Key) when is_binary(Msg) ->
+ pubkey_crypto:sign(Msg, Key);
+
+sign(#'OTPTBSCertificate'{signature = SigAlg} = TBSCert, Key) ->
+ Msg = pubkey_cert_records:encode_tbs_cert(TBSCert),
+ DigestType = pubkey_cert:digest_type(SigAlg),
+ Signature = pubkey_crypto:sign(DigestType, Msg, Key),
+ Cert = #'OTPCertificate'{tbsCertificate= TBSCert,
+ signatureAlgorithm = SigAlg,
+ signature = {0, Signature}
+ },
+ pkix_encode_cert(Cert).
+
+sign(DigestType, Msg, Key) ->
+ pubkey_crypto:sign(DigestType, Msg, Key).
+
+%%--------------------------------------------------------------------
+%% Function: verify_signature(PlainText, DigestType, Signature, Key) ->
+%% verify_signature(PlainText, DigestType,
+%% Signature, Key, KeyParams) ->
+%% verify_signature(DerCert, Key, KeyParams) ->
+%%
+%% PlainText = binary()
+%% DigestType = md5 | sha
+%% DerCert = asn1_der_encoded()
+%% Signature = binary()
+%% Key = public_key()
+%% KeyParams = key_params()
+%% Verified = boolean()
+%%
+%% Description: Verifies the signature <Signature>.
+%%--------------------------------------------------------------------
+verify_signature(PlainText, DigestType, Signature, #'RSAPublicKey'{} = Key)
+ when is_binary(PlainText), is_binary(Signature), DigestType == sha;
+ DigestType == md5 ->
+ pubkey_crypto:verify(DigestType, PlainText, Signature, Key, undefined).
+
+verify_signature(PlainText, DigestType, Signature, #'RSAPublicKey'{} = Key,
+ KeyParams)
+ when is_binary(PlainText), is_binary(Signature), DigestType == sha;
+ DigestType == md5 ->
+ pubkey_crypto:verify(DigestType, PlainText, Signature, Key, KeyParams);
+verify_signature(PlainText, sha, Signature, Key, #'Dss-Parms'{} = KeyParams)
+ when is_binary(PlainText), is_binary(Signature), is_integer(Key) ->
+ pubkey_crypto:verify(sha, PlainText, Signature, Key, KeyParams).
+
+verify_signature(DerCert, Key, #'Dss-Parms'{} = KeyParams)
+ when is_binary(DerCert), is_integer(Key) ->
+ pubkey_cert:verify_signature(DerCert, Key, KeyParams);
+verify_signature(DerCert, #'RSAPublicKey'{} = Key, KeyParams)
+ when is_binary(DerCert) ->
+ pubkey_cert:verify_signature(DerCert, Key, KeyParams).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+path_validation([], #path_validation_state{working_public_key_algorithm
+ = Algorithm,
+ working_public_key =
+ PublicKey,
+ working_public_key_parameters
+ = PublicKeyParams,
+ valid_policy_tree = Tree,
+ acc_errors = AccErrors
+ }, _, _) ->
+ {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree, AccErrors}};
+
+path_validation([DerCert | Rest], ValidationState = #path_validation_state{
+ max_path_length = Len},
+ Fun, Verify) when Len >= 0 ->
+ try validate(DerCert,
+ ValidationState#path_validation_state{last_cert=Rest=:=[]},
+ Fun, Verify) of
+ #path_validation_state{} = NewValidationState ->
+ path_validation(Rest, NewValidationState, Fun, Verify)
+ catch
+ throw:Reason ->
+ {error, Reason}
+ end;
+
+path_validation(_, _, _, true) ->
+ {error, {bad_cert, max_path_length_reached}};
+
+path_validation(_, #path_validation_state{working_public_key_algorithm
+ = Algorithm,
+ working_public_key =
+ PublicKey,
+ working_public_key_parameters
+ = PublicKeyParams,
+ valid_policy_tree = Tree,
+ acc_errors = AccErrors
+ }, _, false) ->
+ {ok, {{Algorithm, PublicKey, PublicKeyParams}, Tree,
+ [{bad_cert, max_path_length_reached}|AccErrors]}}.
+
+validate(DerCert, #path_validation_state{working_issuer_name = Issuer,
+ working_public_key = Key,
+ working_public_key_parameters =
+ KeyParams,
+ permitted_subtrees = Permit,
+ excluded_subtrees = Exclude,
+ last_cert = Last,
+ user_state = UserState0,
+ acc_errors = AccErr0} =
+ ValidationState0, ValidateExtensionFun, Verify) ->
+ {ok, OtpCert} = pkix_decode_cert(DerCert, otp),
+ %% All validate functions will throw {bad_cert, Reason} if they
+ %% fail and Verify = true if Verify = false errors
+ %% will be accumulated in the validationstate
+ AccErr1 = pubkey_cert:validate_time(OtpCert, AccErr0, Verify),
+
+ AccErr2 = pubkey_cert:validate_issuer(OtpCert, Issuer, AccErr1, Verify),
+
+ AccErr3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last,
+ AccErr2, Verify),
+ AccErr4 =
+ pubkey_cert:validate_revoked_status(OtpCert, Verify, AccErr3),
+
+ {ValidationState1, UnknownExtensions0, AccErr5} =
+ pubkey_cert:validate_extensions(OtpCert, ValidationState0, Verify,
+ AccErr4),
+ %% We want the key_usage extension to be checked before we validate
+ %% the signature.
+ AccErr6 =
+ pubkey_cert:validate_signature(OtpCert, DerCert, Key, KeyParams,
+ AccErr5, Verify),
+
+ {UnknownExtensions, UserState, AccErr7} =
+ ValidateExtensionFun(UnknownExtensions0, UserState0, Verify, AccErr6),
+
+ %% Check that all critical extensions have been handled
+ AccErr =
+ pubkey_cert:validate_unknown_extensions(UnknownExtensions, AccErr7,
+ Verify),
+ ValidationState =
+ ValidationState1#path_validation_state{user_state = UserState,
+ acc_errors = AccErr},
+ pubkey_cert:prepare_for_next_cert(OtpCert, ValidationState).