use std::fs::File;
use std::io;
use std::path::Path;
use std::sync::Arc;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::pem::PemObject;
use rustls::{ClientConfig, RootCertStore};
pub fn config_with_native_roots() -> io::Result<Arc<ClientConfig>> {
let mut roots = RootCertStore::empty();
let loaded = rustls_native_certs::load_native_certs();
for cert in loaded.certs {
let _ = roots.add(cert);
}
if roots.is_empty() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"tls: no native root certificates found",
));
}
Ok(build(roots))
}
pub fn config_with_custom_ca(ca_pem: &Path) -> io::Result<Arc<ClientConfig>> {
let mut roots = RootCertStore::empty();
let file = File::open(ca_pem).map_err(|err| {
io::Error::new(
err.kind(),
format!("tls: open ca {}: {err}", ca_pem.display()),
)
})?;
let mut added = 0;
for cert in CertificateDer::pem_reader_iter(file) {
let cert = cert.map_err(|err| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("tls: parse ca {}: {err}", ca_pem.display()),
)
})?;
roots
.add(cert)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, format!("tls: {err}")))?;
added += 1;
}
if added == 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("tls: no certificates in {}", ca_pem.display()),
));
}
Ok(build(roots))
}
fn build(roots: RootCertStore) -> Arc<ClientConfig> {
Arc::new(
ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth(),
)
}