1 use super::{gen_keypair, KeyPair, KeyType, SubjectAttribute};
2 use crate::b64_encode;
3 use crate::crypto::HashFunction;
4 use crate::error::Error;
5 use openssl::asn1::Asn1Time;
6 use openssl::bn::{BigNum, MsbOption};
7 use openssl::hash::MessageDigest;
8 use openssl::stack::Stack;
9 use openssl::x509::extension::{BasicConstraints, SubjectAlternativeName};
10 use openssl::x509::{X509Builder, X509Extension, X509NameBuilder, X509Req, X509ReqBuilder, X509};
11 use std::collections::{HashMap, HashSet};
12 use std::net::IpAddr;
13 use std::time::{Duration, SystemTime, UNIX_EPOCH};
14
get_digest(digest: HashFunction, key_pair: &KeyPair) -> MessageDigest15 fn get_digest(digest: HashFunction, key_pair: &KeyPair) -> MessageDigest {
16 #[cfg(not(any(ed25519, ed448)))]
17 let digest = digest.native_digest();
18 let _ = key_pair;
19 #[cfg(any(ed25519, ed448))]
20 let digest = match key_pair.key_type {
21 #[cfg(ed25519)]
22 KeyType::Ed25519 => MessageDigest::null(),
23 #[cfg(ed448)]
24 KeyType::Ed448 => MessageDigest::null(),
25 _ => digest.native_digest(),
26 };
27 digest
28 }
29
30 pub struct Csr {
31 inner_csr: X509Req,
32 }
33
34 impl Csr {
new( key_pair: &KeyPair, digest: HashFunction, domains: &[String], ips: &[String], subject_attributes: &HashMap<SubjectAttribute, String>, ) -> Result<Self, Error>35 pub fn new(
36 key_pair: &KeyPair,
37 digest: HashFunction,
38 domains: &[String],
39 ips: &[String],
40 subject_attributes: &HashMap<SubjectAttribute, String>,
41 ) -> Result<Self, Error> {
42 let mut builder = X509ReqBuilder::new()?;
43 builder.set_pubkey(&key_pair.inner_key)?;
44 if !subject_attributes.is_empty() {
45 let mut snb = X509NameBuilder::new()?;
46 for (sattr, val) in subject_attributes.iter() {
47 snb.append_entry_by_nid(sattr.get_nid(), &val)?;
48 }
49 let name = snb.build();
50 builder.set_subject_name(&name)?;
51 }
52 let ctx = builder.x509v3_context(None);
53 let mut san = SubjectAlternativeName::new();
54 for dns in domains.iter() {
55 san.dns(&dns);
56 }
57 for ip in ips.iter() {
58 san.ip(&ip);
59 }
60 let san = san.build(&ctx)?;
61 let mut ext_stack = Stack::new()?;
62 ext_stack.push(san)?;
63 builder.add_extensions(&ext_stack)?;
64 let digest = get_digest(digest, key_pair);
65 builder.sign(&key_pair.inner_key, digest)?;
66 Ok(Csr {
67 inner_csr: builder.build(),
68 })
69 }
70
to_der_base64(&self) -> Result<String, Error>71 pub fn to_der_base64(&self) -> Result<String, Error> {
72 let csr = self.inner_csr.to_der()?;
73 let csr = b64_encode(&csr);
74 Ok(csr)
75 }
76
to_pem(&self) -> Result<String, Error>77 pub fn to_pem(&self) -> Result<String, Error> {
78 let csr = self.inner_csr.to_pem()?;
79 Ok(String::from_utf8(csr)?)
80 }
81 }
82
83 pub struct X509Certificate {
84 pub inner_cert: X509,
85 }
86
87 impl X509Certificate {
from_pem(pem_data: &[u8]) -> Result<Self, Error>88 pub fn from_pem(pem_data: &[u8]) -> Result<Self, Error> {
89 Ok(X509Certificate {
90 inner_cert: X509::from_pem(pem_data)?,
91 })
92 }
93
from_pem_native(pem_data: &[u8]) -> Result<native_tls::Certificate, Error>94 pub fn from_pem_native(pem_data: &[u8]) -> Result<native_tls::Certificate, Error> {
95 Ok(native_tls::Certificate::from_pem(pem_data)?)
96 }
97
from_acme_ext( domain: &str, acme_ext: &str, key_type: KeyType, digest: HashFunction, ) -> Result<(KeyPair, Self), Error>98 pub fn from_acme_ext(
99 domain: &str,
100 acme_ext: &str,
101 key_type: KeyType,
102 digest: HashFunction,
103 ) -> Result<(KeyPair, Self), Error> {
104 let key_pair = gen_keypair(key_type)?;
105 let digest = get_digest(digest, &key_pair);
106 let inner_cert = gen_certificate(domain, &key_pair, &digest, acme_ext)?;
107 let cert = X509Certificate { inner_cert };
108 Ok((key_pair, cert))
109 }
110
expires_in(&self) -> Result<Duration, Error>111 pub fn expires_in(&self) -> Result<Duration, Error> {
112 let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as libc::time_t;
113 let now = Asn1Time::from_unix(timestamp)?;
114 let not_after = self.inner_cert.not_after();
115 let diff = now.diff(not_after)?;
116 let nb_secs = diff.days * 24 * 60 * 60 + diff.secs;
117 let nb_secs = if nb_secs > 0 { nb_secs as u64 } else { 0 };
118 Ok(Duration::from_secs(nb_secs))
119 }
120
subject_alt_names(&self) -> HashSet<String>121 pub fn subject_alt_names(&self) -> HashSet<String> {
122 match self.inner_cert.subject_alt_names() {
123 Some(s) => s
124 .iter()
125 .filter(|v| v.dnsname().is_some() || v.ipaddress().is_some())
126 .map(|v| match v.dnsname() {
127 Some(d) => d.to_string(),
128 None => match v.ipaddress() {
129 Some(i) => match i.len() {
130 4 => {
131 let ipv4: [u8; 4] = [i[0], i[1], i[2], i[3]];
132 IpAddr::from(ipv4).to_string()
133 }
134 16 => {
135 let ipv6: [u8; 16] = [
136 i[0], i[1], i[2], i[3], i[4], i[5], i[6], i[7], i[8], i[9],
137 i[10], i[11], i[12], i[13], i[14], i[15],
138 ];
139 IpAddr::from(ipv6).to_string()
140 }
141 _ => String::new(),
142 },
143 None => String::new(),
144 },
145 })
146 .collect(),
147 None => HashSet::new(),
148 }
149 }
150 }
151
gen_certificate( domain: &str, key_pair: &KeyPair, digest: &MessageDigest, acme_ext: &str, ) -> Result<X509, Error>152 fn gen_certificate(
153 domain: &str,
154 key_pair: &KeyPair,
155 digest: &MessageDigest,
156 acme_ext: &str,
157 ) -> Result<X509, Error> {
158 let mut x509_name = X509NameBuilder::new()?;
159 x509_name.append_entry_by_text("O", super::APP_ORG)?;
160 let ca_name = format!("{} TLS-ALPN-01 Authority", super::APP_NAME);
161 x509_name.append_entry_by_text("CN", &ca_name)?;
162 let x509_name = x509_name.build();
163
164 let mut builder = X509Builder::new()?;
165 builder.set_version(super::X509_VERSION)?;
166 let serial_number = {
167 let mut serial = BigNum::new()?;
168 serial.rand(super::CRT_SERIAL_NB_BITS - 1, MsbOption::MAYBE_ZERO, false)?;
169 serial.to_asn1_integer()?
170 };
171 builder.set_serial_number(&serial_number)?;
172 builder.set_subject_name(&x509_name)?;
173 builder.set_issuer_name(&x509_name)?;
174 builder.set_pubkey(&key_pair.inner_key)?;
175 let not_before = Asn1Time::days_from_now(0)?;
176 builder.set_not_before(¬_before)?;
177 let not_after = Asn1Time::days_from_now(super::CRT_NB_DAYS_VALIDITY)?;
178 builder.set_not_after(¬_after)?;
179
180 builder.append_extension(BasicConstraints::new().build()?)?;
181 let ctx = builder.x509v3_context(None, None);
182 let san_ext = SubjectAlternativeName::new().dns(domain).build(&ctx)?;
183 builder.append_extension(san_ext)?;
184
185 if !acme_ext.is_empty() {
186 let ctx = builder.x509v3_context(None, None);
187 let mut v: Vec<&str> = acme_ext.split('=').collect();
188 let value = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?;
189 let acme_ext_name = v.pop().ok_or_else(|| Error::from(super::INVALID_EXT_MSG))?;
190 if !v.is_empty() {
191 return Err(Error::from(super::INVALID_EXT_MSG));
192 }
193 let acme_ext = X509Extension::new(None, Some(&ctx), &acme_ext_name, &value)
194 .map_err(|_| Error::from(super::INVALID_EXT_MSG))?;
195 builder
196 .append_extension(acme_ext)
197 .map_err(|_| Error::from(super::INVALID_EXT_MSG))?;
198 }
199
200 builder.sign(&key_pair.inner_key, *digest)?;
201 let cert = builder.build();
202 Ok(cert)
203 }
204