public_key

MODULE

public_key

MODULE SUMMARY

API module for public-key infrastructure.

DESCRIPTION

This module provides functions to handle public-key infrastructure. It can encode/decode different file formats (PEM, OpenSSH), sign and verify digital signatures, and validate certificate paths and certificate revocation lists.

public_key

  • Public Key requires the Crypto and ASN1 applications, the latter as OTP R16 (hopefully the runtime dependency on ASN1 will be removed again in the future).
  • Supports RFC 5280 - Internet X.509 Public-Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile
  • Supports PKCS-1 - RSA Cryptography Standard
  • Supports DSS - Digital Signature Standard (DSA - Digital Signature Algorithm)
  • Supports PKCS-3 - Diffie-Hellman Key Agreement Standard
  • Supports PKCS-5 - Password-Based Cryptography Standard
  • Supports PKCS-8 - Private-Key Information Syntax Standard
  • Supports PKCS-10 - Certification Request Syntax Standard

DATA TYPES

Note

All records used in this Reference Manual are generated from ASN.1 specifications and are documented in the User's Guide. See Public-key Records.

Use the following include directive to get access to the records and constant macros described here and in the User's Guide:

 -include_lib("public_key/include/public_key.hrl").

The following data types are used in the functions for public_key:

oid()

Object identifier, a tuple of integers as generated by the ASN.1 compiler.

boolean() =

true | false

string() =

[bytes()]

der_encoded() =

binary()

pki_asn1_type() =

'Certificate'

| 'RSAPrivateKey'

| 'RSAPublicKey'

| 'DSAPrivateKey'

| 'DSAPublicKey'

| 'DHParameter'

| 'SubjectPublicKeyInfo'

| 'PrivateKeyInfo'

| 'CertificationRequest'

| 'CertificateList'

| 'ECPrivateKey'

| 'EcpkParameters'

pem_entry () =

{pki_asn1_type(), binary(), %% DER or encrypted DER not_encrypted

| cipher_info()}

cipher_info() =

{"RC2-CBC" | "DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)

| {#'PBEParameter{}, digest_type()} | #'PBES2-params'{}}

public_key() =

rsa_public_key() | dsa_public_key() | ec_public_key()

private_key() =

rsa_private_key() | dsa_private_key() | ec_private_key()

rsa_public_key() =

#'RSAPublicKey'{}

rsa_private_key() =

#'RSAPrivateKey'{}

dsa_public_key() =

{integer(), #'Dss-Parms'{}}

dsa_private_key() =

#'DSAPrivateKey'{}

ec_public_key()

= {#'ECPoint'{}, #'EcpkParameters'{} | {namedCurve, oid()}}

ec_private_key() =

#'ECPrivateKey'{}

public_crypt_options() =

[{rsa_pad, rsa_padding()}]

rsa_padding() =

'rsa_pkcs1_padding'

| 'rsa_pkcs1_oaep_padding'

| 'rsa_no_padding'

digest_type() =

Union of rsa_digest_type(), dss_digest_type(), and ecdsa_digest_type().

rsa_digest_type() =

'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'

dss_digest_type() =

'sha'

ecdsa_digest_type() =

'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'

crl_reason() =

unspecified

| keyCompromise

| cACompromise

| affiliationChanged

| superseded

| cessationOfOperation

| certificateHold

| privilegeWithdrawn

| aACompromise

issuer_name() =

{rdnSequence,[#'AttributeTypeAndValue'{}]}

ssh_file() =

openssh_public_key

| rfc4716_public_key

| known_hosts

| auth_keys

EXPORTS

compute_key(OthersKey, MyKey)->
compute_key(OthersKey, MyKey, Params)->

Types:

OthersKey = #'ECPoint'{} | binary(), MyKey = #'ECPrivateKey'{} | binary()
Params = #'DHParameter'{}

Computes shared secret.

decrypt_private(CipherText, Key) -> binary()
decrypt_private(CipherText, Key, Options) -> binary()

Types:

CipherText = binary()
Key = rsa_private_key()
Options = public_crypt_options()

Public-key decryption using the private key. See also crypto:private_decrypt/4

decrypt_public(CipherText, Key) - > binary()
decrypt_public(CipherText, Key, Options) - > binary()

Types:

CipherText = binary()
Key = rsa_public_key()
Options = public_crypt_options()

Public-key decryption using the public key. See also crypto:public_decrypt/4

der_decode(Asn1type, Der) -> term()

Types:

Asn1Type = atom()
ASN.1 type present in the Public Key applications ASN.1 specifications.
Der = der_encoded()

Decodes a public-key ASN.1 DER encoded entity.

der_encode(Asn1Type, Entity) -> der_encoded()

Types:

Asn1Type = atom()
ASN.1 type present in the Public Key applications ASN.1 specifications.
Entity = term()
Erlang representation of Asn1Type

Encodes a public-key entity with ASN.1 DER encoding.

encrypt_private(PlainText, Key) -> binary()

Types:

PlainText = binary()
Key = rsa_private_key()

Public-key encryption using the private key. See also crypto:private_encrypt/4.

encrypt_public(PlainText, Key) -> binary()

Types:

PlainText = binary()
Key = rsa_public_key()

Public-key encryption using the public key. See also crypto:public_encrypt/4.

generate_key(Params) -> {Public::binary(), Private::binary()} | #'ECPrivateKey'{}

Types:

Params = #'DHParameter'{} | {namedCurve, oid()} | #'ECParameters'{}

Generates a new keypair.

pem_decode(PemBin) -> [pem_entry()]

Types:

PemBin = binary()
Example {ok, PemBin} = file:read_file("cert.pem").

Decodes PEM binary data and returns entries as ASN.1 DER encoded entities.

pem_encode(PemEntries) -> binary()

Types:

PemEntries = [pem_entry()]

Creates a PEM binary.

pem_entry_decode(PemEntry) -> term()
pem_entry_decode(PemEntry, Password) -> term()

Types:

PemEntry = pem_entry()
Password = string()

Decodes a PEM entry. pem_decode/1 returns a list of PEM entries. Notice that if the PEM entry is of type 'SubjectPublickeyInfo', it is further decoded to an rsa_public_key() or dsa_public_key().

pem_entry_encode(Asn1Type, Entity) -> pem_entry()
pem_entry_encode(Asn1Type, Entity, {CipherInfo, Password}) -> pem_entry()

Types:

Asn1Type = pki_asn1_type()
Entity = term()
Erlang representation of Asn1Type. If Asn1Type is 'SubjectPublicKeyInfo', Entity must be either an rsa_public_key() or a dsa_public_key() and this function creates the appropriate 'SubjectPublicKeyInfo' entry.
CipherInfo = cipher_info()
Password = string()

Creates a PEM entry that can be feed to pem_encode/1.

pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{}

Types:

Cert = der_encoded()

Decodes an ASN.1 DER-encoded PKIX certificate. Option otp uses the customized ASN.1 specification OTP-PKIX.asn1 for decoding and also recursively decode most of the standard parts.

pkix_encode(Asn1Type, Entity, otp | plain) -> der_encoded()

Types:

Asn1Type = atom()
The ASN.1 type can be 'Certificate', 'OTPCertificate' or a subtype of either.
Entity = #'Certificate'{} | #'OTPCertificate'{} | a valid subtype

DER encodes a PKIX x509 certificate or part of such a certificate. This function must be used for encoding certificates or parts of certificates that are decoded/created in the otp format, whereas for the plain format this function directly calls der_encode/2.

pkix_is_issuer(Cert, IssuerCert) -> boolean()

Types:

Cert = der_encoded() | #'OTPCertificate'{} | #'CertificateList'{}
IssuerCert = der_encoded() | #'OTPCertificate'{}

Checks if IssuerCert issued Cert.

pkix_is_fixed_dh_cert(Cert) -> boolean()

Types:

Cert = der_encoded() | #'OTPCertificate'{}

Checks if a certificate is a fixed Diffie-Hellman certificate.

pkix_is_self_signed(Cert) -> boolean()

Types:

Cert = der_encoded() | #'OTPCertificate'{}

Checks if a certificate is self-signed.

pkix_issuer_id(Cert, IssuedBy) -> {ok, IssuerID} | {error, Reason}

Types:

Cert = der_encoded() | #'OTPCertificate'{}
IssuedBy = self | other
IssuerID = {integer(), issuer_name()}
The issuer id consists of the serial number and the issuers name.
Reason = term()

Returns the issuer id.

pkix_normalize_name(Issuer) -> Normalized

Types:

Issuer = issuer_name()
Normalized = issuer_name()

Normalizes an issuer name so that it can be easily compared to another issuer name.

pkix_path_validation(TrustedCert, CertChain, Options) -> {ok, {PublicKeyInfo, PolicyTree}} | {error, {bad_cert, Reason}}

Types:

TrustedCert = #'OTPCertificate'{} | der_encoded() | atom()
Normally a trusted certificate, but it can also be a path-validation error that can be discovered while constructing the input to this function and that is to be run through the verify_fun. Examples are unknown_ca and selfsigned_peer.
CertChain = [der_encoded()]
A list of DER-encoded certificates in trust order ending with the peer certificate.
Options = proplists:proplist()
PublicKeyInfo = {?'rsaEncryption' | ?'id-dsa', rsa_public_key() | integer(), 'NULL' | 'Dss-Parms'{}}
PolicyTree = term()
At the moment this is always an empty list as policies are not currently supported.
Reason = cert_expired | invalid_issuer | invalid_signature | name_not_permitted | missing_basic_constraint | invalid_key_usage | {revoked, crl_reason()} | atom()

Performs a basic path validation according to RFC 5280. However, CRL validation is done separately by pkix_crls_validate/3 and is to be called from the supplied verify_fun.

Available options:

{verify_fun, fun()}

The fun must be defined as:

fun(OtpCert :: #'OTPCertificate'{},
    Event :: {bad_cert, Reason :: atom() | {revoked, atom()}} |
             {extension, #'Extension'{}},
    InitialUserState :: term()) ->
	{valid, UserState :: term()} |
	{valid_peer, UserState :: term()} |
	{fail, Reason :: term()} |
	{unknown, UserState :: term()}.
	  

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 called with an extension unknown to the user application, the return value {unknown, UserState} is to be used.

{max_path_length, integer()}
The max_path_length is the maximum number of non-self-issued intermediate certificates that can follow the peer certificate in a valid certification path. So, if max_path_length is 0, 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.

Possible reasons for a bad certificate:

cert_expired

Certificate is no longer valid as its expiration date has passed.

invalid_issuer

Certificate issuer name does not match the name of the issuer certificate in the chain.

invalid_signature

Certificate was not signed by its issuer certificate in the chain.

name_not_permitted

Invalid Subject Alternative Name extension.

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.

atom()

Application-specific error reason that is to be checked by the verify_fun.

pkix_crl_issuer(CRL) -> issuer_name()

Types:

CRL = der_encoded() | #'CertificateList'{}

Returns the issuer of the CRL.

pkix_crls_validate(OTPCertificate, DPAndCRLs, Options) -> CRLStatus()

Types:

OTPCertificate = #'OTPCertificate'{}
DPAndCRLs = [{DP::#'DistributionPoint'{}, {DerCRL::der_encoded(), CRL::#'CertificateList'{}}}]
Options = proplists:proplist()
CRLStatus() = valid | {bad_cert, revocation_status_undetermined} | {bad_cert, {revoked, crl_reason()}}

Performs CRL validation. It is intended to be called from the verify fun of pkix_path_validation/3 .

Available options:

{update_crl, fun()}

The fun has the following type specification:

 fun(#'DistributionPoint'{}, #'CertificateList'{}) ->
        #'CertificateList'{}

The fun uses the information in the distribution point to access the latest possible version of the CRL. If this fun is not specified, Public Key uses the default implementation:

 fun(_DP, CRL) -> CRL end
{issuer_fun, fun()}

The fun has the following type specification:

fun(#'DistributionPoint'{}, #'CertificateList'{},
    {rdnSequence,[#'AttributeTypeAndValue'{}]}, term()) ->
	{ok, #'OTPCertificate'{}, [der_encoded]}

The fun returns the root certificate and certificate chain that has signed the CRL.

 fun(DP, CRL, Issuer, UserState) -> {ok, RootCert, CertChain}

pkix_crl_verify(CRL, Cert) -> boolean()

Types:

CRL = der_encoded() | #'CertificateList'{}
Cert = der_encoded() | #'OTPCertificate'{}

Verify that Cert is the CRL signer.

pkix_dist_point(Cert) -> DistPoint

Types:

Cert = der_encoded() | #'OTPCertificate'{}
DistPoint = #'DistributionPoint'{}

Creates a distribution point for CRLs issued by the same issuer as Cert. Can be used as input to pkix_crls_validate/3

pkix_dist_points(Cert) -> DistPoints

Types:

Cert = der_encoded() | #'OTPCertificate'{}
DistPoints = [#'DistributionPoint'{}]

Extracts distribution points from the certificates extensions.

pkix_sign(#'OTPTBSCertificate'{}, Key) -> der_encoded()

Types:

Key = rsa_private_key() | dsa_private_key()

Signs an 'OTPTBSCertificate'. Returns the corresponding DER-encoded certificate.

pkix_sign_types(AlgorithmId) -> {DigestType, SignatureType}

Types:

AlgorithmId = oid()
Signature OID from a certificate or a certificate revocation list.
DigestType = rsa_digest_type() | dss_digest_type()
SignatureType = rsa | dsa | ecdsa

Translates signature algorithm OID to Erlang digest and signature types.

pkix_verify(Cert, Key) -> boolean()

Types:

Cert = der_encoded()
Key = rsa_public_key() | dsa_public_key() | ec_public_key()

Verifies PKIX x.509 certificate signature.

sign(Msg, DigestType, Key) -> binary()

Types:

Msg = binary() | {digest,binary()}
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.
DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()
Key = rsa_private_key() | dsa_private_key() | ec_private_key()

Creates a digital signature.

ssh_decode(SshBin, Type) -> [{public_key(), Attributes::list()}]

Types:

SshBin = binary()
Example {ok, SshBin} = file:read_file("known_hosts").
Type = public_key | ssh_file()
If Type is public_key the binary can be either an RFC4716 public key or an OpenSSH public key.

Decodes an SSH file-binary. In the case of know_hosts or auth_keys, the binary can include one or more lines of the file. Returns a list of public keys and their attributes, possible attribute values depends on the file type represented by the binary.

RFC4716 attributes - see RFC 4716.

{headers, [{string(), utf8_string()}]}

auth_key attributes - see manual page for sshd.
{comment, string()}
{options, [string()]}

{bits, integer()} - In SSH version 1 files.

known_host attributes - see manual page for sshd.
{hostnames, [string()]}
{comment, string()}

{bits, integer()} - In SSH version 1 files.

ssh_encode([{Key, Attributes}], Type) -> binary()

Types:

Key = public_key()
Attributes = list()
Type = ssh_file()

Encodes a list of SSH file entries (public keys and attributes) to a binary. Possible attributes depend on the file type, see ssh_decode/2 .

verify(Msg, DigestType, Signature, Key) -> boolean()

Types:

Msg = binary() | {digest,binary()}
The Msg is either the binary "plain text" data or it is the hashed value of "plain text", that is, the digest.
DigestType = rsa_digest_type() | dss_digest_type() | ecdsa_digest_type()
Signature = binary()
Key = rsa_public_key() | dsa_public_key() | ec_public_key()

Veryfies a digital signature.