use crate::Error;
use rcgen::{Certificate, CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_RSA_SHA256};
use rsa::{RsaPrivateKey, RsaPublicKey, pkcs8::EncodePrivateKey, pkcs1::EncodeRsaPublicKey};
use rand::rngs::OsRng;
use std::time::{Duration, SystemTime};
use time::OffsetDateTime;
#[derive(Debug, Clone)]
pub struct MikeyKeyPair {
pub private_key: Vec<u8>,
pub public_key: Vec<u8>,
pub certificate: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CertificateConfig {
pub common_name: String,
pub organization: String,
pub organizational_unit: String,
pub country: String,
pub state: String,
pub locality: String,
pub validity_duration: Duration,
pub key_size: usize,
}
impl Default for CertificateConfig {
fn default() -> Self {
Self {
common_name: "MIKEY-PKE Entity".to_string(),
organization: "Enterprise Communications".to_string(),
organizational_unit: "Secure Multimedia".to_string(),
country: "US".to_string(),
state: "California".to_string(),
locality: "San Francisco".to_string(),
validity_duration: Duration::from_secs(365 * 24 * 60 * 60), key_size: 2048,
}
}
}
impl CertificateConfig {
pub fn enterprise_server(hostname: &str) -> Self {
Self {
common_name: hostname.to_string(),
organization: "Enterprise Corp".to_string(),
organizational_unit: "Media Server".to_string(),
country: "US".to_string(),
state: "California".to_string(),
locality: "San Francisco".to_string(),
validity_duration: Duration::from_secs(2 * 365 * 24 * 60 * 60), key_size: 2048,
}
}
pub fn enterprise_client(user_id: &str) -> Self {
Self {
common_name: format!("User {}", user_id),
organization: "Enterprise Corp".to_string(),
organizational_unit: "Media Client".to_string(),
country: "US".to_string(),
state: "California".to_string(),
locality: "San Francisco".to_string(),
validity_duration: Duration::from_secs(365 * 24 * 60 * 60), key_size: 2048,
}
}
pub fn high_security(entity_name: &str) -> Self {
Self {
common_name: entity_name.to_string(),
organization: "Secure Communications Inc".to_string(),
organizational_unit: "High Security Division".to_string(),
country: "US".to_string(),
state: "Virginia".to_string(),
locality: "Washington DC".to_string(),
validity_duration: Duration::from_secs(90 * 24 * 60 * 60), key_size: 4096, }
}
}
pub fn generate_key_pair_and_certificate(config: CertificateConfig) -> Result<MikeyKeyPair, Error> {
let mut rng = OsRng;
let rsa_private_key = RsaPrivateKey::new(&mut rng, config.key_size)
.map_err(|_| Error::CryptoError("Failed to generate RSA private key".into()))?;
let rsa_public_key = RsaPublicKey::from(&rsa_private_key);
let private_key_der = rsa_private_key.to_pkcs8_der()
.map_err(|_| Error::CryptoError("Failed to encode private key to PKCS#8".into()))?;
let public_key_der = rsa_public_key.to_pkcs1_der()
.map_err(|_| Error::CryptoError("Failed to encode public key to PKCS#1".into()))?;
let mut params = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, config.common_name);
dn.push(DnType::OrganizationName, config.organization);
dn.push(DnType::OrganizationalUnitName, config.organizational_unit);
dn.push(DnType::CountryName, config.country);
dn.push(DnType::StateOrProvinceName, config.state);
dn.push(DnType::LocalityName, config.locality);
params.distinguished_name = dn;
params.not_before = OffsetDateTime::from(SystemTime::now());
params.not_after = OffsetDateTime::from(SystemTime::now() + config.validity_duration);
params.alg = &PKCS_RSA_SHA256;
let key_pair = KeyPair::from_der(&private_key_der.as_bytes())
.map_err(|_| Error::CryptoError("Failed to create KeyPair from private key".into()))?;
params.key_pair = Some(key_pair);
let cert = Certificate::from_params(params)
.map_err(|_| Error::CryptoError("Failed to generate certificate".into()))?;
let certificate_der = cert.serialize_der()
.map_err(|_| Error::CryptoError("Failed to serialize certificate".into()))?;
Ok(MikeyKeyPair {
private_key: private_key_der.as_bytes().to_vec(),
public_key: public_key_der.as_bytes().to_vec(),
certificate: certificate_der,
})
}
pub fn generate_ca_certificate(config: CertificateConfig) -> Result<MikeyKeyPair, Error> {
let mut rng = OsRng;
let rsa_private_key = RsaPrivateKey::new(&mut rng, config.key_size)
.map_err(|_| Error::CryptoError("Failed to generate CA RSA private key".into()))?;
let rsa_public_key = RsaPublicKey::from(&rsa_private_key);
let private_key_der = rsa_private_key.to_pkcs8_der()
.map_err(|_| Error::CryptoError("Failed to encode CA private key to PKCS#8".into()))?;
let public_key_der = rsa_public_key.to_pkcs1_der()
.map_err(|_| Error::CryptoError("Failed to encode CA public key to PKCS#1".into()))?;
let mut params = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, format!("{} CA", config.common_name));
dn.push(DnType::OrganizationName, config.organization);
dn.push(DnType::OrganizationalUnitName, "Certificate Authority".to_string());
dn.push(DnType::CountryName, config.country);
dn.push(DnType::StateOrProvinceName, config.state);
dn.push(DnType::LocalityName, config.locality);
params.distinguished_name = dn;
params.not_before = OffsetDateTime::from(SystemTime::now());
params.not_after = OffsetDateTime::from(SystemTime::now() + config.validity_duration * 2);
params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
params.alg = &PKCS_RSA_SHA256;
let key_pair = KeyPair::from_der(&private_key_der.as_bytes())
.map_err(|_| Error::CryptoError("Failed to create KeyPair from CA private key".into()))?;
params.key_pair = Some(key_pair);
let cert = Certificate::from_params(params)
.map_err(|_| Error::CryptoError("Failed to generate CA certificate".into()))?;
let certificate_der = cert.serialize_der()
.map_err(|_| Error::CryptoError("Failed to serialize CA certificate".into()))?;
Ok(MikeyKeyPair {
private_key: private_key_der.as_bytes().to_vec(),
public_key: public_key_der.as_bytes().to_vec(),
certificate: certificate_der,
})
}
pub fn sign_certificate_with_ca(
ca_cert: &MikeyKeyPair,
subject_config: CertificateConfig
) -> Result<MikeyKeyPair, Error> {
let mut rng = OsRng;
let rsa_private_key = RsaPrivateKey::new(&mut rng, subject_config.key_size)
.map_err(|_| Error::CryptoError("Failed to generate subject RSA private key".into()))?;
let rsa_public_key = RsaPublicKey::from(&rsa_private_key);
let private_key_der = rsa_private_key.to_pkcs8_der()
.map_err(|_| Error::CryptoError("Failed to encode subject private key to PKCS#8".into()))?;
let public_key_der = rsa_public_key.to_pkcs1_der()
.map_err(|_| Error::CryptoError("Failed to encode subject public key to PKCS#1".into()))?;
let ca_info = extract_certificate_info(&ca_cert.certificate)?;
let mut params = CertificateParams::default();
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, subject_config.common_name);
dn.push(DnType::OrganizationName, subject_config.organization);
dn.push(DnType::OrganizationalUnitName, subject_config.organizational_unit);
dn.push(DnType::CountryName, subject_config.country);
dn.push(DnType::StateOrProvinceName, subject_config.state);
dn.push(DnType::LocalityName, subject_config.locality);
params.distinguished_name = dn;
params.not_before = OffsetDateTime::from(SystemTime::now());
params.not_after = OffsetDateTime::from(SystemTime::now() + subject_config.validity_duration);
params.alg = &PKCS_RSA_SHA256;
let key_pair = KeyPair::from_der(&private_key_der.as_bytes())
.map_err(|_| Error::CryptoError("Failed to create KeyPair from subject private key".into()))?;
params.key_pair = Some(key_pair);
let cert = Certificate::from_params(params)
.map_err(|_| Error::CryptoError("Failed to generate subject certificate".into()))?;
let certificate_der = cert.serialize_der()
.map_err(|_| Error::CryptoError("Failed to serialize subject certificate".into()))?;
Ok(MikeyKeyPair {
private_key: private_key_der.as_bytes().to_vec(),
public_key: public_key_der.as_bytes().to_vec(),
certificate: certificate_der,
})
}
pub fn validate_certificate_chain(
subject_cert: &[u8],
ca_cert: &[u8]
) -> Result<(), Error> {
let (_, subject) = x509_parser::parse_x509_certificate(subject_cert)
.map_err(|_| Error::CryptoError("Failed to parse subject certificate".into()))?;
let (_, ca) = x509_parser::parse_x509_certificate(ca_cert)
.map_err(|_| Error::CryptoError("Failed to parse CA certificate".into()))?;
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let not_before = subject.validity().not_before.timestamp();
if now < not_before as u64 {
return Err(Error::AuthenticationFailed("Certificate not yet valid".into()));
}
let not_after = subject.validity().not_after.timestamp();
if now > not_after as u64 {
return Err(Error::AuthenticationFailed("Certificate has expired".into()));
}
Ok(())
}
pub fn extract_certificate_info(cert_der: &[u8]) -> Result<CertificateInfo, Error> {
let (_, cert) = x509_parser::parse_x509_certificate(cert_der)
.map_err(|_| Error::CryptoError("Failed to parse certificate".into()))?;
let subject = cert.subject();
let issuer = cert.issuer();
Ok(CertificateInfo {
subject_cn: extract_cn_from_name(subject),
issuer_cn: extract_cn_from_name(issuer),
serial_number: format!("{:?}", cert.serial),
not_before: cert.validity().not_before.timestamp(),
not_after: cert.validity().not_after.timestamp(),
})
}
#[derive(Debug, Clone)]
pub struct CertificateInfo {
pub subject_cn: String,
pub issuer_cn: String,
pub serial_number: String,
pub not_before: i64,
pub not_after: i64,
}
fn extract_cn_from_name(name: &x509_parser::x509::X509Name) -> String {
for rdn in name.iter() {
for attr in rdn.iter() {
if let Ok(cn) = attr.attr_value().as_str() {
if attr.attr_type() == &x509_parser::oid_registry::OID_X509_COMMON_NAME {
return cn.to_string();
}
}
}
}
"Unknown".to_string()
}