use crate::tls::{nid::Nid, pkey::PKey, pkey::Private, x509::X509};
use crate::Result;
use pingora_error::{ErrorType::*, OrErr};
use std::hash::{Hash, Hasher};
fn get_subject_name(cert: &X509, name_type: Nid) -> Option<String> {
cert.subject_name()
.entries_by_nid(name_type)
.next()
.map(|name| {
name.data()
.as_utf8()
.map(|s| s.to_string())
.unwrap_or_default()
})
}
pub fn get_organization(cert: &X509) -> Option<String> {
get_subject_name(cert, Nid::ORGANIZATIONNAME)
}
pub fn get_common_name(cert: &X509) -> Option<String> {
get_subject_name(cert, Nid::COMMONNAME)
}
pub fn get_organization_unit(cert: &X509) -> Option<String> {
get_subject_name(cert, Nid::ORGANIZATIONALUNITNAME)
}
pub fn get_serial(cert: &X509) -> Result<String> {
let bn = cert
.serial_number()
.to_bn()
.or_err(InvalidCert, "Invalid serial")?;
let hex = bn.to_hex_str().or_err(InvalidCert, "Invalid serial")?;
let hex_str: &str = hex.as_ref();
Ok(hex_str.to_owned())
}
#[derive(Clone)]
pub struct CertKey {
certificates: Vec<X509>,
key: PKey<Private>,
}
impl CertKey {
pub fn new(certificates: Vec<X509>, key: PKey<Private>) -> CertKey {
assert!(
!certificates.is_empty(),
"expected a non-empty vector of certificates in CertKey::new"
);
CertKey { certificates, key }
}
pub fn leaf(&self) -> &X509 {
&self.certificates[0]
}
pub fn key(&self) -> &PKey<Private> {
&self.key
}
pub fn intermediates(&self) -> &[X509] {
if self.certificates.len() <= 1 {
return &[];
}
&self.certificates[1..]
}
pub fn organization(&self) -> Option<String> {
get_organization(self.leaf())
}
pub fn serial(&self) -> Result<String> {
get_serial(self.leaf())
}
}
impl Hash for CertKey {
fn hash<H: Hasher>(&self, state: &mut H) {
for certificate in &self.certificates {
if let Ok(serial) = get_serial(certificate) {
serial.hash(state)
}
}
}
}
impl std::fmt::Debug for CertKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CertKey")
.field("X509", &self.leaf())
.finish()
}
}
impl std::fmt::Display for CertKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let leaf = self.leaf();
if let Some(cn) = get_common_name(leaf) {
write!(f, "CN: {cn},")?;
} else if let Some(org_unit) = get_organization_unit(leaf) {
write!(f, "Org Unit: {org_unit},")?;
}
write!(f, ", expire: {}", leaf.not_after())
}
}