Files
proxy-inspector/src/cert.rs
2024-03-30 16:42:22 +08:00

90 lines
3.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Cert, String> {
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<Certificate, String> {
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))
}