diff options
-rw-r--r-- | src/x509.erl | 73 |
1 files changed, 27 insertions, 46 deletions
diff --git a/src/x509.erl b/src/x509.erl index 32ade83..c815ca4 100644 --- a/src/x509.erl +++ b/src/x509.erl @@ -9,7 +9,6 @@ -type reason() :: {chain_too_long | root_unknown | - chain_broken | signature_mismatch | encoding_invalid}. @@ -58,7 +57,10 @@ valid_chain_p(AcceptableRootCerts, [BottomCert|Rest], MaxChainLength) -> Err -> Err end. -%% @doc Return first cert in list signing Cert, or notfound. +%% @doc Return first cert in list signing Cert, or notfound. NOTE: +%% This is potentially expensive. It'd be more efficient to search for +%% Cert.issuer in a list of Issuer.subject's. If so, maybe make the +%% matching somewhat fuzzy unless that too is expensive. -spec signer(binary(), [binary()]) -> notfound | binary(). signer(_Cert, []) -> notfound; @@ -68,15 +70,15 @@ signer(Cert, [H|T]) -> {false, _} -> signer(Cert, T) end. -%% encoded_tbs_cert: verbatim from pubkey_cert.erl -encoded_tbs_cert(Cert) -> +%% Code from pubkey_cert:encoded_tbs_cert/1. +encoded_tbs_cert(DerCert) -> {ok, PKIXCert} = - 'OTP-PUB-KEY':decode_TBSCert_exclusive(Cert), - {'Certificate', - {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = PKIXCert, + 'OTP-PUB-KEY':decode_TBSCert_exclusive(DerCert), + {'Certificate', {'Certificate_tbsCertificate', EncodedTBSCert}, _, _} = + PKIXCert, EncodedTBSCert. -%% extract_verify_data: close to pubkey_cert:extract_verify_data/2 +%% Code from pubkey_cert:extract_verify_data/2. verifydata_from_cert(Cert, DerCert) -> PlainText = encoded_tbs_cert(DerCert), {_, Sig} = Cert#'Certificate'.signature, @@ -85,18 +87,20 @@ verifydata_from_cert(Cert, DerCert) -> {DigestType,_} = public_key:pkix_sign_types(SigAlg), {PlainText, DigestType, Sig}. -verify(Cert, DerCert, - #'Certificate'{ +%% @doc Verify that Cert/DerCert is signed by Issuer. +verify(Cert, DerCert, % Certificate to verify. + #'Certificate'{ % Issuer. tbsCertificate = #'TBSCertificate'{ subjectPublicKeyInfo = IssuerSPKI}}) -> - {DigestOrPlainText, DigestType, Signature} = - verifydata_from_cert(Cert, DerCert), + + %% Dig out digest, digest type and signature from Cert/DerCert. + {DigestOrPlainText, DigestType, Signature} = verifydata_from_cert(Cert, + DerCert), + %% Dig out issuer key from issuer cert. #'SubjectPublicKeyInfo'{ algorithm = #'AlgorithmIdentifier'{algorithm = Alg, parameters = Params}, subjectPublicKey = {0, Key0}} = IssuerSPKI, KeyType = pubkey_cert_records:supportedPublicKeyAlgorithms(Alg), - - %% public_key:pem_entry_decode() IssuerKey = case KeyType of 'RSAPublicKey' -> @@ -107,43 +111,20 @@ verify(Cert, DerCert, 'ECPoint' -> public_key:der_decode(KeyType, Key0) end, + + %% Verify the signature. public_key:verify(DigestOrPlainText, DigestType, Signature, IssuerKey). +%% @doc Is Cert signed by Issuer? Only verify that the signature +%% matches and don't check things like Cert.issuer == Issuer.subject. -spec signed_by_p(binary(), binary()) -> true | {false, reason()}. signed_by_p(DerCert, IssuerDerCert) when is_binary(DerCert), is_binary(IssuerDerCert) -> - Cert = public_key:pkix_decode_cert(DerCert, plain), - TBSCert = Cert#'Certificate'.tbsCertificate, - IssuerCert = public_key:pkix_decode_cert(IssuerDerCert, plain), - IssuerTBSCert = IssuerCert#'Certificate'.tbsCertificate, - case pubkey_cert:is_issuer(TBSCert#'TBSCertificate'.issuer, - IssuerTBSCert#'TBSCertificate'.subject) of - false -> - {false, chain_broken}; - true -> % Verify signature. - case verify(Cert, DerCert, IssuerCert) of - false -> {false, signature_mismatch}; - true -> true - end - end; -signed_by_p(#'OTPCertificate'{} = Cert, - #'OTPCertificate'{} = IssuerCert) -> - %% FIXME: Validate presence and contents (against constraints) of - %% names (subject, subjectAltName, emailAddress) too? - case (catch public_key:pkix_is_issuer(Cert, IssuerCert)) of - {'EXIT', Reason} -> - lager:info("invalid certificate: ~p: ~p", - [cert_string(Cert), Reason]), - {false, encoding_invalid}; - true -> - %% Cert.issuer does match IssuerCert.subject. Now verify - %% the signature. - case public_key:pkix_verify(Cert, public_key(IssuerCert)) of - true -> true; - false -> {false, signature_mismatch} - end; - false -> - {false, chain_broken} + case verify(public_key:pkix_decode_cert(DerCert, plain), + DerCert, + public_key:pkix_decode_cert(IssuerDerCert, plain)) of + false -> {false, signature_mismatch}; + true -> true end. -spec public_key(binary() | #'OTPCertificate'{}) -> public_key:public_key(). |