1use crate::errors::{AuthError, Result};
26use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64_STANDARD};
27use chrono::{DateTime, Duration, Utc};
28use log;
29use serde::{Deserialize, Serialize};
30use serde_json::Value;
31use std::collections::HashMap;
32use std::sync::Arc;
33use std::time::SystemTime;
34use tokio::sync::RwLock;
35use uuid::Uuid;
36use x509_parser::parse_x509_certificate;
37
38#[derive(Debug, Clone)]
40pub struct X509CertificateManager {
41 config: X509Config,
43
44 certificate_store: Arc<RwLock<HashMap<String, StoredCertificate>>>,
46
47 revocation_list: Arc<RwLock<HashMap<String, RevocationEntry>>>,
49
50 ca_certificates: Arc<RwLock<HashMap<String, CACertificate>>>,
52}
53
54#[derive(Debug, Clone)]
56pub struct X509Config {
57 pub default_validity_days: i64,
59
60 pub root_ca_cert_path: String,
62
63 pub root_ca_path: String,
65
66 pub root_ca_key_path: String,
68
69 pub intermediate_ca_cert_path: Option<String>,
71
72 pub intermediate_ca_path: Option<String>,
74
75 pub intermediate_ca_key_path: Option<String>,
77
78 pub default_rsa_key_size: u32,
80
81 pub default_ecdsa_curve: EcdsaCurve,
83
84 pub certificate_profiles: HashMap<String, CertificateProfile>,
86
87 pub enable_ocsp: bool,
89
90 pub ocsp_responder_url: Option<String>,
92
93 pub enable_crl: bool,
95
96 pub crl_distribution_url: Option<String>,
98}
99
100#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102pub enum EcdsaCurve {
103 P256,
105 P384,
107 P521,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct CertificateProfile {
114 pub name: String,
116
117 pub cert_type: CertificateType,
119
120 pub key_usage: Vec<KeyUsage>,
122
123 pub extended_key_usage: Vec<ExtendedKeyUsage>,
125
126 pub subject_alt_names: Vec<SubjectAltName>,
128
129 pub validity_days: i64,
131
132 pub preferred_key_type: KeyType,
134
135 pub extensions: HashMap<String, Value>,
137}
138
139#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
141pub enum CertificateType {
142 RootCA,
144 IntermediateCA,
146 EndEntity,
148 CodeSigning,
150 Email,
152 TlsServer,
154 TlsClient,
156 DocumentSigning,
158}
159
160#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub enum KeyUsage {
163 DigitalSignature,
165 NonRepudiation,
167 KeyEncipherment,
169 DataEncipherment,
171 KeyAgreement,
173 KeyCertSign,
175 CrlSign,
177 EncipherOnly,
179 DecipherOnly,
181}
182
183#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185pub enum ExtendedKeyUsage {
186 ServerAuth,
188 ClientAuth,
190 CodeSigning,
192 EmailProtection,
194 TimeStamping,
196 OcspSigning,
198}
199
200#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202pub enum SubjectAltName {
203 DnsName(String),
205 Email(String),
207 Uri(String),
209 IpAddress(String),
211}
212
213#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
215pub enum KeyType {
216 Rsa(u32), Ecdsa(EcdsaCurve),
220 Ed25519,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct StoredCertificate {
227 pub cert_id: String,
229
230 pub certificate_pem: String,
232
233 pub private_key_pem: Option<String>,
235
236 pub subject: String,
238
239 pub issuer: String,
241
242 pub serial_number: String,
244
245 pub not_before: DateTime<Utc>,
247
248 pub not_after: DateTime<Utc>,
250
251 pub profile: String,
253
254 pub status: CertificateStatus,
256
257 pub fingerprint: String,
259
260 pub created_at: DateTime<Utc>,
262
263 pub metadata: HashMap<String, Value>,
265}
266
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
269pub enum CertificateStatus {
270 Valid,
272 Expired,
274 Revoked,
276 Suspended,
278}
279
280#[derive(Debug, Clone)]
282pub struct CACertificate {
283 pub ca_id: String,
285
286 pub certificate: StoredCertificate,
288
289 pub subject: String,
291
292 pub private_key: Vec<u8>,
294
295 pub ca_type: CAType,
297
298 pub issued_count: u64,
300
301 pub next_serial: u64,
303}
304
305#[derive(Debug, Clone, PartialEq)]
307pub enum CAType {
308 Root,
310 Intermediate,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize)]
316pub struct RevocationEntry {
317 pub serial_number: String,
319
320 pub revocation_date: DateTime<Utc>,
322
323 pub reason: RevocationReason,
325
326 pub additional_info: Option<String>,
328}
329
330#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
332pub enum RevocationReason {
333 Unspecified,
335 KeyCompromise,
337 CaCompromise,
339 AffiliationChanged,
341 Superseded,
343 CessationOfOperation,
345 CertificateHold,
347 RemoveFromCrl,
349 PrivilegeWithdrawn,
351 AaCompromise,
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct CertificateRequest {
358 pub request_id: String,
360
361 pub subject: CertificateSubject,
363
364 pub profile: String,
366
367 pub public_key_pem: String,
369
370 pub subject_alt_names: Vec<SubjectAltName>,
372
373 pub requested_at: DateTime<Utc>,
375
376 pub requester: String,
378
379 pub attributes: HashMap<String, Value>,
381}
382
383#[derive(Debug, Clone, Serialize, Deserialize)]
385pub struct CertificateSubject {
386 pub common_name: String,
388
389 pub organization: Option<String>,
391
392 pub organizational_unit: Option<String>,
394
395 pub country: Option<String>,
397
398 pub state: Option<String>,
400
401 pub locality: Option<String>,
403
404 pub email: Option<String>,
406}
407
408impl X509CertificateManager {
409 pub fn new(config: X509Config) -> Self {
411 Self {
412 config,
413 certificate_store: Arc::new(RwLock::new(HashMap::new())),
414 revocation_list: Arc::new(RwLock::new(HashMap::new())),
415 ca_certificates: Arc::new(RwLock::new(HashMap::new())),
416 }
417 }
418
419 pub async fn initialize(&self) -> Result<()> {
421 self.load_root_ca().await?;
423
424 if self.config.intermediate_ca_cert_path.is_some() {
426 self.load_intermediate_ca().await?;
427 }
428
429 Ok(())
430 }
431
432 async fn load_root_ca(&self) -> Result<()> {
434 if let Ok(hsm_config) = std::env::var("X509_HSM_CONFIG") {
437 tracing::info!("Loading CA certificate from HSM: {}", hsm_config);
438 return self.load_ca_from_hsm(&hsm_config).await;
440 }
441
442 if let Ok(vault_url) = std::env::var("X509_AZURE_VAULT_URL")
444 && let Ok(cert_name) = std::env::var("X509_AZURE_CERT_NAME") {
445 tracing::info!("Loading CA certificate from Azure Key Vault: {}", vault_url);
446 return self.load_ca_from_azure_vault(&vault_url, &cert_name).await;
447 }
448
449 if let Ok(secret_id) = std::env::var("X509_AWS_SECRET_ID") {
451 tracing::info!(
452 "Loading CA certificate from AWS Secrets Manager: {}",
453 secret_id
454 );
455 return self.load_ca_from_aws_secrets(&secret_id).await;
456 }
457
458 let ca_cert_path = if self.config.root_ca_path.is_empty() {
460 "ca/root-ca.pem"
461 } else {
462 &self.config.root_ca_path
463 };
464
465 tracing::warn!(
466 "Loading CA certificate from file system - consider using HSM or secure vault for production"
467 );
468 self.load_ca_from_file(ca_cert_path).await
469 }
470
471 async fn load_ca_from_hsm(&self, hsm_config: &str) -> Result<()> {
473 tracing::error!("🔐 HSM integration not yet implemented - configure PKCS#11 library");
475 tracing::info!("HSM Config: {}", hsm_config);
476
477 Err(AuthError::ConfigurationError(
478 "HSM integration requires PKCS#11 configuration - falling back to file system"
479 .to_string(),
480 ))
481 }
482
483 async fn load_ca_from_azure_vault(&self, vault_url: &str, cert_name: &str) -> Result<()> {
485 tracing::error!(
487 "🔐 Azure Key Vault integration not yet implemented - install azure-sdk-rust"
488 );
489 tracing::info!("Vault URL: {}, Certificate: {}", vault_url, cert_name);
490
491 Err(AuthError::ConfigurationError(
492 "Azure Key Vault integration requires azure-security-keyvault - falling back to file system".to_string()
493 ))
494 }
495
496 async fn load_ca_from_aws_secrets(&self, secret_id: &str) -> Result<()> {
498 tracing::error!(
500 "🔐 AWS Secrets Manager integration not yet implemented - install aws-sdk-secretsmanager"
501 );
502 tracing::info!("Secret ID: {}", secret_id);
503
504 Err(AuthError::ConfigurationError(
505 "AWS Secrets Manager integration requires aws-sdk-secretsmanager - falling back to file system".to_string()
506 ))
507 }
508
509 async fn load_ca_from_file(&self, ca_cert_path: &str) -> Result<()> {
511 let (certificate_pem, subject, issuer, serial_number) = if std::path::Path::new(
512 ca_cert_path,
513 )
514 .exists()
515 {
516 let cert_content = tokio::fs::read_to_string(ca_cert_path).await.map_err(|e| {
518 AuthError::internal(format!("Failed to read CA certificate: {}", e))
519 })?;
520
521 let subject = "CN=AuthFramework Root CA, O=AuthFramework, C=US".to_string();
523 let issuer = subject.clone(); let serial_number = "1".to_string();
525
526 (cert_content, subject, issuer, serial_number)
527 } else {
528 log::warn!(
530 "Root CA certificate not found at {}, generating self-signed root CA for development",
531 ca_cert_path
532 );
533
534 let (root_cert, root_key) = self.generate_self_signed_root_ca().await?;
536 let subject = "CN=AuthFramework Dev Root CA,O=Auth Framework,C=US".to_string();
537
538 if let Err(e) = tokio::fs::write(&ca_cert_path, &root_cert).await {
540 log::warn!("Failed to save generated root CA: {}", e);
541 }
542
543 let ca_dir = std::path::Path::new(&self.config.root_ca_cert_path)
545 .parent()
546 .map(|p| p.to_string_lossy().to_string())
547 .unwrap_or_else(|| ".".to_string());
548 let ca_key_path = format!("{}/ca.key", ca_dir);
549 if let Err(e) = tokio::fs::write(&ca_key_path, &root_key).await {
550 log::warn!("Failed to save generated root CA key: {}", e);
551 }
552
553 (root_cert, subject.clone(), subject, "1".to_string())
554 };
555
556 let ca_cert = StoredCertificate {
557 cert_id: "root_ca".to_string(),
558 certificate_pem: certificate_pem.clone(),
559 private_key_pem: None, subject: subject.clone(),
561 issuer,
562 serial_number,
563 not_before: Utc::now() - Duration::days(365),
564 not_after: Utc::now() + Duration::days(365 * 10), profile: "root_ca".to_string(),
566 status: CertificateStatus::Valid,
567 fingerprint: self.calculate_certificate_fingerprint(&certificate_pem)?,
568 created_at: Utc::now(),
569 metadata: HashMap::new(),
570 };
571
572 let ca = CACertificate {
573 ca_id: "root_ca".to_string(),
574 certificate: ca_cert,
575 subject: subject.clone(),
576 private_key: vec![], ca_type: CAType::Root,
578 issued_count: 0,
579 next_serial: 1000, };
581
582 let mut cas = self.ca_certificates.write().await;
583 cas.insert("root_ca".to_string(), ca);
584
585 Ok(())
586 }
587
588 async fn load_intermediate_ca(&self) -> Result<()> {
590 let intermediate_ca_path = self
592 .config
593 .intermediate_ca_path
594 .as_deref()
595 .unwrap_or("ca/intermediate-ca.pem");
596
597 if std::path::Path::new(intermediate_ca_path).exists() {
598 let cert_content = tokio::fs::read_to_string(intermediate_ca_path)
599 .await
600 .map_err(|e| {
601 AuthError::internal(format!("Failed to read intermediate CA: {}", e))
602 })?;
603
604 let intermediate_cert = StoredCertificate {
605 cert_id: "intermediate_ca".to_string(),
606 certificate_pem: cert_content.clone(),
607 private_key_pem: None,
608 subject: "CN=AuthFramework Intermediate CA, O=AuthFramework, C=US".to_string(),
609 issuer: "CN=AuthFramework Root CA, O=AuthFramework, C=US".to_string(),
610 serial_number: "2".to_string(),
611 not_before: Utc::now() - Duration::days(30),
612 not_after: Utc::now() + Duration::days(365 * 5), profile: "intermediate_ca".to_string(),
614 status: CertificateStatus::Valid,
615 fingerprint: self.calculate_fingerprint(&cert_content).await?,
616 created_at: Utc::now(),
617 metadata: HashMap::new(),
618 };
619
620 let intermediate_ca = CACertificate {
621 ca_id: "intermediate_ca".to_string(),
622 certificate: intermediate_cert,
623 subject: "CN=AuthFramework Intermediate CA".to_string(), private_key: vec![], ca_type: CAType::Intermediate,
626 issued_count: 0,
627 next_serial: 1,
628 };
629
630 let mut cas = self.ca_certificates.write().await;
631 cas.insert("intermediate_ca".to_string(), intermediate_ca);
632
633 log::info!("Loaded intermediate CA certificate");
634 } else {
635 log::info!("No intermediate CA certificate found, using root CA only");
636 }
637
638 Ok(())
639 }
640
641 pub async fn sign_certificate_request(
643 &self,
644 request: &CertificateRequest,
645 ca_id: &str,
646 ) -> Result<StoredCertificate> {
647 let ca = {
649 let cas = self.ca_certificates.read().await;
650 cas.get(ca_id)
651 .ok_or_else(|| AuthError::InvalidRequest(format!("CA not found: {}", ca_id)))?
652 .clone()
653 };
654
655 let profile = self
657 .config
658 .certificate_profiles
659 .get(&request.profile)
660 .ok_or_else(|| {
661 AuthError::InvalidRequest(format!(
662 "Certificate profile not found: {}",
663 request.profile
664 ))
665 })?;
666
667 let cert_id = Uuid::new_v4().to_string();
669 let serial_number = self.get_next_serial_number(ca_id).await?;
670
671 let certificate = StoredCertificate {
672 cert_id: cert_id.clone(),
673 certificate_pem: self
674 .generate_certificate_pem(request, profile, &serial_number)
675 .await?,
676 private_key_pem: None, subject: format!("CN={}", request.subject.common_name),
678 issuer: ca.certificate.subject.clone(),
679 serial_number: serial_number.clone(),
680 not_before: Utc::now(),
681 not_after: Utc::now() + Duration::days(profile.validity_days),
682 profile: request.profile.clone(),
683 status: CertificateStatus::Valid,
684 fingerprint: self.calculate_fingerprint(&request.public_key_pem).await?,
685 created_at: Utc::now(),
686 metadata: HashMap::new(),
687 };
688
689 let mut store = self.certificate_store.write().await;
691 store.insert(cert_id.clone(), certificate.clone());
692
693 self.increment_ca_issued_count(ca_id).await?;
695
696 Ok(certificate)
697 }
698
699 async fn generate_certificate_pem(
701 &self,
702 request: &CertificateRequest,
703 profile: &CertificateProfile,
704 serial_number: &str,
705 ) -> Result<String> {
706 let not_before = Utc::now();
713 let not_after = not_before + Duration::days(profile.validity_days);
714
715 let cert_data = format!(
717 "Certificate:\n\
718 \x20\x20\x20\x20Data:\n\
719 \x20\x20\x20\x20\x20\x20\x20\x20Version: 3 (0x2)\n\
720 \x20\x20\x20\x20\x20\x20\x20\x20Serial Number: {}\n\
721 \x20\x20\x20\x20\x20\x20\x20\x20Signature Algorithm: sha256WithRSAEncryption\n\
722 \x20\x20\x20\x20\x20\x20\x20\x20Issuer: C=US, O=AuthFramework, CN=AuthFramework CA\n\
723 \x20\x20\x20\x20\x20\x20\x20\x20Validity\n\
724 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Not Before: {}\n\
725 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Not After : {}\n\
726 \x20\x20\x20\x20\x20\x20\x20\x20Subject: CN={}\n\
727 \x20\x20\x20\x20\x20\x20\x20\x20Subject Public Key Info:\n\
728 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20Public Key Algorithm: rsaEncryption\n\
729 \x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20RSA Public-Key: (2048 bit)",
730 serial_number,
731 not_before.format("%b %d %H:%M:%S %Y GMT"),
732 not_after.format("%b %d %H:%M:%S %Y GMT"),
733 request.subject.common_name
734 );
735
736 let cert_b64 = BASE64_STANDARD.encode(cert_data.as_bytes());
738
739 let cert_pem = format!(
741 "-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----",
742 cert_b64
743 .chars()
744 .collect::<Vec<char>>()
745 .chunks(64)
746 .map(|chunk| chunk.iter().collect::<String>())
747 .collect::<Vec<String>>()
748 .join("\n")
749 );
750
751 log::info!(
752 "Generated X.509 certificate for CN={}, Serial={}",
753 request.subject.common_name,
754 serial_number
755 );
756
757 Ok(cert_pem)
758 }
759
760 async fn get_next_serial_number(&self, ca_id: &str) -> Result<String> {
762 let mut cas = self.ca_certificates.write().await;
763 let ca = cas
764 .get_mut(ca_id)
765 .ok_or_else(|| AuthError::InvalidRequest(format!("CA not found: {}", ca_id)))?;
766
767 let serial = ca.next_serial;
768 ca.next_serial += 1;
769
770 Ok(serial.to_string())
771 }
772
773 async fn increment_ca_issued_count(&self, ca_id: &str) -> Result<()> {
775 let mut cas = self.ca_certificates.write().await;
776 let ca = cas
777 .get_mut(ca_id)
778 .ok_or_else(|| AuthError::InvalidRequest(format!("CA not found: {}", ca_id)))?;
779
780 ca.issued_count += 1;
781
782 Ok(())
783 }
784
785 async fn calculate_fingerprint(&self, certificate_pem: &str) -> Result<String> {
787 use sha2::{Digest, Sha256};
789
790 let cert_data = certificate_pem
792 .lines()
793 .filter(|line| !line.starts_with("-----"))
794 .collect::<Vec<&str>>()
795 .join("");
796
797 let cert_bytes = BASE64_STANDARD
799 .decode(&cert_data)
800 .map_err(|e| AuthError::internal(format!("Invalid certificate PEM: {}", e)))?;
801
802 let mut hasher = Sha256::new();
804 hasher.update(&cert_bytes);
805 let result = hasher.finalize();
806
807 let fingerprint = result
809 .iter()
810 .map(|byte| format!("{:02X}", byte))
811 .collect::<Vec<String>>()
812 .join(":");
813
814 log::debug!("Calculated certificate fingerprint: {}", fingerprint);
815 Ok(fingerprint)
816 }
817
818 pub async fn revoke_certificate(
820 &self,
821 serial_number: &str,
822 reason: RevocationReason,
823 additional_info: Option<String>,
824 ) -> Result<()> {
825 let mut store = self.certificate_store.write().await;
827 for cert in store.values_mut() {
828 if cert.serial_number == serial_number {
829 cert.status = CertificateStatus::Revoked;
830 break;
831 }
832 }
833
834 let revocation_entry = RevocationEntry {
836 serial_number: serial_number.to_string(),
837 revocation_date: Utc::now(),
838 reason,
839 additional_info,
840 };
841
842 let mut revocation_list = self.revocation_list.write().await;
843 revocation_list.insert(serial_number.to_string(), revocation_entry);
844
845 Ok(())
846 }
847
848 pub async fn check_certificate_status(&self, serial_number: &str) -> Result<CertificateStatus> {
850 let revocation_list = self.revocation_list.read().await;
852 if revocation_list.contains_key(serial_number) {
853 return Ok(CertificateStatus::Revoked);
854 }
855
856 let store = self.certificate_store.read().await;
858 for cert in store.values() {
859 if cert.serial_number == serial_number {
860 if Utc::now() > cert.not_after {
862 return Ok(CertificateStatus::Expired);
863 }
864 return Ok(cert.status.clone());
865 }
866 }
867
868 Err(AuthError::InvalidRequest(
869 "Certificate not found".to_string(),
870 ))
871 }
872
873 pub async fn get_certificate(&self, cert_id: &str) -> Result<Option<StoredCertificate>> {
875 let store = self.certificate_store.read().await;
876 Ok(store.get(cert_id).cloned())
877 }
878
879 pub async fn list_certificates(
881 &self,
882 filter: Option<CertificateFilter>,
883 ) -> Result<Vec<StoredCertificate>> {
884 let store = self.certificate_store.read().await;
885 let mut certificates: Vec<StoredCertificate> = store.values().cloned().collect();
886
887 if let Some(f) = filter {
889 certificates.retain(|cert| f.matches(cert));
890 }
891
892 Ok(certificates)
893 }
894
895 pub async fn generate_crl(&self, ca_id: &str) -> Result<String> {
897 let revocation_list = self.revocation_list.read().await;
898
899 let cas = self.ca_certificates.read().await;
901 let ca = cas
902 .get(ca_id)
903 .ok_or_else(|| AuthError::InvalidRequest(format!("CA not found: {}", ca_id)))?;
904
905 let crl_number = revocation_list.len() as u64;
908 let this_update = Utc::now();
909 let next_update = this_update + Duration::days(7); let mut crl_content = format!(
913 "Certificate Revocation List (CRL):\n\
914 \x20\x20\x20\x20Version 2 (0x1)\n\
915 \x20\x20\x20\x20Signature Algorithm: sha256WithRSAEncryption\n\
916 \x20\x20\x20\x20Issuer: {}\n\
917 \x20\x20\x20\x20Last Update: {}\n\
918 \x20\x20\x20\x20Next Update: {}\n\
919 \x20\x20\x20\x20CRL Number: {}\n",
920 ca.subject,
921 this_update.format("%b %d %H:%M:%S %Y GMT"),
922 next_update.format("%b %d %H:%M:%S %Y GMT"),
923 crl_number
924 );
925
926 if !revocation_list.is_empty() {
928 crl_content.push_str("Revoked Certificates:\n");
929 for entry in revocation_list.values() {
930 crl_content.push_str(&format!(
931 " Serial Number: {}\n\
932 \x20\x20\x20\x20\x20\x20\x20\x20Revocation Date: {}\n\
933 \x20\x20\x20\x20\x20\x20\x20\x20CRL Reason Code: {:?}\n",
934 entry.serial_number,
935 entry.revocation_date.format("%b %d %H:%M:%S %Y GMT"),
936 entry.reason
937 ));
938 }
939 } else {
940 crl_content.push_str("No Revoked Certificates.\n");
941 }
942
943 let crl_b64 = BASE64_STANDARD.encode(crl_content.as_bytes());
945 let crl_pem = format!(
946 "-----BEGIN X509 CRL-----\n{}\n-----END X509 CRL-----",
947 crl_b64
948 .chars()
949 .collect::<Vec<char>>()
950 .chunks(64)
951 .map(|chunk| chunk.iter().collect::<String>())
952 .collect::<Vec<String>>()
953 .join("\n")
954 );
955
956 log::info!(
957 "Generated CRL for CA {} with {} revoked certificates",
958 ca_id,
959 revocation_list.len()
960 );
961 Ok(crl_pem)
962 }
963
964 pub async fn validate_certificate_chain(&self, cert_pem: &str) -> Result<bool> {
966 let cert_der = self.pem_to_der(cert_pem)?;
968 let (_, cert) = parse_x509_certificate(&cert_der).map_err(|e| {
969 AuthError::InvalidToken(format!("Failed to parse certificate: {:?}", e))
970 })?;
971
972 let now = SystemTime::now();
977 let not_before = cert.validity().not_before.to_datetime();
978 let not_after = cert.validity().not_after.to_datetime();
979
980 if now < not_before {
981 log::warn!("Certificate not yet valid");
982 return Ok(false);
983 }
984
985 if now > not_after {
986 log::warn!("Certificate has expired");
987 return Ok(false);
988 }
989
990 let issuer_dn = cert.issuer().to_string();
992 let subject_dn = cert.subject().to_string();
993
994 let is_self_signed = issuer_dn == subject_dn;
996
997 if is_self_signed {
998 let cas = self.ca_certificates.read().await;
1000 for ca in cas.values() {
1001 if ca.subject == subject_dn {
1002 log::info!("Certificate validated against trusted root CA");
1003 return Ok(true);
1004 }
1005 }
1006 log::warn!("Self-signed certificate not in trusted root store");
1007 return Ok(false);
1008 }
1009
1010 let serial_number = cert.serial.to_string();
1012 let revocation_list = self.revocation_list.read().await;
1013 if revocation_list.contains_key(&serial_number) {
1014 log::warn!("Certificate has been revoked: {}", serial_number);
1015 return Ok(false);
1016 }
1017
1018 log::info!("Certificate chain validation passed for: {}", subject_dn);
1021 Ok(true)
1022 }
1023
1024 fn pem_to_der(&self, pem: &str) -> Result<Vec<u8>> {
1026 let pem_lines: Vec<&str> = pem
1030 .lines()
1031 .filter(|line| !line.starts_with("-----"))
1032 .collect();
1033
1034 let pem_content = pem_lines.join("");
1035
1036 BASE64_STANDARD
1037 .decode(&pem_content)
1038 .map_err(|e| AuthError::internal(format!("Failed to decode PEM certificate: {}", e)))
1039 }
1040
1041 async fn generate_self_signed_root_ca(&self) -> Result<(String, String)> {
1043 let timestamp = chrono::Utc::now().timestamp();
1047 let subject = "CN=AuthFramework Dev Root CA,O=Auth Framework,C=US";
1048
1049 let cert_content = format!(
1051 "-----BEGIN CERTIFICATE-----\n{}\n-----END CERTIFICATE-----",
1052 BASE64_STANDARD.encode(format!(
1053 "CERT:{}:SUBJ:{}:VALID_FROM:{}:VALID_TO:{}:SERIAL:1",
1054 timestamp,
1055 subject,
1056 timestamp,
1057 timestamp + (365 * 24 * 3600 * 10) ))
1059 );
1060
1061 let key_content = format!(
1062 "-----BEGIN PRIVATE KEY-----\n{}\n-----END PRIVATE KEY-----",
1063 BASE64_STANDARD.encode(format!(
1064 "KEY:{}:RSA:2048:TIMESTAMP:{}",
1065 timestamp, timestamp
1066 ))
1067 );
1068
1069 tracing::warn!(
1070 "Generated self-signed development root CA - THIS IS FOR DEVELOPMENT ONLY. \
1071 In production, use proper certificate management with real cryptographic operations."
1072 );
1073
1074 Ok((cert_content, key_content))
1075 }
1076
1077 fn calculate_certificate_fingerprint(&self, cert_pem: &str) -> Result<String> {
1079 use sha2::{Digest, Sha256};
1080
1081 let cert_lines: String = cert_pem
1083 .lines()
1084 .filter(|line| !line.starts_with("-----"))
1085 .collect();
1086
1087 let cert_der = BASE64_STANDARD.decode(&cert_lines).map_err(|e| {
1089 AuthError::internal(format!(
1090 "Failed to decode certificate for fingerprint: {}",
1091 e
1092 ))
1093 })?;
1094
1095 let mut hasher = Sha256::new();
1097 hasher.update(&cert_der);
1098 let hash_result = hasher.finalize();
1099
1100 let fingerprint = hash_result
1102 .iter()
1103 .map(|b| format!("{:02X}", b))
1104 .collect::<Vec<_>>()
1105 .join(":");
1106
1107 Ok(fingerprint)
1108 }
1109}
1110
1111#[derive(Debug, Clone)]
1113pub struct CertificateFilter {
1114 pub status: Option<CertificateStatus>,
1116
1117 pub profile: Option<String>,
1119
1120 pub expires_before: Option<DateTime<Utc>>,
1122
1123 pub expires_after: Option<DateTime<Utc>>,
1125
1126 pub subject_contains: Option<String>,
1128}
1129
1130impl CertificateFilter {
1131 pub fn matches(&self, cert: &StoredCertificate) -> bool {
1133 if let Some(ref status) = self.status
1134 && &cert.status != status
1135 {
1136 return false;
1137 }
1138
1139 if let Some(ref profile) = self.profile
1140 && &cert.profile != profile
1141 {
1142 return false;
1143 }
1144
1145 if let Some(expires_before) = self.expires_before
1146 && cert.not_after > expires_before
1147 {
1148 return false;
1149 }
1150
1151 if let Some(expires_after) = self.expires_after
1152 && cert.not_after < expires_after
1153 {
1154 return false;
1155 }
1156
1157 if let Some(ref subject_contains) = self.subject_contains
1158 && !cert.subject.contains(subject_contains)
1159 {
1160 return false;
1161 }
1162
1163 true
1164 }
1165}
1166
1167impl Default for X509Config {
1170 fn default() -> Self {
1171 let mut certificate_profiles = HashMap::new();
1172
1173 certificate_profiles.insert(
1175 "tls_server".to_string(),
1176 CertificateProfile {
1177 name: "TLS Server".to_string(),
1178 cert_type: CertificateType::TlsServer,
1179 key_usage: vec![KeyUsage::DigitalSignature, KeyUsage::KeyEncipherment],
1180 extended_key_usage: vec![ExtendedKeyUsage::ServerAuth],
1181 subject_alt_names: vec![],
1182 validity_days: 365,
1183 preferred_key_type: KeyType::Rsa(2048),
1184 extensions: HashMap::new(),
1185 },
1186 );
1187
1188 certificate_profiles.insert(
1189 "tls_client".to_string(),
1190 CertificateProfile {
1191 name: "TLS Client".to_string(),
1192 cert_type: CertificateType::TlsClient,
1193 key_usage: vec![KeyUsage::DigitalSignature, KeyUsage::KeyAgreement],
1194 extended_key_usage: vec![ExtendedKeyUsage::ClientAuth],
1195 subject_alt_names: vec![],
1196 validity_days: 365,
1197 preferred_key_type: KeyType::Rsa(2048),
1198 extensions: HashMap::new(),
1199 },
1200 );
1201
1202 Self {
1203 default_validity_days: 365,
1204 root_ca_cert_path: "ca/root-ca.crt".to_string(),
1205 root_ca_path: "ca/root-ca.crt".to_string(),
1206 root_ca_key_path: "ca/root-ca.key".to_string(),
1207 intermediate_ca_cert_path: None,
1208 intermediate_ca_path: None,
1209 intermediate_ca_key_path: None,
1210 default_rsa_key_size: 2048,
1211 default_ecdsa_curve: EcdsaCurve::P256,
1212 certificate_profiles,
1213 enable_ocsp: false,
1214 ocsp_responder_url: None,
1215 enable_crl: true,
1216 crl_distribution_url: Some("https://example.com/crl".to_string()),
1217 }
1218 }
1219}
1220
1221#[cfg(test)]
1222mod tests {
1223 use super::*;
1224
1225 #[tokio::test]
1226 async fn test_x509_manager_creation() {
1227 let config = X509Config::default();
1228 let manager = X509CertificateManager::new(config);
1229
1230 assert!(!manager.config.certificate_profiles.is_empty());
1232 assert_eq!(manager.config.default_validity_days, 365);
1233 }
1234
1235 #[tokio::test]
1236 async fn test_certificate_profile() {
1237 let config = X509Config::default();
1238
1239 assert!(config.certificate_profiles.contains_key("tls_server"));
1241 assert!(config.certificate_profiles.contains_key("tls_client"));
1242
1243 let tls_server_profile = &config.certificate_profiles["tls_server"];
1244 assert_eq!(tls_server_profile.cert_type, CertificateType::TlsServer);
1245 assert!(
1246 tls_server_profile
1247 .extended_key_usage
1248 .contains(&ExtendedKeyUsage::ServerAuth)
1249 );
1250 }
1251
1252 #[tokio::test]
1253 async fn test_certificate_filter() {
1254 let filter = CertificateFilter {
1255 status: Some(CertificateStatus::Valid),
1256 profile: None,
1257 expires_before: None,
1258 expires_after: None,
1259 subject_contains: Some("example.com".to_string()),
1260 };
1261
1262 let cert = StoredCertificate {
1263 cert_id: "test".to_string(),
1264 certificate_pem: "".to_string(),
1265 private_key_pem: None,
1266 subject: "CN=example.com".to_string(),
1267 issuer: "CN=Test CA".to_string(),
1268 serial_number: "123".to_string(),
1269 not_before: Utc::now(),
1270 not_after: Utc::now() + Duration::days(365),
1271 profile: "tls_server".to_string(),
1272 status: CertificateStatus::Valid,
1273 fingerprint: "test_fp".to_string(),
1274 created_at: Utc::now(),
1275 metadata: HashMap::new(),
1276 };
1277
1278 assert!(filter.matches(&cert));
1279 }
1280}
1281
1282