rmqtt_net/
cert.rs

1#[cfg(feature = "ws")]
2use crate::ws::WsStream;
3use std::fmt;
4
5/// TLS certificate information extracted from peer
6#[derive(Debug, Clone, Default)]
7pub struct CertInfo {
8    /// Common Name from certificate subject
9    pub common_name: Option<String>,
10    /// Full subject distinguished name
11    pub subject: String,
12    /// Certificate serial number
13    pub serial: Option<String>,
14    /// Organization
15    pub organization: Option<String>,
16}
17
18impl CertInfo {
19    pub fn new() -> Self {
20        Self::default()
21    }
22}
23
24impl fmt::Display for CertInfo {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        write!(f, "CN: {:?}, Subject: {}, Org: {:?}", self.common_name, self.subject, self.organization)
27    }
28}
29
30/// Trait for extracting TLS certificate information from streams
31pub trait TlsCertExtractor {
32    fn extract_cert_info(&self) -> Option<CertInfo>;
33}
34
35// Implementation for TLS streams
36#[cfg(feature = "tls")]
37impl<S> TlsCertExtractor for tokio_rustls::server::TlsStream<S>
38where
39    S: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
40{
41    fn extract_cert_info(&self) -> Option<CertInfo> {
42        use x509_parser::parse_x509_certificate;
43
44        let (_, session) = self.get_ref();
45        let certs = session.peer_certificates()?;
46        let cert = certs.first()?;
47
48        let (_, parsed) = parse_x509_certificate(cert.as_ref()).ok()?;
49
50        let common_name =
51            parsed.subject().iter_common_name().next().and_then(|cn| cn.as_str().ok()).map(|s| s.to_string());
52
53        let organization = parsed
54            .subject()
55            .iter_organization()
56            .next()
57            .and_then(|org| org.as_str().ok())
58            .map(|s| s.to_string());
59
60        let subject = parsed.subject().to_string();
61
62        let serial = parsed.serial.to_str_radix(16);
63
64        Some(CertInfo { common_name, subject, serial: Some(serial), organization })
65    }
66}
67
68// Default implementation for non-TLS streams (TCP)
69impl TlsCertExtractor for tokio::net::TcpStream {
70    fn extract_cert_info(&self) -> Option<CertInfo> {
71        None
72    }
73}
74
75#[cfg(feature = "ws")]
76impl TlsCertExtractor for WsStream<tokio::net::TcpStream> {
77    fn extract_cert_info(&self) -> Option<CertInfo> {
78        None
79    }
80}
81
82#[cfg(feature = "ws")]
83#[cfg(feature = "tls")]
84impl TlsCertExtractor for WsStream<tokio_rustls::server::TlsStream<tokio::net::TcpStream>> {
85    fn extract_cert_info(&self) -> Option<CertInfo> {
86        self.get_inner().get_ref().extract_cert_info()
87    }
88}