1pub use x509_cert;
6
7#[allow(missing_docs)]
9#[derive(Debug)]
10pub struct Params<'a> {
11 pub common_name: &'a str,
12 pub subject_alt_names: &'a [&'a str],
13 pub valid_days_before: u32,
14 pub valid_days_after: u32,
15}
16
17impl<'a> Params<'a> {
18 pub fn new(common_name: &'a str, subject_alt_names: &'a [&'a str]) -> Self {
22 Self {
23 common_name,
24 subject_alt_names,
25 valid_days_before: 0,
26 valid_days_after: 14,
27 }
28 }
29}
30
31const DAY: std::time::Duration = std::time::Duration::from_secs(60 * 60 * 24);
33
34#[allow(missing_docs)]
35#[derive(Debug, thiserror::Error)]
36pub enum Error {
37 #[error("bad common name value: {0}")]
38 BadCommonName(der::Error),
39 #[error("bad valid days before value")]
40 BadValidDaysBefore,
41 #[error("bad valid days after value")]
42 BadValidDaysAfter,
43 #[error("bad subject alt name value")]
44 BadSubjectAltName,
45 #[error("bad key")]
46 BadKey,
47 #[error("unable to create a certificate builder")]
48 Builder(x509_cert::builder::Error),
49 #[error("unable to add a subject alt name extension")]
50 SubjectAltNameExtension(x509_cert::builder::Error),
51 #[error("unable to build the certificate")]
52 Build(x509_cert::builder::Error),
53}
54
55impl Params<'_> {
56 pub fn self_signed<Key, Signature>(&self, key: &Key) -> Result<x509_cert::Certificate, Error>
88 where
89 Key: x509_cert::spki::DynSignatureAlgorithmIdentifier,
90 Key: signature::Keypair,
91 Key::VerifyingKey: x509_cert::spki::EncodePublicKey,
92 Key: signature::Signer<Signature>,
93 Signature: x509_cert::spki::SignatureBitStringEncoding,
94 {
95 use x509_cert::builder::Builder;
96
97 let now = std::time::SystemTime::now();
98
99 let subject: x509_cert::name::Name = format!("CN={}", self.common_name)
100 .parse()
101 .map_err(Error::BadCommonName)?;
102
103 let profile = x509_cert::builder::Profile::Leaf {
104 issuer: subject.clone(),
105 enable_key_agreement: true,
106 enable_key_encipherment: true,
107 };
108 let serial_number = x509_cert::serial_number::SerialNumber::from(1u8);
109 let validity = x509_cert::time::Validity {
110 not_before: (now - (self.valid_days_before * DAY))
111 .try_into()
112 .map_err(|_| Error::BadValidDaysBefore)?,
113 not_after: (now + (self.valid_days_after * DAY))
114 .try_into()
115 .map_err(|_| Error::BadValidDaysAfter)?,
116 };
117
118 let subject_public_key_info =
119 x509_cert::spki::SubjectPublicKeyInfoOwned::from_key(key.verifying_key())
120 .map_err(|_| Error::BadKey)?;
121
122 let mut builder = x509_cert::builder::CertificateBuilder::new(
123 profile,
124 serial_number,
125 validity,
126 subject,
127 subject_public_key_info,
128 key,
129 )
130 .map_err(Error::Builder)?;
131
132 let parse_subject_alt_name =
133 |s: &str| -> Result<x509_cert::ext::pkix::name::GeneralName, Error> {
134 Ok(match s.parse::<core::net::IpAddr>() {
135 Ok(ip) => x509_cert::ext::pkix::name::GeneralName::from(ip),
136 Err(_) => {
137 let val =
138 der::asn1::Ia5String::new(s).map_err(|_| Error::BadSubjectAltName)?;
139 x509_cert::ext::pkix::name::GeneralName::DnsName(val)
140 }
141 })
142 };
143
144 let subject_alt_names = self
145 .subject_alt_names
146 .iter()
147 .copied()
148 .map(parse_subject_alt_name)
149 .collect::<Result<_, _>>()?;
150
151 builder
152 .add_extension(&x509_cert::ext::pkix::SubjectAltName(subject_alt_names))
153 .map_err(Error::SubjectAltNameExtension)?;
154
155 let cert = builder.build().map_err(Error::Builder)?;
156 Ok(cert)
157 }
158}