Skip to main content

cert_helper/
lib.rs

1//! # Cert-Helper
2//!
3//! A lightweight helper library for managing X.509 certificates using OpenSSL.
4//! Provides convenient tools for generating Certificate Signing Requests (CSRs),
5//! Certificate Revocation Lists (CRLs), and handling private keys.
6//!
7//! ## Description
8//!
9//! A minimal wrapper combining `openssl`, `yasna`, and `x509-parser` crates
10//! to simplify common certificate operations such as creation, signing, parsing, and revocation.
11//!
12//! The package has not been reviewed for any security issues and is intended for testing purposes only.
13//!
14//! This library provides a set of utility functions to simplify common tasks such as:
15//! - Creating self-signed or CA-signed certificates
16//! - Generating RSA, ECDSA,or Ed25519 private keys, note that Ed25519 do not require any hash variant
17//! - Optionally, post-quantum signing keys (ML-DSA, SLH-DSA) behind the `pqc` Cargo feature — see [Post-Quantum keys](#post-quantum-keys-experimental)
18//! - Creating Certificate Signing Requests (CSRs)
19//! - Signing certificates from CSRs using a CA certificate and key
20//! - Reading and writing certificates, keys, and CSRs in PEM format
21//! - Validating certificate chains and properties
22//! - Create or update certificate revocation list(crl)
23//!   - Note that this is a simple crl parser that only handle the fields that are included then
24//!     generating a crl with this code
25//!
26//! ### Post-Quantum keys (experimental)
27//!
28//! Build with `--features pqc` to enable NIST-standardized post-quantum
29//! algorithms as new `KeyType` variants. There are two distinct families with
30//! different roles and **different KeyUsage rules** — the library enforces these at
31//! build time on both the certificate and CSR paths.
32//!
33//! **Signature keys** — FIPS 204 / FIPS 205. These sign; they cannot encrypt.
34//!
35//! - `MlDsa44`, `MlDsa65`, `MlDsa87` — FIPS 204 (ML-DSA, formerly Dilithium)
36//! - `SlhDsaSha2_128s`, `SlhDsaSha2_192s`, `SlhDsaSha2_256s` — FIPS 205 (SLH-DSA, formerly SPHINCS+)
37//!
38//!   - **KeyUsage:** use `digitalSignature` (`Usage::signature`), plus
39//!     `keyCertSign`/`cRLSign` (`Usage::certsign` / `Usage::crlsign`) for a CA.
40//!   - **Restriction:** `keyEncipherment` (`Usage::encipherment`) is **rejected** —
41//!     these algorithms are signature-only and cannot perform key encipherment.
42//!   - Can self-sign, sign CSRs, sign other certificates, and sign CRLs.
43//!
44//! **Key-encapsulation keys** — FIPS 203. These encapsulate (encrypt); they cannot sign.
45//!
46//! - `MlKem512`, `MlKem768`, `MlKem1024` — FIPS 203 (ML-KEM, formerly Kyber),
47//!   OIDs `2.16.840.1.101.3.4.4.{1,2,3}`
48//!
49//!   - **KeyUsage:** if KeyUsage is present it MUST be **exactly `keyEncipherment`**
50//!     (`Usage::encipherment`) and nothing else — per
51//!     [draft-ietf-lamps-kyber-certificates]. Any other bit (`digitalSignature`,
52//!     `keyAgreement`, `dataEncipherment`, `keyCertSign`, `cRLSign`) is **rejected**.
53//!     Although ML-KEM is a Key Encapsulation Mechanism, the LAMPS group modeled it
54//!     like RSA key transport, so it lands on `keyEncipherment`, not `keyAgreement`.
55//!   - **Restriction:** ML-KEM cannot produce signatures, so it can be neither
56//!     self-signed (`build_and_self_sign`) nor used to sign a CSR
57//!     (`certificate_signing_request`) — both return an error. Issue an ML-KEM
58//!     certificate via `build_and_sign()` with a separate signing CA (e.g. an
59//!     ML-DSA or ECDSA CA).
60//!
61//! **Runtime requirement:** OpenSSL **≥ 3.5** at build and runtime (enforced at
62//! `build.rs` time) — this covers both the FIPS 204/205 signature algorithms and
63//! FIPS 203 ML-KEM. The `openssl`
64//! Rust crate does not yet expose safe high-level wrappers for these algorithms —
65//! this implementation uses `openssl-sys` FFI directly, mirroring the Ed25519
66//! digest-less signing path. Availability and stability track upstream; expect
67//! churn until safe bindings land.
68//!
69//! ### Certificate Signing Requirements
70//! To sign another certificate, the signing certificate must:
71//! - Have the `CA` (Certificate Authority) flag set to `true`
72//! - Include the `KeyUsage` extension with the `keyCertSign` bit enabled
73//!
74//! These constraints ensure that the certificate is recognized as a valid CA and can be used to issue other certificates.
75//!
76//! ### Use Cases
77//! - Generating certificates for local development or internal services
78//! - Creating a simple certificate authority for testing
79//! - Validating certificate chains in custom TLS setups
80//! - Creating CSRs to be signed by external or internal CAs
81//! - Issuing signed certificates from CSRs for controlled certificate management
82//! - Create crl for testing how a client handle certificate revocations, optionally add crl reason for the revoked certificate
83//!
84//!
85//! ## Basic Example creating a certificate and private key
86//! ```rust
87//! use cert_helper::certificate::{CertBuilder, Certificate, HashAlg, KeyType, Usage, verify_cert, UseesBuilderFields};
88//!
89//! // create a self signed certificate with several optional values set
90//! let ca = CertBuilder::new()
91//!     .common_name("My Test Ca")
92//!     .country_name("SE")
93//!     .state_province("Stockholm")
94//!     .organization("my org")
95//!     .locality_time("Stockholm")
96//!     .is_ca(true)
97//!     .key_type(KeyType::P521)
98//!     .signature_alg(HashAlg::SHA512)
99//!     .key_usage([Usage::certsign, Usage::crlsign].into_iter().collect());
100//! let root_cert = ca.build_and_self_sign();
101//! assert!(root_cert.is_ok())
102//! // to write data to file you need to use X509Common to access the save
103//! // ca.save("./certs/", "mytestca")?;
104//!```
105//! ## Basic Example creating a certificate signing request and private key
106//! ```rust
107//! use cert_helper::certificate::{Usage, Csr, verify_cert, UseesBuilderFields,CsrBuilder};
108//!
109//! // create a certificate signing request and private key
110//! let csr_builder = CsrBuilder::new()
111//!    .common_name("example2.com")
112//!    .country_name("SE")
113//!    .state_province("Stockholm")
114//!    .organization("My org")
115//!    .locality_time("Stockholm")
116//!    .alternative_names(vec!["example2.com", "www.example2.com"])
117//!    .key_usage(
118//!        [
119//!            Usage::contentcommitment,
120//!            Usage::encipherment,
121//!            Usage::serverauth,
122//!        ]
123//!        .into_iter()
124//!        .collect(),
125//!    );
126//! let csr = csr_builder.certificate_signing_request();
127//! assert!(csr.is_ok());
128//!
129//! // to write data to file you need to use X509Common to access the save
130//! // csr.save("./certs/", "mytestca")?;
131//!
132//!```
133//! ## Basic Example creating a signed certificate from a signing request
134//! ```rust
135//! use cert_helper::certificate::{CertBuilder, Csr, verify_cert, UseesBuilderFields, CsrBuilder,CsrOptions};
136//!
137//! let ca = CertBuilder::new().common_name("My Test Ca").is_ca(true);
138//! let root_cert = ca.build_and_self_sign().expect("failed to create root certificate");
139//!
140//! let csr_builder = CsrBuilder::new().common_name("example2.com");
141//! let csr = csr_builder.certificate_signing_request().expect("Failed to generate csr");
142//! let options = CsrOptions::new();// used for enabling csr for CA certficates
143//! let cert = csr.build_signed_certificate(&root_cert, options);
144//! assert!(cert.is_ok());
145//! ```
146//!
147//! ## Basic Example creating a chain of signed certificates and verify the chain
148//! ```rust
149//! use cert_helper::certificate::{CertBuilder, verify_cert, UseesBuilderFields};
150//!
151//! let cert = CertBuilder::new().common_name("Cert-1").is_ca(true);
152//! let cert_1 = cert.build_and_self_sign().expect("Failed to create certificate");
153//! let cert = CertBuilder::new().common_name("Cert-2").is_ca(true);
154//! let cert_2 = cert.build_and_sign(&cert_1).expect("Failed to create certificate");
155//! let cert = CertBuilder::new().common_name("Cert-3");
156//! let cert_3 = cert.build_and_sign(&cert_2).expect("Failed to create certificate");
157//!
158//! match verify_cert(&cert_3.x509, &cert_1.x509, vec![&cert_2.x509]) {
159//!    Ok(true) => println!("verify ok"),
160//!    _ => println!("failed verify"),
161//! }
162//!
163//! ```
164//!
165//! ## Post-Quantum keys (experimental)
166//!
167//! Build with `--features pqc` to enable NIST-standardized post-quantum
168//! signature algorithms as new [`KeyType`](certificate::KeyType) variants:
169//!
170//! - `MlDsa44`, `MlDsa65`, `MlDsa87` — FIPS 204 (ML-DSA, formerly Dilithium)
171//! - `SlhDsaSha2_128s`, `SlhDsaSha2_192s`, `SlhDsaSha2_256s` — FIPS 205 (SLH-DSA, formerly SPHINCS+)
172//!
173//! **Runtime requirement:** OpenSSL **≥ 3.5** at build and runtime (enforced
174//! in `build.rs`). The `openssl` Rust crate does not yet expose safe high-level
175//! wrappers for these algorithms — this implementation uses `openssl-sys` FFI
176//! directly, reusing the Ed25519 digest-less signing path. Availability and
177//! stability track upstream; expect churn until safe bindings land.
178//!
179//! The following example only compiles when the `pqc` feature is enabled — it
180//! is hidden from the default doctest build and exercised by `cargo test --features pqc`.
181//!
182//! ```
183//! # #[cfg(feature = "pqc")] {
184//! use cert_helper::certificate::{CertBuilder, KeyType, UseesBuilderFields};
185//!
186//! // Self-signed CA with an ML-DSA-65 key. Same builder surface as classical keys —
187//! // the digest-less signing path and build-time OpenSSL 3.5+ check are implicit.
188//! let ca = CertBuilder::new()
189//!     .common_name("My PQC CA")
190//!     .is_ca(true)
191//!     .key_type(KeyType::MlDsa65)
192//!     .build_and_self_sign()
193//!     .expect("self-sign ML-DSA-65");
194//!
195//! // PQC-signed certs are interoperable with OpenSSL's verifier; the signature
196//! // algorithm OID in the PEM will read "ML-DSA-65" (2.16.840.1.101.3.4.3.18).
197//! assert_eq!(
198//!     ca.x509.issuer_name().to_der().ok(),
199//!     ca.x509.subject_name().to_der().ok()
200//! );
201//! # }
202//! ```
203//!
204//! A PQC CA can also sign classical CSRs (and vice versa); see the
205//! `pqc_crl_example` and `pqc_all_variants` examples in `examples/` for full
206//! chain and CRL workflows.
207//!
208//! ## Example on how to create a certifcate revocation list(clr)
209//!
210//! Create a crl, with one revoked certificate that have CRL Reason: Key Compromise
211//!
212//! ```rust
213//! use cert_helper::certificate::{CertBuilder, UseesBuilderFields};
214//! use cert_helper::crl::{X509CrlBuilder,CrlReason,X509CrlWrapper};
215//! use chrono::Utc;
216//! use num_bigint::BigUint;
217//!
218//! let ca = CertBuilder::new()
219//!    .common_name("My Test Ca")
220//!    .is_ca(true)
221//!    .build_and_self_sign()
222//!    .unwrap();
223//! let mut builder = X509CrlBuilder::new(ca.clone());
224//!     let revocked = CertBuilder::new()
225//!    .common_name("My Test")
226//!    .build_and_self_sign()
227//!    .unwrap();
228//!
229//! let bytes = revocked.x509.serial_number().to_bn().unwrap().to_vec();
230//! builder.add_revoked_cert_with_reason(BigUint::from_bytes_be(&bytes),
231//!                          Utc::now(),
232//!                          vec![CrlReason::KeyCompromise]);
233//!
234//! let wrapper = builder.build_and_sign().unwrap();
235//! // to save crl as pem use the helper function
236//! //  wrapper.save_as_pem("./certs", "crl.pem").expect("failed to save crl as pem file");
237//!
238//! // use the wrapper to check sign, revocations
239//! let result = wrapper.verify_signature(ca.x509.public_key().as_ref().unwrap());
240//! assert!(result.unwrap());
241//! let is_revoked = wrapper.revoked(revocked.x509.serial_number());
242//! assert!(is_revoked);
243//! ```
244//!
245//! ## Config
246//!
247//! Values that can be selected for building a certificate
248//! | keyword | description | options |
249//! | ----------------- | --------------------------------------------------------------------------- | ----------------------------------- |
250//! | common_name | the common name this certificate shoud have, mandatory field | string: www.foo.se |
251//! | key_type  | key type to be used, defaults to RSA2048 | enum: RSA2048, RSA4096, P224, P256, P384, P521, Ed25519, and with `--features pqc`: MlDsa44, MlDsa65, MlDsa87, SlhDsaSha2_128s, SlhDsaSha2_192s, SlhDsaSha2_256s |
252//! | ca | is this certificate used to sign other certificates, default value is false | boolean: true or false |
253//! | country_name | the country code to use,must follow the standard defined by ISO 3166-1 alpha-2. | string: SE |
254//! | organization | organisation name | string: test |
255//! | state_province | some name | string: test |
256//! | locality_time | Stockholm | string: Stockholm |
257//! | alternative_names | list of alternative DNS names this certificate is valid for | string: valid dns names |
258//! | signature_alg | which algorithm to be used for signature, default is SHA256 | enum: SHA1, SHA256, SHA384, SHA512 |
259//! | valid_from | Start date then the certificate is valid, default is now | string: 2010-01-01 |
260//! | valid_to | End date then the certificate is not valid, default is 1 year | string: 2020-01-01 |
261//! | usage | Key usage to add to the certificates, see list below for options | list of enums, defined in Key Usage table |
262//! | certificate_policy | optional certificate policies to add | AnyPolicy, DomainValidation, OrganizationValidated, IndividualValidated, ExtendedValidation|
263//!
264//! ### Key usage
265//!
266//! If CA is true the key usages to sign certificates and crl lists are added automatically.
267//!
268//! | keyword           | description                                                |
269//! | ----------------- | ---------------------------------------------------------- |
270//! | certsign          | allowed to sign certificates                               |
271//! | crlsign           | allowed to sign crl                                        |
272//! | encipherment      | allowed to enciphering private or secret keys              |
273//! | clientauth        | allowed to authenticate as client                          |
274//! | serverauth        | allowed ot be used for server authenthication              |
275//! | signature         | allowed to perfom digital signature (For auth)             |
276//! | contentcommitment | allowed to perfom document signature (prev non repudation) |
277
278pub mod certificate;
279pub mod crl;