apimock_server/tls.rs
1use std::path::PathBuf;
2
3use rustls::pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject};
4
5use crate::error::{ServerError, ServerResult, TlsKind};
6
7/// Load TLS/SSL certificates (leaf + any intermediates) from a PEM file.
8///
9/// # Why this returns `AppResult` instead of panicking
10///
11/// In 4.6.x this function used `.expect(...)` and panicked on any read
12/// error. Because TLS paths come from user config, a typo was enough to
13/// abort the process with a backtrace. Returning `AppResult` lets the
14/// caller surface a single readable line and exit cleanly.
15pub fn load_certs(file_path: &str) -> ServerResult<Vec<CertificateDer<'static>>> {
16 let path = PathBuf::from(file_path);
17
18 // Collecting eagerly means a PEM parse error in the middle of the
19 // file surfaces before we try to hand the cert chain to rustls —
20 // the later rustls error would be much less descriptive.
21 let iter = CertificateDer::pem_file_iter(file_path).map_err(|e| ServerError::TlsLoad {
22 kind: TlsKind::Certificate,
23 path: path.clone(),
24 reason: e.to_string(),
25 })?;
26
27 let mut certs = Vec::new();
28 for (idx, item) in iter.enumerate() {
29 let cert = item.map_err(|e| ServerError::TlsLoad {
30 kind: TlsKind::Certificate,
31 path: path.clone(),
32 reason: format!("failed to parse certificate #{}: {}", idx + 1, e),
33 })?;
34 certs.push(cert);
35 }
36
37 if certs.is_empty() {
38 return Err(ServerError::TlsLoad {
39 kind: TlsKind::Certificate,
40 path,
41 reason: "no certificates found in PEM file".to_owned(),
42 });
43 }
44
45 Ok(certs)
46}
47
48/// Load a TLS/SSL private key from a PEM file.
49///
50/// Accepts any PEM-encoded key format that `rustls-pki-types` understands
51/// (PKCS#8, PKCS#1, SEC1). We don't narrow the accepted format because
52/// rustls itself is tolerant and picking one here would just make the
53/// error message less obvious ("wrong format" vs. the real "file missing").
54pub fn load_private_key(file_path: &str) -> ServerResult<PrivateKeyDer<'static>> {
55 PrivateKeyDer::from_pem_file(file_path).map_err(|e| ServerError::TlsLoad {
56 kind: TlsKind::PrivateKey,
57 path: PathBuf::from(file_path),
58 reason: e.to_string(),
59 })
60}