Open-Source Wikis

/

OpenSSL

/

Features

/

X.509 and PKI

openssl/openssl

X.509 and PKI

Active contributors: Tomas Mraz, Dr. David von Oheimb, Pauli, Matt Caswell, Stephen Henson (historical)

Purpose

OpenSSL's X.509 stack covers everything needed to issue, parse, build, validate, and revoke RFC 5280 / RFC 6818 certificates, plus the surrounding PKI machinery: CRLs, OCSP, Certificate Transparency (RFC 6962), RFC 3161 timestamps, attribute certificates (RFC 5755 / RFC 9913), and the various extensions used in IETF, IEEE, and ITU-T profiles.

The implementation is one of the oldest and most heavily-trafficked parts of libcrypto. The verifier (crypto/x509/x509_vfy.c, ~136 KB) is the single largest file in the tree.

Directory layout

crypto/
├── x509/                       -- core X.509 stack
│   ├── x_x509.c, x509_set.c, x509_lu.c, x509_vfy.c, x509_vpm.c, x509_cmp.c, x509_txt.c, x509_trust.c, x_pubkey.c
│   ├── x_crl.c, x509cset.c, x509rset.c, x509name.c, x509_obj.c
│   ├── v3_*.c                  -- per-extension parsers/printers (BasicConstraints, KeyUsage, SubjectAltName, AIA, CRLDistributionPoints, CertificatePolicies, NameConstraints, ProxyCert, IPAddrBlocks, AS, …)
│   ├── pcy_*.c                 -- the certificate policy tree (RFC 5280 §6.1.4)
│   ├── x509_acert.c            -- attribute certificates
│   ├── by_dir.c, by_file.c, by_store.c -- X509_LOOKUP backends
│   └── t_*.c                   -- pretty-printers
├── ocsp/                       -- RFC 6960 OCSP client + server-side helpers
├── ct/                         -- RFC 6962 Certificate Transparency client
├── ts/                         -- RFC 3161 timestamp client + server
├── pem/                        -- PEM I/O for certs/keys/CRLs/CSRs
├── http/                       -- minimal HTTP client used for AIA / OCSP / CRLDP / CMP
└── store/                      -- the OSSL_STORE URI loader (file:, pkcs11:, …)

Key types

Type Schema source Purpose
X509 x_x509.c A certificate (cert + sig + cached state).
X509_CRL x_crl.c A Certificate Revocation List.
X509_REQ x_req.c A PKCS#10 Certificate Signing Request.
X509_NAME x_name.c A Name (sequence of RelativeDistinguishedName).
X509_EXTENSION / X509V3_EXT_METHOD crypto/asn1/x_attrib.c + crypto/x509/v3_*.c Generic extension carrier + per-OID parser registry.
X509_STORE, X509_STORE_CTX x509_lu.c, x509_vfy.c The trust store and a per-verification context.
X509_VERIFY_PARAM x509_vpm.c Tunable verification parameters (depth, purpose, time, hostflags, …).
X509_PUBKEY x_pubkey.c The SubjectPublicKeyInfo wrapper.
X509_ACERT x509_acert.c An attribute certificate.
X509_LOOKUP_METHOD by_dir.c, by_file.c, by_store.c Pluggable lookup backends for the trust store.

Reading and writing certificates

X509 *cert = NULL;
BIO *bio = BIO_new_file("cert.pem", "r");
PEM_read_bio_X509(bio, &cert, NULL, NULL);

const ASN1_TIME *not_after = X509_get0_notAfter(cert);
const X509_NAME *subject = X509_get_subject_name(cert);

X509_print(BIO_new_fp(stdout, BIO_NOCLOSE), cert);

X509_free(cert);
BIO_free(bio);

For DER input use d2i_X509_bio or d2i_X509(NULL, &p, len).

For new code, the OSSL_STORE URI loader is preferred for one-stop loading of bundles:

OSSL_STORE_CTX *s = OSSL_STORE_open("file:certs.pem", NULL, NULL, NULL, NULL);
OSSL_STORE_INFO *info;
while ((info = OSSL_STORE_load(s)) != NULL) {
    if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_CERT)
        handle(OSSL_STORE_INFO_get0_CERT(info));
    OSSL_STORE_INFO_free(info);
}

Verification

sequenceDiagram
    participant App
    participant Ctx as X509_STORE_CTX (x509_vfy.c)
    participant Store as X509_STORE (x509_lu.c)
    participant Ext as v3_*.c parsers
    participant Pcy as pcy_tree.c (policies)
    participant Ocsp as crypto/ocsp/
    App->>Ctx: X509_STORE_CTX_init(ctx, store, leaf, untrusted_chain)
    App->>Ctx: X509_verify_cert(ctx)
    Ctx->>Store: X509_STORE_get_by_subject (find issuer at each step)
    loop for each cert in candidate chain
        Ctx->>Ctx: check signature with issuer's pubkey
        Ctx->>Ext: validate every critical extension
        Ctx->>Ctx: check NotBefore/NotAfter vs param-time
        Ctx->>Ctx: check name constraints, key usage, ext key usage, basic constraints, path length
    end
    Ctx->>Pcy: build policy tree from CertificatePolicies / PolicyMappings / NameConstraints
    Ctx->>Store: lookup CRL or stapled OCSP for each cert
    opt CRL/OCSP attached
        Ctx->>Ctx: check revocation
        Ctx->>Ocsp: OCSP_basic_verify
    end
    Ctx-->>App: verify_cb invoked at each issue; final result via X509_STORE_CTX_get_error

The verifier is path-discovery + path-validation. It does:

  1. Chain construction. Walk from the leaf to a trust anchor, considering untrusted certs supplied by the caller as candidates and the trust store as the source of anchors. Issuer matching uses subject/issuer DN comparison with AuthorityKeyIdentifier as a tiebreaker.
  2. Signature validation. For each non-anchor cert, the parent's EVP_PKEY validates the cert's tbsCertificate digest+signature.
  3. Extension validation. Every critical extension must be understood and validated; e.g. BasicConstraints (CA flag, pathLen), KeyUsage, ExtendedKeyUsage, NameConstraints, PolicyConstraints, InhibitAnyPolicy, SubjectAltName, …
  4. Time validation. notBefore <= now <= notAfter for every cert, against the X509_VERIFY_PARAM_set_time value (defaults to current).
  5. Hostname / IP / email matching if requested via X509_VERIFY_PARAM_set1_host / _ip / _email. The matcher in crypto/x509/v3_utl.c implements wildcard rules per RFC 6125 and RFC 9525.
  6. Policy processing if any CertificatePolicies extension is present; the policy tree state machine lives in crypto/x509/pcy_tree.c.
  7. Revocation if a CRL or OCSP responder is configured.

The caller can hook in at every step via the X509_STORE_set_verify_cb() callback, which is invoked on each verified-or-failed cert with the pending error. The default callback returns 0 (failed) without modification; some applications return non-zero from the callback to ignore specific errors.

Verification options

X509_VERIFY_PARAM (in x509_vpm.c) holds the tunable knobs:

  • set_purposeX509_PURPOSE_SSL_CLIENT, _SSL_SERVER, _NS_SSL_SERVER, _SMIME_SIGN, _SMIME_ENCRYPT, _CRL_SIGN, _TIMESTAMP_SIGN, _OCSP_HELPER, _ANY. Each sets the required EKU and TLS-feature checks.
  • set_trustX509_TRUST_SSL_CLIENT, etc.
  • set_flagsX509_V_FLAG_CRL_CHECK, _CRL_CHECK_ALL, _X509_STRICT, _PARTIAL_CHAIN, _NO_CHECK_TIME, _USE_DELTAS, _TRUSTED_FIRST, _USE_DELTAS, _PARTIAL_CHAIN, _POLICY_CHECK, _EXPLICIT_POLICY, _INHIBIT_ANY, _INHIBIT_MAP, _NOTIFY_POLICY, _EXTENDED_CRL_SUPPORT, _NO_ALT_CHAINS, _LEGACY_VERIFY, _CHECK_SS_SIGNATURE, _CB_ISSUER_CHECK, …
  • set_depth — max chain depth.
  • set_auth_level — minimum security strength bits.
  • set1_host / set1_ip / set1_email — name matching.
  • set1_policies / set_inh_flags — policy tree controls.

Building certificates

To issue a cert, the typical code path:

X509 *crt = X509_new();
X509_set_version(crt, X509_VERSION_3);
ASN1_INTEGER_set(X509_get_serialNumber(crt), 1);
X509_gmtime_adj(X509_getm_notBefore(crt), 0);
X509_gmtime_adj(X509_getm_notAfter(crt), 365 * 24 * 3600);
X509_set_subject_name(crt, subject);
X509_set_issuer_name(crt, issuer);
X509_set_pubkey(crt, pubkey);
X509_add_ext(crt, ext_basic_constraints_ca_false, -1);
X509_add_ext(crt, ext_key_usage_dig_sig_kex, -1);
X509_add_ext(crt, ext_san, -1);
X509_sign(crt, ca_privkey, EVP_sha256());

apps/openssl ca (the simple CA in apps/ca.c) and apps/openssl x509 -req -CA ... are command-line wrappers around the same machinery.

Extensions

Every standard extension has its own file crypto/x509/v3_<name>.c. The registry is in crypto/x509/v3_lib.c (X509V3_EXT_get, X509V3_EXT_add, X509V3_EXT_d2i, X509V3_EXT_i2d). The extension method has hooks for parsing from text config (X509V3_EXT_conf, X509V3_EXT_conf_nid) — this is what openssl req -extensions v3_ca -config openssl.cnf uses.

To add a new extension:

  1. Allocate a new NID_<name> in crypto/objects/objects.txt, run make update.
  2. Write a new v3_<name>.c with an X509V3_EXT_METHOD instance (parser, encoder, conf parser, printer).
  3. Register the method in crypto/x509/standard_exts.h and ext_dat.h.

CRLs

X509_CRL lives in x_crl.c. Loaded the same way as certificates (PEM_read_bio_X509_CRL, d2i_X509_CRL_bio). The verifier consults the store for CRLs by issuer when X509_V_FLAG_CRL_CHECK is set; the typical lookup backend is by_dir.c which searches ~/.openssl/certs or /etc/ssl/certs-style hashed directories.

Issuing a CRL: build an X509_CRL, add X509_REVOKED entries for each revoked serial, sign with X509_CRL_sign.

OCSP

crypto/ocsp/:

  • ocsp_asn.c — schema (OCSP_REQUEST, OCSP_RESPONSE, OCSP_BASICRESP, …).
  • ocsp_lib.c — request/response build helpers.
  • ocsp_cl.c — client side: build a request from cert, parse a response.
  • ocsp_srv.c — server side: build a response.
  • ocsp_ext.c — OCSP-specific extensions (Nonce, AcceptableResponses, CrlReferences).
  • ocsp_vfy.c — verify a basic OCSP response (signed by the issuer or a delegated responder with the OCSPSigning EKU).
  • ocsp_ht.c — HTTP transport (calls crypto/http/).

The apps/openssl ocsp subcommand uses these.

Certificate Transparency

crypto/ct/ parses Signed Certificate Timestamps (SCTs) from the 1.3.6.1.4.1.11129.2.4.2 X.509 extension, OCSP responses, or the TLS extension. It validates them against a configured set of CT log public keys (CTLOG_STORE). The implementation is conservative — it only ever validates SCTs, never produces them.

Timestamps (RFC 3161)

crypto/ts/:

  • Builds and parses TimeStampReq / TimeStampResp.
  • Verifies that a timestamp covers a specified hash and was signed by an authorised TSA.

apps/openssl ts is the CLI front end.

Attribute Certificates

crypto/x509/x509_acert.c implements RFC 5755 / RFC 9913 attribute certificates. Same overall shape as X509 but the holder/issuer/attributes structure differs. Verification follows the same chain-construction approach, with attribute-cert-specific extension validation in v3_ac_tgt.c, v3_aaa.c, etc.

Trust store backends

X509_LOOKUP_METHOD is the abstraction for a lookup source:

  • by_file.c — load a single PEM bundle.
  • by_dir.c — hashed-directory lookup (e.g. c_rehash-style).
  • by_store.c — delegate to the OSSL_STORE URI loader (so pkcs11:-backed CAs work transparently).

X509_STORE_load_locations and X509_STORE_load_path are the convenience wrappers.

OSSL_STORE

crypto/store/ is the URI-based loader. Built-in scheme: file:. With third-party providers, pkcs11:, tpm2:, etc. become available. The result is a stream of OSSL_STORE_INFO of types CERT, CRL, NAME (alias), PUBKEY, PARAMS, PKEY.

Integration points

  • libssl uses X509_STORE_CTX to verify the peer chain at handshake.
  • crypto/cms/, crypto/pkcs7/, crypto/pkcs12/ all consume X509/X509_NAME/X509_EXTENSION when signing/verifying their structures.
  • crypto/cmp/ issues new certs through this stack; CRMF requests are X.509-shaped.
  • The Java / Python / Node.js / Erlang / Ruby / etc. bindings are mostly thin marshalling wrappers around these types.

Entry points for modification

  • New extension: see "Extensions" above.
  • New trust constraint or check: thread it through x509_vfy.c and X509_VERIFY_PARAM flags.
  • New profile / purpose: register in crypto/x509/v3_purp.c.
  • New name-matching mode: extend crypto/x509/v3_utl.c (do_x509_check).

Documentation

  • doc/man3/X509_verify_cert.pod, X509_STORE_CTX_new.pod, X509_VERIFY_PARAM_set_flags.pod, X509_check_host.pod.
  • doc/man3/X509_get_extension_flags.pod — the cached extension-check state.
  • doc/man3/PEM_read_X509.pod, d2i_X509.pod, OSSL_STORE_open.pod.
  • doc/man3/OCSP_*.pod, TS_*.pod, CT_*.pod.
  • doc/man7/x509.pod, ossl-guide-x509-introduction.pod.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

X.509 and PKI – OpenSSL wiki | Factory