public_key(3)
πΈ NAME πΈ
public_key β okay bestie, this is the Erlang Module Definition for the public_key(3) manual page, and it's literally the API module for public-key infrastructure. π
π¦ DESCRIPTION π¦
So this module hands you all the functions you need to handle public-key infrastructure. If you want the deep lore, go read the public_key application docs, mwah. π
β¨ NOTE: literally all the records used in this Reference Manual are generated from ASN.1 specifications and they're documented in the User's Guide. See Public-key Records.
To actually get your hands on those records and the constant macros described in here (and in the User's Guide), you drop in this include directive, no cap:
-include_lib("public_key/include/public_key.hrl").
β¨ DATA TYPES β¨
π asn1_type()
asn1_type() :: atom().
An ASN.1 type that actually lives in the Public Key applications' ASN.1 specifications.
π bad_cert_reason()
bad_cert_reason() ::
cert_expired | invalid_issuer | invalid_signature |
distinguished_name_not_permitted | name_not_permitted |
missing_basic_constraint | invalid_key_usage |
duplicate_cert_in_path |
{key_usage_mismatch, term()} |
{policy_requirement_not_met, term()} |
{invalid_policy_mapping, term()} |
{revoked, crl_reason()} |
invalid_validity_dates | policy_tree_exceeded |
{revocation_status_undetermined, term()} |
atom().
The reason a certificate got rejected by certificate path validation. It's giving "here's exactly why you're not coming to the party" energy. π«
π cert()
cert() :: der_encoded() | #'OTPCertificate'{}.
An encoded or decoded certificate β she contains multitudes.
π§ cert_id()
cert_id() :: {SerialNr :: integer(), issuer_name()}.
A certificate is identified by its serial-number and Issuer Name. That's the whole identity, periodt.
β cert_opt()
cert_opt() ::
{digest, digest_type()} |
{key,
{namedCurve, oid()} |
#'ECParameters'{} |
{rsa, Size :: pos_integer(), Prime :: pos_integer()} |
private_key()} |
{validity,
{From :: erlang:timestamp(), To :: erlang:timestamp()}} |
{extensions, [#'Extension'{}]}.
Options to customize generated test certificates. Slay, custom test certs. π§ͺ
πΈ chain_opts()
chain_opts() ::
#{root := [cert_opt()],
intermediates => [[cert_opt()]],
peer := [cert_opt()]}.
Certificate customize options for different parts of the certificate test chain.
π¦ combined_cert()
combined_cert() :: #cert{}.
A record that can be used to provide the certificate on both the DER encoded and the OTP decode format.
Such a construct can be useful to avoid conversions and problems that can arise due to relaxed decoding rules. Avoiding conversion drama? iconic behavior.
π
conf_opt()
conf_opt() ::
{cert, der_encoded()} |
{key, private_key() | {both, public_key(), private_key()}} |
{cacerts, [der_encoded()]}.
Configuration options for the generated certificate test chain.
π₯ crl_reason()
crl_reason() ::
unspecified | keyCompromise | cACompromise |
affiliationChanged | superseded | cessationOfOperation |
certificateHold | privilegeWithdrawn | aACompromise.
The reason a certificate has been revoked, as defined by RFC 5280. Getting revoked is not the vibe. π
π custom_key_opts()
custom_key_opts() :: [term()].
Can be provided together with a custom private key, that specifies a key fun, to provide additional options understood by the fun.
π der_encoded()
der_encoded() :: binary().
ASN.1 DER encoded entity.
π digest_type()
digest_type() ::
crypto:sha2() | crypto:sha1() | legacy_digest_type() | none.
Hash function used to create a message digest.
π§ dsa_private_key()
dsa_private_key() :: #'DSAPrivateKey'{}.
ASN.1 defined private key format for the DSA algorithm.
β¨ dsa_public_key()
dsa_public_key() :: {dss_public_key(), #'Dss-Parms'{}}.
ASN.1 defined public key format for the DSA algorithm.
πΈ dss_public_key()
dss_public_key() :: pos_integer().
ASN.1 defined public key format for the DSS algorithm (part of DSA key).
π ecdsa_private_key()
ecdsa_private_key() :: #'ECPrivateKey'{}.
ASN.1 defined private key format for the ECDSA algorithm.
π¦ ecdsa_public_key()
ecdsa_public_key() ::
{#'ECPoint'{}, {namedCurve, oid()} | #'ECParameters'{}}.
ASN.1 defined public key format for the ECDSA algorithm.
π eddsa_private_key()
eddsa_private_key() ::
#'ECPrivateKey'{parameters :: {namedCurve, oid()}}.
ASN.1 defined private key format for the EDDSA algorithm. Possible oids: ?'id-Ed25519' | ?'id-Ed448'
β eddsa_public_key()
eddsa_public_key() :: {#'ECPoint'{}, {namedCurve, oid()}}.
ASN.1 defined public key format for the EDDSA algorithm. Possible oids: ?'id-Ed25519' | ?'id-Ed448'
π issuer_name()
issuer_name() :: {rdnSequence, [[#'AttributeTypeAndValue'{}]]}.
The value of the issuer part of a certificate.
π key_params()
key_params() ::
'NULL' |
#'RSASSA-PSS-params'{} |
{namedCurve, oid()} |
#'ECParameters'{} |
#'Dss-Parms'{}.
ASN.1 defined parameters for public key algorithms.
π legacy_digest_type()
legacy_digest_type() :: md5 | md2.
Yep, that's the whole thing. She's legacy, no description attached, we simply respect the elders. π«Ά
π§ ml_kem_private_key()
ml_kem_private_key() :: #'ML-KEMPrivateKey'{}.
ML-KEM private key.
πΈ ml_kem_public_key()
ml_kem_public_key() :: #'ML-KEMPublicKey'{}.
ML-KEM public key.
π mldsa_private_key()
mldsa_private_key() :: #'ML-DSAPrivateKey'{}.
ML-DSA private key.
π¦ mldsa_public_key()
mldsa_public_key() :: #'ML-DSAPublicKey'{}.
ML-DSA public key.
β oid()
oid() :: tuple().
Object identifier β a tuple of integers as generated by the ASN.1 compiler.
π pem_entry()
pem_entry() ::
{pki_asn1_type(),
DerOrDerEncrypted :: binary(),
not_encrypted |
{Cipher :: iodata(),
Salt ::
binary() |
{#'PBEParameter'{}, digest_type()} |
#'PBES2-params'{}}}.
Possible Ciphers are "RC2-CBC" | "DES-CBC" | "DES-EDE3-CBC". Salt could be generated with crypto:strong_rand_bytes(8).
π₯ pki_asn1_type()
pki_asn1_type() ::
'Certificate' | 'RSAPrivateKey' | 'RSAPublicKey' |
'SubjectPublicKeyInfo' | 'DSAPrivateKey' | 'DHParameter' |
'PrivateKeyInfo' | 'CertificationRequest' | 'ContentInfo' |
'CertificateList' | 'ECPrivateKey' | 'OneAsymmetricKey' |
'EcpkParameters'.
The ASN.1 type that can be found in PEM files that can be decoded by the public_key application.
π policy_node()
policy_node() ::
#{valid_policy := oid(),
qualifier_set := [#'UserNotice'{} | {uri, string()}],
expected_policy_set := [oid()]}.
Certificate policy information.
π
private_key()
private_key() ::
rsa_private_key() |
rsa_pss_private_key() |
dsa_private_key() |
ecdsa_private_key() |
eddsa_private_key() |
mldsa_private_key() |
ml_kem_private_key() |
slh_dsa_private_key() |
#{algorithm :=
slh_dsa | mldsa | eddsa | rsa_pss_pss | ecdsa | rsa |
dsa,
sign_fun => fun()}.
Supported private keys. Yes queen, there are a lot of them.
π public_key()
public_key() ::
rsa_public_key() |
rsa_pss_public_key() |
dsa_public_key() |
ecdsa_public_key() |
eddsa_public_key() |
mldsa_public_key() |
ml_kem_public_key() |
slh_dsa_public_key().
Supported public keys.
β¨ public_key_info()
public_key_info() ::
{oid(),
rsa_public_key() | #'ECPoint'{} | dss_public_key(),
key_params()}.
Information a certificate's public key.
Possible oids: ?'rsaEncryption' | ?'id-RSASSA-PSS' | ?'id-ecPublicKey' | ?'id-Ed25519' | ?'id-Ed448' | ?'id-dsa'
π rsa_private_key()
rsa_private_key() ::
#'RSAPrivateKey'{} | #{algorithm := rsa, encrypt_fun => fun()}.
ASN.1 defined private key format, plain RSA algorithm, or customization fun.
π§ rsa_pss_private_key()
rsa_pss_private_key() ::
{#'RSAPrivateKey'{}, #'RSASSA-PSS-params'{}}.
ASN.1 defined private key format, the RSSASSA-PSS algorithm, or customization fun.
πΈ rsa_pss_public_key()
rsa_pss_public_key() :: {rsa_public_key(), #'RSASSA-PSS-params'{}}.
ASN.1 defined public key format for the RSSASSA-PSS algorithm.
π rsa_public_key()
rsa_public_key() :: #'RSAPublicKey'{}.
ASN.1 defined public key format for plain RSA algorithm.
π¦ slh_dsa_private_key()
slh_dsa_private_key() :: #'SLH-DSAPrivateKey'{}.
SLH-DSA private key.
β slh_dsa_public_key()
slh_dsa_public_key() :: #'SLH-DSAPublicKey'{}.
SLH-DSA public key.
π FUNCTIONS π
π§Ή cacerts_clear() -> boolean().
cacerts_clear() -> boolean().
Clears any loaded CA certificates, and returns true if any was actually loaded.
π«Ά cacerts_get() -> [combined_cert()].
cacerts_get() -> [combined_cert()].
Returns the trusted CA certificates if any are loaded. If none are loaded, it uses cacerts_load/0 to load them. The function fails if no cacerts could be loaded. π
π cacerts_load() -> ok | {error, Reason :: term()}.
cacerts_load() -> ok | {error, Reason :: term()}.
Loads the OS supplied trusted CA certificates.
This can be overridden by setting the cacerts_path environment key of the public_key application with the location of an alternative certificate. You can set it via the command line as:
erl -public_key cacerts_path '"/path/to/certs.pem"'
π¨ Use it with care. It is your responsibility to ensure that the certificates found in this alternative path can be trusted by the running system. No pressure, bestie. π
π cacerts_load(File :: file:filename_all()) -> ok | {error, Reason :: term()}.
cacerts_load(File :: file:filename_all()) ->
ok | {error, Reason :: term()}.
Loads the trusted CA certificates from a file.
π€ compute_key(OthersECDHkey, MyECDHkey) -> SharedSecret
compute_key(OthersECDHkey, MyECDHkey) -> SharedSecret
when
OthersECDHkey :: #'ECPoint'{},
MyECDHkey :: #'ECPrivateKey'{},
SharedSecret :: binary().
Computes shared secret. π€«β¨
π€ compute_key(OthersDHkey, MyDHkey, DHparms) -> SharedSecret
compute_key(OthersDHkey, MyDHkey, DHparms) -> SharedSecret
when
OthersDHkey :: crypto:dh_public(),
MyDHkey :: crypto:dh_private(),
DHparms :: #'DHParameter'{},
SharedSecret :: binary().
Computes shared secret. Again. Sharing is caring. π
π decrypt_private(CipherText, Key, Options) -> PlainText
decrypt_private(CipherText, Key, Options) -> PlainText
when
CipherText :: binary(),
Key :: rsa_private_key(),
Options :: crypto:pk_encrypt_decrypt_opts(),
PlainText :: binary().
Public-key decryption using the private key. See also crypto:private_decrypt/4.
β οΈ WARNING: This is a legacy function. For security reasons, do not use with rsa_pkcs1_padding. I'm serious, girlie. π
okay and that's just the setup β the rest is still rolling in, so don't go anywhere π
decrypt_public(CipherText, Key, Options) -> PlainTextπ
decrypt_public(CipherText, Key, Options) -> PlainText
when
CipherText :: binary(),
Key :: rsa_public_key(),
Options :: crypto:pk_encrypt_decrypt_opts(),
PlainText :: binary().
Public-key decryption β but like, using the public key, bestie. yes really. also go say hi to crypto:public_decrypt/4 for the crypto-side twin π«Ά
WARNING: this is a legacy function and for security reasons you should not use it withrsa_pkcs1_padding. For digital signatures, usingverify/4together withsign/3is the preferred solution. do not skip this part, i'm literally begging π
der_decode(Asn1Type, Der) -> EntityπΈ
der_decode(Asn1Type, Der) -> Entity
when
Asn1Type :: asn1_type(),
Der :: der_encoded(),
Entity :: term().
decodes a public-key ASN.1 DER encoded entity β¨
der_encode(Asn1Type, Entity) -> Derπ
der_encode(Asn1Type, Entity) -> Der
when
Asn1Type :: asn1_type(),
Entity :: term(),
Der :: binary().
encodes a public-key entity with ASN.1 DER encoding. it's giving encode, it's giving decode, iconic duo fr fr π
dh_gex_group(MinSize, SuggestedSize, MaxSize, Groups) -> {ok, {Size, Group}} | {error, term()}π
dh_gex_group(MinSize, SuggestedSize, MaxSize, Groups) ->
{ok, {Size, Group}} | {error, term()}
when
MinSize :: pos_integer(),
SuggestedSize :: pos_integer(),
MaxSize :: pos_integer(),
Groups :: undefined | [{Size, [Group]}],
Size :: pos_integer(),
Group :: {G, P},
G :: pos_integer(),
P :: pos_integer().
picks a group for Diffie-Hellman key exchange, where the key size sits in the range MinSize...MaxSize and lands as close to SuggestedSize as possible. if Groups == undefined you get the default set, otherwise it grabs the group from Groups π¦
so the order of operations is: first it picks a size as close as possible to SuggestedSize, then it randomly picks one group with that key size out of the set you gave it. and if there's literally no size available inside the MinSize/MaxSize limits? you get {error,no_group_found}. no group found, no group bestie, that's the whole vibe π
the default set of groups is listed in lib/public_key/priv/moduli. you can regenerate that file, like this:
$> cd $ERL_TOP/lib/public_key/priv/
$> generate
---- wait until all background jobs has finished. It may take several days !
$> cat moduli-* > moduli
$> cd ..; make
heads up: that "wait until all background jobs has finished" is not a drill β it may take several days. pack a snack π
encrypt_private(PlainText, Key, Options) -> CipherTextπ§
encrypt_private(PlainText, Key, Options) -> CipherText
when
PlainText :: binary(),
Key :: rsa_private_key(),
Options ::
crypto:pk_encrypt_decrypt_opts() |
custom_key_opts(),
CipherText :: binary().
public-key encryption using the private key. yes, the naming situation is what it is, we don't make the rules π
see also crypto:private_encrypt/4. and here's the fun part: besides a standard RSA key, the key can be a map specifying the key algorithm rsa and a fun to handle the encryption operation. that's your moment if you want a customized encryption operation with, like, hardware security modules (HSM) or trusted platform modules (TPM) π
WARNING: this is a legacy function β for security reasons do not use it withrsa_pkcs1_padding. For digital signatures,sign/3together withverify/4is the preferred solution. periodt.
encrypt_public(PlainText, Key, Options) -> CipherTextπ₯
encrypt_public(PlainText, Key, Options) -> CipherText
when
PlainText :: binary(),
Key :: rsa_public_key(),
Options :: crypto:pk_encrypt_decrypt_opts(),
CipherText :: binary().
public-key encryption using the public key. see also crypto:public_encrypt/4 β¨
WARNING: another legacy function β for security reasons, do not use it with rsa_pkcs1_padding. that's the whole warning, bestie, take it seriously π
generate_key(DHparams | ECparams | RSAparams | MLDSA | SLHDSA) -> DHkeys | ECkey | RSAkey | MLDSAKeys | SLHDSAKeysπ
generate_key(DHparams | ECparams | RSAparams | MLDSA | SLHDSA) ->
DHkeys | ECkey | RSAkey | MLDSAKeys | SLHDSAKeys
when
DHparams :: #'DHParameter'{},
DHkeys ::
{PublicDH :: binary(),
PrivateDH :: binary()},
ECparams ::
{namedCurve, oid() | atom()} |
#'ECParameters'{},
ECkey :: #'ECPrivateKey'{},
RSAparams :: {rsa, Size, PubExp},
Size :: pos_integer(),
PubExp :: pos_integer(),
RSAkey :: #'RSAPrivateKey'{},
MLDSA :: crypto:mldsa(),
MLDSAKeys ::
{#'ML-DSAPublicKey'{},
#'ML-DSAPrivateKey'{}},
SLHDSA :: crypto:slh_dsa(),
SLHDSAKeys ::
{#'SLH-DSAPublicKey'{},
#'SLH-DSAPrivateKey'{}}.
generates a brand new key pair β¨ and here's the tea: except for Diffie-Hellman, the public key is included inside the private key structure. so no, you're not getting a separate little public key package, it's all in there π
see also crypto:generate_key/2 π«Ά
pem_decode(binary()) -> [pem_entry()]π
decodes PEM binary data and hands you back the entries as ASN.1 DER encoded entities.
example, bestie:
{ok, PemBin} = file:read_file("cert.pem").
PemEntries = public_key:pem_decode(PemBin).
pem_encode([pem_entry()]) -> binary()π
creates a PEM binary. that's it, that's the function π
pem_entry_decode(PemEntry, Password) -> term()π
pem_entry_decode(PemEntry, Password) -> term()
when
PemEntry :: pem_entry(),
Password :: iodata() | fun(() -> iodata()).
decodes one PEM entry. remember, pem_decode/1 gives you a whole list of PEM entries, so you pick one and feed it here π¦
oh and important: if the PEM entry is of type 'SubjectPublickeyInfo', it gets further decoded into an rsa_public_key/0 or dsa_public_key/0. surprise upgrade, slay β¨
Password can be either an octet string, or a function which returns that same type πΈ
pem_entry_encode(Asn1Type, Entity, InfoPwd) -> pem_entry()π
pem_entry_encode(Asn1Type, Entity, InfoPwd) -> pem_entry()
when
Asn1Type :: pki_asn1_type(),
Entity :: term(),
InfoPwd :: {CipherInfo, Password},
CipherInfo ::
{Cipher :: iodata(),
Salt ::
binary() |
{#'PBEParameter'{},
digest_type()} |
#'PBES2-params'{}},
Password :: iodata().
creates a PEM entry that you can then feed straight into pem_encode/1 π½οΈ
and if Asn1Type is 'SubjectPublicKeyInfo', then Entity must be either an rsa_public_key/0, a dsa_public_key/0 or an ecdsa_public_key/0, and this function builds the appropriate 'SubjectPublicKeyInfo' entry for you. it literally does the whole thing, main character energy π
pkix_crl_issuer(CRL) -> Issuerπ΅οΈ
pkix_crl_issuer(CRL) -> Issuer
when
CRL :: der_encoded() | #'CertificateList'{},
Issuer :: issuer_name().
returns the issuer of the CRL. who signed it? now you know π
pkix_crl_verify(CRL, Cert) -> boolean()β
pkix_crl_verify(CRL, Cert) -> boolean()
when
CRL :: der_encoded() | #'CertificateList'{},
Cert :: cert().
verifies that Cert is the CRL signer. vibe check on the signer, basically π
pkix_crls_validate(OTPcertificate, DPandCRLs, Options) -> CRLstatusπ
pkix_crls_validate(OTPcertificate, DPandCRLs, Options) ->
CRLstatus
when
OTPcertificate :: #'OTPCertificate'{},
DPandCRLs :: [DPandCRL],
DPandCRL :: {DP, {DerCRL, CRL}},
DP :: #'DistributionPoint'{},
DerCRL :: der_encoded(),
CRL :: #'CertificateList'{},
Options :: [{atom(), term()}],
CRLstatus ::
valid | {bad_cert, BadCertReason},
BadCertReason ::
revocation_status_undetermined |
{revocation_status_undetermined,
Reason :: term()} |
{revoked, crl_reason()}.
performs CRL validation. it's meant to be called from the verify fun of pkix_path_validation/3 β that's its whole calling context, iykyk π«Ά
available options:
{update_crl, fun()}β this fun has the following type specification:
fun(#'DistributionPoint'{}, #'CertificateList'{}) ->
#'CertificateList'{}
the fun uses the info in the distribution point to go get the latest possible version of the CRL. don't specify it? then Public Key just uses the default implementation:
fun(_DP, CRL) -> CRL end
{issuer_fun, {fun(), UserState::term()}}β this fun has the following type specification:
fun(#'DistributionPoint'{}, #'CertificateList'{},
{rdnSequence,[#'AttributeTypeAndValue'{}]}, UserState::term()) ->
{ok, #'OTPCertificate'{}, [der_encoded]}
the fun returns the root certificate and the certificate chain that signed the CRL.
fun(DP, CRL, Issuer, UserState) -> {ok, RootCert, CertChain}
{undetermined_details, boolean()}β defaults tofalse. when the revocation status can't be determined, and you've set this option totrue, the details of why no CRLs were accepted get included in the return value. so you actually find out what happened instead of just vibing in the dark π
pkix_decode_cert(Cert, Type) -> #'Certificate'{} | #'OTPCertificate'{}π
pkix_decode_cert(Cert, Type) ->
#'Certificate'{} | #'OTPCertificate'{}
when
Cert :: der_encoded(),
Type :: plain | otp | relaxed.
decodes an ASN.1 DER-encoded PKIX certificate β¨
the otp option uses the customized ASN.1 specification OTP-PKIX.asn1 for decoding, and it also recursively decodes most of the standard parts. so it's the otp flavor of decode, and it goes deep π
- Option
relaxedis lowkey justotpwith β¨permissionβ¨ β it's similar to optionotp, but it also letsissuerandsubjecthave empty RDNs. Live your truth bestie.
pkix_dist_point(Cert) -> DistPoint
pkix_dist_point(Cert) -> DistPoint
when
Cert :: cert(),
DistPoint :: #'DistributionPoint'{}.
Builds a distribution point for CRLs issued by the same issuer as Cert, bestie π
. You can feed it straight into pkix_crls_validate/3.
pkix_dist_points(Cert) -> DistPoints
pkix_dist_points(Cert) -> DistPoints
when
Cert :: cert(),
DistPoints :: [#'DistributionPoint'{}].
Extracts all the distribution points out of the certificate's extensions β¨. Plural energy, plural points, we love to see it.
pkix_encode(Asn1Type, Entity, Type) -> Der
pkix_encode(Asn1Type, Entity, Type) -> Der
when
Asn1Type :: asn1_type(),
Entity :: term(),
Type :: otp | plain,
Der :: der_encoded().
DER-encodes a PKIX x509 certificate, or honestly just a part of one π.
So here's the tea: you must use this function for encoding certificates (or parts of certificates) that were decoded/created in the otp format. But for the plain format? This function just goes "k, bye" and directly calls der_encode/2. Same destination, different route π«.
β οΈ NOTE: Subtle ASN-1 encoding errors in certificates may be worked around when decoding, and this may have the effect that encoding a certificate back to DER may generate different bytes than the supplied original. So yeah β not a perfect round-trip, no cap. Don't be shocked π.
pkix_hash_type(HashOid :: oid()) -> DigestType :: digest_type()
Translates an OID into an Erlang digest type πΈ. Tiny function, big job.
pkix_is_fixed_dh_cert(Cert) -> boolean() when Cert :: cert()
Checks if a certificate is a fixed Diffie-Hellman certificate π₯. Yes/no, that's the whole vibe check.
pkix_is_issuer(CertorCRL, IssuerCert) -> boolean()
pkix_is_issuer(CertorCRL, IssuerCert) -> boolean()
when
CertorCRL :: cert() | #'CertificateList'{},
IssuerCert :: cert().
Checks if IssuerCert actually issued Cert π. Like, "did you sign this?" energy.
pkix_is_self_signed(Cert) -> boolean() when Cert :: cert()
Checks if a certificate is self-signed β¨. Main character behavior tbh β signing your own thing.
pkix_issuer_id(Cert, IssuedBy) -> {ok, ID :: cert_id()} | {error, Reason}
pkix_issuer_id(Cert, IssuedBy) ->
{ok, ID :: cert_id()} | {error, Reason}
when
Cert :: cert(),
IssuedBy :: self | other,
Reason :: term().
Returns the x509 certificate's issuer id β if it can be determined, bestie π«Ά. Sometimes it just can't, and that's what the {error, Reason} is for.
pkix_match_dist_point(CRL, DistPoint) -> boolean()
pkix_match_dist_point(CRL, DistPoint) -> boolean()
when
CRL ::
der_encoded() |
#'CertificateList'{},
DistPoint :: #'DistributionPoint'{}.
Checks whether the given distribution point matches the Issuing Distribution Point of the CRL, exactly as described in RFC 5280 π .
And here's the cute part: if the CRL doesn't have an Issuing Distribution Point extension, the distribution point always matches π. Free win.
pkix_normalize_name(Issuer) -> Normalized
pkix_normalize_name(Issuer) -> Normalized
when
Issuer :: issuer_name() |
der_encoded(),
Normalized :: issuer_name().
Normalizes an issuer name so it can be easily compared to another issuer name β¨. Basically giving two names a glow-up so they can vibe on the same wavelength.
pkix_ocsp_validate(Cert, IssuerCert, OcspRespDer, NonceExt, Options) -> {ok, Details} | {error, {bad_cert, Reason}}
pkix_ocsp_validate(Cert, IssuerCert, OcspRespDer, NonceExt,
Options) ->
{ok, Details} | {error, {bad_cert, Reason}}
when
Cert :: cert(),
IssuerCert :: cert(),
OcspRespDer :: der_encoded(),
NonceExt :: undefined | binary(),
Options ::
[{is_trusted_responder_fun,
fun((combined_cert()) ->
boolean)}],
Details :: list(),
Reason :: bad_cert_reason().
Does OCSP response validation according to RFC 6960 π. It returns {'ok', Details} when the OCSP response is successfully validated, and {error, {bad_cert, Reason}} otherwise. One or the other, no in-between.
Available options, bestie:
{is_trusted_responder_fun, fun()}β the fun has this type specification:
fun(#cert{}) ->
boolean()
The fun returns true if the certificate in the argument is trusted. If you don't specify this fun, Public Key just uses the default implementation:
fun(_) -> false end
β οΈ NOTE: An OCSP response can be provided without a nonce value β even if it was requested by the client π. In those cases,{missing, ocsp_nonce}will be returned in theDetailslist. So check your Details, girlies.
pkix_path_validation(Cert, CertChain, Options) -> {ok, {PublicKeyInfo, ConstrainedPolicyNodes}} | {error, {bad_cert, Reason :: bad_cert_reason()}}
pkix_path_validation(Cert, CertChain, Options) ->
{ok,
{PublicKeyInfo, ConstrainedPolicyNodes}}
|
{error,
{bad_cert, Reason :: bad_cert_reason()}}
when
Cert ::
cert() | combined_cert() | atom(),
CertChain ::
[cert() | combined_cert()],
Options ::
[{max_path_length, integer()} |
{verify_fun, {fun(), term()}}],
PublicKeyInfo :: public_key_info(),
ConstrainedPolicyNodes ::
[policy_node()].
Performs a basic path validation according to RFC 5280 π.
Small plot twist though: CRL validation is done separately by pkix_crls_validate/3, and you're supposed to call it from the supplied verify_fun. That's the arrangement, periodt.
The policy tree check was added in OTP-26.2, and if the certificates include policies, the constrained policy set with potential qualifiers will be returned β¨. These values are derived from the policy tree created as part of the path validation algorithm. The constrained set can be constrained only by the Certificate Authorities, or also by the user when the option policy_set is provided to this function. The qualifiers convey information about the valid policy and are intended as information to end users π«Ά.
Available options:
{verify_fun, {fun(), UserState::term()}β the fun must be defined as:
fun(OtpCert :: #'OTPCertificate'{},
Event :: {bad_cert, Reason :: bad_cert_reason() | {revoked, atom()}} |
{extension, #'Extension'{}},
UserState :: term()) ->
{valid, UserState :: term()} |
{valid_peer, UserState :: term()} |
{fail, Reason :: term()} |
{unknown, UserState :: term()}.
or as:
fun(OtpCert :: #'OTPCertificate'{},
DerCert :: der_encoded(),
Event :: {bad_cert, Reason :: bad_cert_reason() | {revoked, atom()}} |
{extension, #'Extension'{}},
UserState :: term()) ->
{valid, UserState :: term()} |
{valid_peer, UserState :: term()} |
{fail, Reason :: term()} |
{unknown, UserState :: term()}.
The verify callback can have 3 or 4 arguments, in case the DER encoded version is needed by the callback π«.
If the verify callback fun returns {fail, Reason}, the verification process is immediately stopped π. If the verify callback fun returns {valid, UserState}, the verification process is continued. This can be used to accept specific path validation errors, such as selfsigned_peer, as well as verifying application-specific extensions. If it's called with an extension unknown to the user application, the return value {unknown, UserState} is the one to use πΈ.
β οΈ NOTE: If you need the DER encoded version of the certificate and you have the OTP decoded version, encoding it back can fail to give the correct result β due to workarounds for common misbehaving encoders π. So it is recommended to callpkix_path_validationwithCertandCertChainarguments asder_encoded() | #cert{}and[der_encoded() | #cert{}]. Also note that the path validation itself needs both the encoded and the decoded version of the certificate. Both. It's giving overachiever.
π¨ WARNING: Note that user defined customverify_funmay alter original path validation error (e.gselfsigned_peer). Use with caution, bestie. Like, actually be careful.
{max_path_length, integer()}β themax_path_lengthis the maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path. So:- if
max_path_lengthis0, the PEER must be signed by the trusted ROOT-CA directly π - if it is
1, the path can be PEER, CA, ROOT-CA - if it is
2, the path can be PEER, CA, CA, ROOT-CA - and so on, forever and ever, chain chain chain β¨
- if
{policy_set, [oid()]}β (Since OTP 26.2) The set of policies that will be accepted, defaults to the special value[?anyPolicy]that will accept all policies. Iconic default behavior, honestly.{explicit_policy, boolean()}β (Since OTP 26.2) Explicitly require that each certificate in the path must include at least one of the certificate policies in thepolicy_set. No freeloaders π .{inhibit_policy_mapping, boolean()}β (Since OTP 26.2) Prevent policies to be mapped to other policies. Stay in your lane.{inhibit_any_policy, boolean()}β (Since OTP 26.2) Prevent the special policy?anyPolicyfrom being accepted. Not today.
π Explanations of reasons for a bad certificate π
Here's the lore, so you know exactly why it's mad at you:
cert_expiredβ Certificate is no longer valid as its expiration date has passed. She's expired, hun. Let her go.invalid_issuerβ Certificate issuer name does not match the name of the issuer certificate in the chain. Identity crisis detected π©.invalid_signatureβ Certificate was not signed by its issuer certificate in the chain. Somebody forged that signature, and we noticed.distinguished_name_not_permittedβ Subject Name does not adhere to name constraints, which is mandatory in RFC 5280. Rules are rules, bestie πΈ.
Okay so the error reasons you might catch are giving choices, and some of them are lowkey scary but here's the tea:
name_not_permittedβ Subject Alternative Name does not adhere to name constraints, which is optional in RFC 5280. πmissing_basic_constraintβ Certificate, required to have the basic constraints extension, does not have a basic constraints extension. πinvalid_key_usageβ Certificate key is used in an invalid way according to the key-usage extension. π«{revoked, crl_reason()}β Certificate has been revoked. Bye bestie πinvalid_validity_datesβ The validity section of the X.509 certificate(s) contains invalid date formats not matching the RFC. πpolicy_tree_exceededβ The certificate chain's policy tree exceeded the maximum allowed node count. This indicates a malformed or malicious chain with exponentially expanding policy mappings. This error cannot be overridden by theverify_fun. β οΈatom()β Application-specific error reason that is to be checked by theverify_fun. π§
pkix_sign βοΈ
pkix_sign(Cert, Key) -> Der
when
Cert :: #'OTPTBSCertificate'{},
Key :: private_key(),
Der :: der_encoded().
Signs an 'OTPTBSCertificate'. Returns the corresponding DER-encoded certificate. Slay, now your cert is signed and ready to serve. π
pkix_sign_types π
pkix_sign_types(AlgorithmId) -> {DigestType, SignatureType}
when
AlgorithmId :: oid(),
DigestType :: digest_type(),
SignatureType ::
rsa | dsa | ecdsa | eddsa |
crypto:mldsa() |
crypto:slh_dsa().
Translates signature algorithm OID to Erlang digest and signature types. The AlgorithmId is the signature OID from a certificate or a certificate revocation list. Basically it's the translator bestie. π£οΈ
pkix_subject_id πͺͺ
pkix_subject_id(Cert) -> ID when Cert :: cert(), ID :: cert_id().
Returns the X509 certificate subject id. It's giving ID check. π
pkix_test_data π§ͺ
pkix_test_data(ChainConf) -> TestConf
when
ChainConf ::
#{server_chain := chain_opts(),
client_chain := chain_opts()} |
chain_opts(),
TestConf ::
#{server_config := [conf_opt()],
client_config := [conf_opt()]} |
[conf_opt()].
Creates certificate configuration(s) consisting of certificate and its private key plus CA certificate bundle, for a client and a server, intended to facilitate automated testing of applications using X509-certificates, often through SSL/TLS. The test data can be used when you have control over both the client and the server in a test scenario. It's the ultimate test-mode glow-up. β¨
When this function is called with a map containing client and server chain specifications; it generates both a client and a server certificate chain where the cacerts returned for the server contains the root cert the server should trust and the intermediate certificates the server should present to connecting clients. The root cert the server should trust is the one used as root of the client certificate chain. Vice versa applies to the cacerts returned for the client. The root cert(s) can either be pre-generated with pkix_test_root_cert/2 , or if options are specified; it is (they are) generated.
When this function is called with a list of certificate options; it generates a configuration with just one node certificate where cacerts contains the root cert and the intermediate certs that should be presented to a peer. In this case the same root cert must be used for all peers. This is useful in for example an Erlang distributed cluster where any node, towards another node, acts either as a server or as a client depending on who connects to whom. The generated certificate contains a subject altname, which is not needed in a client certificate, but makes the certificate useful for both roles.
Explanation of the options used to customize certificates in the generated chains:
{digest, digest_type()}β Hash algorithm to be used for signing the certificate together with thekeyoption. Defaults toshathat issha1. π{key, ec_params()| {rsa, Size:pos_integer(), Prime::pos_integer()} | private_key()}β Parameters to be used to callpublic_key:generate_key/1, to generate a key, or an existing key. Defaults to generating an ECDSA key. Note this could fail if Erlang/OTP is compiled with a very old cryptolib. π{validity, {From::erlang:timestamp(), To::erlang:timestamp()}}β The validity period of the certificate. π{extensions, [#'Extension'{}]}β Extensions to include in the certificate.
Default extensions included in CA certificates if not otherwise specified are:
[#'Extension'{extnID = ?'id-ce-keyUsage',
extnValue = [keyCertSign, cRLSign],
critical = false},
#'Extension'{extnID = ?'id-ce-basicConstraints',
extnValue = #'BasicConstraints'{cA = true},
critical = true}]
Default extensions included in the server peer cert if not otherwise specified are:
[#'Extension'{extnID = ?'id-ce-keyUsage',
extnValue = [digitalSignature, keyAgreement],
critical = false},
#'Extension'{extnID = ?'id-ce-subjectAltName',
extnValue = [{dNSName, Hostname}],
critical = false}]
Hostname is the result of calling net_adm:localhost() in the Erlang node where this function is called.
NOTE: Note that the generated certificates and keys does not provide a formally correct PKIX-trust-chain and they cannot be used to achieve real security. This function is provided for testing purposes only. π¨
pkix_test_root_cert π±
pkix_test_root_cert(Name, Options) -> RootCertAndKey
when
Name :: string(),
Options :: [cert_opt()],
RootCertAndKey ::
#{cert := der_encoded(),
key := private_key()}.
Generates a root certificate that can be used in multiple calls to pkix_test_data/1 when you want the same root certificate for several generated certificates. It's the mother of all test certs. π
pkix_verify β
pkix_verify(Cert, Key) -> boolean()
when Cert :: der_encoded(), Key :: public_key().
Verifies PKIX x.509 certificate signature. Periodt. If it's valid, you get a boolean; if not, you get a sad boolean. π
pkix_verify_hostname π¦
pkix_verify_hostname(Cert, ReferenceIDs, Options) -> boolean()
when
Cert :: cert(),
ReferenceIDs ::
[{uri_id | dns_id | ip | srv_id |
atom() |
oid(),
string()} |
{ip,
inet:ip_address() | string()}],
Options ::
[{match_fun | fail_callback |
fqdn_fun,
fun()}].
This function checks that the Presented Identifier (e.g hostname) in a peer certificate is in agreement with at least one of the Reference Identifier that the client expects to be connected to. It's the vibe check for hostnames. π
The function is intended to be added as an extra client check of the peer certificate when performing public_key:pkix_path_validation/3
See RFC 6125 for detailed information about hostname verification. The User's Guide and code examples describes this function more detailed.
The option funs are described here:
match_funβ
fun(ReferenceId::ReferenceId() | FQDN::string(),
PresentedId::{dNSName,string()} | {uniformResourceIdentifier,string() |
{iPAddress,list(byte())} | {OtherId::atom()|oid(),term()}})
This function replaces the default host name matching rules. The fun should return a boolean to tell if the Reference ID and Presented ID matches or not. The match fun can also return a third value, value, the atom default, if the default matching rules shall apply. This makes it possible to augment the tests with a special case:
fun(....) -> true; % My special case
(_, _) -> default % all others falls back to the inherit tests
end
See pkix_verify_hostname_match_fun/1 for a function that takes a protocol name as argument and returns a fun/2 suitable for this option and Re-defining the match operation in the User's Guide for an example.
NOTE: Reference Id values given as binaries will be converted to strings, and ip references may be given in string format that is"10.0.1.1"or"1234::5678:9012"as well as on the formatinet:ip_address/0
fail_callbackβ If a matching fails, there could be circumstances when the certificate should be accepted anyway. Think for example of a web browser where you choose to accept an outdated certificate. This option enables implementation of such an exception but for hostnames. Thisfun/1is called when noReferenceIDmatches. The return value of the fun (aboolean/0) decides the outcome. Iftruethe certificate is accepted otherwise it is rejected. See "Pinning" a Certificate in the User's Guide. π«Άfqdn_funβ This option augments the host name extraction from URIs and other Reference IDs. It could for example be a very special URI that is not standardised. The fun takes a Reference ID as argument and returns one of:- the hostname
defaultβ the default host name extract function will be used πundefinedβ a host name could not be extracted. Thepkix_verify_hostname/3will returnfalseπ
For an example, see Hostname extraction in the User's Guide.
β¨ pkix_verify_hostname_match_fun/1 β¨
pkix_verify_hostname_match_fun(Protocol) -> Result
when
Protocol :: https,
Result :: fun().
The return value of calling this function is intended to be used in the match_fun option in pkix_verify_hostname/3.
The returned fun augments the verify hostname matching according to the specific rules for the protocol in the argument.
NOTE: Currently supported https fun will allow wildcard certificate matching as specified by the HTTP standard. Note that for instance LDAP have a different set of wildcard matching rules. If you do not want to allow wildcard certificates (recommended from a security perspective) or otherwise customize the hostname match the default match function used by ssl application will be sufficient.
β¨ short_name_hash/1 β¨
short_name_hash(Name) -> string() when Name :: issuer_name().
Generates a short hash of an issuer name. The hash is returned as a string containing eight hexadecimal digits.
The return value of this function is the same as the result of the commands openssl crl -hash and openssl x509 -issuer_hash, when passed the issuer name of a CRL or a certificate, respectively. This hash is used by the c_rehash tool to maintain a directory of symlinks to CRL files, in order to facilitate looking up a CRL by its issuer name.
β¨ sign/5 β¨
sign(Msg, DigestType, Key, Options) -> Signature
when
Msg :: binary() | {digest, binary()},
DigestType :: digest_type(),
Key :: private_key(),
Options ::
crypto:pk_sign_verify_opts() | custom_key_opts(),
Signature :: binary().
Creates a digital signature.
The Msg is either the binary "plain text" data to be signed or it is the hashed value of "plain text", that is, the digest. The key can, besides a standard key, be a map specifing a key algorithm and a fun that should handle the signing. This may be used for customized signing with for instance hardware security modules (HSM) or trusted platform modules (TPM).
β¨ verify/5 β¨
verify(Msg, DigestType, Signature, Key, Options) -> boolean()
when
Msg :: binary() | {digest, binary()},
DigestType :: digest_type(),
Signature :: binary(),
Key :: public_key(),
Options :: crypto:pk_sign_verify_opts().
Verifies a digital signature.
The Msg is either the binary "plain text" data or it is the hashed value of "plain text", that is, the digest.
And that's a wrap, bestie! π This manual page comes from Ericsson AB, version public_key 1.21.5, and it's the public_key(3) page. Stay slay! πβ¨
π¦ where this page comes from
- ubuntu stonking: erlang-doc 1:29.0.6+dfsg-1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms