Skip to main content

rustls_mitm/
ca.rs

1use rcgen::{CertificateParams, IsCa, KeyPair, KeyUsagePurpose};
2use rustls::pki_types::{CertificateDer, PrivateKeyDer};
3
4use crate::Error;
5
6/// Wraps a CA certificate and key pair used to sign per-host certificates.
7pub struct CertificateAuthority {
8    pub(crate) cert: rcgen::Certificate,
9    pub(crate) key: KeyPair,
10}
11
12impl CertificateAuthority {
13    /// Create from PEM-encoded strings.
14    pub fn from_pem(cert_pem: &str, key_pem: &str) -> Result<Self, Error> {
15        let key = KeyPair::from_pem(key_pem)?;
16        let params = CertificateParams::from_ca_cert_pem(cert_pem)?;
17        let cert = params.self_signed(&key)?;
18        Ok(Self { cert, key })
19    }
20
21    /// Create from PEM files on disk.
22    pub fn from_pem_files(
23        cert_path: impl AsRef<std::path::Path>,
24        key_path: impl AsRef<std::path::Path>,
25    ) -> Result<Self, Error> {
26        let cert_pem = std::fs::read_to_string(cert_path)?;
27        let key_pem = std::fs::read_to_string(key_path)?;
28        Self::from_pem(&cert_pem, &key_pem)
29    }
30
31    /// Generate a new self-signed CA certificate and key pair with CN `"MITM CA"`.
32    pub fn generate() -> Result<Self, Error> {
33        Self::generate_with_cn("MITM CA")
34    }
35
36    /// Generate a new self-signed CA certificate and key pair with the given common name.
37    pub fn generate_with_cn(cn: &str) -> Result<Self, Error> {
38        let mut params = CertificateParams::default();
39        params.is_ca = IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
40        params
41            .distinguished_name
42            .push(rcgen::DnType::CommonName, cn);
43        params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
44
45        let key = KeyPair::generate()?;
46        let cert = params.self_signed(&key)?;
47        Ok(Self { cert, key })
48    }
49
50    /// Write the CA certificate and key as PEM files to disk.
51    pub fn to_pem_files(
52        &self,
53        cert_path: impl AsRef<std::path::Path>,
54        key_path: impl AsRef<std::path::Path>,
55    ) -> Result<(), Error> {
56        std::fs::write(cert_path, self.cert.pem())?;
57        std::fs::write(key_path, self.key.serialize_pem())?;
58        Ok(())
59    }
60
61    /// Return the CA certificate in PEM format.
62    pub fn cert_pem(&self) -> String {
63        self.cert.pem()
64    }
65
66    /// Return the CA private key in PEM format.
67    pub fn key_pem(&self) -> String {
68        self.key.serialize_pem()
69    }
70
71    /// Generate a leaf certificate for the given hostname, signed by this CA.
72    pub fn generate_cert(
73        &self,
74        hostname: &str,
75    ) -> Result<(CertificateDer<'static>, PrivateKeyDer<'static>), Error> {
76        let mut params = CertificateParams::new(vec![hostname.to_string()])?;
77        params.is_ca = IsCa::NoCa;
78        params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
79        params
80            .distinguished_name
81            .push(rcgen::DnType::CommonName, hostname);
82
83        let key_pair = KeyPair::generate()?;
84        let key_der = PrivateKeyDer::Pkcs8(key_pair.serialized_der().to_vec().into());
85        let cert = params.signed_by(&key_pair, &self.cert, &self.key)?;
86        let cert_der = cert.der().clone();
87
88        Ok((cert_der, key_der))
89    }
90}