Packages

otpbp

5.35.2
7.18.0 7.16.1 7.15.0 7.14.0 7.13.0 7.12.0 7.11.0 7.10.2 7.9.3 7.9.2 7.9.1 7.8.7 7.8.6 7.8.5 7.8.4 7.7.1 7.7.0 7.6.3 7.6.2 7.6.1 7.6.0 7.5.1 7.5.0 7.4.0 7.3.4 7.3.3 7.3.2 7.3.1 7.3.0 7.2.0 7.1.1 7.1.0 7.0.4 7.0.3 7.0.2 7.0.1 6.26.0 6.25.0 6.24.0 6.23.0 6.22.2 6.21.3 6.21.2 6.21.1 6.20.2 6.19.1 6.19.0 6.18.3 6.18.2 6.18.1 6.18.0 6.17.1 6.17.0 6.16.0 6.15.4 6.15.3 6.15.2 6.15.1 6.15.0 6.14.0 6.13.5 6.13.4 6.13.3 6.13.2 6.13.1 6.13.0 6.12.0 6.11.2 6.11.1 6.10.0 6.9.2 6.9.1 6.9.0 6.8.0 6.7.1 6.7.0 6.6.4 6.6.3 6.6.2 6.6.1 6.6.0 6.5.0 6.4.0 6.3.3 6.3.2 6.3.1 6.3.0 6.2.1 6.2.0 6.1.0 6.0.0 5.39.0 5.38.0 5.37.0 5.36.0 5.35.2 5.34.3 5.34.2 5.34.1 5.33.1 5.32.1 5.32.0 5.31.3 5.31.2 5.31.1 5.31.0 5.30.1 5.30.0 5.29.0 5.28.4 5.28.3 5.28.2 5.28.1 5.28.0 5.27.0 5.26.5 5.26.4 5.26.3 5.26.2 5.26.1 5.26.0 5.25.0 5.24.2 5.24.1 5.23.0 5.22.2 5.22.1 5.22.0 5.21.0 5.20.1 5.20.0 5.19.4 5.19.3 5.19.2 5.19.1 5.19.0 5.18.0 5.17.0 5.16.3 5.16.1 5.16.0 5.15.1 5.15.0 5.14.0 5.13.1 5.13.0 5.12.1 5.12.0 5.11.2 5.11.1 5.10.1 5.10.0 5.9.0 5.8.0 5.7.0 5.6.1 5.6.0 5.5.0 5.4.2 5.4.1 5.4.0 5.3.0 5.2.2 5.2.1 5.2.0 5.1.2 5.1.1 4.79.0 4.78.0 4.77.0 4.76.2 4.75.3 4.75.2 4.75.1 4.74.0 4.73.1 4.73.0 4.72.3 4.72.2 4.72.1 4.72.0 4.71.1 4.71.0 4.70.0 4.69.4 4.69.3 4.69.2 4.69.1 4.69.0 4.68.0 4.67.5 4.67.4 4.67.3 4.67.2 4.67.0 4.66.0 4.65.2 4.65.1 4.64.0 4.63.2 4.63.1 4.63.0 4.62.0 4.61.1 4.61.0 4.60.4 4.60.3 4.60.2 4.60.1 4.60.0 4.59.0 4.58.0 4.57.2 4.57.1 4.57.0 4.56.2 4.56.1 4.56.0 4.55.0 4.54.0 4.53.0 4.52.1 4.52.0 4.51.3 4.51.2 4.51.0 4.50.0 4.49.3 4.49.2

OTP backports

Current section

Files

Jump to
otpbp src otpbp_pubkey_cert.erl
Raw

src/otpbp_pubkey_cert.erl

-module(otpbp_pubkey_cert).
-include_lib("public_key/include/OTP-PUB-KEY.hrl").
-ifndef(HAVE_pubkey_cert__gen_test_certs_1).
% OTP 20.1
-export([gen_test_certs/1]).
-endif.
-ifndef(HAVE_pubkey_cert__root_cert_2).
% OTP 20.2
-export([root_cert/2]).
-endif.
-ifndef(HAVE_pubkey_cert__subject_id_1).
% OTP 23.1
-export([subject_id/1]).
-endif.
-ifndef(HAVE_pubkey_cert__gen_test_certs_1).
-ifdef(HAVE_pubkey_cert__root_cert_2).
-import(pubkey_cert, [root_cert/2]).
-endif.
-endif.
-ifndef(HAVE_pubkey_cert__gen_test_certs_1).
gen_test_certs(#{client_chain := #{root := ClientRoot, intermediates := ClientCAs, peer := ClientPeer},
server_chain := #{root := ServerRoot, intermediates := ServerCAs, peer := ServerPeer}}) ->
#{cert := ServerRootCert, key := ServerRootKey} = if
is_map(ServerRoot) -> ServerRoot;
is_list(ServerRoot) -> root_cert("SERVER ROOT CA", ServerRoot)
end,
#{cert := ClientRootCert, key := ClientRootKey} = if
is_map(ClientRoot) -> ClientRoot;
is_list(ClientRoot) -> root_cert("CLIENT ROOT CA", ClientRoot)
end,
[{ServerDERCert, ServerDERKey}|ServerCAsKeys] = config(server, ServerRootCert, ServerRootKey,
lists:reverse([ServerPeer|lists:reverse(ServerCAs)])),
[{ClientDERCert, ClientDERKey}|ClientCAsKeys] = config(client, ClientRootCert, ClientRootKey,
lists:reverse([ClientPeer|lists:reverse(ClientCAs)])),
#{server_config => [{cert, ServerDERCert}, {key, ServerDERKey}, {cacerts, ca_config(ClientRootCert, ServerCAsKeys)}],
client_config => [{cert, ClientDERCert}, {key, ClientDERKey},
{cacerts, ca_config(ServerRootCert, ClientCAsKeys)}]};
gen_test_certs(#{root := Root, intermediates := CAs, peer := Peer}) ->
#{cert := RootCert, key := RootKey} = if
is_map(Root) -> Root;
is_list(Root) -> root_cert("SERVER ROOT CA", Root)
end,
[{DERCert, DERKey}|CAsKeys] = config(server, RootCert, RootKey, lists:reverse([Peer|lists:reverse(CAs)])),
[{cert, DERCert}, {key, DERKey}, {cacerts, ca_config(RootCert, CAsKeys)}].
config(Role, Root, Key, Opts) -> cert_chain(Role, Root, Key, Opts).
cert_chain(Role, Root, RootKey, Opts) -> cert_chain(Role, Root, RootKey, Opts, 0, []).
cert_chain(Role, IssuerCert, IssuerKey, [PeerOpts], _, Acc) ->
Key = gen_key(proplists:get_value(key, PeerOpts, default_key_gen())),
[{cert(Role, public_key:pkix_decode_cert(IssuerCert, otp), IssuerKey, Key, "admin", " Peer cert", PeerOpts, peer),
encode_key(Key)},
{IssuerCert, encode_key(IssuerKey)}|Acc];
cert_chain(Role, IssuerCert, IssuerKey, [CAOpts|Rest], N, Acc) ->
Key = gen_key(proplists:get_value(key, CAOpts, default_key_gen())),
cert_chain(Role,
cert(Role, public_key:pkix_decode_cert(IssuerCert, otp), IssuerKey, Key, "webadmin", "
Intermediate CA " ++ integer_to_list(N), CAOpts, ca),
Key, Rest, N + 1, [{IssuerCert, encode_key(IssuerKey)}|Acc]).
cert(Role, #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Issuer}},
PrivKey, Key, Contact, Name, Opts, Type) ->
TBS = cert_template(),
SignAlgoId = sign_algorithm(PrivKey, Opts),
public_key:pkix_sign(TBS#'OTPTBSCertificate'{signature = SignAlgoId,
issuer = Issuer,
validity = validity(Opts),
subject = subject(Contact, atom_to_list(Role) ++ Name),
subjectPublicKeyInfo = public_key(Key, SignAlgoId),
extensions = extensions(Role, Type, Opts)},
PrivKey).
ca_config(Root, CAsKeys) -> [Root|[CA || {CA, _} <- CAsKeys]].
encode_key({#'RSAPrivateKey'{}, #'RSASSA-PSS-params'{}} = Key) ->
{Asn1Type, DER, _} = public_key:pem_entry_encode('PrivateKeyInfo', Key),
{Asn1Type, DER};
encode_key(#'RSAPrivateKey'{} = Key) -> {'RSAPrivateKey', public_key:der_encode('RSAPrivateKey', Key)};
encode_key(#'ECPrivateKey'{} = Key) -> {'ECPrivateKey', public_key:der_encode('ECPrivateKey', Key)};
encode_key(#'DSAPrivateKey'{} = Key) -> {'DSAPrivateKey', public_key:der_encode('DSAPrivateKey', Key)}.
-ifndef(NEED_default_key_gen_0).
-define(NEED_default_key_gen_0, true).
-endif.
-ifndef(NEED_gen_key_1).
-define(NEED_gen_key_1, true).
-endif.
-ifndef(NEED_cert_template_0).
-define(NEED_cert_template_0, true).
-endif.
-ifndef(NEED_sign_algorithm_2).
-define(NEED_sign_algorithm_2, true).
-endif.
-ifndef(NEED_validity_1).
-define(NEED_validity_1, true).
-endif.
-ifndef(NEED_subject_2).
-define(NEED_subject_2, true).
-endif.
-ifndef(NEED_public_key_2).
-define(NEED_public_key_2, true).
-endif.
-ifndef(NEED_extensions_3).
-define(NEED_extensions_3, true).
-endif.
-endif.
-ifndef(HAVE_pubkey_cert__root_cert_2).
root_cert(Name, Opts) ->
PrivKey = gen_key(proplists:get_value(key, Opts, default_key_gen())),
TBS = cert_template(),
Issuer = subject("root", Name),
SignatureId = sign_algorithm(PrivKey, Opts),
#{cert => public_key:pkix_sign(TBS#'OTPTBSCertificate'{signature = SignatureId,
issuer = Issuer,
validity = validity(Opts),
subject = Issuer,
subjectPublicKeyInfo = public_key(PrivKey, SignatureId),
extensions = extensions(undefined, ca, Opts)},
PrivKey),
key => PrivKey}.
-ifndef(NEED_default_key_gen_0).
-define(NEED_default_key_gen_0, true).
-endif.
-ifndef(NEED_gen_key_1).
-define(NEED_gen_key_1, true).
-endif.
-ifndef(NEED_cert_template_0).
-define(NEED_cert_template_0, true).
-endif.
-ifndef(NEED_sign_algorithm_2).
-define(NEED_sign_algorithm_2, true).
-endif.
-ifndef(NEED_validity_1).
-define(NEED_validity_1, true).
-endif.
-ifndef(NEED_subject_2).
-define(NEED_subject_2, true).
-endif.
-ifndef(NEED_public_key_2).
-define(NEED_public_key_2, true).
-endif.
-ifndef(NEED_extensions_3).
-define(NEED_extensions_3, true).
-endif.
-endif.
-ifndef(HAVE_pubkey_cert__subject_id_1).
subject_id(#'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Subject, serialNumber = SerialNr}}) ->
{SerialNr, pubkey_cert:normalize_general_name(Subject)}.
-endif.
-ifdef(NEED_default_key_gen_0).
default_key_gen() ->
case crypto:ec_curves() of
[] -> {rsa, 2048, 17};
[Curve|_] -> {namedCurve, pubkey_cert_records:namedCurves(Curve)}
end.
-endif.
-ifdef(NEED_gen_key_1).
gen_key(KeyGen) ->
case is_key(KeyGen) of
true -> KeyGen;
false -> public_key:generate_key(KeyGen)
end.
-compile({inline, is_key/1}).
is_key(#'DSAPrivateKey'{}) -> true;
is_key(#'RSAPrivateKey'{}) -> true;
is_key({#'RSAPrivateKey'{}, _}) -> true;
is_key(#'ECPrivateKey'{}) -> true;
is_key(_) -> false.
-endif.
-ifdef(NEED_cert_template_0).
cert_template() ->
#'OTPTBSCertificate'{version = v3,
serialNumber = erlang:unique_integer([positive, monotonic]),
issuerUniqueID = asn1_NOVALUE,
subjectUniqueID = asn1_NOVALUE}.
-endif.
-ifdef(NEED_sign_algorithm_2).
sign_algorithm(#'RSAPrivateKey'{} = Key , Opts) ->
rsa_sign_algo(Key,
rsa_digest_oid(proplists:get_value(digest, Opts,
case proplists:get_value(rsa_padding, Opts,
rsa_pkcs1_pss_padding) of
rsa_pkcs1_pss_padding -> sha1;
rsa_pss_rsae -> sha256
end)),
'NULL');
sign_algorithm({#'RSAPrivateKey'{} = Key, #'RSASSA-PSS-params'{} = Params}, _Opts) ->
rsa_sign_algo(Key, ?'id-RSASSA-PSS', Params);
sign_algorithm(#'DSAPrivateKey'{p = P, q = Q, g = G}, _Opts) ->
#'SignatureAlgorithm'{algorithm = ?'id-dsa-with-sha1', parameters = {params, #'Dss-Parms'{p = P, q = Q, g = G}}};
sign_algorithm(#'ECPrivateKey'{parameters = Parms}, Opts) ->
#'SignatureAlgorithm'{algorithm = ecdsa_digest_oid(proplists:get_value(digest, Opts, sha1)), parameters = Parms}.
rsa_sign_algo(#'RSAPrivateKey'{}, ?'id-RSASSA-PSS', #'RSASSA-PSS-params'{} = Params) ->
#'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS', parameters = Params};
rsa_sign_algo(#'RSAPrivateKey'{}, Type, Parms) -> #'SignatureAlgorithm'{algorithm = Type, parameters = Parms}.
-compile({inline, rsa_digest_oid/1}).
rsa_digest_oid(Oid) when is_tuple(Oid) -> Oid;
rsa_digest_oid(Oid) when Oid =:= sha1; Oid =:= sha -> ?'sha1WithRSAEncryption';
rsa_digest_oid(sha512) -> ?'sha512WithRSAEncryption';
rsa_digest_oid(sha384) -> ?'sha384WithRSAEncryption';
rsa_digest_oid(sha256) -> ?'sha256WithRSAEncryption';
rsa_digest_oid(md5) -> ?'md5WithRSAEncryption'.
-compile({inline, ecdsa_digest_oid/1}).
ecdsa_digest_oid(Oid) when is_tuple(Oid) -> Oid;
ecdsa_digest_oid(Oid) when Oid =:= sha1; Oid =:= sha -> ?'ecdsa-with-SHA1';
ecdsa_digest_oid(sha512) -> ?'ecdsa-with-SHA512';
ecdsa_digest_oid(sha384) -> ?'ecdsa-with-SHA384';
ecdsa_digest_oid(sha256) -> ?'ecdsa-with-SHA256'.
-endif.
-ifdef(NEED_validity_1).
validity(Opts) ->
{DefFrom, DefTo} = proplists:get_value(validity, Opts,
{calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date()) - 1),
calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date()) + 7)}),
GenFormat = fun({Y, M, D}) -> lists:flatten(io_lib:format("~4..0w~2..0w~2..0w130000Z", [Y, M, D])) end,
UTCFormat = fun({Y, M, D}) ->
[_, _, Y3, Y4] = integer_to_list(Y),
lists:flatten(io_lib:format("~s~2..0w~2..0w130000Z", [[Y3, Y4], M, D]))
end,
#'Validity'{notBefore = validity_format(DefFrom, GenFormat, UTCFormat),
notAfter = validity_format(DefTo, GenFormat, UTCFormat)}.
validity_format({Year, _, _} = Validity, GenFormat, _UTCFormat) when Year >= 2049 ->
{generalTime, GenFormat(Validity)};
validity_format(Validity, _GenFormat, UTCFormat) -> {utcTime, UTCFormat(Validity)}.
-endif.
-ifdef(NEED_subject_2).
subject(Contact, Name) ->
subject([{email, Contact ++ "@example.org"},
{name, Name},
{city, "Stockholm"},
{country, "SE"},
{org, "erlang"},
{org_unit, "automated testing"}]).
-compile({inline, subject/1}).
subject(SubjectOpts) when is_list(SubjectOpts) ->
{rdnSequence,
lists:map(fun(Opt) ->
{Type, Value} = subject_enc(Opt),
[#'AttributeTypeAndValue'{type = Type, value = Value}]
end,
SubjectOpts)}.
-compile({inline, subject_enc/1}).
subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
subject_enc({email, Email}) -> {?'id-emailAddress', Email};
subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
subject_enc({country, Country}) -> {?'id-at-countryName', Country}.
-endif.
-ifdef(NEED_public_key_2).
public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E},
#'SignatureAlgorithm'{algorithm = ?rsaEncryption, parameters = #'RSASSA-PSS-params'{} = Params}) ->
#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?rsaEncryption, parameters = Params},
subjectPublicKey = #'RSAPublicKey'{modulus = N, publicExponent = E}};
public_key({#'RSAPrivateKey'{modulus = N, publicExponent = E}, #'RSASSA-PSS-params'{} = Params},
#'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS', parameters = #'RSASSA-PSS-params'{} = Params}) ->
#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-RSASSA-PSS', parameters = Params},
subjectPublicKey = #'RSAPublicKey'{modulus = N, publicExponent = E}};
public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}, _) ->
#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?rsaEncryption, parameters = 'NULL'},
subjectPublicKey = #'RSAPublicKey'{modulus = N, publicExponent = E}};
public_key(#'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}, _) ->
#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-dsa',
parameters = {params,
#'Dss-Parms'{p = P, q = Q, g = G}}},
subjectPublicKey = Y};
public_key(#'ECPrivateKey'{parameters = Params, publicKey = PubKey}, _) ->
#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-ecPublicKey', parameters = Params},
subjectPublicKey = {'ECPoint', PubKey}}.
-endif.
-ifdef(NEED_extensions_3).
extensions(Role, Type, Opts) -> add_default_extensions(Role, Type, proplists:get_value(extensions, Opts, [])).
-compile({inline, add_default_extensions/3}).
add_default_extensions(_, ca, Exts) ->
add_default_extensions([#'Extension'{extnID = ?'id-ce-keyUsage', extnValue = [keyCertSign, cRLSign],
critical = false},
#'Extension'{extnID = ?'id-ce-basicConstraints', extnValue = #'BasicConstraints'{cA = true},
critical = true}],
Exts);
add_default_extensions(server, peer, Exts) ->
add_default_extensions([#'Extension'{extnID = ?'id-ce-keyUsage', extnValue = [digitalSignature, keyAgreement],
critical = false},
#'Extension'{extnID = ?'id-ce-subjectAltName', extnValue = [{dNSName, net_adm:localhost()}],
critical = false}],
Exts);
add_default_extensions(client, peer, Exts) -> Exts.
add_default_extensions(Defaults, Exts) ->
Exts ++ [Ext || #'Extension'{extnID = ID} = Ext <- Defaults, not lists:keymember(ID, 2, Exts)].
-endif.