use rcgen::{Certificate, CertificateParams, DistinguishedName, IsCa, KeyPair};
use rustls::pki_types::CertificateDer;
pub struct CertAuthority {
pub cert: Certificate,
pub key_pair: KeyPair,
pub cert_der: CertificateDer<'static>,
cert_pem: String,
}
impl CertAuthority {
pub fn generate() -> Self {
let mut params = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(rcgen::DnType::CommonName, "microsandbox CA");
dn.push(rcgen::DnType::OrganizationName, "microsandbox");
params.distinguished_name = dn;
params.is_ca = IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
params.key_usages = vec![
rcgen::KeyUsagePurpose::KeyCertSign,
rcgen::KeyUsagePurpose::CrlSign,
];
let key_pair = KeyPair::generate().expect("failed to generate CA key pair");
let cert = params
.self_signed(&key_pair)
.expect("failed to self-sign CA certificate");
let cert_pem = cert.pem();
let cert_der = CertificateDer::from(cert.der().to_vec());
Self {
cert,
key_pair,
cert_der,
cert_pem,
}
}
pub fn load(cert_pem_bytes: &[u8], key_pem_bytes: &[u8]) -> Result<Self, String> {
let cert_pem_str =
std::str::from_utf8(cert_pem_bytes).map_err(|e| format!("invalid cert PEM: {e}"))?;
let key_pem_str =
std::str::from_utf8(key_pem_bytes).map_err(|e| format!("invalid key PEM: {e}"))?;
let key_pair =
KeyPair::from_pem(key_pem_str).map_err(|e| format!("failed to parse CA key: {e}"))?;
let params = CertificateParams::from_ca_cert_pem(cert_pem_str)
.map_err(|e| format!("failed to parse CA cert: {e}"))?;
let cert = params
.self_signed(&key_pair)
.map_err(|e| format!("failed to re-sign CA cert: {e}"))?;
let original_der = pem_to_der(cert_pem_str)?;
Ok(Self {
cert,
key_pair,
cert_der: CertificateDer::from(original_der),
cert_pem: cert_pem_str.to_string(),
})
}
pub fn cert_pem(&self) -> Vec<u8> {
self.cert_pem.as_bytes().to_vec()
}
pub fn key_pem(&self) -> Vec<u8> {
self.key_pair.serialize_pem().as_bytes().to_vec()
}
}
fn pem_to_der(pem: &str) -> Result<Vec<u8>, String> {
use rustls::pki_types::pem::PemObject;
CertificateDer::from_pem_slice(pem.as_bytes())
.map(|cert| cert.to_vec())
.map_err(|e| format!("failed to parse PEM: {e}"))
}