feat: issue certificate success
This commit is contained in:
109
src/cert.rs
Normal file
109
src/cert.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use rcgen::{Certificate, CertificateParams, DnType, ExtendedKeyUsagePurpose, IsCa, KeyPair, KeyUsagePurpose};
|
||||
use time::{Duration, OffsetDateTime};
|
||||
|
||||
const INTERMEDIATE_CERT_ENV_VAR: &str = "INTERMEDIATE_CERT";
|
||||
const INTERMEDIATE_KEY_ENV_VAR: &str = "INTERMEDIATE_KEY";
|
||||
|
||||
static INTERMEDIATE_CA: Lazy<Certificate> = Lazy::new(|| {
|
||||
let cert_fn = std::env::var(INTERMEDIATE_CERT_ENV_VAR)
|
||||
.unwrap_or("__ignore_intermediate_cert.pem".to_string());
|
||||
let key_fn = std::env::var(INTERMEDIATE_KEY_ENV_VAR)
|
||||
.unwrap_or("__ignore_intermediate_pri_key.pem".to_string());
|
||||
let cert_pem = fs::read_to_string(cert_fn).expect("Read cert file failed");
|
||||
let key_pem = fs::read_to_string(key_fn).expect("Read key file failed");
|
||||
let key_pem = parse_pkcs8(&key_pem);
|
||||
let key_pair = KeyPair::from_pem(&key_pem).expect("Parse keypair failed");
|
||||
// 底层逻辑限制,P256 与 SHA256 搭配,P384 与 SHA384 搭配
|
||||
let certificate_params = CertificateParams::from_ca_cert_pem(&cert_pem, key_pair)
|
||||
.expect("Cert and keypair mismatch");
|
||||
Certificate::from_params(certificate_params)
|
||||
.expect("Parse cert params failed")
|
||||
});
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Cert {
|
||||
pub cert_pem: String,
|
||||
pub intermediate_pem: String,
|
||||
pub key_pem: String,
|
||||
}
|
||||
|
||||
static CERTIFICATE_CACHE_MAP: Lazy<RwLock<HashMap<String, Cert>>> = Lazy::new(|| {
|
||||
RwLock::new(HashMap::new())
|
||||
});
|
||||
|
||||
pub fn issue_certificate(domain: &str) -> Cert {
|
||||
{
|
||||
if let Some(cert) = CERTIFICATE_CACHE_MAP.read().unwrap().get(domain) {
|
||||
return cert.clone();
|
||||
}
|
||||
}
|
||||
let cert = new_end_entity(domain);
|
||||
let cert_pem = cert.serialize_pem_with_signer(&INTERMEDIATE_CA).expect("Sign cert failed");
|
||||
let key_pem = cert.serialize_private_key_pem();
|
||||
let intermediate_pem = INTERMEDIATE_CA.serialize_pem().expect("Ser intermediate cert failed");
|
||||
let cert = Cert {
|
||||
cert_pem,
|
||||
intermediate_pem,
|
||||
key_pem,
|
||||
};
|
||||
{
|
||||
CERTIFICATE_CACHE_MAP.write().unwrap().insert(domain.to_string(), cert.clone());
|
||||
}
|
||||
cert
|
||||
}
|
||||
|
||||
fn parse_pkcs8(pem: &str) -> String {
|
||||
{
|
||||
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) -> Certificate {
|
||||
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).expect("Generate domain certificate failed")
|
||||
}
|
||||
|
||||
fn validity_period() -> (OffsetDateTime, OffsetDateTime) {
|
||||
let start = OffsetDateTime::now_utc().checked_sub(Duration::hours(1)).unwrap();
|
||||
let end = OffsetDateTime::now_utc().checked_add(Duration::days(90)).unwrap();
|
||||
(start, end)
|
||||
}
|
||||
Reference in New Issue
Block a user