X.509, ASN.1 and cryptography in C++17, implemented from scratch.
Its own DER codec, its own big-number and binary-field arithmetic, its own
hashes, symmetric ciphers, AEADs, signature schemes and post-quantum
algorithms, and an X.509 layer on top of all of it. There is no third-party
cryptography anywhere in the library — the only vendored dependency,
doctest, is used by the test suite and
is not linked into certpp itself.
#include <certpp.hpp>
using namespace certpp;
using namespace certpp::x509;
// Parse a certificate, read it, and check who signed it.
CCert leaf;
if (leaf.importDer(leafDer) == ERET_OK) {
CName cn;
leaf.subject().tryGet(ENAME_CN, cn);
// One link: this checks the signature, and nothing else.
const bool signedByIssuer = (leaf.verifyBy(issuer) == ERET_OK);
}using namespace certpp::crypto;
// Encrypt a record in place, authenticated. `out` may alias `in`.
CChaCha20Poly1305 aead;
aead.reset(key);
uint8_t tag[16];
aead.seal(nonce, aad,
SReadOnlyByteSpan(record, length),
SByteSpan(record, length),
SByteSpan(tag, sizeof(tag)));More in specs/recipes.md and, compiled as part of the
build, examples/.
Certificates — parse and build DER/PEM Certificate (CCert,
CCertBuilder), CRLs, and OCSP request/response (RFC 6960), with ten concrete
extension types. Signature verification covers PKCS#1 v1.5, RSASSA-PSS
(RFC 4055, reading the real parameters rather than assuming the DER defaults),
ECDSA, EdDSA and ML-DSA. Fourteen X.520 name attribute types, including
organizationIdentifier and domainComponent. PKCS#10 certification requests
(CCertRequest, CCertRequestBuilder, RFC 2986) in both directions, including
the PKCS#9 extensionRequest attribute, with the self-signature checked at
import rather than merely offered as a method to call.
Chains and containers — CCertCollection holds a set of certificates and
orders them by who issued whom (buildChain()), and verifyLinks() checks
each link's signature. Two container formats over a collection: PEM, pairing
private keys to their certificates cryptographically rather than by position,
and PKCS#12/PFX (RFC 7292) over PBES2/AES-256-CBC with the MAC verified before
anything is decrypted. There is no path validation: validity periods,
basicConstraints, keyUsage, name constraints, policies and revocation are
not checked, and nothing here decides whether a root is one you trust. An
ordered chain out of this library is a validator's input, not its verdict.
Hashing — MD4, MD5, SHA-1, SHA-224/256/384/512, SHA3-256/512, SHAKE128/256, BLAKE2s (RFC 7693), GOST R 34.11-2012 "Streebog" (RFC 6986) at both digest lengths.
MACs and KDFs — HMAC (RFC 2104) over any fixed-output hasher above (SHAKE is excluded), HKDF (RFC 5869), PBKDF2 (RFC 8018) for the password-based case, Poly1305 (RFC 8439), BLAKE2s's native keyed MAC, SipHash-2-4 (RFC 9018).
Symmetric and AEAD — AES, ARIA, DES, TripleDES (CBC, PKCS#7-padded or unpadded), and ChaCha20. Three AEADs: ChaCha20-Poly1305 (RFC 8439), XChaCha20-Poly1305 (192-bit nonce), AES-GCM (SP 800-38D). All three work in place, allocate nothing per record for a reused context, and verify the tag in constant time before writing any plaintext.
Asymmetric — RSA (PKCS#1 v1.5 and RSASSA-PSS sign/verify, v1.5 encrypt/decrypt), DSA, ECDSA over NIST P-192…P-521, secp256k1 and the 14 Brainpool curves, ECDSA over the 10 NIST binary/Koblitz curves, Ed25519/Ed448 (RFC 8032), X25519 (RFC 7748), ECDH over the prime curves (RFC 5903), and GOST R 34.10-2012 over nine named parameter sets.
Post-quantum — ML-KEM (FIPS 203) and ML-DSA (FIPS 204), all parameter sets, validated against NIST's ACVP vectors. A real third-party ML-DSA certificate verifies end to end in the test suite.
DNSSEC — DNSKEY/RRSIG/DS conversion (RFC 4034): canonical wire-format names, RDATA, key tags, DS digests, and the re-encoding between DNSSEC's wire formats and this library's keys and signatures.
JSON and BSON — parse and serialize JSON values (CJson, parseJson()),
and encode/decode BSON documents, including nested arrays/objects and escaped
Unicode strings. BSON maps integer values to the library's double number
type and rejects BSON types without a JSON equivalent. Disable the utility at
configure time with -DCERTPP_WITHOUT_JSON=ON.
Object identifiers — COid is an OID and SRawOid is its arcs, with
COid holding a pointer to a cached slot rather than a copy of the OID. The
118 OIDs the library defines are declared once, as static constexpr SKnownOid
members of COid (COid::RSA, COid::CURVE_P256, COid::EXT_BASIC_CONSTRAINTS,
COid::PURPOSE_SERVER_AUTH, COid::PBES2, COid::DOMAIN_COMPONENT, …), and
every OID comparison in the library is on arcs rather than on dotted-decimal
text. The ASN.1 codec reads and writes SRawOid directly; the string-based
readOidString()/encodeOidString() entry points remain for a caller that
holds OID text, and are implemented on top of it.
Hardware acceleration — ADX/BMI2 for big-number arithmetic, PCLMULQDQ for binary fields and GHASH, GFNI reflection in GHASH, SHA-NI for SHA-1/SHA-224/SHA-256, BMI2 RORX for SHA-384/SHA-512, BMI1 ANDN for SHA-3/SHAKE, AES-NI, four-block SSE2 and eight-block AVX2 ChaCha20 keystreams, and AVX512-IFMA for long Poly1305 inputs. Each has a portable fallback expected to produce byte-identical results, a CMake switch to force it, and a runtime CPUID check where the instruction set is not baseline.
docs/CONCEPTS.md has the full breakdown, file by
file.
cmake -S . -B build
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failureShared library by default; -DCERTPP_BUILD_SHARED=OFF for static. Requires
CMake 3.15+ and a C++17 compiler; built with MSVC and GCC.
docs/BUILDING.md has the full option list.
cmake --install build --prefix <prefix>find_package(certpp REQUIRED)
target_link_libraries(myapp PRIVATE certpp::certpp)Add -DCMAKE_PREFIX_PATH=<prefix> if the prefix is not already searched. Note
that a static certpp must be consumed with a matching MSVC runtime.
docs/README.md is the documentation map. It identifies the
right starting point and the document that owns each kind of information. The
complete index lists every maintained document.
Using the library:
specs/— reference material aimed at agents and newcomers: an API map, recipes, and pitfalls — the ways this library can be called wrongly and still appear to work.examples/— a compiled CA-hierarchy walkthrough, plus a compiled example for every public type.- The wiki — one reference page per public class, struct and enum, generated from the headers' own doc comments, each stamped with the commit it came from. Also the integration guide, FAQ and troubleshooting.
Contributing to the library:
CONTRIB.md— the workflow and review checklist, with separate guidance for human contributors and AI coding agents.AGENTS.md— repository-specific instructions for AI coding agents.docs/— architecture, coding conventions, build reference, roadmap, historical decisions, and the post-quantum review.
Maintained reader-facing documentation has a Korean translation beside it,
named <name>.ko.md.
Every algorithm is checked against published vectors rather than only against
itself — NIST ACVP for ML-KEM and ML-DSA, the relevant RFC's own test vectors
elsewhere, and real commercially-issued certificates on disk under
tests/x509/certs/. New units additionally get a negative control: an
invariant is deliberately broken to confirm the tests actually fail, because a
suite that passes against broken code measures nothing. See
docs/HISTORY.md for cases where that caught something
the published vectors could not.
Detailed benchmark results and methodology
Early-stage and under active development; interfaces may still change. Not
audited by a third party. Parts are constant-time by construction and say so
in their doc comments; parts explicitly are not, including CBigNum and
therefore prime-curve ECDSA/ECDH. specs/pitfalls.md is
the honest list. Weigh that before using it where timing is an adversary's
input.
Licensed under the MIT License. The vendored
doctest is MIT-licensed too and is used only by the
test suite; certpp itself links nothing third-party.