use rcgen::{generate_simple_self_signed, CertifiedKey}; use rustls::{ client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}, pki_types::{CertificateDer, ServerName, UnixTime}, ClientConfig, DigitallySignedStruct, Error as TlsError, SignatureScheme, }; use sha2::{Digest, Sha256}; use std::{ collections::HashMap, sync::{Arc, RwLock}, time::Duration, }; #[derive(Debug)] pub struct PinnedCertVerifier { trusted_fingerprints: Arc>>, provider: Arc, } impl PinnedCertVerifier { pub fn new(trusted_fingerprints: Arc>>) -> Self { Self { trusted_fingerprints, provider: Arc::new(rustls::crypto::ring::default_provider()), } } fn cert_fingerprint(cert: &CertificateDer<'_>) -> String { let mut hasher = Sha256::new(); hasher.update(cert.as_ref()); hex::encode(hasher.finalize()) } } impl ServerCertVerifier for PinnedCertVerifier { fn verify_server_cert( &self, end_entity: &CertificateDer<'_>, _intermediates: &[CertificateDer<'_>], server_name: &ServerName<'_>, _ocsp_response: &[u8], _now: UnixTime, ) -> Result { let actual_fp = Self::cert_fingerprint(end_entity); let host = match server_name { ServerName::DnsName(name) => name.as_ref().to_string(), ServerName::IpAddress(ip) => std::net::IpAddr::from(*ip).to_string(), _ => return Err(TlsError::General("Неподдерживаемое имя узла".into())), }; let trusted = self.trusted_fingerprints.read().unwrap(); match trusted.get(&host) { Some(expected_fp) if expected_fp.eq_ignore_ascii_case(&actual_fp) => { Ok(ServerCertVerified::assertion()) } Some(_) => Err(TlsError::General(format!( "ВНИМАНИЕ: Сертификат узла {} не совпадает с заверенным!", host ))), None => Err(TlsError::General(format!( "Узел {} не заверен! Нажмите «Заверить» в списке устройств.", host ))), } } fn verify_tls12_signature( &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct, ) -> Result { verify_tls12_signature( message, cert, dss, &self.provider.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, message: &[u8], cert: &CertificateDer<'_>, dss: &DigitallySignedStruct, ) -> Result { verify_tls13_signature( message, cert, dss, &self.provider.signature_verification_algorithms, ) } fn supported_verify_schemes(&self) -> Vec { self.provider .signature_verification_algorithms .supported_schemes() } } /// Генерирует самоподписанный сертификат и приватный ключ в ОЗУ pub fn generate_in_memory_cert(ip: &str) -> (Vec, Vec, String) { let san = vec!["localhost".to_string(), ip.to_string()]; let CertifiedKey { cert, key_pair } = generate_simple_self_signed(san).expect("Ошибка генерации сертификата"); let cert_der = cert.der().to_vec(); let key_der = key_pair.serialize_der(); let mut hasher = Sha256::new(); hasher.update(&cert_der); let fingerprint = hex::encode(hasher.finalize()); (cert_der, key_der, fingerprint) } /// Создает преднастроенный HTTPS-клиент с кастомным верификатором pub fn build_pinned_http_client( trusted_fingerprints: Arc>>, ) -> reqwest::Client { let verifier = Arc::new(PinnedCertVerifier::new(trusted_fingerprints)); let rustls_client_config = ClientConfig::builder_with_provider(Arc::new( rustls::crypto::ring::default_provider(), )) .with_safe_default_protocol_versions() .unwrap() .dangerous() .with_custom_certificate_verifier(verifier) .with_no_client_auth(); reqwest::Client::builder() .use_preconfigured_tls(rustls_client_config) .timeout(Duration::from_secs(30)) .build() .expect("Ошибка создания HTTPS-клиента") }