Packages
otpbp
5.24.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
Current section
Files
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.