use digest::Digest; use rustls::Certificate; use sha2::Sha256; use std::fmt::{Write, self}; use x509_parser::prelude::*; pub trait Fingerprint { type Error; fn fingerprint(&self) -> Result<(String, String), Self::Error>; } #[derive(Clone, Debug, PartialEq)] pub enum Error { Fmt, X509(X509Error), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Fmt => write!(f, "FingerPrint: format error"), Self::X509(e) => write!(f, "FingerPrint: {e}"), } } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::Fmt => None, Self::X509(e) => Some(e), } } } impl From for Error { fn from(_value: fmt::Error) -> Self { Self::Fmt } } impl From> for Error { fn from(value: x509_parser::nom::Err) -> Self { Self::X509(value.into()) } } impl Fingerprint for Certificate { type Error = Error; fn fingerprint(&self) -> Result<(String, String), Self::Error> { let (_, pk) = X509Certificate::from_der(self.as_ref())?; let subject = pk.subject().to_string(); let key = pk.public_key().subject_public_key.as_ref(); let mut hasher = Sha256::new(); hasher.update(key); let res = hasher.finalize(); let mut s = String::with_capacity(res.len()); for c in res { write!(s, "{c:02x}")?; } Ok((subject[3..].to_string(), s)) } }