use crate::connector::ConnectorOptions;
use crate::tls;
use std::fs::File;
use std::io::{self, BufReader, ErrorKind};
use std::path::PathBuf;
use tokio_rustls::rustls::{self, Certificate, OwnedTrustAnchor, PrivateKey};
use tokio_rustls::webpki::TrustAnchor;
pub(crate) async fn load_certs(path: PathBuf) -> io::Result<Vec<Certificate>> {
tokio::task::spawn_blocking(move || {
let file = std::fs::File::open(path)?;
let mut reader = BufReader::new(file);
let certs = rustls_pemfile::certs(&mut reader)?
.iter()
.map(|v| Certificate(v.clone()))
.collect();
Ok(certs)
})
.await?
}
pub(crate) async fn load_key(path: PathBuf) -> io::Result<PrivateKey> {
tokio::task::spawn_blocking(move || {
let file = std::fs::File::open(path)?;
let mut reader = BufReader::new(file);
loop {
match rustls_pemfile::read_one(&mut reader)? {
Some(rustls_pemfile::Item::RSAKey(key))
| Some(rustls_pemfile::Item::PKCS8Key(key))
| Some(rustls_pemfile::Item::ECKey(key)) => return Ok(PrivateKey(key)),
Some(rustls_pemfile::Item::X509Certificate(_)) | Some(_) => {}
None => break,
}
}
Err(io::Error::new(
ErrorKind::NotFound,
"could not find client key in the path",
))
})
.await?
}
pub(crate) async fn config_tls(options: &ConnectorOptions) -> io::Result<rustls::ClientConfig> {
let mut root_store = rustls::RootCertStore::empty();
for cert in rustls_native_certs::load_native_certs().map_err(|err| {
io::Error::new(
ErrorKind::Other,
format!("could not load platform certs: {}", err),
)
})? {
root_store
.add(&rustls::Certificate(cert.0))
.map_err(|err| {
io::Error::new(
ErrorKind::Other,
format!("failed to read root certificates: {}", err),
)
})?;
}
let tls_config = {
if let Some(config) = &options.tls_client_config {
Ok(config.to_owned())
} else {
for cafile in &options.certificates {
let mut pem = BufReader::new(File::open(cafile)?);
let certs = rustls_pemfile::certs(&mut pem)?;
let trust_anchors = certs.iter().map(|cert| {
let ta = TrustAnchor::try_from_cert_der(&cert[..])
.map_err(|err| {
io::Error::new(
ErrorKind::InvalidInput,
format!("could not load certs: {}", err),
)
})
.unwrap();
OwnedTrustAnchor::from_subject_spki_name_constraints(
ta.subject,
ta.spki,
ta.name_constraints,
)
});
root_store.add_server_trust_anchors(trust_anchors);
}
let builder = rustls::ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(root_store);
if let Some(cert) = options.client_cert.clone() {
if let Some(key) = options.client_key.clone() {
let key = tls::load_key(key).await?;
let cert = tls::load_certs(cert).await?;
builder.with_single_cert(cert, key).map_err(|_| {
io::Error::new(ErrorKind::Other, "could not add certificate or key")
})
} else {
Err(io::Error::new(
ErrorKind::Other,
"found certificate, but no key",
))
}
} else {
Ok(builder.with_no_client_auth())
}
}
}?;
Ok(tls_config)
}