1use anyhow::{ensure, Context, Result};
4use rcgen::{
5 BasicConstraints, CertificateParams, CertificateSigningRequestParams, CertifiedIssuer, DnType,
6 ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, SanType, SerialNumber,
7};
8use ring::rand::SecureRandom;
9
10pub struct Ca {
11 issuer: CertifiedIssuer<'static, KeyPair>,
12}
13
14pub struct CsrSummary {
16 pub dns_names: Vec<String>,
17 pub common_name: Option<String>,
18 pub other_names: usize,
19 params: CertificateSigningRequestParams,
20}
21
22pub struct Issued {
24 pub der: Vec<u8>,
25 pub pem: String,
26 pub serial: String,
27 pub not_after: String,
28}
29
30impl Ca {
31 pub fn new(name: &str) -> Result<Self> {
32 let key = KeyPair::generate()?;
33 let mut params = CertificateParams::new(Vec::<String>::new())?;
34 params.distinguished_name.push(DnType::CommonName, name);
35 params.is_ca = IsCa::Ca(BasicConstraints::Constrained(0));
36 params.key_usages = vec![
37 KeyUsagePurpose::KeyCertSign,
38 KeyUsagePurpose::CrlSign,
39 KeyUsagePurpose::DigitalSignature,
40 ];
41 let now = time::OffsetDateTime::now_utc();
42 params.not_before = now - time::Duration::hours(1);
43 params.not_after = now + time::Duration::days(3650);
44 Ok(Self {
45 issuer: CertifiedIssuer::self_signed(params, key)?,
46 })
47 }
48
49 pub fn pem(&self) -> String {
50 self.issuer.pem()
51 }
52
53 pub fn read_csr(der: &[u8]) -> Result<CsrSummary> {
55 ensure!(der.len() <= 16 * 1024, "CSR over 16 KiB");
56 let params = CertificateSigningRequestParams::from_der(&der.to_vec().into())
57 .ok()
58 .context("CSR does not parse or its signature does not verify")?;
59 let mut dns_names = Vec::new();
60 let mut other_names = 0;
61 for san in ¶ms.params.subject_alt_names {
62 match san {
63 SanType::DnsName(n) => dns_names.push(n.as_str().to_ascii_lowercase()),
64 _ => other_names += 1,
65 }
66 }
67 let common_name = params
68 .params
69 .distinguished_name
70 .get(&DnType::CommonName)
71 .map(dn_text);
72 Ok(CsrSummary {
73 dns_names,
74 common_name,
75 other_names,
76 params,
77 })
78 }
79
80 pub fn issue(&self, csr: CsrSummary, names: &[String], validity_days: u32) -> Result<Issued> {
81 let mut params = CertificateParams::new(names.to_vec())?;
82 params
83 .distinguished_name
84 .push(DnType::CommonName, names[0].as_str());
85 let mut serial = [0u8; 16];
86 ring::rand::SystemRandom::new()
87 .fill(&mut serial)
88 .ok()
89 .context("no randomness")?;
90 serial[0] &= 0x7f;
91 params.serial_number = Some(SerialNumber::from_slice(&serial));
92 let now = time::OffsetDateTime::now_utc();
93 params.not_before = now - time::Duration::minutes(1);
94 params.not_after = now + time::Duration::days(validity_days as i64);
95 params.is_ca = IsCa::NoCa;
98 params.key_usages = vec![
99 KeyUsagePurpose::DigitalSignature,
100 KeyUsagePurpose::KeyEncipherment,
101 ];
102 params.extended_key_usages = vec![
103 ExtendedKeyUsagePurpose::ServerAuth,
104 ExtendedKeyUsagePurpose::ClientAuth,
105 ];
106 params.use_authority_key_identifier_extension = true;
107 let not_after = params
108 .not_after
109 .format(&time::format_description::well_known::Rfc3339)?;
110 let request = CertificateSigningRequestParams {
111 params,
112 public_key: csr.params.public_key,
113 };
114 let cert = request.signed_by(&self.issuer)?;
115 Ok(Issued {
116 der: cert.der().to_vec(),
117 pem: format!("{}{}", cert.pem(), self.issuer.pem()),
118 serial: serial.iter().map(|b| format!("{b:02x}")).collect(),
119 not_after,
120 })
121 }
122}
123
124fn dn_text(v: &rcgen::DnValue) -> String {
125 match v {
126 rcgen::DnValue::Utf8String(s) => s.clone(),
127 rcgen::DnValue::PrintableString(s) => s.as_str().to_owned(),
128 rcgen::DnValue::Ia5String(s) => s.as_str().to_owned(),
129 rcgen::DnValue::TeletexString(s) => s.as_str().to_owned(),
130 _ => String::new(),
131 }
132}