use pkcs8::der::Decode;
use pkcs8::{
pkcs5::pbes2::Parameters, DecodePrivateKey, LineEnding, PrivateKeyInfo, SecretDocument,
};
use rcgen::{
date_time_ymd, BasicConstraints, Certificate, CertificateParams, DistinguishedName, DnType,
IsCa, KeyPair, KeyUsagePurpose, SerialNumber, PKCS_RSA_SHA256,
};
use rustls_pki_types::CertificateDer;
use std::{fs::File, path::Path, sync::Arc};
use x509_parser::prelude::{oid_registry, GeneralName, ParsedExtension, X509Certificate};
use crate::error::Error;
#[derive(Clone)]
pub struct CertificateAuthority {
pub cert: Arc<Certificate>,
pub key_pair: Arc<KeyPair>,
}
impl CertificateAuthority {
pub fn new(certificate_name: Option<String>) -> Result<Self, Error> {
let key = KeyPair::generate_for(&PKCS_RSA_SHA256)?;
let mut dn = DistinguishedName::new();
let mut params = match certificate_name {
Some(name) => {
dn.push(DnType::CommonName, name.clone());
CertificateParams::new(vec![name])?
}
None => {
dn.push(DnType::CommonName, "localhost");
CertificateParams::new(vec!["localhost".to_string()])?
}
};
params.distinguished_name = dn;
params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
params.serial_number = Some(SerialNumber::from_slice(
&rand::random::<[u8; 16]>(),
));
params.not_before = date_time_ymd(2025, 1, 1);
params.not_after = date_time_ymd(2045, 1, 1);
let cert = params.self_signed(&key)?;
Ok(Self {
cert: Arc::new(cert),
key_pair: Arc::new(key),
})
}
pub fn load_from_pem_files<P: AsRef<Path>, Q: AsRef<Path>>(
cert_file: P,
key_file: Q,
passphrase: Option<String>,
) -> Result<Self, Error> {
let private_key = decrypt_private_key(key_file, passphrase)?;
let key = KeyPair::from_pem(&private_key)?;
let cert_pem = String::from_utf8(get_bytes_from_file(cert_file)?)?;
let cert = CertificateParams::from_ca_cert_pem(&cert_pem)?;
let cert = cert.self_signed(&key)?;
Ok(Self {
cert: Arc::new(cert),
key_pair: Arc::new(key),
})
}
pub fn create_signed_certificate_for_domain(
&self,
domain: &str,
san: Option<Vec<String>>,
) -> Result<Certificate, Error> {
let mut params = if let Some(san_list) = san {
CertificateParams::new(san_list)?
} else {
CertificateParams::new(vec![domain.to_string()])?
};
let mut dn = DistinguishedName::new();
dn.push(DnType::CommonName, domain);
params.distinguished_name = dn;
params.not_before = date_time_ymd(1995, 1, 1);
params.not_after = date_time_ymd(1995, 1, 1);
let cert = params.signed_by(&*self.key_pair, &self.cert, &self.key_pair)?;
Ok(cert)
}
pub fn spoof_certificate(
&self,
certificate: CertificateDer<'static>,
) -> Result<CertificateDer<'static>, Error> {
let certificate_params = CertificateParams::from_ca_cert_der(&certificate)?;
let certificate =
certificate_params.signed_by(&*self.key_pair, &self.cert, &self.key_pair)?;
Ok(certificate.der().clone())
}
}
pub fn encrypt_private_key(
key_pair: &KeyPair,
passphrase: &Option<String>,
) -> Result<String, Error> {
let private_key_der = key_pair.serialize_der();
let private_key = match passphrase {
Some(passphrase) => {
let private_key_info = PrivateKeyInfo::from_der(&private_key_der)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?;
let pbkdf2_iterations = 2048; let pbkdf2_salt = rand::random::<[u8; 16]>(); let aes_iv = rand::random::<[u8; 16]>();
let pbes2_params =
Parameters::pbkdf2_sha256_aes256cbc(pbkdf2_iterations, &pbkdf2_salt, &aes_iv)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?;
let encrypted_key = private_key_info
.encrypt_with_params(pbes2_params, passphrase)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?;
encrypted_key
.to_pem("ENCRYPTED PRIVATE KEY", LineEnding::LF)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?
.to_string()
}
None => {
key_pair.serialize_pem()
}
};
Ok(private_key)
}
fn decrypt_private_key<Q: AsRef<Path>>(
key_file: Q,
passphrase: Option<String>,
) -> Result<String, Error> {
let private_key_pem = String::from_utf8(get_bytes_from_file(key_file)?)?;
let private_key = match passphrase {
Some(passphrase) => {
SecretDocument::from_pkcs8_encrypted_pem(&private_key_pem, passphrase.as_bytes())
.map_err(|e| Error::Pkcs8Error(e.to_string()))?
.to_pem("PRIVATE KEY", LineEnding::LF)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?
.to_string()
}
None => SecretDocument::from_pkcs8_pem(&private_key_pem)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?
.to_pem("PRIVATE KEY", LineEnding::LF)
.map_err(|e| Error::Pkcs8Error(e.to_string()))?
.to_string(),
};
Ok(private_key)
}
fn get_bytes_from_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
let mut file = File::open(path)?;
let mut bytes: Vec<u8> = vec![];
std::io::copy(&mut file, &mut bytes)?;
Ok(bytes)
}
pub fn print_certificate(certificate: &X509Certificate) {
println!("New certificate");
println!("subject_name:");
for entry in certificate.subject().iter_common_name() {
println!(
"CommonName: {}",
entry.as_str().unwrap_or("<invalid UTF-8>")
);
}
println!("issuer_name:");
for entry in certificate.issuer().iter_common_name() {
println!(
"CommonName: {}",
entry.as_str().unwrap_or("<invalid UTF-8>")
);
}
println!("subject_alt_names:");
if let Some(san) = certificate.subject_alternative_name().unwrap() {
for general_name in &san.value.general_names {
print_general_name(general_name);
}
} else {
println!("No subject alternative names found.");
}
println!("issuer_alt_names:");
if let Some(ian_ext) = certificate
.extensions()
.iter()
.find(|ext| ext.oid == oid_registry::OID_X509_EXT_ISSUER_ALT_NAME)
{
if let ParsedExtension::IssuerAlternativeName(ian) = &ian_ext.parsed_extension() {
for general_name in &ian.general_names {
print_general_name(general_name);
}
} else {
println!("Issuer Alternative Name found, but format is unrecognized.");
}
} else {
println!("No issuer alternative names found.");
}
println!("public_key: {:?}", certificate.public_key());
println!(
"Validity period:\n Not Before: {:?}\n Not After: {:?}",
certificate.validity.not_before.to_rfc2822(),
certificate.validity.not_after.to_rfc2822()
);
println!("Signature: {:x?}", certificate.signature_value.as_ref());
println!(
"Signature algorithm: {}",
certificate.signature_algorithm.algorithm
);
println!("ocsp_responders:");
if let Some(aia_extension) = certificate
.extensions()
.iter()
.find(|ext| ext.oid == oid_registry::OID_PKIX_AUTHORITY_INFO_ACCESS)
{
if let ParsedExtension::AuthorityInfoAccess(access_info) = &aia_extension.parsed_extension()
{
for access in &access_info.accessdescs {
println!("OCSP Responder: {}", access.access_location);
}
} else {
println!("Error parsing AIA extension.");
}
} else {
println!("No OCSP responders found.");
}
println!("Serial number : {}", certificate.serial);
}
fn print_general_name(general_name: &GeneralName) {
match general_name {
GeneralName::DNSName(name) => println!("DNSName: {}", name),
GeneralName::IPAddress(ip) => println!("IPAddress: {:?}", ip),
GeneralName::URI(uri) => println!("URI: {}", uri),
GeneralName::RFC822Name(email) => println!("Email: {}", email),
_ => println!("Other GeneralName: {:?}", general_name),
}
}