use std::io::{Read, Write};
use std::net::TcpStream;
use std::sync::Arc;
use rustls::pki_types::ServerName;
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, WebPkiSupportedAlgorithms};
use rustls::{ClientConfig, DigitallySignedStruct, SignatureScheme};
use crate::cert_parser::{self, CertInfo, CertFormat};
#[derive(Debug)]
struct NoVerification(WebPkiSupportedAlgorithms);
impl NoVerification {
fn new() -> Self {
let algos = rustls::crypto::CryptoProvider::get_default()
.unwrap()
.signature_verification_algorithms
.clone();
NoVerification(algos)
}
}
impl ServerCertVerifier for NoVerification {
fn verify_server_cert(
&self,
_end_entity: &rustls::pki_types::CertificateDer<'_>,
_intermediates: &[rustls::pki_types::CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls12_signature(message, cert, dss, &self.0)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls::pki_types::CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
verify_tls13_signature(message, cert, dss, &self.0)
}
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
self.0.supported_schemes()
}
}
pub fn extract_cert_info(domain: &str, port: u16) -> Result<CertInfo, Box<dyn std::error::Error>> {
let config = ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoVerification::new()))
.with_no_client_auth();
let server_name = ServerName::try_from(domain.to_string())
.map_err(|_| "Invalid domain name")?;
let mut conn = rustls::ClientConnection::new(Arc::new(config), server_name)?;
let resolved = dns_lookup::lookup_host(domain)?;
let addr = resolved
.iter()
.next()
.ok_or("DNS resolution returned no addresses")?;
let sock_addr = std::net::SocketAddr::new(*addr, port);
let mut sock = TcpStream::connect_timeout(
&sock_addr,
std::time::Duration::from_secs(10),
)?;
sock.set_read_timeout(Some(std::time::Duration::from_secs(10)))?;
sock.set_write_timeout(Some(std::time::Duration::from_secs(10)))?;
let mut stream = rustls::Stream::new(&mut conn, &mut sock);
stream.write_all(b"GET / HTTP/1.1\r\nHost: ")?;
stream.write_all(domain.as_bytes())?;
stream.write_all(b"\r\nConnection: close\r\n\r\n")?;
stream.flush()?;
let mut buf = [0u8; 1];
let _ = stream.read(&mut buf);
let certs = conn.peer_certificates().ok_or("No certificates presented")?;
let leaf = certs.first().ok_or("Empty certificate chain")?;
let der = leaf.to_vec();
let pem_str = cert_parser::der_to_pem(&der);
let mut info = cert_parser::parse_x509(&der, &pem_str)?;
info.source_type = format!("TLS ({})", domain);
info.format = CertFormat::Pem;
info.der = der;
Ok(info)
}