Skip to main content

cert_helper/
certificate.rs

1use chrono::{DateTime, NaiveDate, NaiveDateTime, TimeZone, Utc};
2use foreign_types::ForeignType;
3use openssl::asn1::{Asn1Object, Asn1OctetString, Asn1Time};
4use openssl::bn::BigNum;
5use openssl::ec::{EcGroup, EcKey};
6use openssl::error::ErrorStack;
7use openssl::hash::{MessageDigest, hash};
8use openssl::nid::Nid;
9use openssl::pkey::{Id, PKey, Private};
10use openssl::rsa::Rsa;
11use openssl::stack::Stack;
12use openssl::x509::extension::{
13    AuthorityKeyIdentifier, BasicConstraints, ExtendedKeyUsage, KeyUsage, SubjectAlternativeName,
14};
15use openssl::x509::{
16    X509, X509Builder, X509Extension, X509NameBuilder, X509Req, X509ReqBuilder, X509StoreContext,
17    store::X509StoreBuilder,
18};
19use std::collections::{HashMap, HashSet};
20use std::fs::{File, create_dir_all};
21use std::io::Write;
22use std::path::Path;
23
24use x509_parser::certification_request::X509CertificationRequest;
25use x509_parser::extensions::ParsedExtension;
26use x509_parser::parse_x509_certificate;
27use x509_parser::prelude::FromDer;
28
29unsafe extern "C" {
30    pub fn X509_sign(
31        x: *mut openssl_sys::X509,
32        pkey: *mut openssl_sys::EVP_PKEY,
33        md: *const openssl_sys::EVP_MD,
34    ) -> ::std::os::raw::c_int;
35    pub fn X509_sign_ctx(
36        x: *mut openssl_sys::X509,
37        ctx: *mut openssl_sys::EVP_MD_CTX,
38    ) -> ::std::os::raw::c_int;
39}
40
41unsafe extern "C" {
42    pub fn X509_REQ_sign(
43        req: *mut openssl_sys::X509_REQ,
44        pkey: *mut openssl_sys::EVP_PKEY,
45        md: *const openssl_sys::EVP_MD,
46    ) -> ::std::os::raw::c_int;
47    pub fn X509_REQ_sign_ctx(
48        req: *mut openssl_sys::X509_REQ,
49        ctx: *mut openssl_sys::EVP_MD_CTX,
50    ) -> ::std::os::raw::c_int;
51}
52
53/// Sign a just-built `X509` in-place with a digest-less key (Ed25519 or PQC).
54///
55/// We avoid `X509_sign(x, pkey, NULL)` because OpenSSL 3.5+ infers a default
56/// digest for ML-DSA/SLH-DSA in that path, which their providers then reject.
57/// Instead we initialise an `EVP_MD_CTX` with an explicit NULL `mdname` and
58/// hand it to `X509_sign_ctx`.
59/// Sign a just-built `X509` in-place with a digest-less key (Ed25519 or PQC).
60///
61/// Ed25519 uses the plain `X509_sign(x, pkey, NULL)` path that has always
62/// worked. PQC keys (ML-DSA / SLH-DSA) need a workaround on OpenSSL 3.5+:
63/// `X509_sign(_, _, NULL)` triggers default-digest inference in
64/// `do_sigver_init`, and the PQC providers then reject the inferred digest
65/// with "Explicit digest not supported". We instead initialise an `EVP_MD_CTX`
66/// with an *empty* C string as `mdname` — that bypasses the default-digest
67/// lookup inside OpenSSL while still satisfying the provider's
68/// `mdname[0] != '\0'` guard — and hand the ctx to `X509_sign_ctx`.
69fn sign_certificate_digestless(
70    cert: &X509,
71    pkey: &PKey<openssl::pkey::Private>,
72) -> Result<(), String> {
73    if !is_digestless_key(pkey) {
74        return Err("sign_certificate_digestless called with non-digestless key".to_string());
75    }
76    let cert_ptr = cert.as_ptr();
77    let pkey_ptr = pkey.as_ptr();
78
79    if pkey.id() == Id::ED25519 {
80        let result = unsafe { X509_sign(cert_ptr, pkey_ptr, std::ptr::null()) };
81        return if result > 0 {
82            Ok(())
83        } else {
84            Err("Failed to sign certificate with Ed25519".to_string())
85        };
86    }
87
88    // PQC path: EVP_DigestSignInit (non-ex) with NULL mdname + X509_sign_ctx.
89    // `Signer::new_without_digest` in the openssl crate uses this exact call and
90    // it works for ML-DSA/SLH-DSA whereas `EVP_DigestSignInit_ex` does not.
91    let result = unsafe {
92        let ctx = openssl_sys::EVP_MD_CTX_new();
93        if ctx.is_null() {
94            return Err("EVP_MD_CTX_new returned NULL".to_string());
95        }
96        let init = openssl_sys::EVP_DigestSignInit(
97            ctx,
98            std::ptr::null_mut(),
99            std::ptr::null(),
100            std::ptr::null_mut(),
101            pkey_ptr,
102        );
103        if init <= 0 {
104            openssl_sys::EVP_MD_CTX_free(ctx);
105            return Err("EVP_DigestSignInit failed for PQC key".to_string());
106        }
107        let rc = X509_sign_ctx(cert_ptr, ctx);
108        openssl_sys::EVP_MD_CTX_free(ctx);
109        rc
110    };
111
112    if result > 0 {
113        Ok(())
114    } else {
115        Err("X509_sign_ctx failed for PQC key".to_string())
116    }
117}
118
119/// Same as `sign_certificate_digestless` but for `X509Req`. See the
120/// `sign_certificate_digestless` docstring for why Ed25519 and PQC take
121/// different OpenSSL paths.
122fn sign_x509_req_digestless(req: &X509Req, pkey: &PKey<Private>) -> Result<(), String> {
123    if !is_digestless_key(pkey) {
124        return Err("sign_x509_req_digestless called with non-digestless key".to_string());
125    }
126    let req_ptr = req.as_ptr();
127    let pkey_ptr = pkey.as_ptr();
128
129    if pkey.id() == Id::ED25519 {
130        let result = unsafe { X509_REQ_sign(req_ptr, pkey_ptr, std::ptr::null()) };
131        return if result > 0 {
132            Ok(())
133        } else {
134            Err("Failed to sign X509Req with Ed25519".to_string())
135        };
136    }
137
138    let result = unsafe {
139        let ctx = openssl_sys::EVP_MD_CTX_new();
140        if ctx.is_null() {
141            return Err("EVP_MD_CTX_new returned NULL".to_string());
142        }
143        let init = openssl_sys::EVP_DigestSignInit(
144            ctx,
145            std::ptr::null_mut(),
146            std::ptr::null(),
147            std::ptr::null_mut(),
148            pkey_ptr,
149        );
150        if init <= 0 {
151            openssl_sys::EVP_MD_CTX_free(ctx);
152            return Err("EVP_DigestSignInit failed for PQC key".to_string());
153        }
154        let rc = X509_REQ_sign_ctx(req_ptr, ctx);
155        openssl_sys::EVP_MD_CTX_free(ctx);
156        rc
157    };
158
159    if result > 0 {
160        Ok(())
161    } else {
162        Err("X509_REQ_sign_ctx failed for PQC key".to_string())
163    }
164}
165
166/// Returns true for keys whose OpenSSL EVP signing path does not take an
167/// external digest. Today: Ed25519 and (when the `pqc` feature is enabled)
168/// the six FIPS 204 / 205 post-quantum variants.
169pub(crate) fn is_digestless_key(pkey: &PKey<Private>) -> bool {
170    if pkey.id() == Id::ED25519 {
171        return true;
172    }
173    #[cfg(feature = "pqc")]
174    {
175        return is_pqc_pkey(pkey);
176    }
177    #[allow(unreachable_code)]
178    false
179}
180
181#[cfg(feature = "pqc")]
182fn is_pqc_pkey(pkey: &PKey<Private>) -> bool {
183    use std::ffi::CString;
184    use std::sync::OnceLock;
185
186    // Cache the CStrings so we don't rebuild them per call.
187    static NAMES: OnceLock<[CString; 6]> = OnceLock::new();
188    let names = NAMES.get_or_init(|| {
189        [
190            CString::new("ML-DSA-44").unwrap(),
191            CString::new("ML-DSA-65").unwrap(),
192            CString::new("ML-DSA-87").unwrap(),
193            CString::new("SLH-DSA-SHA2-128s").unwrap(),
194            CString::new("SLH-DSA-SHA2-192s").unwrap(),
195            CString::new("SLH-DSA-SHA2-256s").unwrap(),
196        ]
197    });
198    use foreign_types::ForeignType;
199    let ptr = pkey.as_ptr();
200    names
201        .iter()
202        // SAFETY: EVP_PKEY_is_a accepts any NUL-terminated C string and a
203        // valid EVP_PKEY*; returns 0 for mismatch, 1 for match — never UB.
204        .any(|n| unsafe { pqc::EVP_PKEY_is_a(ptr, n.as_ptr()) } == 1)
205}
206
207#[cfg(feature = "pqc")]
208mod pqc {
209    use foreign_types::ForeignType;
210    use openssl::error::ErrorStack;
211    use openssl::pkey::{PKey, Private};
212    use std::ffi::CString;
213
214    unsafe extern "C" {
215        fn EVP_PKEY_CTX_new_from_name(
216            libctx: *mut std::ffi::c_void,
217            name: *const std::os::raw::c_char,
218            propquery: *const std::os::raw::c_char,
219        ) -> *mut openssl_sys::EVP_PKEY_CTX;
220        fn EVP_PKEY_keygen_init(ctx: *mut openssl_sys::EVP_PKEY_CTX) -> std::os::raw::c_int;
221        fn EVP_PKEY_generate(
222            ctx: *mut openssl_sys::EVP_PKEY_CTX,
223            ppkey: *mut *mut openssl_sys::EVP_PKEY,
224        ) -> std::os::raw::c_int;
225        fn EVP_PKEY_CTX_free(ctx: *mut openssl_sys::EVP_PKEY_CTX);
226        /// Returns 1 if `pkey` is of algorithm `name`, 0 otherwise.
227        /// Use this instead of `EVP_PKEY_id` for provider-only algorithms
228        /// (ML-DSA, SLH-DSA) whose legacy NID is -1.
229        pub fn EVP_PKEY_is_a(
230            pkey: *mut openssl_sys::EVP_PKEY,
231            name: *const std::os::raw::c_char,
232        ) -> std::os::raw::c_int;
233    }
234
235    /// RAII guard that frees an `EVP_PKEY_CTX` on drop, including unwinds.
236    struct PkeyCtx(*mut openssl_sys::EVP_PKEY_CTX);
237
238    impl Drop for PkeyCtx {
239        fn drop(&mut self) {
240            if !self.0.is_null() {
241                unsafe { EVP_PKEY_CTX_free(self.0) }
242            }
243        }
244    }
245
246    /// Generate a post-quantum signing key by OpenSSL EVP algorithm name.
247    ///
248    /// Accepts the FIPS 204 / FIPS 205 canonical names:
249    /// `"ML-DSA-44"`, `"ML-DSA-65"`, `"ML-DSA-87"`,
250    /// `"SLH-DSA-SHA2-128s"`, `"SLH-DSA-SHA2-192s"`, `"SLH-DSA-SHA2-256s"`.
251    ///
252    /// Returns `Err(ErrorStack)` if the algorithm is unknown to the linked
253    /// OpenSSL, keygen init fails, or key generation fails. Never panics,
254    /// never leaks the `EVP_PKEY_CTX`.
255    pub(super) fn generate_pqc_key(alg_name: &str) -> Result<PKey<Private>, ErrorStack> {
256        let cname = CString::new(alg_name).expect("alg_name contains interior NUL");
257
258        // SAFETY: NULL libctx => default library context. NULL propquery matches
259        // every provider. The returned ctx is owned by PkeyCtx, freed on all paths.
260        let ctx_ptr = unsafe {
261            EVP_PKEY_CTX_new_from_name(std::ptr::null_mut(), cname.as_ptr(), std::ptr::null())
262        };
263        if ctx_ptr.is_null() {
264            return Err(ErrorStack::get());
265        }
266        let ctx = PkeyCtx(ctx_ptr);
267
268        if unsafe { EVP_PKEY_keygen_init(ctx.0) } <= 0 {
269            return Err(ErrorStack::get());
270        }
271
272        let mut pkey_ptr: *mut openssl_sys::EVP_PKEY = std::ptr::null_mut();
273        if unsafe { EVP_PKEY_generate(ctx.0, &mut pkey_ptr) } <= 0 {
274            return Err(ErrorStack::get());
275        }
276        if pkey_ptr.is_null() {
277            return Err(ErrorStack::get());
278        }
279
280        // SAFETY: EVP_PKEY_generate returned ownership of a freshly-allocated
281        // EVP_PKEY. PKey::from_ptr takes ownership and frees on drop.
282        Ok(unsafe { PKey::<Private>::from_ptr(pkey_ptr) })
283    }
284}
285
286#[cfg(feature = "pqc")]
287use pqc::generate_pqc_key;
288
289macro_rules! vec_str_to_hs {
290    ($vec:expr) => {
291        $vec.iter()
292            .map(|s| s.to_string())
293            .collect::<HashSet<String>>()
294    };
295}
296/// Defines what type of key that can be used with the certificate
297#[derive(Debug, Clone, PartialEq)]
298pub enum KeyType {
299    /// RSA key with a 2048-bit length.
300    RSA2048,
301    /// RSA key with a 4096-bit length.
302    RSA4096,
303    /// Elliptic Curve key using the NIST P-224 curve (secp224r1).
304    P224,
305    /// Elliptic Curve key using the NIST P-256 curve (secp256r1). Also known as prime256v1.
306    P256,
307    /// Elliptic Curve key using the NIST P-384 curve (secp384r1).
308    P384,
309    /// Elliptic Curve key using the NIST P-521 curve (secp521r1).
310    P521,
311    /// Edwards-curve Digital Signature Algorithm using Ed25519.
312    Ed25519,
313    /// ML-DSA-44 (FIPS 204, formerly Dilithium2). Post-quantum lattice signature.
314    #[cfg(feature = "pqc")]
315    MlDsa44,
316    /// ML-DSA-65 (FIPS 204, formerly Dilithium3). Post-quantum lattice signature.
317    #[cfg(feature = "pqc")]
318    MlDsa65,
319    /// ML-DSA-87 (FIPS 204, formerly Dilithium5). Post-quantum lattice signature.
320    #[cfg(feature = "pqc")]
321    MlDsa87,
322    /// SLH-DSA-SHA2-128s (FIPS 205, formerly SPHINCS+). Hash-based signature, small variant.
323    #[cfg(feature = "pqc")]
324    SlhDsaSha2_128s,
325    /// SLH-DSA-SHA2-192s (FIPS 205). Hash-based signature, medium variant.
326    #[cfg(feature = "pqc")]
327    SlhDsaSha2_192s,
328    /// SLH-DSA-SHA2-256s (FIPS 205). Hash-based signature, large variant.
329    #[cfg(feature = "pqc")]
330    SlhDsaSha2_256s,
331}
332/// Defines which hash algorithm to be used in certificate signing
333#[derive(Debug, Clone)]
334pub enum HashAlg {
335    /// SHA-1 (Secure Hash Algorithm 1), now considered weak and generally discouraged for new certificates.
336    SHA1,
337    /// SHA-256 (part of SHA-2 family)
338    SHA256,
339    /// SHA-384 (SHA-2 family), offers stronger security and is often used with larger key sizes.
340    SHA384,
341    /// SHA-512 (SHA-2 family), provides the highest bit-length hash in the SHA-2 family.
342    SHA512,
343}
344/// Represents the allowed usages for a certificate, used in KeyUsage and ExtendedKeyUsage extensions.
345#[allow(non_camel_case_types)]
346#[derive(Hash, Eq, PartialEq, Debug, Clone)]
347pub enum Usage {
348    /// Allows the certificate to sign other certificates (typically used for CA certificates).
349    certsign,
350    /// Allows the certificate to sign certificate revocation lists (CRLs).
351    crlsign,
352    /// Allows the certificate to be used for encrypting data (e.g., key encipherment).
353    encipherment,
354    /// Indicates the certificate can be used for client authentication in TLS.
355    clientauth,
356    /// Indicates the certificate can be used for server authentication in TLS.
357    serverauth,
358    /// Allows the certificate to be used for digital signatures.
359    signature,
360    /// Indicates the certificate can be used for content commitment (non-repudiation).
361    contentcommitment,
362}
363
364/// Common functionality for extracting PEM-encoded data and private keys from X509-related types
365pub trait X509Parts {
366    /// Returns the PEM-encoded representation of the X.509 object (e.g., certificate or CSR).
367    ///
368    /// # Returns
369    /// A `Vec<u8>` containing the PEM data, or an error if encoding fails.
370    fn get_pem(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
371    /// Returns the PEM-encoded private key associated with the X.509 object.
372    ///
373    /// # Returns
374    /// A `Vec<u8>` containing the PEM-encoded private key, or an error if retrieval fails.
375    fn get_private_key(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>>;
376    /// Returns the file extension typically used for the PEM output (e.g., `_cert.pem.`, `_csr.pem`, `_peky.pem`).
377    ///
378    /// # Returns
379    /// A static string slice representing the file extension.
380    fn pem_extension(&self) -> &'static str;
381}
382
383/// Provides a method to save the private key and X509 certificate or CSR data to files.
384pub trait X509Common {
385    /// Saves the X.509 object (e.g., certificate, CSR, or private key) to a file.
386    ///
387    /// # Arguments
388    /// * `path` - The directory path where the file should be saved.
389    /// * `filename` - The name of the file (without extension).
390    ///
391    /// The file extension is typically determined by the object's type (e.g., `.crt`, `.csr`, `.key`)
392    /// and is provided by the [`X509Parts::pem_extension`] method if implemented.
393    ///
394    /// # Returns
395    /// * `Ok(())` if the file was successfully written.
396    /// * `Err` if an error occurred during file creation or writing.
397    fn save<P: AsRef<Path>, F: AsRef<Path>>(
398        &self,
399        path: P,
400        filename: F,
401    ) -> Result<(), Box<dyn std::error::Error>>;
402}
403
404/// Implements `X509Common` for all types that implement `X509Parts`.
405///
406/// # Example
407/// ```no_run
408/// use cert_helper::certificate::{Certificate, X509Common};
409/// let cert = Certificate::load_cert_and_key("cert.pem", "key.pem").expect("Failed to generate certificate");
410/// cert.save("output", "mycert");
411/// ```
412impl<T: X509Parts> X509Common for T {
413    /// Will save the cert/csr  and private key to pem file
414    /// if path = /path/foo/bar and filename = mytest
415    /// For example with certificate it will be:
416    /// /path/foo/bar/mytest_cert.pem
417    /// /path/foo/bar/mytest_pkey.pem
418    /// and for certificate signing request:
419    /// /path/foo/bar/mytest_csr.pem
420    /// /path/foo/bar/mytest_pkey.pem
421    ///
422    /// If the path do not exist it will be created
423    fn save<P: AsRef<Path>, F: AsRef<Path>>(
424        &self,
425        path: P,
426        filename: F,
427    ) -> Result<(), Box<dyn std::error::Error>> {
428        create_dir_all(&path)?;
429
430        let os_file = filename
431            .as_ref()
432            .file_name()
433            .ok_or("Failed to extract file name")?;
434
435        let write_file = |suffix: &str, content: &[u8]| -> Result<(), Box<dyn std::error::Error>> {
436            let mut new_name = os_file.to_os_string();
437            new_name.push(suffix);
438            let full_path = path.as_ref().join(new_name);
439            let mut file = File::create(full_path)?;
440            file.write_all(content)?;
441            Ok(())
442        };
443        if let Ok(ref key) = self.get_private_key() {
444            write_file("_pkey.pem", key)?;
445        }
446        write_file(self.pem_extension(), &self.get_pem()?)?;
447        Ok(())
448    }
449}
450/// Holds the generated Certificate Signing Request (CSR) and its associated private key.
451pub struct Csr {
452    /// The X.509 certificate signing request.
453    pub csr: X509Req,
454    /// The private key used to generate the CSR.
455    ///
456    /// This is optional to allow flexibility in cases where the key is managed or stored separately.
457    pub pkey: Option<PKey<Private>>,
458}
459
460impl X509Parts for Csr {
461    fn get_pem(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
462        Ok(self.csr.to_pem()?)
463    }
464
465    fn get_private_key(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
466        match self.pkey {
467            Some(ref pkey) => Ok(pkey.private_key_to_pem_pkcs8()?),
468            _ => Err("No private key found".into()),
469        }
470    }
471    fn pem_extension(&self) -> &'static str {
472        "_csr.pem"
473    }
474}
475/// Helper trait to document that Csr implements X509Common
476pub trait CsrX509Common: X509Common {}
477impl CsrX509Common for Csr {}
478
479/// Holds configuration options for creating a certificate from a Certificate Signing Request (CSR).
480pub struct CsrOptions {
481    valid_to: Asn1Time,
482    valid_from: Asn1Time,
483    ca: bool,
484}
485impl Default for CsrOptions {
486    fn default() -> Self {
487        Self::new()
488    }
489}
490
491impl CsrOptions {
492    /// Creates a default `CsrOptions` instance:
493    /// - `valid_from` is set to today.
494    /// - `valid_to` is set to one year from today.
495    /// - `ca` is set to `false`.
496    pub fn new() -> Self {
497        Self {
498            ca: false,
499            valid_from: Asn1Time::days_from_now(0).unwrap(), // today
500            valid_to: Asn1Time::days_from_now(365).unwrap(), // one year from now
501        }
502    }
503
504    /// Sets the start date from which the certificate should be valid.
505    ///
506    /// # Arguments
507    /// * `valid_from` - A string in the format `yyyy-mm-dd`.
508    pub fn valid_from(mut self, valid_from: &str) -> Self {
509        self.valid_from =
510            create_asn1_time_from_date(valid_from).expect("Failed to parse valid_from date");
511        self
512    }
513
514    /// Sets the end date after which the certificate should no longer be valid.
515    ///
516    /// # Arguments
517    /// * `valid_to` - A string in the format `yyyy-mm-dd`.
518    pub fn valid_to(mut self, valid_to: &str) -> Self {
519        self.valid_to =
520            create_asn1_time_from_date(valid_to).expect("Failed to parse valid_to date");
521        self
522    }
523
524    /// Specifies whether the certificate should be a Certificate Authority (CA).
525    ///
526    /// # Arguments
527    /// * `ca` - `true` if the certificate should be a CA, `false` otherwise.
528    pub fn is_ca(mut self, ca: bool) -> Self {
529        self.ca = ca;
530        self
531    }
532}
533impl Csr {
534    /// Read a certificate signing request from file
535    pub fn load_csr<C: AsRef<Path>>(csr_pem_file: C) -> Result<Self, Box<dyn std::error::Error>> {
536        let cert_pem = std::fs::read(csr_pem_file)?;
537        let cs_req = X509Req::from_pem(&cert_pem)?;
538        Ok(Self {
539            csr: cs_req,
540            pkey: None,
541        })
542    }
543    /// Create a signed certificate from a certificate signing request(csr)
544    pub fn build_signed_certificate(
545        &self,
546        signer: &Certificate,
547        options: CsrOptions,
548    ) -> Result<Certificate, Box<dyn std::error::Error>> {
549        let can_sign = can_sign_cert(&signer.x509)?;
550        if !can_sign {
551            let err = format!(
552                "Trying to sign with non CA and/or no key usage that allow signing for signer certificate:{:?}",
553                signer.x509.issuer_name()
554            );
555            return Err(err.into());
556        }
557        let mut builder = X509Builder::new()?;
558        builder.set_version(2)?;
559        builder.set_subject_name(self.csr.subject_name())?;
560        builder.set_issuer_name(signer.x509.subject_name())?;
561        let csr_public_key = self.csr.public_key()?;
562        builder.set_pubkey(&csr_public_key)?;
563
564        let der = self.csr.to_der()?;
565        let parsed_csr = X509CertificationRequest::from_der(&der)?;
566
567        let req_ext = parsed_csr.1.requested_extensions();
568        let mut any_key_used = false;
569        if let Some(exts) = req_ext {
570            for ext in exts {
571                match ext {
572                    ParsedExtension::KeyUsage(ku) => {
573                        any_key_used = true;
574                        let mut cert_sign_added = false;
575                        let mut crl_sign_added = false;
576                        let mut usage = openssl::x509::extension::KeyUsage::new();
577                        if ku.digital_signature() {
578                            usage.digital_signature();
579                        }
580                        if ku.key_encipherment() {
581                            usage.key_encipherment();
582                        }
583                        if ku.key_cert_sign() {
584                            cert_sign_added = true;
585                            usage.key_cert_sign();
586                        }
587                        if ku.non_repudiation() {
588                            usage.non_repudiation();
589                        }
590                        if ku.crl_sign() {
591                            crl_sign_added = true;
592                            usage.crl_sign();
593                        }
594
595                        if options.ca {
596                            if !cert_sign_added {
597                                usage.key_cert_sign();
598                            }
599                            if !crl_sign_added {
600                                usage.crl_sign();
601                            }
602                        }
603                        builder.append_extension(usage.build()?)?;
604                    }
605                    ParsedExtension::ExtendedKeyUsage(eku) => {
606                        let mut ext = openssl::x509::extension::ExtendedKeyUsage::new();
607                        if eku.server_auth {
608                            ext.server_auth();
609                        }
610                        if eku.client_auth {
611                            ext.client_auth();
612                        }
613                        if eku.code_signing {
614                            ext.code_signing();
615                        }
616                        if eku.email_protection {
617                            ext.email_protection();
618                        }
619                        builder.append_extension(ext.build()?)?;
620                    }
621                    ParsedExtension::SubjectAlternativeName(san) => {
622                        let mut openssl_san =
623                            openssl::x509::extension::SubjectAlternativeName::new();
624                        for name in &san.general_names {
625                            if let x509_parser::extensions::GeneralName::DNSName(dns) = name {
626                                openssl_san.dns(dns);
627                            }
628                        }
629                        builder.append_extension(
630                            openssl_san.build(&builder.x509v3_context(None, None))?,
631                        )?;
632                    }
633                    _ => {
634                        println!("Unsupported extension: {:?}", ext);
635                    }
636                }
637            }
638        }
639        if options.ca {
640            builder.append_extension(BasicConstraints::new().ca().critical().build()?)?;
641            if !any_key_used {
642                let key_usage = KeyUsage::new().key_cert_sign().crl_sign().build().unwrap();
643                builder.append_extension(key_usage)?;
644            }
645        } else {
646            builder.append_extension(BasicConstraints::new().build()?)?;
647        }
648        builder.set_not_before(&options.valid_from)?;
649        builder.set_not_after(&options.valid_to)?;
650        let serial_number = {
651            let mut serial = BigNum::new()?;
652            serial.rand(159, openssl::bn::MsbOption::MAYBE_ZERO, false)?;
653            serial.to_asn1_integer()?
654        };
655        builder.set_serial_number(&serial_number)?;
656        if signer.x509.subject_key_id().is_some() {
657            let aki = AuthorityKeyIdentifier::new()
658                .keyid(true)
659                .issuer(false)
660                .build(&builder.x509v3_context(Some(&signer.x509), None))?;
661            builder.append_extension(aki)?;
662        }
663        let oid = Asn1Object::from_str("2.5.29.14")?; // OID för Subject Key Identifier (SKI)
664        let pubkey_der = self.csr.public_key().unwrap().public_key_to_der()?;
665        let ski_hash = hash(MessageDigest::sha1(), &pubkey_der)?;
666        let der_encoded = yasna::construct_der(|writer| {
667            writer.write_bytes(ski_hash.as_ref());
668        });
669        let ski_asn1 = Asn1OctetString::new_from_bytes(&der_encoded)?;
670        let ext = X509Extension::new_from_der(oid.as_ref(), false, &ski_asn1)?;
671        builder.append_extension(ext)?;
672        let cert: X509 = if is_digestless_key(signer.pkey.as_ref().unwrap()) {
673            let builder_cert = builder.build();
674            sign_certificate_digestless(&builder_cert, signer.pkey.as_ref().unwrap())
675                .map_err(|e| format!("Failed to sign certificate with digestless key: {}", e))?;
676            builder_cert
677        } else {
678            builder.sign(signer.pkey.as_ref().unwrap(), MessageDigest::sha256())?;
679            builder.build()
680        };
681
682        Ok(Certificate {
683            x509: cert,
684            pkey: None,
685        })
686    }
687}
688/// Holds the generated X.509 certificate and its associated private key.
689#[derive(Clone)]
690pub struct Certificate {
691    /// The X.509 certificate.
692    pub x509: X509,
693    /// The private key used to generate or sign the certificate.
694    ///
695    /// This is optional to allow for cases where the key is stored or managed separately.
696    pub pkey: Option<PKey<Private>>,
697}
698
699impl X509Parts for Certificate {
700    fn get_pem(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
701        Ok(self.x509.to_pem()?)
702    }
703
704    fn get_private_key(&self) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
705        match self.pkey {
706            Some(ref pkey) => Ok(pkey.private_key_to_pem_pkcs8()?),
707            _ => Err("No private key found".into()),
708        }
709    }
710    fn pem_extension(&self) -> &'static str {
711        "_cert.pem"
712    }
713}
714
715/// Helper trait to document that Certificate implements X509Common
716pub trait CertificateX509Common: X509Common {}
717impl CertificateX509Common for Certificate {}
718
719impl Certificate {
720    /// Loads a certificate and private key that are in PEM format from file
721    /// and creates an X509 and PKey object.
722    pub fn load_cert_and_key<C: AsRef<Path>, K: AsRef<Path>>(
723        cert_pem_file: C,
724        key_pem_file: K,
725    ) -> Result<Self, Box<dyn std::error::Error>> {
726        let cert_pem = std::fs::read(cert_pem_file)?;
727        let key_pem = std::fs::read(key_pem_file)?;
728        let cert = X509::from_pem(&cert_pem)?;
729        let pkey = PKey::private_key_from_pem(&key_pem)?;
730        Ok(Self {
731            x509: cert,
732            pkey: Some(pkey),
733        })
734    }
735}
736
737/// Defines a common interface for setting X509 certificate or CSR builder fields.
738pub trait BuilderCommon {
739    fn set_common_name(&mut self, name: &str);
740    fn set_signer(&mut self, signer: &str);
741    fn set_country_name(&mut self, name: &str);
742    fn set_state_province(&mut self, name: &str);
743    fn set_organization(&mut self, name: &str);
744    fn set_organization_unit(&mut self, name: &str);
745    fn set_alternative_names(&mut self, alternative_names: Vec<&str>);
746    fn set_locality_time(&mut self, locality_time: &str);
747    fn set_key_type(&mut self, key_type: KeyType);
748    fn set_signature_alg(&mut self, signature_alg: HashAlg);
749    fn set_key_usage(&mut self, key_usage: HashSet<Usage>);
750}
751
752/// Stores common configurable fields used during X509 certificate or CSR generation.
753#[derive(Debug)]
754pub struct BuilderFields {
755    common_name: String,
756    signer: Option<String>, //place holder for maybe future use??
757    alternative_names: HashSet<String>,
758    organization_unit: String,
759    country_name: String,
760    state_province: String,
761    organization: String,
762    locality_time: String,
763    key_type: Option<KeyType>,
764    signature_alg: Option<HashAlg>,
765    usage: Option<HashSet<Usage>>,
766}
767impl BuilderCommon for BuilderFields {
768    // Sets the common name, CN. This value will also be added to alternaitve_names
769    fn set_common_name(&mut self, common_name: &str) {
770        self.common_name = common_name.into();
771        self.alternative_names.insert(String::from(common_name));
772    }
773    // A list of altrnative names(SAN) the Common Name(CN) is always included
774    fn set_alternative_names(&mut self, alternative_names: Vec<&str>) {
775        self.alternative_names
776            .extend(vec_str_to_hs!(alternative_names));
777    }
778    // maybe
779    fn set_signer(&mut self, signer: &str) {
780        self.signer = Some(signer.into());
781    }
782    // Country, a valid two char value
783    fn set_country_name(&mut self, country_name: &str) {
784        self.country_name = country_name.into();
785    }
786    // State, province an utf-8 value
787    fn set_state_province(&mut self, state_province: &str) {
788        self.state_province = state_province.into();
789    }
790    // Org. an utf-8 value
791    fn set_organization(&mut self, organization: &str) {
792        self.organization = organization.into();
793    }
794    // Org. unit an utf-8 value
795    fn set_organization_unit(&mut self, organization_unit: &str) {
796        self.organization_unit = organization_unit.into();
797    }
798    // Locality, represents the city, town, or locality of the certificate subject
799    fn set_locality_time(&mut self, locality_time: &str) {
800        self.locality_time = locality_time.into();
801    }
802    // Selects what type of key to use RSA or elliptic
803    fn set_key_type(&mut self, key_type: KeyType) {
804        self.key_type = Some(key_type);
805    }
806    // Selects what alg to use for signature
807    fn set_signature_alg(&mut self, signature_alg: HashAlg) {
808        self.signature_alg = Some(signature_alg);
809    }
810
811    // Set what the certificate are allowed to do, KeyUsage and ExtendeKeyUsage
812    fn set_key_usage(&mut self, key_usage: HashSet<Usage>) {
813        match &mut self.usage {
814            Some(existing_usage) => {
815                existing_usage.extend(key_usage);
816            }
817            None => {
818                self.usage = Some(key_usage);
819            }
820        };
821    }
822}
823
824impl Default for BuilderFields {
825    /// Returns default values for all fields
826    fn default() -> Self {
827        Self {
828            common_name: Default::default(),
829            signer: Default::default(),
830            alternative_names: Default::default(),
831            country_name: Default::default(),
832            state_province: Default::default(),
833            organization: Default::default(),
834            organization_unit: Default::default(),
835            locality_time: Default::default(),
836            key_type: Default::default(),
837            signature_alg: Default::default(),
838            usage: Default::default(),
839        }
840    }
841}
842/// Provides a builder interface for configuring X509 certificate or CSR fields.
843pub trait UseesBuilderFields: Sized {
844    /// Returns a mutable reference to the internal `BuilderFields` structure.
845    fn fields_mut(&mut self) -> &mut BuilderFields;
846
847    /// Sets the Common Name (CN) of the certificate subject.
848    ///
849    /// This value will also be added to the list of Subject Alternative Names (SAN).
850    fn common_name(mut self, common_name: &str) -> Self {
851        self.fields_mut().set_common_name(common_name);
852        self
853    }
854    /// Sets the signer name or identifier for the certificate.
855    fn signer(mut self, signer: &str) -> Self {
856        self.fields_mut().set_signer(signer);
857        self
858    }
859    /// Sets the list of Subject Alternative Names (SAN).
860    ///
861    /// The Common Name (CN) is always included automatically.
862    fn alternative_names(mut self, alternative_names: Vec<&str>) -> Self {
863        self.fields_mut().set_alternative_names(alternative_names);
864        self
865    }
866    /// Sets the country name (C), which must be a valid two-letter country code.
867    fn country_name(mut self, country_name: &str) -> Self {
868        self.fields_mut().set_country_name(country_name);
869        self
870    }
871    /// Sets the state or province name (ST) as a UTF-8 string.
872    fn state_province(mut self, state_province: &str) -> Self {
873        self.fields_mut().set_state_province(state_province);
874        self
875    }
876    /// Sets the organization name (O) as a UTF-8 string.
877    fn organization(mut self, organization: &str) -> Self {
878        self.fields_mut().set_organization(organization);
879        self
880    }
881    /// Sets the locality name (L), typically representing the city or town.
882    fn locality_time(mut self, locality_time: &str) -> Self {
883        self.fields_mut().set_locality_time(locality_time);
884        self
885    }
886    /// Sets the type of key to generate (e.g., RSA or Elliptic Curve).
887    fn key_type(mut self, key_type: KeyType) -> Self {
888        self.fields_mut().set_key_type(key_type);
889        self
890    }
891    /// Sets the signature algorithm to use when signing the certificate.
892    fn signature_alg(mut self, signature_alg: HashAlg) -> Self {
893        self.fields_mut().set_signature_alg(signature_alg);
894        self
895    }
896
897    /// Sets the allowed usages for the certificate (e.g., key signing, digital signature).
898    ///
899    /// This includes both `KeyUsage` and `ExtendedKeyUsage` extensions.
900    fn key_usage(mut self, key_usage: HashSet<Usage>) -> Self {
901        self.fields_mut().set_key_usage(key_usage);
902        self
903    }
904}
905
906/// Builder for creating a new certificate and private key
907pub struct CertBuilder {
908    fields: BuilderFields,
909    valid_from: Asn1Time,
910    valid_to: Asn1Time,
911    ca: bool,
912}
913
914impl UseesBuilderFields for CertBuilder {
915    fn fields_mut(&mut self) -> &mut BuilderFields {
916        &mut self.fields
917    }
918}
919impl Default for CertBuilder {
920    fn default() -> Self {
921        Self::new()
922    }
923}
924
925impl CertBuilder {
926    /// Create a new CertBuilder with defaults and one year from now as valid date
927    pub fn new() -> Self {
928        Self {
929            fields: BuilderFields::default(),
930            valid_from: Asn1Time::days_from_now(0).unwrap(), // today
931            valid_to: Asn1Time::days_from_now(365).unwrap(), // one year from now
932            ca: false,
933        }
934    }
935    /// Sets the start date from which the certificate should be valid.
936    ///
937    /// # Arguments
938    /// * `valid_from` - A string in the format `yyyy-mm-dd`.
939    pub fn valid_from(mut self, valid_from: &str) -> Self {
940        self.valid_from =
941            create_asn1_time_from_date(valid_from).expect("Failed to parse valid_from date");
942        self
943    }
944    /// Sets the end date after which the certificate should no longer be valid.
945    ///
946    /// # Arguments
947    /// * `valid_to` - A string in the format `yyyy-mm-dd`.
948    pub fn valid_to(mut self, valid_to: &str) -> Self {
949        self.valid_to =
950            create_asn1_time_from_date(valid_to).expect("Failed to parse valid_to date");
951        self
952    }
953    /// Specifies whether the certificate should be a Certificate Authority (CA).
954    ///
955    /// # Arguments
956    /// * `ca` - `true` if the certificate should be a CA, `false` otherwise.
957    pub fn is_ca(mut self, ca: bool) -> Self {
958        if ca {
959            self.ca = ca;
960            self.fields
961                .set_key_usage(HashSet::from([Usage::certsign, Usage::crlsign]));
962        }
963        self
964    }
965
966    /// create a self signed x509 certificate and private key
967    pub fn build_and_self_sign(&self) -> Result<Certificate, Box<dyn std::error::Error>> {
968        let (mut builder, pkey) = self.prepare_x509_builder(None)?;
969        let ca_cert: X509 = if is_digestless_key(&pkey) {
970            let build_cert = builder.build();
971            sign_certificate_digestless(&build_cert, &pkey)
972                .map_err(|e| format!("Failed to sign certificate with digestless key: {}", e))?;
973            build_cert
974        } else {
975            builder.sign(&pkey, select_hash(&self.fields.signature_alg))?;
976            builder.build()
977        };
978
979        Ok(Certificate {
980            x509: ca_cert,
981            pkey: Some(pkey),
982        })
983    }
984    /// Create a signed certificate and private key
985    pub fn build_and_sign(
986        &self,
987        signer: &Certificate,
988    ) -> Result<Certificate, Box<dyn std::error::Error>> {
989        let can_sign = can_sign_cert(&signer.x509)?;
990        if !can_sign {
991            let err = format!(
992                "Trying to sign with non CA and/or no key usage that allow signing for signer certificate:{:?}",
993                signer.x509.issuer_name()
994            );
995            return Err(err.into());
996        }
997        let (mut builder, pkey) = self.prepare_x509_builder(Some(signer))?;
998        let signer_key = signer.pkey.as_ref().unwrap();
999        let cert: X509 = if is_digestless_key(signer_key) {
1000            let build_cert = builder.build();
1001            sign_certificate_digestless(&build_cert, signer_key)
1002                .map_err(|e| format!("Failed to sign certificate with digestless key: {}", e))?;
1003            build_cert
1004        } else {
1005            builder.sign(signer_key, select_hash(&self.fields.signature_alg))?;
1006            builder.build()
1007        };
1008        Ok(Certificate {
1009            x509: cert,
1010            pkey: Some(pkey),
1011        })
1012    }
1013
1014    fn prepare_x509_builder(
1015        &self,
1016        signer: Option<&Certificate>,
1017    ) -> Result<(X509Builder, PKey<Private>), Box<dyn std::error::Error>> {
1018        let mut name_builder = X509NameBuilder::new()?;
1019        name_builder.append_entry_by_nid(Nid::COMMONNAME, &self.fields.common_name)?;
1020        if !self.fields.country_name.trim().is_empty() {
1021            name_builder.append_entry_by_nid(Nid::COUNTRYNAME, &self.fields.country_name)?;
1022        }
1023        if !self.fields.state_province.trim().is_empty() {
1024            name_builder
1025                .append_entry_by_nid(Nid::STATEORPROVINCENAME, &self.fields.state_province)?;
1026        }
1027        if !self.fields.locality_time.trim().is_empty() {
1028            name_builder.append_entry_by_nid(Nid::LOCALITYNAME, &self.fields.locality_time)?;
1029        }
1030        if !self.fields.organization.trim().is_empty() {
1031            name_builder.append_entry_by_nid(Nid::ORGANIZATIONNAME, &self.fields.organization)?;
1032        }
1033        if !self.fields.organization_unit.trim().is_empty() {
1034            name_builder
1035                .append_entry_by_nid(Nid::ORGANIZATIONALUNITNAME, &self.fields.organization_unit)?;
1036        }
1037
1038        let name = name_builder.build();
1039
1040        let mut builder = X509::builder()?;
1041        builder.set_version(2)?;
1042
1043        let serial_number = {
1044            let mut serial = BigNum::new()?;
1045            serial.rand(159, openssl::bn::MsbOption::MAYBE_ZERO, false)?;
1046            serial.to_asn1_integer()?
1047        };
1048
1049        let pkey = select_key(&self.fields.key_type).unwrap();
1050        builder.set_serial_number(&serial_number)?;
1051        builder.set_subject_name(&name)?;
1052        builder.set_pubkey(&pkey)?;
1053        builder.set_not_before(&self.valid_from)?;
1054        builder.set_not_after(&self.valid_to)?;
1055        match signer {
1056            Some(signer) => builder.set_issuer_name(signer.x509.subject_name())?,
1057            None => builder.set_issuer_name(&name)?,
1058        }
1059
1060        let key_usage = self.fields.usage.clone().unwrap_or_default();
1061        if self.ca {
1062            builder.append_extension(BasicConstraints::new().ca().critical().build()?)?;
1063        } else {
1064            builder.append_extension(BasicConstraints::new().build()?)?;
1065        }
1066
1067        let (tracked_key_usage, tracked_extended_key_usage) = get_key_usage(&Some(key_usage));
1068        if tracked_key_usage.is_used() {
1069            builder.append_extension(tracked_key_usage.into_inner().build()?)?;
1070        }
1071        if tracked_extended_key_usage.is_used() {
1072            builder.append_extension(tracked_extended_key_usage.into_inner().build()?)?;
1073        }
1074
1075        let mut san = SubjectAlternativeName::new();
1076        for s in &self.fields.alternative_names {
1077            san.dns(s);
1078        }
1079        if let Some(signer_cert) = signer {
1080            builder.append_extension(
1081                san.build(&builder.x509v3_context(Some(&signer_cert.x509), None))?,
1082            )?;
1083            if signer_cert.x509.subject_key_id().is_some() {
1084                let aki = AuthorityKeyIdentifier::new()
1085                    .keyid(true)
1086                    .issuer(false)
1087                    .build(&builder.x509v3_context(Some(&signer_cert.x509), None))?;
1088                builder.append_extension(aki)?;
1089            }
1090        } else {
1091            // add aki that is the same as ski for self signed
1092            builder.append_extension(san.build(&builder.x509v3_context(None, None))?)?;
1093            let oid = Asn1Object::from_str("2.5.29.35")?; // OID för Authority Key Identifier (AKI)
1094            let pubkey_der = pkey.public_key_to_der()?;
1095            let aki_hash = hash(MessageDigest::sha1(), &pubkey_der)?;
1096            let der_encoded = yasna::construct_der(|writer| {
1097                writer.write_sequence(|writer| {
1098                    writer
1099                        .next()
1100                        .write_tagged_implicit(yasna::Tag::context(0), |writer| {
1101                            writer.write_bytes(aki_hash.as_ref());
1102                        })
1103                })
1104            });
1105            let aki_asn1 = Asn1OctetString::new_from_bytes(&der_encoded)?;
1106            let ext = X509Extension::new_from_der(oid.as_ref(), false, &aki_asn1)?;
1107            builder.append_extension(ext)?;
1108        }
1109        // tried
1110        // let ski = SubjectKeyIdentifier::new().build(&builder.x509v3_context(None, None))?;
1111        // but got miss/match in hash values so I calculate the ski explicitly with sha1
1112        // to verify with openssl cli
1113        // RSA:
1114        // openssl x509 -in mytestca_cert.pem -inform PEM -pubkey -noout | openssl rsa -pubin -outform DER | openssl dgst -c -sha1
1115        // EC:
1116        // openssl x509 -in mytestca_cert.pem -inform PEM -pubkey -noout| openssl pkey -pubin -outform DER| openssl dgst -c -sha1
1117        let oid = Asn1Object::from_str("2.5.29.14")?; // OID för Subject Key Identifier (SKI)
1118        let pubkey_der = pkey.public_key_to_der()?;
1119        let ski_hash = hash(MessageDigest::sha1(), &pubkey_der)?;
1120        let der_encoded = yasna::construct_der(|writer| {
1121            writer.write_bytes(ski_hash.as_ref());
1122        });
1123        let ski_asn1 = Asn1OctetString::new_from_bytes(&der_encoded)?;
1124        let ext = X509Extension::new_from_der(oid.as_ref(), false, &ski_asn1)?;
1125        builder.append_extension(ext)?;
1126
1127        Ok((builder, pkey))
1128    }
1129}
1130
1131/// Builder for creating a new certificate signing request and private key
1132pub struct CsrBuilder {
1133    fields: BuilderFields,
1134}
1135impl UseesBuilderFields for CsrBuilder {
1136    fn fields_mut(&mut self) -> &mut BuilderFields {
1137        &mut self.fields
1138    }
1139}
1140impl Default for CsrBuilder {
1141    fn default() -> Self {
1142        Self::new()
1143    }
1144}
1145
1146impl CsrBuilder {
1147    /// Create a new CsrBuilder with defaults
1148    pub fn new() -> Self {
1149        Self {
1150            fields: BuilderFields::default(),
1151        }
1152    }
1153
1154    /// Builds and returns a Certificate Signing Request (CSR) based on the configured builder fields.
1155    ///
1156    /// This function constructs the subject name, sets the public key, and adds relevant X.509 extensions
1157    /// such as Key Usage, Extended Key Usage, and Subject Alternative Names (SAN).
1158    /// It supports signing with both traditional algorithms and Ed25519.
1159    ///
1160    /// # Returns
1161    /// - `Ok(Csr)` if the CSR was successfully built and signed.
1162    /// - `Err(Box<dyn std::error::Error>)` if any step in the CSR creation process fails.
1163    ///
1164    /// # Errors
1165    /// This function may return errors in the following cases:
1166    /// - Failure to initialize or build the X509 name or request.
1167    /// - Failure to select or use the appropriate key type.
1168    /// - Failure to build or add X.509 extensions.
1169    /// - Failure to sign the CSR, especially with Ed25519.
1170    ///
1171    /// # Extensions Added
1172    /// - **Key Usage** and **Extended Key Usage**: Based on the builder's `usage` field.
1173    /// - **Subject Alternative Names (SAN)**: Includes all entries from `alternative_names`.
1174    ///
1175    /// # Signing Behavior
1176    /// - If the key type is Ed25519, uses a custom signing function.
1177    /// - Otherwise, signs using the selected hash algorithm.
1178    ///
1179    /// # Example
1180    /// ```rust
1181    /// use cert_helper::certificate::CsrBuilder;
1182    /// use crate::cert_helper::certificate::UseesBuilderFields;
1183    /// let builder = CsrBuilder::new().common_name("example.com");
1184    /// let csr = builder.certificate_signing_request().unwrap();
1185    /// ```
1186    pub fn certificate_signing_request(self) -> Result<Csr, Box<dyn std::error::Error>> {
1187        let mut name_builder = X509NameBuilder::new()?;
1188        name_builder.append_entry_by_nid(Nid::COMMONNAME, &self.fields.common_name)?;
1189        if !self.fields.country_name.trim().is_empty() {
1190            name_builder.append_entry_by_nid(Nid::COUNTRYNAME, &self.fields.country_name)?;
1191        }
1192        if !self.fields.state_province.trim().is_empty() {
1193            name_builder
1194                .append_entry_by_nid(Nid::STATEORPROVINCENAME, &self.fields.state_province)?;
1195        }
1196        if !self.fields.locality_time.trim().is_empty() {
1197            name_builder.append_entry_by_nid(Nid::LOCALITYNAME, &self.fields.locality_time)?;
1198        }
1199        if !self.fields.organization.trim().is_empty() {
1200            name_builder.append_entry_by_nid(Nid::ORGANIZATIONNAME, &self.fields.organization)?;
1201        }
1202        let name = name_builder.build();
1203        let mut builder = X509ReqBuilder::new()?;
1204        builder.set_version(0)?;
1205        builder.set_subject_name(&name)?;
1206        let pkey = select_key(&self.fields.key_type).unwrap();
1207        builder.set_pubkey(&pkey)?;
1208        let key_usage = self.fields.usage.clone().unwrap_or_default();
1209
1210        let mut extensions = Stack::new()?;
1211
1212        let (tracked_key_usage, tracked_extended_key_usage) = get_key_usage(&Some(key_usage));
1213        if tracked_key_usage.is_used() {
1214            extensions.push(tracked_key_usage.inner.build()?)?;
1215        }
1216        if tracked_extended_key_usage.is_used() {
1217            extensions.push(tracked_extended_key_usage.inner.build()?)?;
1218        }
1219
1220        let mut san = SubjectAlternativeName::new();
1221        for s in &self.fields.alternative_names {
1222            san.dns(s);
1223        }
1224        extensions.push(san.build(&builder.x509v3_context(None))?)?;
1225
1226        builder.add_extensions(&extensions)?;
1227        let csr: X509Req = if is_digestless_key(&pkey) {
1228            let builder_csr = builder.build();
1229            sign_x509_req_digestless(&builder_csr, &pkey)
1230                .map_err(|e| format!("Failed to sign certificate with digestless key: {}", e))?;
1231            builder_csr
1232        } else {
1233            builder.sign(&pkey, select_hash(&self.fields.signature_alg))?;
1234            builder.build()
1235        };
1236        Ok(Csr {
1237            csr,
1238            pkey: Some(pkey),
1239        })
1240    }
1241}
1242struct TrackedExtendedKeyUsage {
1243    inner: ExtendedKeyUsage,
1244    used: bool,
1245}
1246
1247impl TrackedExtendedKeyUsage {
1248    fn new() -> Self {
1249        Self {
1250            inner: ExtendedKeyUsage::new(),
1251            used: false,
1252        }
1253    }
1254
1255    fn client_auth(&mut self) {
1256        self.inner.client_auth();
1257        self.used = true;
1258    }
1259
1260    fn server_auth(&mut self) {
1261        self.inner.server_auth();
1262        self.used = true;
1263    }
1264
1265    fn is_used(&self) -> bool {
1266        self.used
1267    }
1268
1269    fn into_inner(self) -> ExtendedKeyUsage {
1270        self.inner
1271    }
1272}
1273
1274struct TrackedKeyUsage {
1275    inner: KeyUsage,
1276    used: bool,
1277}
1278
1279impl TrackedKeyUsage {
1280    fn new() -> Self {
1281        Self {
1282            inner: KeyUsage::new(),
1283            used: false,
1284        }
1285    }
1286
1287    fn digital_signature(&mut self) {
1288        self.inner.digital_signature();
1289        self.used = true;
1290    }
1291
1292    fn non_repudiation(&mut self) {
1293        self.inner.non_repudiation();
1294        self.used = true;
1295    }
1296
1297    fn key_encipherment(&mut self) {
1298        self.inner.key_encipherment();
1299        self.used = true;
1300    }
1301
1302    fn key_cert_sign(&mut self) {
1303        self.inner.key_cert_sign();
1304        self.used = true;
1305    }
1306
1307    fn crl_sign(&mut self) {
1308        self.inner.crl_sign();
1309        self.used = true;
1310    }
1311
1312    fn is_used(&self) -> bool {
1313        self.used
1314    }
1315
1316    fn into_inner(self) -> KeyUsage {
1317        self.inner
1318    }
1319}
1320
1321/// Verifies a certificate against a root certificate and the intermediate
1322/// chain leading up to it.
1323/// Note: The root certificate should not be included in the chain.
1324pub fn verify_cert(
1325    cert: &X509,
1326    ca: &X509,
1327    cert_chain: Vec<&X509>,
1328) -> Result<bool, Box<dyn std::error::Error>> {
1329    // Build a certificate store and add the issuer
1330    let mut store_builder = X509StoreBuilder::new()?;
1331    store_builder.add_cert(ca.clone())?;
1332    let store = store_builder.build();
1333
1334    // Create a verification context
1335    let mut ctx = X509StoreContext::new()?;
1336    let mut chain = Stack::new()?; // create an empty chain
1337    cert_chain
1338        .iter()
1339        .try_for_each(|c| chain.push((*c).clone()))?;
1340    ctx.init(&store, cert, &chain, |c| c.verify_cert())?;
1341    let verified = ctx.error() == openssl::x509::X509VerifyResult::OK;
1342    Ok(verified)
1343}
1344
1345/// Takes a vector of certificates and returns a vector ordered
1346/// from the root to the leaf, with the leaf certificate as the last element.
1347///
1348/// For example, if the input list contains `ca2`, `leaf`, and `ca1`,
1349/// and the signing order is `ca1 -> ca2 -> leaf`,
1350/// the returned vector will be `[ca1, ca2, leaf]`.
1351///
1352/// If multiple valid chains are possible, the longest one is returned.
1353pub fn create_cert_chain_from_cert_list(
1354    certs: Vec<X509>,
1355) -> Result<Vec<X509>, Box<dyn std::error::Error>> {
1356    let mut subject_map: HashMap<Vec<u8>, X509> = HashMap::new();
1357    let mut issuer_map: HashMap<Vec<u8>, Vec<u8>> = HashMap::new();
1358
1359    for cert in &certs {
1360        let subject = cert.subject_name().to_der()?;
1361        let issuer = cert.issuer_name().to_der()?;
1362        subject_map.insert(subject.clone(), cert.clone());
1363        issuer_map.insert(subject, issuer);
1364    }
1365
1366    // Find leaf certificates (those that are not issuers of any other cert)
1367    let all_issuers: Vec<Vec<u8>> = issuer_map.values().cloned().collect();
1368    let leaf_certs: Vec<X509> = subject_map
1369        .iter()
1370        .filter(|(subject, _)| !all_issuers.contains(subject))
1371        .map(|(_, cert)| cert.clone())
1372        .collect();
1373
1374    // Try to build the longest chain from each leaf
1375    let mut longest_chain = Vec::new();
1376
1377    for leaf in leaf_certs {
1378        let mut chain = vec![leaf.clone()];
1379        let mut current_cert = leaf;
1380
1381        while let Ok(issuer_der) = current_cert.issuer_name().to_der() {
1382            if let Some(parent_cert) = subject_map.get(&issuer_der) {
1383                if parent_cert.subject_name().to_der()? == current_cert.subject_name().to_der()? {
1384                    break; // Self-signed, stop here
1385                }
1386                chain.push(parent_cert.clone());
1387                current_cert = parent_cert.clone();
1388            } else {
1389                break; // Issuer not found in the list
1390            }
1391        }
1392
1393        if chain.len() > longest_chain.len() {
1394            longest_chain = chain;
1395        }
1396    }
1397
1398    // Reverse to have root (or highest known CA) first
1399    longest_chain.reverse();
1400    Ok(longest_chain)
1401}
1402
1403fn create_asn1_time_from_date(date_str: &str) -> Result<Asn1Time, Box<dyn std::error::Error>> {
1404    let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d")?;
1405    let datetime = NaiveDateTime::new(date, chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap());
1406    let utc_datetime = Utc.from_utc_datetime(&datetime);
1407    let asn1_time_str = utc_datetime.format("%Y%m%d%H%M%SZ").to_string();
1408    let asn1_time = Asn1Time::from_str(&asn1_time_str)?;
1409    Ok(asn1_time)
1410}
1411
1412fn select_key(key_type: &Option<KeyType>) -> Result<PKey<Private>, ErrorStack> {
1413    match key_type {
1414        Some(KeyType::P224) => {
1415            let group = EcGroup::from_curve_name(Nid::SECP224R1)?;
1416            let ec_key = EcKey::generate(&group)?;
1417            PKey::from_ec_key(ec_key)
1418        }
1419        Some(KeyType::P256) => {
1420            let group = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)?;
1421            let ec_key = EcKey::generate(&group)?;
1422            PKey::from_ec_key(ec_key)
1423        }
1424        Some(KeyType::P384) => {
1425            let group = EcGroup::from_curve_name(Nid::SECP384R1)?;
1426            let ec_key = EcKey::generate(&group)?;
1427            PKey::from_ec_key(ec_key)
1428        }
1429        Some(KeyType::P521) => {
1430            let group = EcGroup::from_curve_name(Nid::SECP521R1)?;
1431            let ec_key = EcKey::generate(&group)?;
1432            PKey::from_ec_key(ec_key)
1433        }
1434        Some(KeyType::Ed25519) => PKey::generate_ed25519(),
1435        #[cfg(feature = "pqc")]
1436        Some(KeyType::MlDsa44) => generate_pqc_key("ML-DSA-44"),
1437        #[cfg(feature = "pqc")]
1438        Some(KeyType::MlDsa65) => generate_pqc_key("ML-DSA-65"),
1439        #[cfg(feature = "pqc")]
1440        Some(KeyType::MlDsa87) => generate_pqc_key("ML-DSA-87"),
1441        #[cfg(feature = "pqc")]
1442        Some(KeyType::SlhDsaSha2_128s) => generate_pqc_key("SLH-DSA-SHA2-128s"),
1443        #[cfg(feature = "pqc")]
1444        Some(KeyType::SlhDsaSha2_192s) => generate_pqc_key("SLH-DSA-SHA2-192s"),
1445        #[cfg(feature = "pqc")]
1446        Some(KeyType::SlhDsaSha2_256s) => generate_pqc_key("SLH-DSA-SHA2-256s"),
1447        Some(KeyType::RSA4096) => {
1448            let rsa = Rsa::generate(4096)?;
1449            PKey::from_rsa(rsa)
1450        }
1451        _ => {
1452            let rsa = Rsa::generate(2048)?;
1453            PKey::from_rsa(rsa)
1454        }
1455    }
1456}
1457
1458fn select_hash(hash_type: &Option<HashAlg>) -> MessageDigest {
1459    match hash_type {
1460        Some(HashAlg::SHA1) => MessageDigest::sha1(),
1461        Some(HashAlg::SHA384) => MessageDigest::sha384(),
1462        Some(HashAlg::SHA512) => MessageDigest::sha512(),
1463        _ => MessageDigest::sha256(),
1464    }
1465}
1466
1467fn get_key_usage(usage: &Option<HashSet<Usage>>) -> (TrackedKeyUsage, TrackedExtendedKeyUsage) {
1468    let mut ku = TrackedKeyUsage::new();
1469    let mut eku = TrackedExtendedKeyUsage::new();
1470    if let Some(usages) = usage {
1471        for u in usages {
1472            match u {
1473                Usage::contentcommitment => {
1474                    ku.non_repudiation();
1475                }
1476                Usage::encipherment => {
1477                    ku.key_encipherment();
1478                }
1479                Usage::certsign => {
1480                    ku.key_cert_sign();
1481                }
1482                Usage::clientauth => {
1483                    eku.client_auth();
1484                }
1485                Usage::signature => {
1486                    ku.digital_signature();
1487                }
1488                Usage::crlsign => {
1489                    ku.crl_sign();
1490                }
1491                Usage::serverauth => {
1492                    eku.server_auth();
1493                }
1494            }
1495        }
1496    }
1497
1498    (ku, eku)
1499}
1500
1501fn can_sign_cert(cert: &X509) -> Result<bool, Box<dyn std::error::Error>> {
1502    let der = cert.to_der()?;
1503    let (_, parsed_cert) = parse_x509_certificate(&der)?;
1504
1505    let mut is_ca = false;
1506    let mut can_sign = false;
1507
1508    let not_after_asn1_time = cert.not_after().to_string();
1509    let naive =
1510        NaiveDateTime::parse_from_str(&not_after_asn1_time, "%b %e %H:%M:%S %Y GMT").unwrap();
1511    let not_after_utc: DateTime<Utc> = Utc.from_utc_datetime(&naive);
1512
1513    let not_before_asn1_time = cert.not_before().to_string();
1514    let naive =
1515        NaiveDateTime::parse_from_str(&not_before_asn1_time, "%b %e %H:%M:%S %Y GMT").unwrap();
1516    let not_before_utc: DateTime<Utc> = Utc.from_utc_datetime(&naive);
1517
1518    let now = Utc::now();
1519    let valid_time = (now < not_after_utc) && (now >= not_before_utc);
1520
1521    for ext in parsed_cert.tbs_certificate.extensions().iter() {
1522        match &ext.parsed_extension() {
1523            ParsedExtension::BasicConstraints(bc) => {
1524                is_ca = bc.ca;
1525            }
1526            ParsedExtension::KeyUsage(ku) => {
1527                can_sign = ku.key_cert_sign();
1528            }
1529            _ => {}
1530        }
1531    }
1532    Ok(is_ca && can_sign && valid_time)
1533}
1534
1535#[cfg(test)]
1536mod tests {
1537    use super::*;
1538    use std::io::Write;
1539    use std::path::Path;
1540    use tempfile::NamedTempFile;
1541
1542    #[test]
1543    fn save_certificate() {
1544        let ca = CertBuilder::new().common_name("My Test Ca").is_ca(true);
1545        match ca.build_and_self_sign() {
1546            Ok(cert) => {
1547                let output_file = NamedTempFile::new().unwrap();
1548                let full_path = output_file.path();
1549                let parent_dir: &Path = full_path.parent().unwrap();
1550                let file_name: &str = full_path.file_name().unwrap().to_str().unwrap();
1551                cert.save(parent_dir, file_name)
1552                    .expect("Failed to save certificate and key");
1553                let written_file_path = parent_dir.join(file_name);
1554                assert!(written_file_path.exists(), "File was not created");
1555            }
1556            Err(_) => panic!("Failed to creat certificate"),
1557        }
1558    }
1559
1560    #[test]
1561    fn read_certificate_and_key_from_file() {
1562        let cert_pem = b"-----BEGIN CERTIFICATE-----
1563MIICiDCCAemgAwIBAgIUO3+y1WZPRRNs8dmZZTUHMj6TdiowCgYIKoZIzj0EAwQw
1564WzETMBEGA1UEAwwKTXkgVGVzdCBDYTELMAkGA1UEBhMCU0UxEjAQBgNVBAgMCVN0
1565b2NraG9sbTESMBAGA1UEBwwJU3RvY2tob2xtMQ8wDQYDVQQKDAZteSBvcmcwHhcN
1566MjUwNzA4MTExMzI2WhcNMjYwNzA4MTExMzI2WjBbMRMwEQYDVQQDDApNeSBUZXN0
1567IENhMQswCQYDVQQGEwJTRTESMBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlT
1568dG9ja2hvbG0xDzANBgNVBAoMBm15IG9yZzCBmzAQBgcqhkjOPQIBBgUrgQQAIwOB
1569hgAEADZXcQK2ihgVTJeGx5FKm1x+R+ivygIvMnkv03faq1LpLU3doKX38DEO/cSW
1570Ev5u+kcjspXeeDPhqJFC8rRAz4awAMk+D0mXEms7xpFPh0HmI6NNcJc5eJ/8ZsEJ
1571GH1a34y0Yn6259gqlwAh2Eh9Nx1579BAanRr8lr+n1tZ09T/9AQho0gwRjAMBgNV
1572HRMEBTADAQH/MAsGA1UdDwQEAwIBBjApBgNVHREEIjAgggZjYS5jb22CCnd3dy5j
1573YS5jb22CCk15IFRlc3QgQ2EwCgYIKoZIzj0EAwQDgYwAMIGIAkIB8NVUgRIuNXmJ
1574cLCQ74Ub7Dqo71S0+iCrZF1YyJA8/q65aqMCT54k5Yx7HRBUUVHbCEpDXRqGPsIH
1575frfe5OmS3qICQgDBn07o0CcyfoSEd+Xoj2+/RBuU0vo9lUP7TKj7tssBxzEQFoxX
1576eE1qT98UIe78FZ+zqjwZTN9MCSsatuim6pXvOA==
1577-----END CERTIFICATE-----";
1578
1579        let key_pem = b"-----BEGIN PRIVATE KEY-----
1580MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAgvZTeQgGysadAX0r
1581aZB5Lk4vjHy5iVuKdvcGdYt9NBvYx+Ib3Uk7vqMag7M1jyHL0Xf9uNtT2mxBmzBG
15823CF+EgOhgYkDgYYABAA2V3ECtooYFUyXhseRSptcfkfor8oCLzJ5L9N32qtS6S1N
15833aCl9/AxDv3ElhL+bvpHI7KV3ngz4aiRQvK0QM+GsADJPg9JlxJrO8aRT4dB5iOj
1584TXCXOXif/GbBCRh9Wt+MtGJ+tufYKpcAIdhIfTcdee/QQGp0a/Ja/p9bWdPU//QE
1585IQ==
1586-----END PRIVATE KEY-----";
1587
1588        let mut cert_file = NamedTempFile::new().expect("Failed to create temp cert file");
1589        let mut key_file = NamedTempFile::new().expect("Failed to create temp key file");
1590
1591        cert_file.write_all(cert_pem).expect("Failed to write cert");
1592        key_file.write_all(key_pem).expect("Failed to write key");
1593
1594        let result = Certificate::load_cert_and_key(cert_file.path(), key_file.path());
1595        assert!(
1596            result.is_ok(),
1597            "Failed to load cert and key: {:?}",
1598            result.err()
1599        );
1600    }
1601
1602    #[test]
1603    fn test_reading_csr_from_file() {
1604        let csr_data = b"-----BEGIN CERTIFICATE REQUEST-----
1605MIICzDCCAbQCAQAwXTEVMBMGA1UEAwwMZXhhbXBsZTIuY29tMQswCQYDVQQGEwJT
1606RTESMBAGA1UECAwJU3RvY2tob2xtMRIwEAYDVQQHDAlTdG9ja2hvbG0xDzANBgNV
1607BAoMBk15IG9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAIeAXpCG
1608hbIayESfdTzOO0DxIMsOAu4kUm0zF0W/+xDUHl6bGy3wlB9S9nzBG/qwqFZ27Om3
1609o4zrZ8K8DBx0ERWNuhMmr0Nx8QpAWBEyxOc08Gn4c3XVBBkRZSn4AIqr9DGtcUqW
1610tQZXvMGF6sRRljiEvOxO6zMzZKTGYwzIeQvH85cQ3uXsw0Kknsw/fcuywaAC8SS9
1611aqs4jiEIgzdhxdH2OVXBNGj4cjVhK309JiWFHS9XJLNV/PKC+F1nkaANQwbW5A4F
16129vya4js9gk8f4SfF1u+qOJEvsDvAb+1xdjXPRzf77eGh3rC4KgGWQ6WrWfW8PItF
1613BDg/jskq3bJXNL8CAwEAAaAqMCgGCSqGSIb3DQEJDjEbMBkwFwYDVR0RBBAwDoIM
1614ZXhhbXBsZTIuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQAHeeSW8C6SMVhWiMvPn7iz
1615FUHQedHRyPz6kTEfC01eNIbs0r4YghOAcm8PF67jncIXVrqgxo1uzq12qlV+0YYb
1616jps31IbQNOz0eFLYvij15ielmOYeQZZ/2vqaGi3geVobLc6Ki5tadnA/NhjTN33j
1617QcqDDic8riAOTbSQ6TH9KPTGJQOPk+taMpDGDHskIW0oME5iT2ewbhBHg6v/kSzy
1618tss2kBY5O7vo2COtbNcwX5Xp9S2LH9kVUKr0GIjuQjwbv5xl+GNdDey09W9EDACU
1619jcGV3++2wS4LN4h3CG4pWZ+LTXhm8ymhoWOapN95lfe3xLRAKFJwiLkGwS75++FW
1620-----END CERTIFICATE REQUEST-----";
1621        let mut csr_file = NamedTempFile::new().expect("Failed to create temp csr file");
1622        csr_file.write_all(csr_data).expect("Failed to write csr");
1623        let result = Csr::load_csr(csr_file.path());
1624        assert!(result.is_ok(), "Failed to load csr: {:?}", result.err());
1625    }
1626}