1use crate::error::{Error, Result};
8use moka::future::Cache;
9use rand::RngExt;
10use rcgen::{
11 BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, Issuer, KeyPair,
12 KeyUsagePurpose, SanType,
13};
14use rustls_pki_types::pem::{PemObject, SectionKind};
15use std::net::IpAddr;
16use std::path::{Path, PathBuf};
17use std::sync::Arc;
18use time::{Duration, OffsetDateTime};
19use tokio::fs;
20use tokio::io::AsyncWriteExt;
21use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
22
23const TTL_SECS: i64 = 365 * 24 * 60 * 60;
25const CACHE_TTL: u64 = (TTL_SECS / 2) as u64;
27const NOT_BEFORE_OFFSET: i64 = 60;
29
30pub struct CertificateAuthority {
32 issuer: Issuer<'static, KeyPair>,
34 ca_cert_der: CertificateDer<'static>,
36 #[allow(dead_code)]
38 ca_key_der: PrivateKeyDer<'static>,
39 storage_path: PathBuf,
41}
42
43impl CertificateAuthority {
44 pub async fn new(storage_path: impl AsRef<Path>) -> Result<Self> {
49 let storage_path = storage_path.as_ref().to_path_buf();
50
51 if !storage_path.exists() {
53 fs::create_dir_all(&storage_path).await?;
54 }
55
56 let ca_cert_path = storage_path.join("ca_cert.pem");
57 let ca_key_path = storage_path.join("ca_key.pem");
58
59 let (issuer, ca_cert_der, ca_key_der) = if ca_cert_path.exists() && ca_key_path.exists() {
61 Self::load_ca(&ca_cert_path, &ca_key_path).await?
62 } else {
63 Self::generate_ca(&ca_cert_path, &ca_key_path).await?
64 };
65
66 Ok(Self {
67 issuer,
68 ca_cert_der,
69 ca_key_der,
70 storage_path,
71 })
72 }
73
74 async fn load_ca(
76 cert_path: &Path,
77 key_path: &Path,
78 ) -> Result<(
79 Issuer<'static, KeyPair>,
80 CertificateDer<'static>,
81 PrivateKeyDer<'static>,
82 )> {
83 let cert_pem = fs::read_to_string(cert_path).await?;
84 let key_pem = fs::read_to_string(key_path).await?;
85
86 let key_pair = KeyPair::from_pem(&key_pem)
87 .map_err(|e| Error::certificate_error(format!("Failed to parse CA key: {}", e)))?;
88
89 let issuer = Issuer::from_ca_cert_pem(&cert_pem, key_pair).map_err(|e| {
90 Error::certificate_error(format!("Failed to create issuer from CA cert: {}", e))
91 })?;
92
93 let mut found: Option<Vec<u8>> = None;
96 for item in <(SectionKind, Vec<u8>) as PemObject>::pem_slice_iter(cert_pem.as_bytes()) {
97 match item {
98 Ok((kind, contents)) => {
99 if kind == SectionKind::Certificate {
100 found = Some(contents);
101 break;
102 }
103 }
104 Err(e) => {
105 return Err(Error::certificate_error(format!(
106 "Failed to parse PEM: {}",
107 e
108 )));
109 }
110 }
111 }
112
113 let cert_der_vec =
114 found.ok_or_else(|| Error::certificate_error("No certificate found in PEM"))?;
115 let cert_der = CertificateDer::from(cert_der_vec);
116
117 let key_der = PrivateKeyDer::try_from(issuer.key().serialize_der())
118 .map_err(|_| Error::certificate_error("Failed to serialize CA key"))?;
119
120 Ok((issuer, cert_der, key_der))
121 }
122
123 async fn generate_ca(
125 cert_path: &Path,
126 key_path: &Path,
127 ) -> Result<(
128 Issuer<'static, KeyPair>,
129 CertificateDer<'static>,
130 PrivateKeyDer<'static>,
131 )> {
132 let mut params = CertificateParams::default();
133
134 let mut dn = DistinguishedName::new();
136 dn.push(DnType::CommonName, "Slinger MITM Proxy CA");
137 dn.push(DnType::OrganizationName, "Emo-Crab");
138 dn.push(DnType::CountryName, "CN");
139 dn.push(DnType::LocalityName, "Internet");
140 dn.push(DnType::StateOrProvinceName, "World");
141 params.distinguished_name = dn;
142 params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
144 params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
145
146 let now = OffsetDateTime::now_utc();
148 params.not_before = now;
149 params.not_after = now + Duration::days(3650);
150
151 let key_pair = KeyPair::generate()
152 .map_err(|e| Error::certificate_error(format!("Failed to generate key pair: {}", e)))?;
153
154 let cert = params
155 .self_signed(&key_pair)
156 .map_err(|e| Error::certificate_error(format!("Failed to generate CA: {}", e)))?;
157
158 let cert_pem = cert.pem();
160 let key_pem = key_pair.serialize_pem();
161
162 let mut cert_file = fs::File::create(cert_path).await?;
163 cert_file.write_all(cert_pem.as_bytes()).await?;
164
165 let mut key_file = fs::File::create(key_path).await?;
166 key_file.write_all(key_pem.as_bytes()).await?;
167
168 let cert_der = CertificateDer::from(cert.der().to_vec());
169 let key_der = PrivateKeyDer::try_from(key_pair.serialize_der())
170 .map_err(|_| Error::certificate_error("Failed to serialize CA key DER"))?;
171
172 let issuer = Issuer::from_ca_cert_pem(&cert_pem, key_pair)
174 .map_err(|e| Error::certificate_error(format!("Failed to create issuer: {}", e)))?;
175
176 Ok((issuer, cert_der, key_der))
177 }
178
179 pub fn generate_server_cert(
181 &self,
182 domain: &str,
183 ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
184 let mut params = CertificateParams::default();
185
186 params.serial_number = Some(rand::rng().random::<u64>().into());
188
189 let mut dn = DistinguishedName::new();
191 dn.push(DnType::CommonName, domain);
192 params.distinguished_name = dn;
193
194 params.subject_alt_names = if let Ok(ip) = domain.parse::<IpAddr>() {
199 let mut sans = Vec::new();
200 sans.push(SanType::IpAddress(ip));
201 if let Ok(dns_name) = domain.try_into() {
203 sans.push(SanType::DnsName(dns_name));
204 }
205 sans
206 } else {
207 vec![SanType::DnsName(domain.try_into().map_err(|_| {
208 Error::certificate_error(format!("Invalid domain name: {}", domain))
209 })?)]
210 };
211
212 let now = OffsetDateTime::now_utc();
214 params.not_before = now - Duration::seconds(NOT_BEFORE_OFFSET);
215 params.not_after = now + Duration::seconds(TTL_SECS);
216
217 let key_pair = KeyPair::generate()
218 .map_err(|e| Error::certificate_error(format!("Failed to generate key pair: {}", e)))?;
219
220 let cert = params
221 .signed_by(&key_pair, &self.issuer)
222 .map_err(|e| Error::certificate_error(format!("Failed to sign server cert: {}", e)))?;
223
224 let cert_der = CertificateDer::from(cert.der().to_vec());
225
226 let key_der = PrivateKeyDer::try_from(key_pair.serialize_der())
227 .map_err(|_| Error::certificate_error("Failed to serialize server key"))?;
228
229 Ok((vec![cert_der, self.ca_cert_der.clone()], key_der))
231 }
232
233 pub async fn ca_cert_pem(&self) -> Result<String> {
235 let ca_cert_path = self.storage_path.join("ca_cert.pem");
237 tokio::fs::read_to_string(&ca_cert_path)
238 .await
239 .map_err(|e| Error::certificate_error(format!("Failed to read CA cert: {}", e)))
240 }
241
242 pub fn ca_cert_path(&self) -> PathBuf {
244 self.storage_path.join("ca_cert.pem")
245 }
246}
247
248pub struct CertificateManager {
250 ca: CertificateAuthority,
251 cert_cache: Cache<String, Arc<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>>,
253}
254
255impl CertificateManager {
256 pub async fn new(storage_path: impl AsRef<Path>) -> Result<Self> {
258 let ca = CertificateAuthority::new(storage_path).await?;
259
260 let cert_cache = Cache::builder()
262 .max_capacity(1000)
263 .time_to_live(std::time::Duration::from_secs(CACHE_TTL))
264 .build();
265
266 Ok(Self { ca, cert_cache })
267 }
268
269 pub async fn get_server_cert(
271 &self,
272 domain: &str,
273 ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
274 if domain.parse::<std::net::IpAddr>().is_ok() {
279 let (cert_chain, key) = self.ca.generate_server_cert(domain)?;
280 let cached_cert = (cert_chain.clone(), key.clone_key());
282 self
283 .cert_cache
284 .insert(domain.to_string(), Arc::new(cached_cert))
285 .await;
286 return Ok((cert_chain, key));
287 }
288
289 if let Some(cached) = self.cert_cache.get(domain).await {
291 let (cert_chain, key) = cached.as_ref();
293 return Ok((cert_chain.clone(), key.clone_key()));
294 }
295
296 let (cert_chain, key) = self.ca.generate_server_cert(domain)?;
298
299 let cached_cert = (cert_chain.clone(), key.clone_key());
301
302 self
304 .cert_cache
305 .insert(domain.to_string(), Arc::new(cached_cert))
306 .await;
307
308 Ok((cert_chain, key))
309 }
310
311 pub async fn ca_cert_pem(&self) -> Result<String> {
313 self.ca.ca_cert_pem().await
314 }
315
316 pub fn ca_cert_path(&self) -> PathBuf {
318 self.ca.ca_cert_path()
319 }
320}