diff --git a/src/transport/smtp/client/async_connection.rs b/src/transport/smtp/client/async_connection.rs index f265b95..cdbdec5 100644 --- a/src/transport/smtp/client/async_connection.rs +++ b/src/transport/smtp/client/async_connection.rs @@ -373,4 +373,10 @@ impl AsyncSmtpConnection { pub fn peer_certificate(&self) -> Result, Error> { self.stream.get_ref().peer_certificate() } + + /// All the X509 certificates of the chain (DER encoded) + #[cfg(any(feature = "rustls-tls", feature = "boring-tls"))] + pub fn certificate_chain(&self) -> Result>, Error> { + self.stream.get_ref().certificate_chain() + } } diff --git a/src/transport/smtp/client/async_net.rs b/src/transport/smtp/client/async_net.rs index f4eadb6..b9e89c5 100644 --- a/src/transport/smtp/client/async_net.rs +++ b/src/transport/smtp/client/async_net.rs @@ -431,6 +431,48 @@ impl AsyncNetworkStream { } } + pub fn certificate_chain(&self) -> Result>, Error> { + match &self.inner { + #[cfg(feature = "tokio1")] + InnerAsyncNetworkStream::Tokio1Tcp(_) => { + Err(error::client("Connection is not encrypted")) + } + #[cfg(feature = "tokio1-native-tls")] + InnerAsyncNetworkStream::Tokio1NativeTls(_) => panic!("Unsupported"), + #[cfg(feature = "tokio1-rustls-tls")] + InnerAsyncNetworkStream::Tokio1RustlsTls(stream) => Ok(stream + .get_ref() + .1 + .peer_certificates() + .unwrap() + .iter() + .map(|c| c.to_vec()) + .collect()), + #[cfg(feature = "tokio1-boring-tls")] + InnerAsyncNetworkStream::Tokio1BoringTls(stream) => Ok(stream + .ssl() + .peer_cert_chain() + .unwrap() + .iter() + .map(|c| c.to_der().map_err(error::tls)) + .collect::, _>>()?), + #[cfg(feature = "async-std1")] + InnerAsyncNetworkStream::AsyncStd1Tcp(_) => { + Err(error::client("Connection is not encrypted")) + } + #[cfg(feature = "async-std1-rustls-tls")] + InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream) => Ok(stream + .get_ref() + .1 + .peer_certificates() + .unwrap() + .iter() + .map(|c| c.to_vec()) + .collect()), + InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"), + } + } + pub fn peer_certificate(&self) -> Result, Error> { match &self.inner { #[cfg(feature = "tokio1")] diff --git a/src/transport/smtp/client/connection.rs b/src/transport/smtp/client/connection.rs index b3dc62f..0e6ebdb 100644 --- a/src/transport/smtp/client/connection.rs +++ b/src/transport/smtp/client/connection.rs @@ -307,4 +307,10 @@ impl SmtpConnection { pub fn peer_certificate(&self) -> Result, Error> { self.stream.get_ref().peer_certificate() } + + /// All the X509 certificates of the chain (DER encoded) + #[cfg(any(feature = "rustls-tls", feature = "boring-tls"))] + pub fn certificate_chain(&self) -> Result>, Error> { + self.stream.get_ref().certificate_chain() + } } diff --git a/src/transport/smtp/client/net.rs b/src/transport/smtp/client/net.rs index 992fab5..ac9f588 100644 --- a/src/transport/smtp/client/net.rs +++ b/src/transport/smtp/client/net.rs @@ -223,6 +223,32 @@ impl NetworkStream { } } + #[cfg(any(feature = "rustls-tls", feature = "boring-tls"))] + pub fn certificate_chain(&self) -> Result>, Error> { + match &self.inner { + InnerNetworkStream::Tcp(_) => Err(error::client("Connection is not encrypted")), + #[cfg(feature = "native-tls")] + InnerNetworkStream::NativeTls(_) => panic!("Unsupported"), + #[cfg(feature = "rustls-tls")] + InnerNetworkStream::RustlsTls(stream) => Ok(stream + .conn + .peer_certificates() + .unwrap() + .iter() + .map(|c| c.to_vec()) + .collect()), + #[cfg(feature = "boring-tls")] + InnerNetworkStream::BoringTls(stream) => Ok(stream + .ssl() + .peer_cert_chain() + .unwrap() + .iter() + .map(|c| c.to_der().map_err(error::tls)) + .collect::, _>>()?), + InnerNetworkStream::None => panic!("InnerNetworkStream::None must never be built"), + } + } + #[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))] pub fn peer_certificate(&self) -> Result, Error> { match &self.inner {