A robust, secure, and modern C++ utility for loading and verifying X.509 certificate chains against a local trust store using the OpenSSL library.
This project provides a CertVerifier class that encapsulates the logic for X.509 certificate verification. It is built with a strong emphasis on security and resource safety, using modern C++ RAII patterns to manage OpenSSL's C-style resources.
The work flow of this class is as such:
- Loads all PEM-encoded root CAs from a directory
- Verifies a certificate or full chain against that trust store
- Uses RAII wrappers (
std::unique_ptr) to manage OpenSSL resources
A smallverifier_appexecutable demonstrates usage, and a test suite verifies valid, expired, and untrusted chains.
- Secure by Default: Fails closed and provides clear error messages on verification failure.
- RAII Wrappers: Utilizes
std::unique_ptrwith custom deleters for all OpenSSL objects (X509,X509_STORE_CTX, etc.) to prevent resource leaks. - Dynamic Trust Store: Manual, filesystem-based trust-store loading
- CMake Build System: Plain C++ build with CMake
- Test Suite: Google Test (GTest) framework with test cases covering valid, expired, and untrusted certificate chains
- Certificate creation: Python script to generate test certificates automatically
To build and run this project, you will need:
- C++17-capable compiler (GCC 7+, Clang 6+, or MSVC)
- CMake 3.16 or newer
- OpenSSL development libraries (system installation)
- Python 3.7+ and the
cryptographylibrary (pip install cryptography) - nlohmann/json and Google Test (automatically fetched by CMake)
- Open the “MSYS2 MinGW 64-bit” shell and run:
pacman -Syu
pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-openssl mingw-w64-x86_64-toolchain - Verify:
cmake --version
openssl version-
Clone the repository:
git clone https://github.com/Diogoperei29/x.509-cert-verifier.git x509-verifier cd x509-verifier -
Configure with CMake:
cmake -S . -B buildOn Windows with MSYS2/MinGW, you may need to specify OpenSSL location:
cmake -S . -B build -DOPENSSL_ROOT_DIR="C:/msys64/mingw64"
-
Compile the project:
cmake --build build
The executables
verifier_appandrun_testswill be created in thebuild/directory.
A Python script automates creation of test certs:
scripts/generate_test_certs.py
- Install
cryptography:
pip install cryptography - Run:
python3 scripts/generate_test_certs.py This produces:
certs/trust_store/rootCA.key&rootCA.pemcerts/test_certs/good/valid_chain.pemcerts/test_certs/bad_expired/expired_chain.pemcerts/test_certs/bad_untrusted/untrustedCA.pem&untrusted_chain.pem
The verifier needs a "trust store" — a directory containing the root Certificate Authority (CA) certificates that you trust. These certificates must be in PEM format (.pem, .crt).
-
Create a directory to act as your trust store. For this project, we use
certs/trust_store/. -
Add your trusted root CA PEM files to this directory. You can often acquire these from browser vendors or your operating system's trust store.
For example, to add a root CA:
cp /path/to/your/root-ca.pem certs/trust_store/
The main application verifier_app takes two arguments:
- The path to the trust store directory.
- The path to the certificate (or certificate chain) file to verify.
Example:
# This command assumes you have a valid root CA in the trust_store
# that signed the certificate chain in valid_chain.pem.
./build/verifier_app certs/trust_store certs/test_certs/good/valid_chain.pemExpected Output (Success):
Verification SuccessfulExpected Output (Failure):
Verification Failed: [reason] The project uses Google Test framework with test cases covering valid, expired, and untrusted certificate chains.
Using CTest:
cd build
ctest --output-on-failureRun tests directly:
./build/run_testsX.509-Certificate-Chain-Verifier/
├── include/
│ ├── CertVerifier.hpp # Main certificate verifier class
│ └── OpenSslWrappers.hpp # RAII wrappers for OpenSSL types
├── src/
│ ├── CertVerifier.cpp # Core verification logic
│ ├── CertVerifierError.cpp # Error handling implementation
│ └── main.cpp # Command-line application
├── tests/
│ └── TestVerifier.cpp # GTest test suite
├── scripts/
│ └── generate_test_certs.py # Certificate generation script
├── certs/
│ ├── trust_store/ # Root CA certificates
│ └── test_certs/ # Generated test certificates
│ ├── good/ # Valid certificates
│ ├── bad_expired/ # Expired certificates
│ └── bad_untrusted/ # Untrusted CA certificates
└── CMakeLists.txt # Build configuration
-
CertVerifier: Main class handling certificate verification
- Trust store loading from filesystem
- Certificate chain verification against trust store
- RAII-based resource management
-
OpenSslWrappers: RAII wrappers for OpenSSL C types
X509_ptr,X509_STORE_ptr,X509_STORE_CTX_ptr,BIO_ptr- Automatic resource cleanup
- Exception-safe resource management
- Trust-store integrity: protect certs/trust_store/ with strict permissions
- No revocation checking: consider CRL or OCSP for production
- Keep OpenSSL and the Python
cryptographylibrary up-to-date - Only files ending in .pem, .crt, .cer are loaded as CAs