1use std::fs;
31use std::io::BufReader;
32use std::path::Path;
33use std::sync::Arc;
34use std::time::{Duration, SystemTime};
35
36use parking_lot::RwLock;
37use rcgen::{CertificateParams, DistinguishedName, DnType, Issuer, KeyPair, SanType};
38use rustls::RootCertStore;
39use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
40use tokio::sync::watch;
41use tracing::{debug, error, info, warn};
42use x509_parser::prelude::*;
43
44use crate::error::{NetError, NetResult};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum CertificateFormat {
49 Pem,
51 Der,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum PrivateKeyType {
58 Rsa,
60 Ecdsa,
62 Ed25519,
64 Pkcs8,
66}
67
68#[derive(Debug, Clone)]
70pub struct CertificateInfo {
71 pub common_name: Option<String>,
73 pub subject_alt_names: Vec<String>,
75 pub issuer: Option<String>,
77 pub serial_number: String,
79 pub not_before: SystemTime,
81 pub not_after: SystemTime,
83 pub is_ca: bool,
85 pub key_usage: Vec<String>,
87 pub extended_key_usage: Vec<String>,
89 pub fingerprint_sha256: String,
91}
92
93impl CertificateInfo {
94 pub fn is_valid(&self) -> bool {
96 let now = SystemTime::now();
97 now >= self.not_before && now <= self.not_after
98 }
99
100 pub fn time_to_expiry(&self) -> Option<Duration> {
102 SystemTime::now()
103 .duration_since(self.not_after)
104 .ok()
105 .map(|_| Duration::ZERO)
106 .or_else(|| self.not_after.duration_since(SystemTime::now()).ok())
107 }
108
109 pub fn expires_within(&self, duration: Duration) -> bool {
111 self.time_to_expiry()
112 .is_some_and(|remaining| remaining <= duration)
113 }
114}
115
116#[derive(Debug, Clone)]
118pub struct CertificateLoader {
119 validate_on_load: bool,
121}
122
123impl Default for CertificateLoader {
124 fn default() -> Self {
125 Self::new()
126 }
127}
128
129impl CertificateLoader {
130 pub fn new() -> Self {
132 Self {
133 validate_on_load: true,
134 }
135 }
136
137 pub fn without_validation() -> Self {
139 Self {
140 validate_on_load: false,
141 }
142 }
143
144 pub fn load_pem_file<P: AsRef<Path>>(
154 &self,
155 path: P,
156 ) -> NetResult<Vec<CertificateDer<'static>>> {
157 let path = path.as_ref();
158 debug!(path = %path.display(), "Loading PEM certificates from file");
159
160 let file = fs::File::open(path)
161 .map_err(|e| NetError::InvalidCertificate(format!("Failed to open PEM file: {e}")))?;
162 let mut reader = BufReader::new(file);
163
164 let certs: Vec<_> = rustls_pemfile::certs(&mut reader)
165 .filter_map(|result| result.ok())
166 .collect();
167
168 if certs.is_empty() {
169 return Err(NetError::InvalidCertificate(
170 "No certificates found in PEM file".to_string(),
171 ));
172 }
173
174 if self.validate_on_load {
175 for cert in &certs {
176 self.validate_certificate_der(cert)?;
177 }
178 }
179
180 info!(count = certs.len(), "Loaded certificates from PEM file");
181 Ok(certs)
182 }
183
184 pub fn load_der_file<P: AsRef<Path>>(&self, path: P) -> NetResult<CertificateDer<'static>> {
194 let path = path.as_ref();
195 debug!(path = %path.display(), "Loading DER certificate from file");
196
197 let der_data = fs::read(path)
198 .map_err(|e| NetError::InvalidCertificate(format!("Failed to read DER file: {e}")))?;
199
200 let cert = CertificateDer::from(der_data);
201
202 if self.validate_on_load {
203 self.validate_certificate_der(&cert)?;
204 }
205
206 info!("Loaded DER certificate from file");
207 Ok(cert)
208 }
209
210 pub fn load_pem_bytes(&self, pem_data: &[u8]) -> NetResult<Vec<CertificateDer<'static>>> {
212 let mut reader = BufReader::new(pem_data);
213
214 let certs: Vec<_> = rustls_pemfile::certs(&mut reader)
215 .filter_map(|result| result.ok())
216 .collect();
217
218 if certs.is_empty() {
219 return Err(NetError::InvalidCertificate(
220 "No certificates found in PEM data".to_string(),
221 ));
222 }
223
224 if self.validate_on_load {
225 for cert in &certs {
226 self.validate_certificate_der(cert)?;
227 }
228 }
229
230 Ok(certs)
231 }
232
233 pub fn load_der_bytes(&self, der_data: &[u8]) -> NetResult<CertificateDer<'static>> {
235 let cert = CertificateDer::from(der_data.to_vec());
236
237 if self.validate_on_load {
238 self.validate_certificate_der(&cert)?;
239 }
240
241 Ok(cert)
242 }
243
244 fn validate_certificate_der(&self, cert: &CertificateDer<'_>) -> NetResult<()> {
246 let (_, parsed) = X509Certificate::from_der(cert.as_ref()).map_err(|e| {
247 NetError::InvalidCertificate(format!("Failed to parse certificate: {e}"))
248 })?;
249
250 let now = ASN1Time::now();
252 if parsed.validity().not_before > now {
253 return Err(NetError::InvalidCertificate(
254 "Certificate is not yet valid".to_string(),
255 ));
256 }
257 if parsed.validity().not_after < now {
258 return Err(NetError::InvalidCertificate(
259 "Certificate has expired".to_string(),
260 ));
261 }
262
263 Ok(())
264 }
265
266 pub fn get_certificate_info(&self, cert: &CertificateDer<'_>) -> NetResult<CertificateInfo> {
268 let (_, parsed) = X509Certificate::from_der(cert.as_ref()).map_err(|e| {
269 NetError::InvalidCertificate(format!("Failed to parse certificate: {e}"))
270 })?;
271
272 let common_name = parsed
273 .subject()
274 .iter_common_name()
275 .next()
276 .and_then(|cn| cn.as_str().ok())
277 .map(String::from);
278
279 let issuer = parsed
280 .issuer()
281 .iter_common_name()
282 .next()
283 .and_then(|cn| cn.as_str().ok())
284 .map(String::from);
285
286 let mut subject_alt_names = Vec::new();
287 if let Ok(Some(san)) = parsed.subject_alternative_name() {
288 for name in san.value.general_names.iter() {
289 match name {
290 GeneralName::DNSName(dns) => subject_alt_names.push(dns.to_string()),
291 GeneralName::IPAddress(ip) => {
292 if ip.len() == 4 {
293 subject_alt_names
294 .push(format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]));
295 } else if ip.len() == 16 {
296 let mut parts = Vec::with_capacity(8);
298 for i in 0..8 {
299 let val = u16::from_be_bytes([ip[i * 2], ip[i * 2 + 1]]);
300 parts.push(format!("{val:x}"));
301 }
302 subject_alt_names.push(parts.join(":"));
303 }
304 }
305 GeneralName::RFC822Name(email) => subject_alt_names.push(email.to_string()),
306 GeneralName::URI(uri) => subject_alt_names.push(uri.to_string()),
307 _ => {}
308 }
309 }
310 }
311
312 let serial_number = format!("{:x}", parsed.serial);
313
314 let not_before = asn1_time_to_system_time(&parsed.validity().not_before);
315 let not_after = asn1_time_to_system_time(&parsed.validity().not_after);
316
317 let is_ca = parsed.is_ca();
318
319 let mut key_usage = Vec::new();
320 if let Ok(Some(ku)) = parsed.key_usage() {
321 let flags = ku.value;
322 if flags.digital_signature() {
323 key_usage.push("digitalSignature".to_string());
324 }
325 if flags.non_repudiation() {
326 key_usage.push("nonRepudiation".to_string());
327 }
328 if flags.key_encipherment() {
329 key_usage.push("keyEncipherment".to_string());
330 }
331 if flags.data_encipherment() {
332 key_usage.push("dataEncipherment".to_string());
333 }
334 if flags.key_agreement() {
335 key_usage.push("keyAgreement".to_string());
336 }
337 if flags.key_cert_sign() {
338 key_usage.push("keyCertSign".to_string());
339 }
340 if flags.crl_sign() {
341 key_usage.push("cRLSign".to_string());
342 }
343 }
344
345 let mut extended_key_usage = Vec::new();
346 if let Ok(Some(eku)) = parsed.extended_key_usage() {
347 for oid in eku.value.other.iter() {
348 extended_key_usage.push(oid.to_string());
349 }
350 if eku.value.any {
351 extended_key_usage.push("anyExtendedKeyUsage".to_string());
352 }
353 if eku.value.server_auth {
354 extended_key_usage.push("serverAuth".to_string());
355 }
356 if eku.value.client_auth {
357 extended_key_usage.push("clientAuth".to_string());
358 }
359 if eku.value.code_signing {
360 extended_key_usage.push("codeSigning".to_string());
361 }
362 if eku.value.email_protection {
363 extended_key_usage.push("emailProtection".to_string());
364 }
365 if eku.value.time_stamping {
366 extended_key_usage.push("timeStamping".to_string());
367 }
368 if eku.value.ocsp_signing {
369 extended_key_usage.push("ocspSigning".to_string());
370 }
371 }
372
373 use std::fmt::Write;
375 let fingerprint_sha256 = cert
376 .as_ref()
377 .iter()
378 .take(32) .fold(String::new(), |mut s, b| {
380 let _ = write!(&mut s, "{b:02x}");
381 s
382 });
383
384 Ok(CertificateInfo {
385 common_name,
386 subject_alt_names,
387 issuer,
388 serial_number,
389 not_before,
390 not_after,
391 is_ca,
392 key_usage,
393 extended_key_usage,
394 fingerprint_sha256,
395 })
396 }
397}
398
399fn asn1_time_to_system_time(time: &ASN1Time) -> SystemTime {
401 let timestamp = time.timestamp();
403 if timestamp >= 0 {
404 SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
405 } else {
406 SystemTime::UNIX_EPOCH
408 }
409}
410
411#[derive(Debug, Clone)]
413pub struct PrivateKeyLoader;
414
415impl Default for PrivateKeyLoader {
416 fn default() -> Self {
417 Self::new()
418 }
419}
420
421impl PrivateKeyLoader {
422 pub fn new() -> Self {
424 Self
425 }
426
427 pub fn load_pem_file<P: AsRef<Path>>(&self, path: P) -> NetResult<PrivateKeyDer<'static>> {
431 let path = path.as_ref();
432 debug!(path = %path.display(), "Loading private key from PEM file");
433
434 let file = fs::File::open(path)
435 .map_err(|e| NetError::InvalidCertificate(format!("Failed to open key file: {e}")))?;
436 let mut reader = BufReader::new(file);
437
438 self.load_from_reader(&mut reader)
439 }
440
441 pub fn load_pem_bytes(&self, pem_data: &[u8]) -> NetResult<PrivateKeyDer<'static>> {
443 let mut reader = BufReader::new(pem_data);
444 self.load_from_reader(&mut reader)
445 }
446
447 pub fn load_der_file<P: AsRef<Path>>(
449 &self,
450 path: P,
451 key_type: PrivateKeyType,
452 ) -> NetResult<PrivateKeyDer<'static>> {
453 let path = path.as_ref();
454 debug!(path = %path.display(), "Loading private key from DER file");
455
456 let der_data = fs::read(path)
457 .map_err(|e| NetError::InvalidCertificate(format!("Failed to read key file: {e}")))?;
458
459 self.load_der_bytes(&der_data, key_type)
460 }
461
462 pub fn load_der_bytes(
464 &self,
465 der_data: &[u8],
466 key_type: PrivateKeyType,
467 ) -> NetResult<PrivateKeyDer<'static>> {
468 let key = match key_type {
469 PrivateKeyType::Rsa => PrivateKeyDer::Pkcs1(der_data.to_vec().into()),
470 PrivateKeyType::Ecdsa | PrivateKeyType::Ed25519 => {
471 PrivateKeyDer::Sec1(der_data.to_vec().into())
472 }
473 PrivateKeyType::Pkcs8 => {
474 PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(der_data.to_vec()))
475 }
476 };
477
478 Ok(key)
479 }
480
481 pub fn load_encrypted_pem_file<P: AsRef<Path>>(
494 &self,
495 path: P,
496 password: &str,
497 ) -> NetResult<PrivateKeyDer<'static>> {
498 let path = path.as_ref();
499 debug!(path = %path.display(), "Loading potentially encrypted private key");
500
501 let pem_data = fs::read(path)
502 .map_err(|e| NetError::InvalidCertificate(format!("Failed to read key file: {e}")))?;
503
504 let pem_str = std::str::from_utf8(&pem_data).map_err(|e| {
505 NetError::InvalidCertificate(format!("Key file is not valid UTF-8: {e}"))
506 })?;
507
508 let enc_format = detect_encrypted_pem(pem_str);
509
510 match enc_format {
511 EncryptedPemFormat::NotEncrypted => {
512 debug!("Key is not encrypted, loading directly");
513 self.load_pem_bytes(&pem_data)
514 }
515 EncryptedPemFormat::Pkcs8Encrypted => {
516 let effective_password = resolve_password(password)?;
517 decrypt_pkcs8_encrypted_pem(pem_str, &effective_password)
518 }
519 EncryptedPemFormat::LegacyEncrypted => {
520 let effective_password = resolve_password(password)?;
521 decrypt_legacy_encrypted_pem(pem_str, &effective_password)
522 }
523 }
524 }
525
526 pub fn load_encrypted_pem_file_env<P: AsRef<Path>>(
531 &self,
532 path: P,
533 ) -> NetResult<PrivateKeyDer<'static>> {
534 self.load_encrypted_pem_file(path, "")
535 }
536
537 fn load_from_reader<R: std::io::BufRead>(
539 &self,
540 reader: &mut R,
541 ) -> NetResult<PrivateKeyDer<'static>> {
542 let mut original_data: Vec<u8> = Vec::new();
544 reader
545 .read_to_end(&mut original_data)
546 .map_err(|e| NetError::InvalidCertificate(format!("Failed to read key data: {e}")))?;
547
548 let mut cursor = std::io::Cursor::new(&original_data);
549
550 if let Some(Ok(key)) = rustls_pemfile::pkcs8_private_keys(&mut cursor).next() {
552 info!("Loaded PKCS#8 private key");
553 return Ok(PrivateKeyDer::Pkcs8(key));
554 }
555
556 let mut cursor = std::io::Cursor::new(&original_data);
558 if let Some(Ok(key)) = rustls_pemfile::rsa_private_keys(&mut cursor).next() {
559 info!("Loaded RSA private key");
560 return Ok(PrivateKeyDer::Pkcs1(key));
561 }
562
563 let mut cursor = std::io::Cursor::new(&original_data);
565 if let Some(Ok(key)) = rustls_pemfile::ec_private_keys(&mut cursor).next() {
566 info!("Loaded EC private key");
567 return Ok(PrivateKeyDer::Sec1(key));
568 }
569
570 Err(NetError::InvalidCertificate(
571 "No valid private key found in PEM data (tried PKCS#8, RSA, EC formats)".to_string(),
572 ))
573 }
574}
575
576pub use crate::tls_crypto::{
578 EncryptedPemFormat, detect_encrypted_pem, parse_dek_info, pbkdf2_hmac_sha1, pbkdf2_hmac_sha256,
579};
580use crate::tls_crypto::{
581 decrypt_legacy_encrypted_pem, decrypt_pkcs8_encrypted_pem, resolve_password,
582};
583
584#[derive(Debug, Clone)]
586pub struct SelfSignedGenerator {
587 subject_alt_names: Vec<String>,
589 common_name: String,
591 organization: Option<String>,
593 validity_days: u32,
595 is_ca: bool,
597}
598
599impl SelfSignedGenerator {
600 pub fn new(common_name: impl Into<String>) -> Self {
606 Self {
607 common_name: common_name.into(),
608 subject_alt_names: vec!["localhost".to_string()],
609 organization: None,
610 validity_days: 365,
611 is_ca: false,
612 }
613 }
614
615 pub fn with_san(mut self, san: impl Into<String>) -> Self {
617 self.subject_alt_names.push(san.into());
618 self
619 }
620
621 pub fn with_sans<I, S>(mut self, sans: I) -> Self
623 where
624 I: IntoIterator<Item = S>,
625 S: Into<String>,
626 {
627 self.subject_alt_names
628 .extend(sans.into_iter().map(|s| s.into()));
629 self
630 }
631
632 pub fn with_organization(mut self, org: impl Into<String>) -> Self {
634 self.organization = Some(org.into());
635 self
636 }
637
638 pub fn with_validity_days(mut self, days: u32) -> Self {
640 self.validity_days = days;
641 self
642 }
643
644 pub fn as_ca(mut self) -> Self {
646 self.is_ca = true;
647 self
648 }
649
650 pub fn generate(&self) -> NetResult<(CertificateDer<'static>, PrivateKeyDer<'static>)> {
656 let mut params = CertificateParams::default();
657
658 let mut dn = DistinguishedName::new();
660 dn.push(DnType::CommonName, &self.common_name);
661 if let Some(ref org) = self.organization {
662 dn.push(DnType::OrganizationName, org);
663 }
664 params.distinguished_name = dn;
665
666 params.subject_alt_names = self
668 .subject_alt_names
669 .iter()
670 .map(|name| {
671 if let Ok(ip) = name.parse::<std::net::IpAddr>() {
673 SanType::IpAddress(ip)
674 } else {
675 SanType::DnsName(name.clone().try_into().unwrap_or_else(|_| {
676 "localhost"
677 .to_string()
678 .try_into()
679 .expect("localhost is valid DNS name")
680 }))
681 }
682 })
683 .collect();
684
685 params.not_before = rcgen::date_time_ymd(
687 chrono::Utc::now().year(),
688 chrono::Utc::now().month() as u8,
689 chrono::Utc::now().day() as u8,
690 );
691
692 let future = chrono::Utc::now() + chrono::Duration::days(self.validity_days as i64);
693 params.not_after =
694 rcgen::date_time_ymd(future.year(), future.month() as u8, future.day() as u8);
695
696 if self.is_ca {
698 params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
699 }
700
701 let key_pair = KeyPair::generate().map_err(|e| {
703 NetError::InvalidCertificate(format!("Failed to generate key pair: {e}"))
704 })?;
705
706 let cert = params.self_signed(&key_pair).map_err(|e| {
708 NetError::InvalidCertificate(format!("Failed to generate certificate: {e}"))
709 })?;
710
711 let cert_der = CertificateDer::from(cert.der().to_vec());
712 let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));
713
714 info!(
715 common_name = %self.common_name,
716 is_ca = self.is_ca,
717 validity_days = self.validity_days,
718 "Generated self-signed certificate"
719 );
720
721 Ok((cert_der, key_der))
722 }
723
724 pub fn generate_signed_by_keypair(
729 &self,
730 ca_key_pair: &KeyPair,
731 ca_common_name: &str,
732 ) -> NetResult<(CertificateDer<'static>, PrivateKeyDer<'static>)> {
733 let mut params = CertificateParams::default();
734
735 let mut dn = DistinguishedName::new();
737 dn.push(DnType::CommonName, &self.common_name);
738 if let Some(ref org) = self.organization {
739 dn.push(DnType::OrganizationName, org);
740 }
741 params.distinguished_name = dn;
742
743 params.subject_alt_names = self
745 .subject_alt_names
746 .iter()
747 .map(|name| {
748 if let Ok(ip) = name.parse::<std::net::IpAddr>() {
749 SanType::IpAddress(ip)
750 } else {
751 SanType::DnsName(name.clone().try_into().unwrap_or_else(|_| {
752 "localhost"
753 .to_string()
754 .try_into()
755 .expect("localhost is valid DNS name")
756 }))
757 }
758 })
759 .collect();
760
761 params.not_before = rcgen::date_time_ymd(
763 chrono::Utc::now().year(),
764 chrono::Utc::now().month() as u8,
765 chrono::Utc::now().day() as u8,
766 );
767
768 let future = chrono::Utc::now() + chrono::Duration::days(self.validity_days as i64);
769 params.not_after =
770 rcgen::date_time_ymd(future.year(), future.month() as u8, future.day() as u8);
771
772 let key_pair = KeyPair::generate().map_err(|e| {
774 NetError::InvalidCertificate(format!("Failed to generate key pair: {e}"))
775 })?;
776
777 let mut ca_params = CertificateParams::default();
779 ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
780
781 let mut issuer_dn = DistinguishedName::new();
783 issuer_dn.push(DnType::CommonName, ca_common_name);
784 ca_params.distinguished_name = issuer_dn;
785
786 let issuer = Issuer::from_params(&ca_params, ca_key_pair);
788
789 let signed_cert = params.signed_by(&key_pair, &issuer).map_err(|e| {
791 NetError::InvalidCertificate(format!("Failed to sign certificate: {e}"))
792 })?;
793
794 let cert_der = CertificateDer::from(signed_cert.der().to_vec());
795 let key_der = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key_pair.serialize_der()));
796
797 info!(
798 common_name = %self.common_name,
799 "Generated CA-signed certificate"
800 );
801
802 Ok((cert_der, key_der))
803 }
804}
805
806use chrono::Datelike;
807
808#[derive(Debug)]
810pub struct CertificateStore {
811 roots: Arc<RwLock<RootCertStore>>,
813 cert_chain: Arc<RwLock<Vec<CertificateDer<'static>>>>,
815 cert_info: Arc<RwLock<Vec<CertificateInfo>>>,
817}
818
819impl Default for CertificateStore {
820 fn default() -> Self {
821 Self::new()
822 }
823}
824
825impl Clone for CertificateStore {
826 fn clone(&self) -> Self {
827 Self {
828 roots: Arc::new(RwLock::new((*self.roots.read()).clone())),
829 cert_chain: Arc::new(RwLock::new(self.cert_chain.read().clone())),
830 cert_info: Arc::new(RwLock::new(self.cert_info.read().clone())),
831 }
832 }
833}
834
835impl CertificateStore {
836 pub fn new() -> Self {
838 Self {
839 roots: Arc::new(RwLock::new(RootCertStore::empty())),
840 cert_chain: Arc::new(RwLock::new(Vec::new())),
841 cert_info: Arc::new(RwLock::new(Vec::new())),
842 }
843 }
844
845 pub fn add_system_roots(&mut self) -> NetResult<usize> {
847 let mut roots = self.roots.write();
848 let count_before = roots.len();
849
850 roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
851
852 let added = roots.len() - count_before;
853 info!(count = added, "Added system root certificates");
854 Ok(added)
855 }
856
857 pub fn add_certificate(&mut self, cert: CertificateDer<'static>) -> NetResult<()> {
859 let loader = CertificateLoader::new();
860 let info = loader.get_certificate_info(&cert)?;
861
862 if !info.is_ca {
863 warn!(common_name = ?info.common_name, "Adding non-CA certificate to root store");
864 }
865
866 {
867 let mut roots = self.roots.write();
868 roots.add(cert.clone()).map_err(|e| {
869 NetError::InvalidCertificate(format!("Failed to add certificate: {e}"))
870 })?;
871 }
872
873 {
874 let mut chain = self.cert_chain.write();
875 chain.push(cert);
876 }
877
878 {
879 let mut infos = self.cert_info.write();
880 infos.push(info);
881 }
882
883 Ok(())
884 }
885
886 pub fn add_certificates_from_file<P: AsRef<Path>>(&mut self, path: P) -> NetResult<usize> {
888 let loader = CertificateLoader::new();
889 let certs = loader.load_pem_file(path)?;
890
891 let count = certs.len();
892 for cert in certs {
893 self.add_certificate(cert)?;
894 }
895
896 Ok(count)
897 }
898
899 pub fn get_root_store(&self) -> RootCertStore {
901 self.roots.read().clone()
902 }
903
904 pub fn get_cert_chain(&self) -> Vec<CertificateDer<'static>> {
906 self.cert_chain.read().clone()
907 }
908
909 pub fn len(&self) -> usize {
911 self.roots.read().len()
912 }
913
914 pub fn is_empty(&self) -> bool {
916 self.roots.read().is_empty()
917 }
918
919 pub fn get_certificate_infos(&self) -> Vec<CertificateInfo> {
921 self.cert_info.read().clone()
922 }
923
924 pub fn check_expiring(&self, within: Duration) -> Vec<CertificateInfo> {
926 self.cert_info
927 .read()
928 .iter()
929 .filter(|info| info.expires_within(within))
930 .cloned()
931 .collect()
932 }
933}
934
935#[derive(Debug, Clone)]
937enum PrivateKeyData {
938 Pkcs8(Vec<u8>),
939 Pkcs1(Vec<u8>),
940 Sec1(Vec<u8>),
941}
942
943impl PrivateKeyData {
944 fn from_key(key: &PrivateKeyDer<'_>) -> Self {
946 match key {
947 PrivateKeyDer::Pkcs8(k) => Self::Pkcs8(k.secret_pkcs8_der().to_vec()),
948 PrivateKeyDer::Pkcs1(k) => Self::Pkcs1(k.secret_pkcs1_der().to_vec()),
949 PrivateKeyDer::Sec1(k) => Self::Sec1(k.secret_sec1_der().to_vec()),
950 _ => Self::Pkcs8(Vec::new()), }
952 }
953
954 fn to_key(&self) -> PrivateKeyDer<'static> {
956 match self {
957 Self::Pkcs8(data) => PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(data.clone())),
958 Self::Pkcs1(data) => PrivateKeyDer::Pkcs1(data.clone().into()),
959 Self::Sec1(data) => PrivateKeyDer::Sec1(data.clone().into()),
960 }
961 }
962}
963
964pub struct HotReloadableCertificates {
968 cert_chain: Arc<RwLock<Vec<CertificateDer<'static>>>>,
970 private_key_data: Arc<RwLock<Option<PrivateKeyData>>>,
972 update_tx: watch::Sender<u64>,
974 version: Arc<RwLock<u64>>,
976 cert_path: Arc<RwLock<Option<std::path::PathBuf>>>,
978 key_path: Arc<RwLock<Option<std::path::PathBuf>>>,
980}
981
982impl std::fmt::Debug for HotReloadableCertificates {
983 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
984 f.debug_struct("HotReloadableCertificates")
985 .field("version", &*self.version.read())
986 .field("cert_count", &self.cert_chain.read().len())
987 .field("has_key", &self.private_key_data.read().is_some())
988 .finish()
989 }
990}
991
992impl Default for HotReloadableCertificates {
993 fn default() -> Self {
994 Self::new()
995 }
996}
997
998impl HotReloadableCertificates {
999 pub fn new() -> Self {
1001 let (update_tx, _) = watch::channel(0u64);
1002 Self {
1003 cert_chain: Arc::new(RwLock::new(Vec::new())),
1004 private_key_data: Arc::new(RwLock::new(None)),
1005 update_tx,
1006 version: Arc::new(RwLock::new(0)),
1007 cert_path: Arc::new(RwLock::new(None)),
1008 key_path: Arc::new(RwLock::new(None)),
1009 }
1010 }
1011
1012 pub fn load_from_files<P: AsRef<Path>>(&self, cert_path: P, key_path: P) -> NetResult<()> {
1014 let cert_path = cert_path.as_ref();
1015 let key_path = key_path.as_ref();
1016
1017 let loader = CertificateLoader::new();
1018 let key_loader = PrivateKeyLoader::new();
1019
1020 let certs = loader.load_pem_file(cert_path)?;
1021 let key = key_loader.load_pem_file(key_path)?;
1022
1023 {
1024 let mut chain = self.cert_chain.write();
1025 *chain = certs;
1026 }
1027
1028 {
1029 let mut pk = self.private_key_data.write();
1030 *pk = Some(PrivateKeyData::from_key(&key));
1031 }
1032
1033 {
1034 let mut cp = self.cert_path.write();
1035 *cp = Some(cert_path.to_path_buf());
1036 }
1037
1038 {
1039 let mut kp = self.key_path.write();
1040 *kp = Some(key_path.to_path_buf());
1041 }
1042
1043 self.increment_version();
1044
1045 info!(
1046 cert_path = %cert_path.display(),
1047 key_path = %key_path.display(),
1048 "Loaded certificates from files"
1049 );
1050
1051 Ok(())
1052 }
1053
1054 pub fn reload(&self) -> NetResult<()> {
1056 let cert_path = self.cert_path.read().clone();
1057 let key_path = self.key_path.read().clone();
1058
1059 match (cert_path, key_path) {
1060 (Some(cp), Some(kp)) => {
1061 self.load_from_files(&cp, &kp)?;
1062 info!("Reloaded certificates");
1063 Ok(())
1064 }
1065 _ => Err(NetError::InvalidCertificate(
1066 "No certificate paths configured for reload".to_string(),
1067 )),
1068 }
1069 }
1070
1071 pub fn set_certificates(
1073 &self,
1074 certs: Vec<CertificateDer<'static>>,
1075 key: PrivateKeyDer<'static>,
1076 ) {
1077 {
1078 let mut chain = self.cert_chain.write();
1079 *chain = certs;
1080 }
1081
1082 {
1083 let mut pk = self.private_key_data.write();
1084 *pk = Some(PrivateKeyData::from_key(&key));
1085 }
1086
1087 self.increment_version();
1088 }
1089
1090 pub fn get_cert_chain(&self) -> Vec<CertificateDer<'static>> {
1092 self.cert_chain.read().clone()
1093 }
1094
1095 pub fn get_private_key(&self) -> Option<PrivateKeyDer<'static>> {
1097 self.private_key_data.read().as_ref().map(|k| k.to_key())
1098 }
1099
1100 pub fn get_version(&self) -> u64 {
1102 *self.version.read()
1103 }
1104
1105 pub fn subscribe(&self) -> watch::Receiver<u64> {
1107 self.update_tx.subscribe()
1108 }
1109
1110 fn increment_version(&self) {
1112 let mut version = self.version.write();
1113 *version += 1;
1114 let _ = self.update_tx.send(*version);
1115 }
1116
1117 pub fn start_file_watcher(
1121 self: Arc<Self>,
1122 check_interval: Duration,
1123 ) -> NetResult<tokio::task::JoinHandle<()>> {
1124 let cert_path = self.cert_path.read().clone();
1125 let key_path = self.key_path.read().clone();
1126
1127 let (cert_path, key_path) = match (cert_path, key_path) {
1128 (Some(cp), Some(kp)) => (cp, kp),
1129 _ => {
1130 return Err(NetError::InvalidCertificate(
1131 "No certificate paths configured for file watching".to_string(),
1132 ));
1133 }
1134 };
1135
1136 let handle = tokio::spawn(async move {
1137 let mut last_cert_modified = get_file_modified(&cert_path);
1138 let mut last_key_modified = get_file_modified(&key_path);
1139
1140 loop {
1141 tokio::time::sleep(check_interval).await;
1142
1143 let cert_modified = get_file_modified(&cert_path);
1144 let key_modified = get_file_modified(&key_path);
1145
1146 let cert_changed = cert_modified != last_cert_modified;
1147 let key_changed = key_modified != last_key_modified;
1148
1149 if cert_changed || key_changed {
1150 info!(
1151 cert_changed = cert_changed,
1152 key_changed = key_changed,
1153 "Detected certificate file change, reloading"
1154 );
1155
1156 match self.reload() {
1157 Ok(()) => {
1158 last_cert_modified = cert_modified;
1159 last_key_modified = key_modified;
1160 }
1161 Err(e) => {
1162 error!(error = %e, "Failed to reload certificates");
1163 }
1164 }
1165 }
1166 }
1167 });
1168
1169 Ok(handle)
1170 }
1171}
1172
1173fn get_file_modified<P: AsRef<Path>>(path: P) -> Option<SystemTime> {
1175 fs::metadata(path.as_ref())
1176 .ok()
1177 .and_then(|m| m.modified().ok())
1178}
1179
1180#[cfg(test)]
1181#[path = "tls_tests.rs"]
1182mod tests;