#[cfg(any(feature = "tokio-native-tls", feature = "smol-native-tls"))]
use async_native_tls::{Certificate as NativeCertificate, Identity as NativeIdentity};
#[cfg(any(
feature = "tokio-rust-tls",
feature = "smol-rust-tls",
feature = "compio-rust-tls"
))]
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
#[derive(Debug, Clone)]
pub enum ContentEncoding {
PEM,
DER,
}
#[derive(Debug, Clone)]
pub struct Identity {
cert: Vec<u8>,
key: Option<Vec<u8>>,
#[allow(unused)]
password: Option<String>,
#[allow(unused)]
encoding: Option<ContentEncoding>,
}
#[deprecated(note = "Use `Identity` instead")]
pub type ClientCert = Identity;
impl Identity {
#[cfg(any(feature = "tokio-native-tls", feature = "smol-native-tls"))]
pub fn from_pkcs12(bundle: &[u8], password: Option<String>) -> Self {
Identity { cert: bundle.to_vec(), key: None, password, encoding: None }
}
#[cfg(any(feature = "tokio-native-tls", feature = "smol-native-tls"))]
pub fn from_pkcs12_file(file: &str, password: Option<String>) -> std::io::Result<Self> {
let data = std::fs::read(file)?;
Ok(Identity { cert: data, key: None, password, encoding: None })
}
pub fn from_pkcs8(cert: &[u8], key: &[u8], encoding: ContentEncoding) -> Self {
Identity {
cert: cert.to_vec(),
key: Some(key.to_vec()),
password: None,
encoding: Some(encoding),
}
}
pub fn from_pkcs8_file(
cert: &str,
key: &str,
encoding: ContentEncoding,
) -> std::io::Result<Self> {
let cert = std::fs::read(cert)?;
let key = std::fs::read(key)?;
Ok(Identity { cert, key: Some(key), password: None, encoding: Some(encoding) })
}
}
#[cfg(any(feature = "tokio-rust-tls", feature = "smol-rust-tls", feature = "compio-rust-tls"))]
impl TryFrom<&Identity> for (CertificateDer<'static>, PrivateKeyDer<'static>) {
type Error = std::io::Error;
fn try_from(value: &Identity) -> std::result::Result<Self, Self::Error> {
let cert = value.cert.clone();
let key = value
.key
.as_ref()
.unwrap()
.clone();
let pair = match value.encoding {
Some(ContentEncoding::DER) => {
let cert = CertificateDer::from(cert);
let key = PrivateKeyDer::try_from(key);
if key.is_err() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid certificate",
));
}
(cert, key.unwrap())
}
Some(ContentEncoding::PEM) => {
use rustls_pki_types::pem::PemObject;
let cert = CertificateDer::from_pem_slice(&cert);
if let Err(e) = cert {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Invalid certificate: {}", e),
));
}
let key = PrivateKeyDer::from_pem_slice(&key);
if let Err(e) = key {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Invalid certificate: {}", e),
));
}
(cert.unwrap(), key.unwrap())
}
None => {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid certificate",
));
}
};
Ok(pair)
}
}
#[cfg(any(
feature = "tokio-native-tls",
feature = "smol-native-tls",
feature = "compio-native-tls"
))]
impl TryFrom<&Identity> for NativeIdentity {
type Error = std::io::Error;
fn try_from(value: &Identity) -> std::result::Result<Self, Self::Error> {
let identity = if let Some(password) = &value.password {
let identity = NativeIdentity::from_pkcs12(&value.cert, password);
if identity.is_err() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid certificate",
));
}
identity.unwrap()
} else if let Some(key) = &value.key {
let identity = NativeIdentity::from_pkcs8(&value.cert, key);
if identity.is_err() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Invalid certificate",
));
}
identity.unwrap()
} else {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"You need provide a password or a key",
));
};
Ok(identity)
}
}
#[derive(Debug, Clone)]
pub struct Certificate {
data: Vec<u8>,
encoding: ContentEncoding,
}
impl Certificate {
pub fn from_slice(data: &[u8], encoding: ContentEncoding) -> Self {
Certificate { data: data.to_vec(), encoding }
}
pub fn from_file(file: &str, encoding: ContentEncoding) -> std::io::Result<Self> {
let data = std::fs::read(file)?;
Ok(Certificate { data, encoding })
}
#[inline]
pub fn as_bytes(&self) -> &Vec<u8> {
&self.data
}
}
#[cfg(any(feature = "tokio-rust-tls", feature = "smol-rust-tls", feature = "compio-rust-tls"))]
impl TryFrom<&Certificate> for CertificateDer<'static> {
type Error = std::io::Error;
fn try_from(value: &Certificate) -> std::result::Result<Self, Self::Error> {
let cert = match value.encoding {
ContentEncoding::DER => CertificateDer::from(
value
.as_bytes()
.to_vec(),
),
ContentEncoding::PEM => {
use rustls_pki_types::pem::PemObject;
let result = CertificateDer::from_pem_slice(value.as_bytes());
if let Err(e) = result {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Invalid certificate: {}", e),
));
}
result.unwrap()
}
};
Ok(cert)
}
}
#[cfg(any(
feature = "tokio-native-tls",
feature = "smol-native-tls",
feature = "compio-native-tls"
))]
impl TryFrom<&Certificate> for NativeCertificate {
type Error = std::io::Error;
fn try_from(value: &Certificate) -> std::result::Result<Self, Self::Error> {
let cert = match value.encoding {
ContentEncoding::DER => NativeCertificate::from_der(value.as_bytes()),
ContentEncoding::PEM => NativeCertificate::from_pem(value.as_bytes()),
};
if let Err(e) = cert {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Invalid certificate: {}", e),
));
}
Ok(cert.unwrap())
}
}