use std::fs; use rcgen::{ Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose, }; use time::{Duration, OffsetDateTime}; #[derive(Debug, Clone)] pub struct Cert { pub cert_pem: String, pub key_pem: String, } pub fn load_certificate(cert_fn: &str, key_fn: &str) -> Result<(Certificate, String), String> { let cert_pem = fs::read_to_string(cert_fn).map_err(|e| format!("Read file: {} failed: {}", cert_fn, e))?; let key_pem = fs::read_to_string(key_fn).map_err(|e| format!("Read file: {} failed: {}", key_fn, e))?; let key_pem = parse_pkcs8(&key_pem); let key_pair = KeyPair::from_pem(&key_pem).map_err(|e| format!("Parse key: {} failed: {}", key_fn, e))?; // 底层逻辑限制,P256 与 SHA256 搭配,P384 与 SHA384 搭配 let certificate_params = CertificateParams::from_ca_cert_pem(&cert_pem, key_pair) .map_err(|e| format!("Cert and keypair match failed: {}", e))?; let cert = Certificate::from_params(certificate_params) .map_err(|e| format!("Parse cert params failed: {}", e))?; Ok((cert, cert_pem)) } pub fn issue_certificate(intermediate_certificate: &Certificate, domain: &str) -> Result { let cert = new_end_entity(domain)?; log::info!("New certificate for: {} -> {}", domain, hex::encode(cert.get_key_identifier())); let cert_pem = cert.serialize_pem_with_signer(intermediate_certificate).map_err(|e| format!("Sign cert failed: {}", e))?; let key_pem = cert.serialize_private_key_pem(); Ok(Cert { cert_pem, key_pem, }) } fn parse_pkcs8(pem: &str) -> String { // KeyPair only support PKCS#8 private key with public key, though public key is optional { use p256::{pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, SecretKey}; let secret_key = SecretKey::from_pkcs8_pem(pem); if let Ok(secret_key) = secret_key { if let Ok(pem) = secret_key.to_pkcs8_pem(LineEnding::CR) { return pem.to_string(); } } } { use p384::{pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, SecretKey}; let secret_key = SecretKey::from_pkcs8_pem(pem); if let Ok(secret_key) = secret_key { if let Ok(pem) = secret_key.to_pkcs8_pem(LineEnding::CR) { return pem.to_string(); } } } { use p521::{pkcs8::{DecodePrivateKey, EncodePrivateKey, LineEnding}, SecretKey}; let secret_key = SecretKey::from_pkcs8_pem(pem); if let Ok(secret_key) = secret_key { if let Ok(pem) = secret_key.to_pkcs8_pem(LineEnding::CR) { return pem.to_string(); } } } pem.to_string() } fn new_end_entity(domain: &str) -> Result { let mut params = CertificateParams::new(vec![domain.into()]); let (start, end) = validity_period()?; params.distinguished_name.push(DnType::CommonName, domain); params.use_authority_key_identifier_extension = true; params.key_usages.push(KeyUsagePurpose::DigitalSignature); params.is_ca = IsCa::NoCa; params.extended_key_usages.push(ExtendedKeyUsagePurpose::ServerAuth); params.extended_key_usages.push(ExtendedKeyUsagePurpose::ClientAuth); params.not_before = start; params.not_after = end; Certificate::from_params(params).map_err(|e| format!("New cert failed: {}", e)) } fn validity_period() -> Result<(OffsetDateTime, OffsetDateTime), String> { let start = OffsetDateTime::now_utc().checked_sub(Duration::hours(1)).expect("SHOULD NOT HAPPEN!"); let end = OffsetDateTime::now_utc().checked_add(Duration::days(90)).expect("SHOULD NOT HAPPEN!"); Ok((start, end)) }