1use crate::error::{Error, Result};
8use moka::future::Cache;
9use rand::Rng;
10use rcgen::{
11 BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, Issuer, KeyPair,
12 KeyUsagePurpose, SanType,
13};
14use std::net::IpAddr;
15use std::path::{Path, PathBuf};
16use std::sync::Arc;
17use time::{Duration, OffsetDateTime};
18use tokio::fs;
19use tokio::io::AsyncWriteExt;
20use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
21
22const TTL_SECS: i64 = 365 * 24 * 60 * 60;
24const CACHE_TTL: u64 = (TTL_SECS / 2) as u64;
26const NOT_BEFORE_OFFSET: i64 = 60;
28
29pub struct CertificateAuthority {
31 issuer: Issuer<'static, KeyPair>,
33 ca_cert_der: CertificateDer<'static>,
35 #[allow(dead_code)]
37 ca_key_der: PrivateKeyDer<'static>,
38 storage_path: PathBuf,
40}
41
42impl CertificateAuthority {
43 pub async fn new(storage_path: impl AsRef<Path>) -> Result<Self> {
48 let storage_path = storage_path.as_ref().to_path_buf();
49
50 if !storage_path.exists() {
52 fs::create_dir_all(&storage_path).await?;
53 }
54
55 let ca_cert_path = storage_path.join("ca_cert.pem");
56 let ca_key_path = storage_path.join("ca_key.pem");
57
58 let (issuer, ca_cert_der, ca_key_der) = if ca_cert_path.exists() && ca_key_path.exists() {
60 Self::load_ca(&ca_cert_path, &ca_key_path).await?
61 } else {
62 Self::generate_ca(&ca_cert_path, &ca_key_path).await?
63 };
64
65 Ok(Self {
66 issuer,
67 ca_cert_der,
68 ca_key_der,
69 storage_path,
70 })
71 }
72
73 async fn load_ca(
75 cert_path: &Path,
76 key_path: &Path,
77 ) -> Result<(
78 Issuer<'static, KeyPair>,
79 CertificateDer<'static>,
80 PrivateKeyDer<'static>,
81 )> {
82 let cert_pem = fs::read_to_string(cert_path).await?;
83 let key_pem = fs::read_to_string(key_path).await?;
84
85 let key_pair = KeyPair::from_pem(&key_pem)
86 .map_err(|e| Error::certificate_error(format!("Failed to parse CA key: {}", e)))?;
87
88 let issuer = Issuer::from_ca_cert_pem(&cert_pem, key_pair).map_err(|e| {
89 Error::certificate_error(format!("Failed to create issuer from CA cert: {}", e))
90 })?;
91
92 let cert_der = rustls_pemfile::certs(&mut cert_pem.as_bytes())
94 .next()
95 .ok_or_else(|| Error::certificate_error("No certificate found in PEM"))?
96 .map_err(|e| Error::certificate_error(format!("Failed to parse PEM: {}", e)))?;
97
98 let key_der = PrivateKeyDer::try_from(issuer.key().serialize_der())
99 .map_err(|_| Error::certificate_error("Failed to serialize CA key"))?;
100
101 Ok((issuer, cert_der, key_der))
102 }
103
104 async fn generate_ca(
106 cert_path: &Path,
107 key_path: &Path,
108 ) -> Result<(
109 Issuer<'static, KeyPair>,
110 CertificateDer<'static>,
111 PrivateKeyDer<'static>,
112 )> {
113 let mut params = CertificateParams::default();
114
115 let mut dn = DistinguishedName::new();
117 dn.push(DnType::CommonName, "Slinger MITM Proxy CA");
118 dn.push(DnType::OrganizationName, "Emo-Crab");
119 dn.push(DnType::CountryName, "CN");
120 dn.push(DnType::LocalityName, "Internet");
121 dn.push(DnType::StateOrProvinceName, "World");
122 params.distinguished_name = dn;
123 params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
125 params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
126
127 let now = OffsetDateTime::now_utc();
129 params.not_before = now;
130 params.not_after = now + Duration::days(3650);
131
132 let key_pair = KeyPair::generate()
133 .map_err(|e| Error::certificate_error(format!("Failed to generate key pair: {}", e)))?;
134
135 let cert = params
136 .self_signed(&key_pair)
137 .map_err(|e| Error::certificate_error(format!("Failed to generate CA: {}", e)))?;
138
139 let cert_pem = cert.pem();
141 let key_pem = key_pair.serialize_pem();
142
143 let mut cert_file = fs::File::create(cert_path).await?;
144 cert_file.write_all(cert_pem.as_bytes()).await?;
145
146 let mut key_file = fs::File::create(key_path).await?;
147 key_file.write_all(key_pem.as_bytes()).await?;
148
149 let cert_der = CertificateDer::from(cert.der().to_vec());
150 let key_der = PrivateKeyDer::try_from(key_pair.serialize_der())
151 .map_err(|_| Error::certificate_error("Failed to serialize CA key DER"))?;
152
153 let issuer = Issuer::from_ca_cert_pem(&cert_pem, key_pair)
155 .map_err(|e| Error::certificate_error(format!("Failed to create issuer: {}", e)))?;
156
157 Ok((issuer, cert_der, key_der))
158 }
159
160 pub fn generate_server_cert(
162 &self,
163 domain: &str,
164 ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
165 let mut params = CertificateParams::default();
166
167 params.serial_number = Some(rand::rng().random::<u64>().into());
169
170 let mut dn = DistinguishedName::new();
172 dn.push(DnType::CommonName, domain);
173 params.distinguished_name = dn;
174
175 params.subject_alt_names = if let Ok(ip) = domain.parse::<IpAddr>() {
180 let mut sans = Vec::new();
181 sans.push(SanType::IpAddress(ip));
182 if let Ok(dns_name) = domain.try_into() {
184 sans.push(SanType::DnsName(dns_name));
185 }
186 sans
187 } else {
188 vec![SanType::DnsName(domain.try_into().map_err(|_| {
189 Error::certificate_error(format!("Invalid domain name: {}", domain))
190 })?)]
191 };
192
193 let now = OffsetDateTime::now_utc();
195 params.not_before = now - Duration::seconds(NOT_BEFORE_OFFSET);
196 params.not_after = now + Duration::seconds(TTL_SECS);
197
198 let key_pair = KeyPair::generate()
199 .map_err(|e| Error::certificate_error(format!("Failed to generate key pair: {}", e)))?;
200
201 let cert = params
202 .signed_by(&key_pair, &self.issuer)
203 .map_err(|e| Error::certificate_error(format!("Failed to sign server cert: {}", e)))?;
204
205 let cert_der = CertificateDer::from(cert.der().to_vec());
206
207 let key_der = PrivateKeyDer::try_from(key_pair.serialize_der())
208 .map_err(|_| Error::certificate_error("Failed to serialize server key"))?;
209
210 Ok((vec![cert_der, self.ca_cert_der.clone()], key_der))
212 }
213
214 pub fn ca_cert_pem(&self) -> Result<String> {
216 let ca_cert_path = self.storage_path.join("ca_cert.pem");
218 std::fs::read_to_string(&ca_cert_path)
219 .map_err(|e| Error::certificate_error(format!("Failed to read CA cert: {}", e)))
220 }
221
222 pub fn ca_cert_path(&self) -> PathBuf {
224 self.storage_path.join("ca_cert.pem")
225 }
226}
227
228pub struct CertificateManager {
230 ca: CertificateAuthority,
231 cert_cache: Cache<String, Arc<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)>>,
233}
234
235impl CertificateManager {
236 pub async fn new(storage_path: impl AsRef<Path>) -> Result<Self> {
238 let ca = CertificateAuthority::new(storage_path).await?;
239
240 let cert_cache = Cache::builder()
242 .max_capacity(1000)
243 .time_to_live(std::time::Duration::from_secs(CACHE_TTL))
244 .build();
245
246 Ok(Self { ca, cert_cache })
247 }
248
249 pub async fn get_server_cert(
251 &self,
252 domain: &str,
253 ) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
254 if domain.parse::<std::net::IpAddr>().is_ok() {
259 let (cert_chain, key) = self.ca.generate_server_cert(domain)?;
260 let cached_cert = (cert_chain.clone(), key.clone_key());
262 self
263 .cert_cache
264 .insert(domain.to_string(), Arc::new(cached_cert))
265 .await;
266 return Ok((cert_chain, key));
267 }
268
269 if let Some(cached) = self.cert_cache.get(domain).await {
271 let (cert_chain, key) = cached.as_ref();
273 return Ok((cert_chain.clone(), key.clone_key()));
274 }
275
276 let (cert_chain, key) = self.ca.generate_server_cert(domain)?;
278
279 let cached_cert = (cert_chain.clone(), key.clone_key());
281
282 self
284 .cert_cache
285 .insert(domain.to_string(), Arc::new(cached_cert))
286 .await;
287
288 Ok((cert_chain, key))
289 }
290
291 pub fn ca_cert_pem(&self) -> Result<String> {
293 self.ca.ca_cert_pem()
294 }
295
296 pub fn ca_cert_path(&self) -> PathBuf {
298 self.ca.ca_cert_path()
299 }
300}