rustls_native_certs/
lib.rs

1//! rustls-native-certs allows rustls to use the platform's native certificate
2//! store when operating as a TLS client.
3//!
4//! It provides a single function [`load_native_certs()`], which returns a
5//! collection of certificates found by reading the platform-native
6//! certificate store.
7//!
8//! If the SSL_CERT_FILE environment variable is set, certificates (in PEM
9//! format) are read from that file instead.
10//!
11//! [`Certificate`] here is just a marker newtype that denotes a DER-encoded
12//! X.509 certificate encoded as a `Vec<u8>`.
13//!
14//! If you want to load these certificates into a `rustls::RootCertStore`,
15//! you'll likely want to do something like this:
16//!
17//! ```no_run
18//! let mut roots = rustls::RootCertStore::empty();
19//! for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") {
20//!     roots
21//!         .add(&rustls::Certificate(cert.0))
22//!         .unwrap();
23//! }
24//! ```
25
26#[cfg(all(unix, not(target_os = "macos")))]
27mod unix;
28#[cfg(all(unix, not(target_os = "macos")))]
29use unix as platform;
30
31#[cfg(windows)]
32mod windows;
33#[cfg(windows)]
34use windows as platform;
35
36#[cfg(target_os = "macos")]
37mod macos;
38#[cfg(target_os = "macos")]
39use macos as platform;
40
41use std::env;
42use std::fs::File;
43use std::io::BufReader;
44use std::io::{Error, ErrorKind};
45use std::path::{Path, PathBuf};
46
47/// Load root certificates found in the platform's native certificate store.
48///
49/// If the SSL_CERT_FILE environment variable is set, certificates (in PEM
50/// format) are read from that file instead.
51///
52/// This function fails in a platform-specific way, expressed in a `std::io::Error`.
53///
54/// This function can be expensive: on some platforms it involves loading
55/// and parsing a ~300KB disk file.  It's therefore prudent to call
56/// this sparingly.
57pub fn load_native_certs() -> Result<Vec<Certificate>, Error> {
58    load_certs_from_env().unwrap_or_else(platform::load_native_certs)
59}
60
61/// A newtype representing a single DER-encoded X.509 certificate encoded as a `Vec<u8>`.
62pub struct Certificate(pub Vec<u8>);
63
64impl AsRef<[u8]> for Certificate {
65    fn as_ref(&self) -> &[u8] {
66        &self.0
67    }
68}
69
70const ENV_CERT_FILE: &str = "SSL_CERT_FILE";
71
72/// Returns None if SSL_CERT_FILE is not defined in the current environment.
73///
74/// If it is defined, it is always used, so it must be a path to a real
75/// file from which certificates can be loaded successfully.
76fn load_certs_from_env() -> Option<Result<Vec<Certificate>, Error>> {
77    let cert_var_path = PathBuf::from(env::var_os(ENV_CERT_FILE)?);
78
79    Some(load_pem_certs(&cert_var_path))
80}
81
82fn load_pem_certs(path: &Path) -> Result<Vec<Certificate>, Error> {
83    let f = File::open(path)?;
84    let mut f = BufReader::new(f);
85
86    match rustls_pemfile::certs(&mut f) {
87        Ok(contents) => Ok(contents
88            .into_iter()
89            .map(Certificate)
90            .collect()),
91        Err(err) => Err(Error::new(
92            ErrorKind::InvalidData,
93            format!("Could not load PEM file {path:?}: {err}"),
94        )),
95    }
96}