Skip to main content

async_nats/
tls.rs

1// Copyright 2020-2022 The NATS Authors
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use crate::connector::ConnectorOptions;
15use crate::rustls::pki_types::{CertificateDer, PrivateKeyDer};
16use crate::tls;
17use rustls_pki_types::pem::PemObject;
18use std::io::{self, BufReader, ErrorKind};
19use std::path::PathBuf;
20use tokio_rustls::rustls::{ClientConfig, RootCertStore};
21
22/// Loads client certificates from a `.pem` file.
23/// If the pem file is found, but does not contain any certificates, it will return
24/// empty set of Certificates, not error.
25/// Can be used to parse only client certificates from .pem file containing both client key and certs.
26pub(crate) async fn load_certs(path: PathBuf) -> io::Result<Vec<CertificateDer<'static>>> {
27    tokio::task::spawn_blocking(move || {
28        let file = std::fs::File::open(path)?;
29        let reader = BufReader::new(file);
30        CertificateDer::pem_reader_iter(reader)
31            .collect::<Result<Vec<_>, _>>()
32            .map_err(|e| {
33                io::Error::new(
34                    ErrorKind::InvalidData,
35                    format!("could not load certificates: {}", e),
36                )
37            })
38    })
39    .await?
40}
41
42/// Loads client key from a `.pem` file.
43/// Can be used to parse only client key from .pem file containing both client key and certs.
44pub(crate) async fn load_key(path: PathBuf) -> io::Result<PrivateKeyDer<'static>> {
45    tokio::task::spawn_blocking(move || {
46        let file = std::fs::File::open(path)?;
47        let mut reader = BufReader::new(file);
48        PrivateKeyDer::from_pem_reader(&mut reader).map_err(|e| {
49            io::Error::new(
50                ErrorKind::InvalidData,
51                format!("could not load private key: {}", e),
52            )
53        })
54    })
55    .await?
56}
57
58pub(crate) async fn config_tls(options: &ConnectorOptions) -> io::Result<ClientConfig> {
59    let mut root_store = RootCertStore::empty();
60    // load native system certs only if user did not specify them.
61    if options.tls_client_config.is_some() || options.certificates.is_empty() {
62        let certs_result = rustls_native_certs::load_native_certs();
63        if !certs_result.errors.is_empty() {
64            let errors = certs_result
65                .errors
66                .into_iter()
67                .map(|e| e.to_string())
68                .collect::<Vec<String>>()
69                .join("\n");
70            return Err(io::Error::other(format!(
71                "could not load platform certs: {errors}"
72            )));
73        }
74        root_store.add_parsable_certificates(certs_result.certs);
75    }
76
77    // use provided ClientConfig or built it from options.
78    let tls_config = {
79        if let Some(config) = &options.tls_client_config {
80            Ok(config.to_owned())
81        } else {
82            // Include user-provided certificates.
83            for cafile in &options.certificates {
84                let trust_anchors = load_certs(cafile.to_owned())
85                    .await?
86                    .into_iter()
87                    .map(|cert| {
88                        rustls_webpki::anchor_from_trusted_cert(&cert).map(|ta| ta.to_owned())
89                    })
90                    .collect::<Result<Vec<_>, rustls_webpki::Error>>()
91                    .map_err(|err| {
92                        io::Error::new(
93                            ErrorKind::InvalidInput,
94                            format!("could not load certs: {err}"),
95                        )
96                    })?;
97                root_store.extend(trust_anchors);
98            }
99            let builder = ClientConfig::builder().with_root_certificates(root_store);
100            if let Some(cert) = options.client_cert.clone() {
101                if let Some(key) = options.client_key.clone() {
102                    let key = tls::load_key(key).await?;
103                    let cert = tls::load_certs(cert).await?;
104                    builder
105                        .with_client_auth_cert(cert, key)
106                        .map_err(|_| io::Error::other("could not add certificate or key"))
107                } else {
108                    Err(io::Error::other("found certificate, but no key"))
109                }
110            } else {
111                // if there are no client certs provided, connect with just TLS.
112                Ok(builder.with_no_client_auth())
113            }
114        }
115    }?;
116    Ok(tls_config)
117}