use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, SystemTime};
use rcgen::{
CertificateParams, CertificateSigningRequestParams, DnType, ExtendedKeyUsagePurpose,
IsCa, KeyPair, KeyUsagePurpose, SerialNumber,
};
use rustls_pki_types::CertificateSigningRequestDer;
use crate::ca::profile::CertProfile;
use crate::ca::serial::{random_serial_bytes, SerialStore};
use crate::error::{Error, Result};
#[derive(Clone)]
pub struct Issuer {
inner: Arc<IssuerInner>,
}
struct IssuerInner {
ca_cert: rcgen::Certificate,
ca_key: KeyPair,
ca_der: Vec<u8>,
profile: CertProfile,
serials: Arc<dyn SerialStore>,
}
impl Issuer {
pub fn load_pem(
cert_path: impl AsRef<Path>,
key_path: impl AsRef<Path>,
profile: CertProfile,
serials: Arc<dyn SerialStore>,
) -> Result<Self> {
let cert_pem = std::fs::read_to_string(cert_path.as_ref())?;
let key_pem = std::fs::read_to_string(key_path.as_ref())?;
Self::from_pem(&cert_pem, &key_pem, profile, serials)
}
pub fn from_pem(
cert_pem: &str,
key_pem: &str,
profile: CertProfile,
serials: Arc<dyn SerialStore>,
) -> Result<Self> {
let ca_key =
KeyPair::from_pem(key_pem).map_err(|e| Error::Parse(format!("CA key PEM: {e}")))?;
let params = CertificateParams::from_ca_cert_pem(cert_pem)
.map_err(|e| Error::Parse(format!("CA cert PEM: {e}")))?;
let ca_cert = params
.self_signed(&ca_key)
.map_err(|e| Error::Ca(format!("rebuild CA cert: {e}")))?;
let ca_der = ca_cert.der().to_vec();
Ok(Self { inner: Arc::new(IssuerInner { ca_cert, ca_key, ca_der, profile, serials }) })
}
pub fn generate_self_signed(
common_name: &str,
profile: CertProfile,
serials: Arc<dyn SerialStore>,
) -> Result<Self> {
let mut params = CertificateParams::new(vec![common_name.to_string()])
.map_err(|e| Error::Ca(format!("CA params: {e}")))?;
params.is_ca = IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
params.distinguished_name.push(DnType::CommonName, common_name);
params.key_usages =
vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign, KeyUsagePurpose::DigitalSignature];
let ca_key = KeyPair::generate().map_err(|e| Error::Ca(format!("CA keygen: {e}")))?;
let ca_cert = params
.self_signed(&ca_key)
.map_err(|e| Error::Ca(format!("self-sign CA: {e}")))?;
let ca_der = ca_cert.der().to_vec();
Ok(Self { inner: Arc::new(IssuerInner { ca_cert, ca_key, ca_der, profile, serials }) })
}
pub fn ca_cert_der(&self) -> &[u8] {
&self.inner.ca_der
}
pub fn sign_csr(&self, csr_der: &[u8], principal_id: &str) -> Result<Vec<u8>> {
let profile = &self.inner.profile;
profile.validate_cn(principal_id).map_err(Error::Profile)?;
let der_wrapper = CertificateSigningRequestDer::from(csr_der.to_vec());
let csr = CertificateSigningRequestParams::from_der(&der_wrapper)
.map_err(|e| Error::InvalidCsr(format!("parse PKCS#10: {e}")))?;
let mut params = CertificateParams::default();
params.distinguished_name = Default::default();
params.distinguished_name.push(DnType::CommonName, principal_id);
params.is_ca = IsCa::NoCa;
params.key_usages = vec![KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::KeyEncipherment];
let mut ekus = Vec::new();
if profile.client_auth {
ekus.push(ExtendedKeyUsagePurpose::ClientAuth);
}
if profile.server_auth {
ekus.push(ExtendedKeyUsagePurpose::ServerAuth);
}
params.extended_key_usages = ekus;
let now = SystemTime::now();
params.not_before = now.into();
params.not_after = (now + profile.validity).into();
let _counter = self.inner.serials.next()?;
let sn_bytes = random_serial_bytes();
params.serial_number = Some(SerialNumber::from_slice(&sn_bytes));
let leaf = params
.signed_by(&csr.public_key, &self.inner.ca_cert, &self.inner.ca_key)
.map_err(|e| Error::Ca(format!("sign leaf: {e}")))?;
Ok(leaf.der().to_vec())
}
pub fn leaf_validity(&self) -> Duration {
self.inner.profile.validity
}
}