Files
fileshare-app/src-bak/tls.rs
T
2026-10-22 20:45:13 +03:00

139 lines
4.7 KiB
Rust

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<RwLock<HashMap<String, String>>>,
provider: Arc<CryptoProvider>,
}
impl PinnedCertVerifier {
pub fn new(trusted_fingerprints: Arc<RwLock<HashMap<String, String>>>) -> 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<ServerCertVerified, TlsError> {
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<HandshakeSignatureValid, TlsError> {
verify_tls12_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TlsError> {
verify_tls13_signature(
message,
cert,
dss,
&self.provider.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.provider
.signature_verification_algorithms
.supported_schemes()
}
}
/// Генерирует самоподписанный сертификат и приватный ключ в ОЗУ
pub fn generate_in_memory_cert(ip: &str) -> (Vec<u8>, Vec<u8>, 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<RwLock<HashMap<String, String>>>,
) -> 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-клиента")
}