use rcgen::{DistinguishedName, DnType}; use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; use rustls::{DigitallySignedStruct, Error, SignatureScheme}; use sha2::{Digest, Sha256}; use x509_parser::prelude::*; /// SHA256 fingerprint (truncated to 16 bytes by default) pub fn fingerprint(cert: &CertificateDer<'_>, full: bool) -> String { let hash = Sha256::digest(cert.as_ref()); if full { hex::encode(hash) } else { hex::encode(&hash[..16]) } } pub fn dn(cn: &str, org: &str) -> DistinguishedName { let mut d = DistinguishedName::new(); d.push(DnType::CommonName, cn); d.push(DnType::OrganizationName, org); d.push(DnType::CountryName, "US"); d } /// Parse certs from PEM pub fn parse_pem(pem: &str) -> Result>, std::io::Error> { rustls_pemfile::certs(&mut pem.as_bytes()).collect() } fn schemes() -> Vec { rustls::crypto::ring::default_provider() .signature_verification_algorithms .supported_schemes() } /// Macro to implement the boilerplate verifier methods macro_rules! impl_verifier_base { () => { fn verify_tls12_signature(&self, _: &[u8], _: &CertificateDer<'_>, _: &DigitallySignedStruct) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn verify_tls13_signature(&self, _: &[u8], _: &CertificateDer<'_>, _: &DigitallySignedStruct) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn supported_verify_schemes(&self) -> Vec { schemes() } }; } /// Accepts all certificates, logs info #[derive(Debug)] pub struct LoggingVerifier; impl ServerCertVerifier for LoggingVerifier { fn verify_server_cert(&self, cert: &CertificateDer<'_>, intermediates: &[CertificateDer<'_>], _: &ServerName<'_>, _: &[u8], _: UnixTime) -> Result { println!("\n=== Server Certificate ==="); match X509Certificate::from_der(cert.as_ref()) { Ok((_, x)) => { println!("Subject: {}", x.subject()); println!("Issuer: {}", x.issuer()); println!("SHA256: {}", fingerprint(cert, true)); if x.subject() == x.issuer() { println!("Type: Self-Signed"); } } Err(e) => println!("Parse failed: {}", e), } for (i, c) in intermediates.iter().enumerate() { if let Ok((_, x)) = X509Certificate::from_der(c.as_ref()) { println!("Intermediate #{}: {}", i + 1, x.subject()); } } println!("Chain length: {}\n", 1 + intermediates.len()); Ok(ServerCertVerified::assertion()) } impl_verifier_base!(); }