Welcome to certy’s documentation!

Certy provides a simple API for creating X509 certificates and certificate revocation lists on demand when running unit tests. No more storing test certificates and private keys in the repository!

Python-certy is a version of similar tool for command line and Golang called certyaml and java-certy for Java.

Installation

Install certy from pypi

pip install certy

Examples

Basics: issuing certificates

Following example creates a CA certificate, a server certificate signed by the CA and writes them to files.

1from certy import Credential
2
3ca = Credential().subject("CN=ca")
4ca.write_certificates_as_pem("ca.pem")
5
6cred = Credential().subject("CN=server").issuer(ca)
7cred.write_certificates_as_pem("cert.pem")
8cred.write_private_key_as_pem("key.pem")

Given defaults are typically OK, which makes simple use very simple:

  • CA certificate will automatically include basic constrains extension with CA field set. It is recognized as CA, because ca.issuer() was not called.

  • Server certificate is recognized as end-entity certificate, since it is signed by the CA - server.issuer(ca) was called.

  • Key usage is set according to the certificate type: CA certificates are allowed to sign other certificates, end-entity certificates are allowed to be used for TLS server and client authentication.

  • The validFrom and validTo fields are set to current time and one year in the future, respectively.

  • EC key type of 256 bits is used.

  • Serial number is randomly generated.

Complete example: HTTPS server and client

Following example creates two PKI hierarchies:

  • The server PKI hierarchy contains a root CA and intermediate CA. The server certificate is signed by the intermediate.

  • The client PKI hierarchy contains just a root CA, which is used to sign the client certificate.

The HTTP server validates the client certificate against the client root CA and the client validates the server certificate against the server root CA. The client also validates that the server hostname matches the certificate subject alternative name app.127.0.0.1.nip.io since that hostname is used to connect to the server.

 1import os
 2import ssl
 3import threading
 4from http.server import HTTPServer, SimpleHTTPRequestHandler
 5
 6import requests
 7
 8from certy import Credential
 9
10
11def serve_https(server_cert_path, server_key_path, client_root_ca_path):
12    # Create SSLContext for the server.
13    # Require client to present certificate, signed by the client root CA.
14    context = ssl.create_default_context(
15        ssl.Purpose.CLIENT_AUTH, cafile=client_root_ca_path
16    )
17    context.verify_mode = ssl.CERT_REQUIRED
18
19    # Load server certificate and private key.
20    context.load_cert_chain(certfile=server_cert_path, keyfile=server_key_path)
21
22    http = HTTPServer(("localhost", 8443), SimpleHTTPRequestHandler)
23    http.socket = context.wrap_socket(http.socket, server_side=True)
24    http.serve_forever()
25
26
27# Create root CA and intermediate CA for issuing server certificate.
28server_root_ca_cred = Credential().subject("CN=server-root-ca")
29# Certy does not recognize intermediate CA as CA since it is not self-signed.
30# ca() needs to be called explicitly to prepare the certificate as CA cert.
31server_intermediate_ca_cred = (
32    Credential().subject("CN=server-intermediate-ca").issuer(server_root_ca_cred).ca()
33)
34
35# Create root CA for issuing client certificate.
36client_root_ca_cred = Credential().subject("CN=client-root-ca")
37
38# Create a server certificate, issued by the intermediate server CA.
39server_cred = (
40    Credential()
41    .subject("CN=localhost")
42    .subject_alt_names("DNS:app.127.0.0.1.nip.io")
43    .issuer(server_intermediate_ca_cred)
44)
45
46# Create a client certificate, issued by the server root CA.
47client_cred = Credential().subject("CN=client").issuer(client_root_ca_cred)
48
49# Write the certificates and keys to disk.
50server_root_ca_cred.write_certificates_as_pem("server-root-ca.pem")
51server_cred.write_certificates_as_pem(
52    "server.pem"
53)  # server.pem bundle includes chain: server cert, intermediate CA (in that order).
54server_cred.write_private_key_as_pem("server-key.pem")
55client_root_ca_cred.write_certificates_as_pem("client-root-ca.pem")
56client_cred.write_certificates_as_pem("client.pem")
57client_cred.write_private_key_as_pem("client-key.pem")
58
59# Start a HTTPS server in a separate thread.
60threading.Thread(
61    target=serve_https,
62    args=(
63        "server.pem",  # server_cert_path
64        "server-key.pem",  # server_key_path
65        "client-root-ca.pem",  # client_root_ca_path
66    ),
67).start()
68
69# Make a request to the HTTPS server.
70# Use the client certificate and key for mutual TLS authentication.
71response = requests.get(
72    "https://app.127.0.0.1.nip.io:8443",
73    verify="server-root-ca.pem",
74    cert=("client.pem", "client-key.pem"),
75)
76
77# Print the response.
78print(response.text)

API Reference

Certy is a simple X509 certificate generator for unit and integration tests.

class certy.CertificateRevocationList(issuer: Credential | None = None, revoked_certificates: list[Credential] | None = None, this_update: datetime | None = None, next_update: datetime | None = None)

CertificateRevocationList is a builder for X.509 CRLs.

add(certificate: Credential) CertificateRevocationList

Add a certificate to the CRL.

All certificates added to the CRL must have the same issuer.

Parameters:

certificate (Credential) – The certificate to add to the CRL.

Returns:

self

Return type:

CertificateRevocationList

generate() CertificateRevocationList

Generate the CRL.

This method will (re)generate the CRL. It will be called automatically if the CRL is not yet generated when get_as_pem(), get_as_der(), write_pem() or write_der() is called.

Returns:

self

Return type:

CertificateRevocationList

get_as_der() bytes

Get the CRL as DER.

Returns:

The CRL as DER.

Return type:

bytes

get_as_pem() bytes

Get the CRL as PEM.

Returns:

The CRL as PEM.

Return type:

bytes

issuer(issuer: Credential) CertificateRevocationList

Set the issuer of the CRL.

If not called, the issuer will be inferred from the first certificate added to the CRL by calling add().

Parameters:

issuer (Credential) – The issuer of the CRL.

Returns:

self

Return type:

CertificateRevocationList

next_update(next_update: datetime) CertificateRevocationList

Set the nextUpdate field of the CRL.

If not called, the nextUpdate field will be set to thisUpdate plus 7 days.

Parameters:

next_update (datetime) – The nextUpdate field of the CRL.

Returns:

self

Return type:

CertificateRevocationList

this_update(this_update: datetime) CertificateRevocationList

Set the thisUpdate field of the CRL.

If not called, the thisUpdate field will be set to the current time.

Parameters:

this_update (datetime) – The thisUpdate field of the CRL.

Returns:

self

Return type:

CertificateRevocationList

write_der(filename: str) CertificateRevocationList

Write the CRL as DER to a file.

Parameters:

filename (str) – The filename to write the CRL to.

Returns:

self

Return type:

CertificateRevocationList

write_pem(filename: str) CertificateRevocationList

Write the CRL as PEM to a file.

Parameters:

filename (str) – The filename to write the CRL to.

Returns:

self

Return type:

CertificateRevocationList

class certy.Credential(subject: Name | None = None, subject_alt_names: GeneralNames | None = None, key_type: KeyType | None = None, key_size: int | None = None, expires: timedelta | None = None, not_before: datetime | None = None, not_after: datetime | None = None, issuer: Credential | None = None, is_ca: bool | None = None, key_usages: list[KeyUsage] | None = None, ext_key_usages: list[ExtendedKeyUsage] | None = None, serial: int | None = None, crl_distribution_point_uri: str | None = None)

Credential representing a certificate and associated private key

ca(ca: bool = True) Credential

Set whether this credential is a CA or not.

If CA is set to True, the key usage KeyUsage.KEY_CERT_SIGN and KeyUsage.CRL_SIGN are set to the certificate, and the basic constraints extension is included with ca field set to True`.

If not called, True is used for credentials that are self-signed, False for credentials that are not.

Parameters:

ca (bool) – Whether this credential is a CA or not.

Returns:

This credential instance.

Return type:

Credential

crl_distribution_point_uri(uri: str) Credential

Set the CRL distribution point URI of this credential.

If not called, the CRL distribution point extension is not included in the certificate.

Parameters:

uri (str) – The URI of the CRL distribution point.

Returns:

This credential instance.

Return type:

Credential

expires(expires: timedelta) Credential

Set the expiration time of this credential.

The value is used to calculate the notAfter time for the certificate. If not called, the certificate will expire 365 days from notBefore, unless overridden by calling not_after().

Parameters:

expires (datetime.timedelta) – The expiration time of this credential.

Returns:

This credential instance.

Return type:

Credential

ext_key_usages(*ext_key_usages: ExtendedKeyUsage) Credential

Set the extended key usages of this credential.

If not called, extended key usages extension is not be included in the certificate.

Parameters:

ext_key_usages (tuple[ExtendedKeyUsage]) – The extended key usages of this credential. One or more of ExtendedKeyUsage.

Returns:

This credential instance.

Return type:

Credential

generate() Credential

Generate the credential. This method will (re)generate the private key and the certificate.

Returns:

This credential instance.

Return type:

Credential

get_certificate() Certificate

Get the certificate.

If the certificate has not been generated yet by calling generate(), it is generated automatically.

Returns:

The certificate.

Return type:

cryptography.x509.Certificate

get_certificate_as_pem() bytes

Get the certificate in PEM format.

If the certificate has not been generated yet by calling generate(), it is generated automatically.

Returns:

The certificate in PEM format.

Return type:

bytes

get_certificates() list[Certificate]

Get the certificate chain including the certificate itself and its issuers. It will not include the root CA.

If the certificate has not been generated yet by calling generate(), it is generated automatically.

Returns:

The certificate chain.

Return type:

list[cryptography.x509.Certificate]

get_certificates_as_pem() bytes

Get the certificate chain in PEM format. The PEM bundle includes the certificate itself and its issuers, but not the root CA.

If the certificate has not been generated yet by calling generate(), it is generated automatically.

Returns:

The certificate chain in PEM format.

Return type:

bytes

get_private_key() RSAPrivateKey | EllipticCurvePrivateKey

Get the private key.

If the private key has not been generated yet by calling generate(), it is generated automatically.

Returns:

The private key.

Return type:

cryptography.hazmat.primitives.asymmetric.rsa.RSAPrivateKey | cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePrivateKey

get_private_key_as_pem(password: str | None = None) bytes

Get the private key in PKCS#8 PEM format.

If the private key has not been generated yet by calling generate(), it is generated automatically.

Parameters:

password – The password to encrypt the private key with. If not set, the private key is not encrypted.

Returns:

The private key in PKCS#8 PEM format.

Return type:

bytes

issuer(issuer: Credential) Credential

Set the issuer of this credential.

If not called, the issuer is set to the same value as the subject and the certificate is self-signed.

Parameters:

issuer (Credential) – The issuer of this credential.

Returns:

This credential instance.

Return type:

Credential

key_size(key_size: int) Credential

Set the key size of this credential.

If not called, the key size is 256 for KeyType.EC and 2048 for KeyType.RSA. KeyType.ED25519 has a fixed key size of 256.

Parameters:

key_size (int) – The key size of this credential. Valid values depend on the key type. For EC keys, valid values are 256, 384, and 521. For RSA keys, valid values are 1024, 2048, and 4096. For ED25519 keys, valid value is 256.

Returns:

This credential instance.

Return type:

Credential

key_type(key_type: KeyType) Credential

Set the key type of this credential.

If not called, the key type will be KeyType.EC.

Parameters:

key_type (KeyType) – The key type of this credential. Must be KeyType.EC, KeyType.RSA or KeyType.ED25519.

Returns:

This credential instance.

Return type:

Credential

key_usages(*key_usages: KeyUsage) Credential

Set the key usages of this credential.

If not called, the key usages KeyUsage.DIGITAL_SIGNATURE and KeyUsage.KEY_ENCIPHERMENT are set to end-entity certificates (ca() is False), and KeyUsage.KEY_CERT_SIGN and KeyUsage.CRL_SIGN are set to CA certificates (ca() is True).

Parameters:

key_usages (tuple[certy.KeyUsage]) – The key usages of this credential. One or more of KeyUsage.

Returns:

This credential instance.

Return type:

Credential

not_after(not_after: datetime) Credential

Set the not after time of this credential.

If not called, the not after time will be set to time given by not_before() plus the expiration time set by expires().

Parameters:

not_after (datetime.datetime) – The not after time of this credential.

Returns:

This credential instance.

Return type:

Credential

not_before(not_before: datetime) Credential

Set the not before time of this credential.

If not called, current time is used.

Parameters:

not_before (datetime.datetime) – The not before time of this credential.

Returns:

This credential instance.

Return type:

Credential

serial(serial: int) Credential

Set the serial number of this credential.

If not called, the serial number is set to a random value.

Parameters:

serial (int) – The serial number of this credential.

Returns:

This credential instance.

Return type:

Credential

subject(subject: str) Credential

Set the subject name of this credential.

Parameters:

subject (str) – The subject name of this credential. Must be a valid RFC4514 string, for example CN=example.com.

Returns:

This credential instance.

Return type:

Credential

subject_alt_names(*subject_alt_names: str) Credential

Set the subject alternative names of this credential.

If not called, the subject alternative names extension is not included in the certificate.

Parameters:

subject_alt_names (tuple[str]) – The subject alternative name or names of this credential. Must be one of the following: DNS name for example DNS:example.com, IP address IP:1.2.3.4, or URI URI:https://example.com.

Returns:

This credential instance.

Return type:

Credential

write_certificate_as_pem(path: str) Credential

Write the certificate in PEM format to a file.

If the certificate has not been generated yet by calling generate(), it is generated automatically.

Parameters:

path (str) – The path to the file.

Returns:

This credential instance.

Return type:

Credential

write_certificates_as_pem(path: str) Credential

Write the certificate chain in PEM format to a file. The PEM bundle includes the certificate itself and its issuers, but not the root CA.

If the certificate has not been generated yet by calling generate(), it is generated automatically.

Parameters:

path (str) – The path to the file.

Returns:

This credential instance.

Return type:

Credential

write_private_key_as_pem(path: str, password: str | None = None) Credential

Write the private key in PKCS#8 PEM format to a file.

If the private key has not been generated yet by calling generate(), it is generated automatically.

Parameters:
  • path (str) – The path to the file.

  • password (str | None) – The password to encrypt the private key with. If not set, the private key is not encrypted.

Returns:

This credential instance.

Return type:

Credential

class certy.ExtendedKeyUsage(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Extended key usages are used with Credential.ext_key_usages() to specify the extended key usages for the certificate.

CLIENT_AUTH

Certificate can be used as TLS client certificate.

CODE_SIGNING

Certificate can be used for code signing.

EMAIL_PROTECTION

Certificate can be used for email protection (signing, encryption, key agreement).

OCSP_SIGNING

Private key associated to certificate can be used to sign OCSP response.

SERVER_AUTH

Certificate can be used as TLS server certificate.

TIME_STAMPING

Certificate can be used to bind the hash of an object to a time from a trusted time source.

class certy.KeyType(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Key types are used with Credential.key_type() to specify the type of key to generate.

EC

Elliptic curve key (default).

ED25519

Ed25519 key.

RSA

RSA key.

class certy.KeyUsage(value, names=<not given>, *values, module=None, qualname=None, type=None, start=1, boundary=None)

Key usages are used with Credential.key_usage() to specify the key usages for the certificate.

CRL_SIGN
DATA_ENCIPHERMENT
DECIPHER_ONLY
DIGITAL_SIGNATURE
ENCIPHER_ONLY
KEY_AGREEMENT
KEY_CERT_SIGN
KEY_ENCIPHERMENT
NON_REPUDIATION

Contact information

Please use the github project for reporting bugs, requesting features and submitting pull requests.