use std::fs;
use std::io::BufReader;
use std::sync::Arc;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use crate::error::HttpError;
pub fn server_config(
cert_file: &str,
key_file: &str,
) -> Result<Arc<rustls::ServerConfig>, HttpError> {
let certs = load_certs(cert_file)?;
let key = load_private_key(key_file)?;
let cfg = rustls::ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| HttpError::Tls(e.to_string()))?;
Ok(Arc::new(cfg))
}
pub fn default_client_config() -> Arc<rustls::ClientConfig> {
use std::sync::OnceLock;
static CFG: OnceLock<Arc<rustls::ClientConfig>> = OnceLock::new();
Arc::clone(CFG.get_or_init(|| {
let mut roots = rustls::RootCertStore::empty();
roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
Arc::new(
rustls::ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth(),
)
}))
}
pub fn client_config_with_ca(ca_file: &str) -> Result<Arc<rustls::ClientConfig>, HttpError> {
let mut roots = rustls::RootCertStore::empty();
roots.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
let ca_certs = load_certs(ca_file)?;
for cert in ca_certs {
roots.add(cert).map_err(|e| HttpError::Tls(e.to_string()))?;
}
Ok(Arc::new(
rustls::ClientConfig::builder()
.with_root_certificates(roots)
.with_no_client_auth(),
))
}
fn load_certs(path: &str) -> Result<Vec<CertificateDer<'static>>, HttpError> {
let f = fs::File::open(path).map_err(HttpError::Io)?;
let mut r = BufReader::new(f);
rustls_pemfile::certs(&mut r)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| HttpError::Tls(e.to_string()))
}
fn load_private_key(path: &str) -> Result<PrivateKeyDer<'static>, HttpError> {
let f = fs::File::open(path).map_err(HttpError::Io)?;
let mut r = BufReader::new(f);
rustls_pemfile::private_key(&mut r)
.map_err(|e| HttpError::Tls(e.to_string()))?
.ok_or_else(|| HttpError::Tls(format!("no private key found in {path}")))
}