use digest::Digest;
use serde::Serialize;
use sha1::Sha1;
use sha2::Sha256;
use x509_parser::prelude::*;
#[derive(Serialize)]
pub struct CertInfo {
pub subject: String,
pub issuer: String,
pub serial: String,
pub not_before: String,
pub not_after: String,
pub is_expired: bool,
pub san: Vec<String>,
pub key_algorithm: String,
pub sig_algorithm: String,
pub fingerprint_sha256: String,
pub fingerprint_sha1: String,
pub is_ca: bool,
}
fn colon_hex(bytes: &[u8]) -> String {
bytes
.iter()
.map(|b| format!("{b:02X}"))
.collect::<Vec<_>>()
.join(":")
}
fn oid_name(oid: &oid_registry::Oid) -> String {
x509_parser::objects::oid_registry()
.get(oid)
.map(|entry| entry.sn().to_string())
.unwrap_or_else(|| oid.to_string())
}
#[tauri::command]
pub fn inspect_certificate(pem: String) -> Result<CertInfo, String> {
let (_, pem_block) = parse_x509_pem(pem.as_bytes()).map_err(|e| e.to_string())?;
let cert = pem_block.parse_x509().map_err(|e| e.to_string())?;
let san = cert
.subject_alternative_name()
.ok()
.flatten()
.map(|ext| {
ext.value
.general_names
.iter()
.map(|name| match name {
GeneralName::DNSName(s) => s.to_string(),
GeneralName::IPAddress(ip) => format!("{ip:?}"),
GeneralName::RFC822Name(s) => s.to_string(),
GeneralName::URI(s) => s.to_string(),
other => format!("{other:?}"),
})
.collect()
})
.unwrap_or_default();
let key_algorithm = match cert.public_key().parsed() {
Ok(pk) => {
let kind = match &pk {
x509_parser::public_key::PublicKey::RSA(_) => "RSA",
x509_parser::public_key::PublicKey::EC(_) => "EC",
x509_parser::public_key::PublicKey::DSA(_) => "DSA",
_ => "Unknown",
};
let bits = pk.key_size();
if bits > 0 {
format!("{kind} {bits} bits")
} else {
kind.to_string()
}
}
Err(_) => oid_name(&cert.public_key().algorithm.algorithm),
};
let raw = cert.as_raw();
Ok(CertInfo {
subject: cert.subject().to_string(),
issuer: cert.issuer().to_string(),
serial: cert.raw_serial_as_string(),
not_before: cert.validity().not_before.to_string(),
not_after: cert.validity().not_after.to_string(),
is_expired: !cert.validity().is_valid(),
san,
key_algorithm,
sig_algorithm: oid_name(&cert.signature_algorithm.algorithm),
fingerprint_sha256: colon_hex(&Sha256::digest(raw)),
fingerprint_sha1: colon_hex(&Sha1::digest(raw)),
is_ca: cert.is_ca(),
})
}