Cert-Helper
A lightweight helper library for managing X.509 certificates using OpenSSL. Provides convenient tools for generating Certificate Signing Requests (CSRs), Certificate Revocation Lists (CRLs), and handling private keys.
Description
A minimal wrapper combining openssl, yasna, and x509-parser crates
to simplify common certificate operations such as creation, signing, parsing, and revocation.
The package has not been reviewed for any security issues and is intended for testing purposes only.
This library provides a set of utility functions to simplify common tasks such as:
- Creating self-signed or CA-signed certificates
- Generating RSA, ECDSA,or Ed25519 private keys, note that Ed25519 do not require any hash variant
- Optionally, post-quantum signing keys (ML-DSA, SLH-DSA) behind the
pqcCargo feature — see the Post-Quantum keys section below - Creating Certificate Signing Requests (CSRs)
- Signing certificates from CSRs using a CA certificate and key
- Reading and writing certificates, keys, and CSRs in PEM format
- Validating certificate chains and properties
- Create or update certificate revocation list(crl)
- Note that this is a simple crl parser that only handle the fields that are included then generating a crl with this code
Post-Quantum keys (experimental)
Build with --features pqc to enable NIST-standardized post-quantum
algorithms as new KeyType variants. There are two distinct families with
different roles and different KeyUsage rules — the library enforces these at
build time on both the certificate and CSR paths.
Signature keys — FIPS 204 / FIPS 205. These sign; they cannot encrypt.
-
MlDsa44,MlDsa65,MlDsa87— FIPS 204 (ML-DSA, formerly Dilithium) -
SlhDsaSha2_128s,SlhDsaSha2_192s,SlhDsaSha2_256s— FIPS 205 (SLH-DSA, formerly SPHINCS+)- KeyUsage: use
digitalSignature(Usage::signature), pluskeyCertSign/cRLSign(Usage::certsign/Usage::crlsign) for a CA. - Restriction:
keyEncipherment(Usage::encipherment) is rejected — these algorithms are signature-only and cannot perform key encipherment. - Can self-sign, sign CSRs, sign other certificates, and sign CRLs.
- KeyUsage: use
Key-encapsulation keys — FIPS 203. These encapsulate (encrypt); they cannot sign.
-
MlKem512,MlKem768,MlKem1024— FIPS 203 (ML-KEM, formerly Kyber), OIDs2.16.840.1.101.3.4.4.{1,2,3}- KeyUsage: if KeyUsage is present it MUST be exactly
keyEncipherment(Usage::encipherment) and nothing else — per draft-ietf-lamps-kyber-certificates. Any other bit (digitalSignature,keyAgreement,dataEncipherment,keyCertSign,cRLSign) is rejected. Although ML-KEM is a Key Encapsulation Mechanism, the LAMPS group modeled it like RSA key transport, so it lands onkeyEncipherment, notkeyAgreement. - Restriction: ML-KEM cannot produce signatures, so it can be neither
self-signed (
build_and_self_sign) nor used to sign a CSR (certificate_signing_request) — both return an error. Issue an ML-KEM certificate viabuild_and_sign()with a separate signing CA (e.g. an ML-DSA or ECDSA CA).
- KeyUsage: if KeyUsage is present it MUST be exactly
Runtime requirement: OpenSSL ≥ 3.5 at build and runtime (enforced at
build.rs time) — this covers both the FIPS 204/205 signature algorithms and
FIPS 203 ML-KEM. The openssl
Rust crate does not yet expose safe high-level wrappers for these algorithms —
this implementation uses openssl-sys FFI directly, mirroring the Ed25519
digest-less signing path. Availability and stability track upstream; expect
churn until safe bindings land.
use ;
// PQC signature key: self-signed CA
let ca = new
.common_name
.is_ca
.key_type
.build_and_self_sign?;
// PQC KEM key: must be issued by a signing CA, keyEncipherment only
use ;
use HashSet;
let kem_leaf = new
.common_name
.key_type
.key_usage
.build_and_sign?;
Certificate Signing Requirements
To sign another certificate, the signing certificate must:
- Have the
CA(Certificate Authority) flag set totrue - Include the
KeyUsageextension with thekeyCertSignbit enabled
These constraints ensure that the certificate is recognized as a valid CA and can be used to issue other certificates.
Use Cases
- Generating certificates for local development or internal services
- Creating a simple certificate authority for testing
- Validating certificate chains in custom TLS setups
- Creating CSRs to be signed by external or internal CAs
- Issuing signed certificates from CSRs for controlled certificate management
- Create crl for testing how a client handle certificate revocations, optionally add crl reason for the revoked certificate.
Changelog
See CHANGELOG.md for details.
Basic Example creating a certificate and private key
use ;
// create a self signed certificate with several optional values set
let ca = new
.common_name
.country_name
.state_province
.organization
.locality_time
.is_ca
.key_type
.signature_alg
.key_usage;
let root_cert = ca.build_and_self_sign;
assert!
// to write data to file you need to use X509Common to access the save
// ca.save("./certs/", "mytestca")?;
Basic Example creating a certificate signing request and private key
use ;
// create a certificate signing request and private key
let csr_builder = new
.common_name
.country_name
.state_province
.organization
.locality_time
.alternative_names
.key_usage;
let csr = csr_builder.certificate_signing_request;
assert!;
// to write data to file you need to use X509Common to access the save
// csr.save("./certs/", "mytestca")?;
Basic Example creating a signed certificate from a signing request
use ;
let ca = new.common_name.is_ca;
let root_cert = ca.build_and_self_sign.expect;
let csr_builder = new.common_name;
let csr = csr_builder.certificate_signing_request.expect;
let options = new;// used for enabling csr for CA certficates
let cert = csr.build_signed_certificate;
assert!;
Basic Example creating a chain of signed certificates and verify the chain
use ;
let cert = new.common_name.is_ca;
let cert_1 = cert.build_and_self_sign.expect;
let cert = new.common_name.is_ca;
let cert_2 = cert.build_and_sign.expect;
let cert = new.common_name;
let cert_3 = cert.build_and_sign.expect;
match verify_cert
Limiting CA chain depth with path length constraints
pathlen(n) sets the BasicConstraints path-length constraint: at most n
intermediate CAs may sit below this certificate. When issuing under a chain it is
validated against the signer's remaining budget, so you can't mint a CA that
exceeds what its issuer permits.
use ;
// Root CA that allows at most one CA beneath it.
let root = new
.common_name
.is_ca
.pathlen
.build_and_self_sign
.expect;
// Intermediate CA (pathlen 0 → may only issue end-entity certs), signed by the
// root. The chain is empty because the root is a self-signed trust anchor.
let intermediate = new
.common_name
.is_ca
.pathlen
.build_and_sign_with_chain
.expect;
assert_eq!;
The same constraint applies when issuing from a CSR via CsrOptions. The chain to
validate against is passed alongside the path length (empty here, since the signer
is a self-signed root):
use ;
let ca = new
.common_name
.is_ca
.pathlen
.build_and_self_sign
.expect;
let csr = new
.common_name
.certificate_signing_request
.expect;
let cert = csr
.build_signed_certificate
.expect;
assert_eq!;
Example on how to create a certifcate revocation list(clr)
Create a crl, with one revoked certificate that have CRL Reason: Key Compromise
use ;
use ;
use Utc;
use BigUint;
let ca = new
.common_name
.is_ca
.build_and_self_sign
.unwrap;
let mut builder = new;
let revocked = new
.common_name
.build_and_self_sign
.unwrap;
let bytes = revocked.x509.serial_number.to_bn.unwrap.to_vec;
builder.add_revoked_cert_with_reason;
let wrapper = builder.build_and_sign.unwrap;
// to save crl as pem use the helper function
// wrapper.save_as_pem("./certs", "crl.pem").expect("failed to save crl as pem file");
// use the wrapper to check sign, revocations
let result = wrapper.verify_signature;
assert!;
let is_revoked = wrapper.revoked;
assert!;
Writing to file
save(path, filename) comes from the X509Common trait and writes two files:
the certificate (or CSR) as <filename>_cert.pem / <filename>_csr.pem, and the
private key as <filename>_pkey.pem.
On Unix the private key is created with mode 0600 and the certificate with
0644. The mode is applied when the file is created rather than set afterwards,
so the key is never briefly readable by others. Saving over an existing file
replaces it rather than truncating in place, which means a key written by an
older version of this crate — when keys were left at the umask default — is
tightened to 0600 the next time it is saved.
On non-Unix targets no permission guarantee is made.
Config
Values that can be selected for building a certificate
| keyword | description | options |
|---|---|---|
| common_name | the common name this certificate shoud have, mandatory field. Also added to the SAN of end-entity certificates | string: www.foo.se |
| key_type | key type to generate, defaults to RSA2048. Ignored when private_key is set |
enum: RSA2048, RSA4096, P224, P256, P384, P521, Ed25519, and with --features pqc: MlDsa44, MlDsa65, MlDsa87, SlhDsaSha2_128s, SlhDsaSha2_192s, SlhDsaSha2_256s |
| private_key | use a private key you already hold instead of generating a new one. Takes precedence over key_type |
PKey<Private> |
| ca | is this certificate used to sign other certificates, default value is false. CA certificates are issued without a SAN | boolean: true or false |
| country_name | the country code to use,must follow the standard defined by ISO 3166-1 alpha-2. | string: SE |
| organization | organisation name | string: test |
| state_province | some name | string: test |
| locality_time | Stockholm | string: Stockholm |
| alternative_names | alternative names this certificate is valid for, see Subject alternative names below | string: dns names or IP literals |
| signature_alg | which algorithm to be used for signature, default is SHA256 | enum: SHA1, SHA256, SHA384, SHA512 |
| valid_from | Start date then the certificate is valid, default is now | string: 2010-01-01 |
| valid_to | End date then the certificate is not valid, default is 1 year | string: 2020-01-01 |
| usage | Key usage to add to the certificates, see list below for options | list of enums, defined in Key Usage table |
| certificate_policy | optional certificate policies to add | AnyPolicy, DomainValidation, OrganizationValidated, IndividualValidated, ExtendedValidation |
| pathlen | optional CA path length: max intermediate CAs allowed below this cert (only applies when ca is true) | u32: 0, 1, 2 … |
Subject alternative names
The SAN list is assembled when the certificate is built, from alternative_names
plus — for end-entity certificates only — the common name.
- An entry that parses as an IPv4 or IPv6 address becomes an
iPAddressname; everything else becomes adNSName. - End-entity certificates get the common name added automatically. RFC 6125
verifiers match the hostname against the SAN and ignore the CN, so a
certificate for
localhostneedsDNS:localhostto be usable at all. - CA certificates get no SAN, root and intermediate alike. A CA is identified
during path validation by its distinguished name and key identifier, and no
verifier consults its SAN. Setting
cato true therefore suppresses the extension, including the automatic CN entry. - If there would be no names at all the extension is omitted rather than written empty, which RFC 5280 §4.2.1.6 forbids.
Key usage
If CA is true the key usages to sign certificates and crl lists are added automatically.
| keyword | description |
|---|---|
| certsign | allowed to sign certificates |
| crlsign | allowed to sign crl |
| encipherment | allowed to enciphering private or secret keys |
| clientauth | allowed to authenticate as client |
| serverauth | allowed ot be used for server authenthication |
| signature | allowed to perfom digital signature (For auth) |
| contentcommitment | allowed to perfom document signature (prev non repudation) |