use std::sync::Arc; use rustls::{ client::{ServerCertVerifier, WebPkiVerifier}, Certificate, ClientConfig, RootCertStore, ServerName, }; pub struct WhitelistVerifier { verifier: WebPkiVerifier, dns_names: [&'static str; N], } /// Custom verifier that allow hostname difference with specified dns names. impl WhitelistVerifier { pub fn new(dns_names: [&'static str; N]) -> Self { use rustls::OwnedTrustAnchor; let mut root_cert_store = RootCertStore::empty(); let trust_anchors = webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|trust_anchor| { OwnedTrustAnchor::from_subject_spki_name_constraints( trust_anchor.subject, trust_anchor.spki, trust_anchor.name_constraints, ) }); root_cert_store.add_server_trust_anchors(trust_anchors); let verifier = WebPkiVerifier::new(root_cert_store, None); Self { verifier, dns_names, } } } impl From> for ClientConfig { fn from(v: WhitelistVerifier) -> Self { let mut cfg = ClientConfig::builder() .with_safe_defaults() .with_root_certificates(RootCertStore::empty()) .with_no_client_auth(); cfg.dangerous().set_certificate_verifier(Arc::new(v)); cfg } } impl ServerCertVerifier for WhitelistVerifier { fn verify_server_cert( &self, end_entity: &Certificate, intermediates: &[Certificate], server_name: &rustls::ServerName, scts: &mut dyn Iterator, ocsp_response: &[u8], now: std::time::SystemTime, ) -> Result { let original_validate_result = self.verifier.verify_server_cert( end_entity, intermediates, server_name, scts, ocsp_response, now, ); if original_validate_result.is_ok() { return original_validate_result; } for dns_name in self.dns_names.iter() { if let Ok(dns_name) = ServerName::try_from(*dns_name) { let whitelist_validate_result = self.verifier.verify_server_cert( end_entity, intermediates, &dns_name, scts, ocsp_response, now, ); if whitelist_validate_result.is_ok() { return whitelist_validate_result; } } } original_validate_result } }