Skip to main content

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}