version a0.0.1
This commit is contained in:
commit
9469ef10b6
19 files changed
+4281
No files matched your search
+138
@@ -0,0 +1,138 @@
|
||||
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-клиента")
|
||||
}
|
||||
Reference in new issue
Block a user