1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
//! # 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 `pqc` Cargo feature — see [Post-Quantum keys](#post-quantum-keys-experimental)
//! - 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
//!
//! ### Certificate Signing Requirements
//! To sign another certificate, the signing certificate must:
//! - Have the `CA` (Certificate Authority) flag set to `true`
//! - Include the `KeyUsage` extension with the `keyCertSign` bit 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
//!
//!
//! ## Basic Example creating a certificate and private key
//! ```rust
//! use cert_helper::certificate::{CertBuilder, Certificate, HashAlg, KeyType, Usage, verify_cert, UseesBuilderFields};
//!
//! // create a self signed certificate with several optional values set
//! let ca = CertBuilder::new()
//! .common_name("My Test Ca")
//! .country_name("SE")
//! .state_province("Stockholm")
//! .organization("my org")
//! .locality_time("Stockholm")
//! .is_ca(true)
//! .key_type(KeyType::P521)
//! .signature_alg(HashAlg::SHA512)
//! .key_usage([Usage::certsign, Usage::crlsign].into_iter().collect());
//! let root_cert = ca.build_and_self_sign();
//! assert!(root_cert.is_ok())
//! // 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
//! ```rust
//! use cert_helper::certificate::{Usage, Csr, verify_cert, UseesBuilderFields,CsrBuilder};
//!
//! // create a certificate signing request and private key
//! let csr_builder = CsrBuilder::new()
//! .common_name("example2.com")
//! .country_name("SE")
//! .state_province("Stockholm")
//! .organization("My org")
//! .locality_time("Stockholm")
//! .alternative_names(vec!["example2.com", "www.example2.com"])
//! .key_usage(
//! [
//! Usage::contentcommitment,
//! Usage::encipherment,
//! Usage::serverauth,
//! ]
//! .into_iter()
//! .collect(),
//! );
//! let csr = csr_builder.certificate_signing_request();
//! assert!(csr.is_ok());
//!
//! // 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
//! ```rust
//! use cert_helper::certificate::{CertBuilder, Csr, verify_cert, UseesBuilderFields, CsrBuilder,CsrOptions};
//!
//! let ca = CertBuilder::new().common_name("My Test Ca").is_ca(true);
//! let root_cert = ca.build_and_self_sign().expect("failed to create root certificate");
//!
//! let csr_builder = CsrBuilder::new().common_name("example2.com");
//! let csr = csr_builder.certificate_signing_request().expect("Failed to generate csr");
//! let options = CsrOptions::new();// used for enabling csr for CA certficates
//! let cert = csr.build_signed_certificate(&root_cert, options);
//! assert!(cert.is_ok());
//! ```
//!
//! ## Basic Example creating a chain of signed certificates and verify the chain
//! ```rust
//! use cert_helper::certificate::{CertBuilder, verify_cert, UseesBuilderFields};
//!
//! let cert = CertBuilder::new().common_name("Cert-1").is_ca(true);
//! let cert_1 = cert.build_and_self_sign().expect("Failed to create certificate");
//! let cert = CertBuilder::new().common_name("Cert-2").is_ca(true);
//! let cert_2 = cert.build_and_sign(&cert_1).expect("Failed to create certificate");
//! let cert = CertBuilder::new().common_name("Cert-3");
//! let cert_3 = cert.build_and_sign(&cert_2).expect("Failed to create certificate");
//!
//! match verify_cert(&cert_3.x509, &cert_1.x509, vec![&cert_2.x509]) {
//! Ok(true) => println!("verify ok"),
//! _ => println!("failed verify"),
//! }
//!
//! ```
//!
//! ## Post-Quantum keys (experimental)
//!
//! Build with `--features pqc` to enable NIST-standardized post-quantum
//! signature algorithms as new [`KeyType`](certificate::KeyType) variants:
//!
//! - `MlDsa44`, `MlDsa65`, `MlDsa87` — FIPS 204 (ML-DSA, formerly Dilithium)
//! - `SlhDsaSha2_128s`, `SlhDsaSha2_192s`, `SlhDsaSha2_256s` — FIPS 205 (SLH-DSA, formerly SPHINCS+)
//!
//! **Runtime requirement:** OpenSSL **≥ 3.5** at build and runtime (enforced
//! in `build.rs`). The `openssl` Rust crate does not yet expose safe high-level
//! wrappers for these algorithms — this implementation uses `openssl-sys` FFI
//! directly, reusing the Ed25519 digest-less signing path. Availability and
//! stability track upstream; expect churn until safe bindings land.
//!
//! The following example only compiles when the `pqc` feature is enabled — it
//! is hidden from the default doctest build and exercised by `cargo test --features pqc`.
//!
//! ```
//! # #[cfg(feature = "pqc")] {
//! use cert_helper::certificate::{CertBuilder, KeyType, UseesBuilderFields};
//!
//! // Self-signed CA with an ML-DSA-65 key. Same builder surface as classical keys —
//! // the digest-less signing path and build-time OpenSSL 3.5+ check are implicit.
//! let ca = CertBuilder::new()
//! .common_name("My PQC CA")
//! .is_ca(true)
//! .key_type(KeyType::MlDsa65)
//! .build_and_self_sign()
//! .expect("self-sign ML-DSA-65");
//!
//! // PQC-signed certs are interoperable with OpenSSL's verifier; the signature
//! // algorithm OID in the PEM will read "ML-DSA-65" (2.16.840.1.101.3.4.3.18).
//! assert_eq!(
//! ca.x509.issuer_name().to_der().ok(),
//! ca.x509.subject_name().to_der().ok()
//! );
//! # }
//! ```
//!
//! A PQC CA can also sign classical CSRs (and vice versa); see the
//! `pqc_crl_example` and `pqc_all_variants` examples in `examples/` for full
//! chain and CRL workflows.
//!
//! ## Example on how to create a certifcate revocation list(clr)
//!
//! Create a crl, with one revoked certificate that have CRL Reason: Key Compromise
//!
//! ```rust
//! use cert_helper::certificate::{CertBuilder, UseesBuilderFields};
//! use cert_helper::crl::{X509CrlBuilder,CrlReason,X509CrlWrapper};
//! use chrono::Utc;
//! use num_bigint::BigUint;
//!
//! let ca = CertBuilder::new()
//! .common_name("My Test Ca")
//! .is_ca(true)
//! .build_and_self_sign()
//! .unwrap();
//! let mut builder = X509CrlBuilder::new(ca.clone());
//! let revocked = CertBuilder::new()
//! .common_name("My Test")
//! .build_and_self_sign()
//! .unwrap();
//!
//! let bytes = revocked.x509.serial_number().to_bn().unwrap().to_vec();
//! builder.add_revoked_cert_with_reason(BigUint::from_bytes_be(&bytes),
//! Utc::now(),
//! vec![CrlReason::KeyCompromise]);
//!
//! 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(ca.x509.public_key().as_ref().unwrap());
//! assert!(result.unwrap());
//! let is_revoked = wrapper.revoked(revocked.x509.serial_number());
//! assert!(is_revoked);
//! ```
//!
//! ## Config
//!
//! Values that can be selected for building a certificate
//! | keyword | description | options |
//! | ----------------- | --------------------------------------------------------------------------- | ----------------------------------- |
//! | common_name | the common name this certificate shoud have, mandatory field | string: www.foo.se |
//! | 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 |
//! | ca | is this certificate used to sign other certificates, default value is false | 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 | list of alternative DNS names this certificate is valid for | string: valid dns names |
//! | 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 |
//!
//! ### 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) |