1 #[cfg(feature = "__rustls")]
2 use rustls::{RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError};
3 use std::fmt;
4 #[cfg(feature = "__rustls")]
5 use tokio_rustls::webpki::DNSNameRef;
6 
7 /// Represents a server X509 certificate.
8 #[derive(Clone)]
9 pub struct Certificate {
10     #[cfg(feature = "native-tls-crate")]
11     native: native_tls_crate::Certificate,
12     #[cfg(feature = "__rustls")]
13     original: Cert,
14 }
15 
16 #[cfg(feature = "__rustls")]
17 #[derive(Clone)]
18 enum Cert {
19     Der(Vec<u8>),
20     Pem(Vec<u8>),
21 }
22 
23 /// Represents a private key and X509 cert as a client certificate.
24 pub struct Identity {
25     #[cfg_attr(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))]
26     inner: ClientCert,
27 }
28 
29 enum ClientCert {
30     #[cfg(feature = "native-tls")]
31     Pkcs12(native_tls_crate::Identity),
32     #[cfg(feature = "__rustls")]
33     Pem {
34         key: rustls::PrivateKey,
35         certs: Vec<rustls::Certificate>,
36     },
37 }
38 
39 impl Certificate {
40     /// Create a `Certificate` from a binary DER encoded certificate
41     ///
42     /// # Examples
43     ///
44     /// ```
45     /// # use std::fs::File;
46     /// # use std::io::Read;
47     /// # fn cert() -> Result<(), Box<std::error::Error>> {
48     /// let mut buf = Vec::new();
49     /// File::open("my_cert.der")?
50     ///     .read_to_end(&mut buf)?;
51     /// let cert = reqwest::Certificate::from_der(&buf)?;
52     /// # drop(cert);
53     /// # Ok(())
54     /// # }
55     /// ```
from_der(der: &[u8]) -> crate::Result<Certificate>56     pub fn from_der(der: &[u8]) -> crate::Result<Certificate> {
57         Ok(Certificate {
58             #[cfg(feature = "native-tls-crate")]
59             native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?,
60             #[cfg(feature = "__rustls")]
61             original: Cert::Der(der.to_owned()),
62         })
63     }
64 
65     /// Create a `Certificate` from a PEM encoded certificate
66     ///
67     /// # Examples
68     ///
69     /// ```
70     /// # use std::fs::File;
71     /// # use std::io::Read;
72     /// # fn cert() -> Result<(), Box<std::error::Error>> {
73     /// let mut buf = Vec::new();
74     /// File::open("my_cert.pem")?
75     ///     .read_to_end(&mut buf)?;
76     /// let cert = reqwest::Certificate::from_pem(&buf)?;
77     /// # drop(cert);
78     /// # Ok(())
79     /// # }
80     /// ```
from_pem(pem: &[u8]) -> crate::Result<Certificate>81     pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> {
82         Ok(Certificate {
83             #[cfg(feature = "native-tls-crate")]
84             native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?,
85             #[cfg(feature = "__rustls")]
86             original: Cert::Pem(pem.to_owned()),
87         })
88     }
89 
90     #[cfg(feature = "native-tls-crate")]
add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder)91     pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) {
92         tls.add_root_certificate(self.native);
93     }
94 
95     #[cfg(feature = "__rustls")]
add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()>96     pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> {
97         use rustls::internal::pemfile;
98         use std::io::Cursor;
99 
100         match self.original {
101             Cert::Der(buf) => tls
102                 .root_store
103                 .add(&::rustls::Certificate(buf))
104                 .map_err(|e| crate::error::builder(TLSError::WebPKIError(e)))?,
105             Cert::Pem(buf) => {
106                 let mut pem = Cursor::new(buf);
107                 let certs = pemfile::certs(&mut pem).map_err(|_| {
108                     crate::error::builder(TLSError::General(String::from(
109                         "No valid certificate was found",
110                     )))
111                 })?;
112                 for c in certs {
113                     tls.root_store
114                         .add(&c)
115                         .map_err(|e| crate::error::builder(TLSError::WebPKIError(e)))?;
116                 }
117             }
118         }
119         Ok(())
120     }
121 }
122 
123 impl Identity {
124     /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key.
125     ///
126     /// The archive should contain a leaf certificate and its private key, as well any intermediate
127     /// certificates that allow clients to build a chain to a trusted root.
128     /// The chain certificates should be in order from the leaf certificate towards the root.
129     ///
130     /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created
131     /// with the OpenSSL `pkcs12` tool:
132     ///
133     /// ```bash
134     /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem
135     /// ```
136     ///
137     /// # Examples
138     ///
139     /// ```
140     /// # use std::fs::File;
141     /// # use std::io::Read;
142     /// # fn pkcs12() -> Result<(), Box<std::error::Error>> {
143     /// let mut buf = Vec::new();
144     /// File::open("my-ident.pfx")?
145     ///     .read_to_end(&mut buf)?;
146     /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?;
147     /// # drop(pkcs12);
148     /// # Ok(())
149     /// # }
150     /// ```
151     ///
152     /// # Optional
153     ///
154     /// This requires the `native-tls` Cargo feature enabled.
155     #[cfg(feature = "native-tls")]
from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity>156     pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> {
157         Ok(Identity {
158             inner: ClientCert::Pkcs12(
159                 native_tls_crate::Identity::from_pkcs12(der, password)
160                     .map_err(crate::error::builder)?,
161             ),
162         })
163     }
164 
165     /// Parses PEM encoded private key and certificate.
166     ///
167     /// The input should contain a PEM encoded private key
168     /// and at least one PEM encoded certificate.
169     ///
170     /// # Examples
171     ///
172     /// ```
173     /// # use std::fs::File;
174     /// # use std::io::Read;
175     /// # fn pem() -> Result<(), Box<std::error::Error>> {
176     /// let mut buf = Vec::new();
177     /// File::open("my-ident.pem")?
178     ///     .read_to_end(&mut buf)?;
179     /// let id = reqwest::Identity::from_pem(&buf)?;
180     /// # drop(id);
181     /// # Ok(())
182     /// # }
183     /// ```
184     ///
185     /// # Optional
186     ///
187     /// This requires the `rustls-tls(-...)` Cargo feature enabled.
188     #[cfg(feature = "__rustls")]
from_pem(buf: &[u8]) -> crate::Result<Identity>189     pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
190         use rustls::internal::pemfile;
191         use std::io::Cursor;
192 
193         let (key, certs) = {
194             let mut pem = Cursor::new(buf);
195             let certs = pemfile::certs(&mut pem)
196                 .map_err(|_| TLSError::General(String::from("No valid certificate was found")))
197                 .map_err(crate::error::builder)?;
198             pem.set_position(0);
199             let mut sk = pemfile::pkcs8_private_keys(&mut pem)
200                 .and_then(|pkcs8_keys| {
201                     if pkcs8_keys.is_empty() {
202                         Err(())
203                     } else {
204                         Ok(pkcs8_keys)
205                     }
206                 })
207                 .or_else(|_| {
208                     pem.set_position(0);
209                     pemfile::rsa_private_keys(&mut pem)
210                 })
211                 .map_err(|_| TLSError::General(String::from("No valid private key was found")))
212                 .map_err(crate::error::builder)?;
213             if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
214                 (sk, certs)
215             } else {
216                 return Err(crate::error::builder(TLSError::General(String::from(
217                     "private key or certificate not found",
218                 ))));
219             }
220         };
221 
222         Ok(Identity {
223             inner: ClientCert::Pem { key, certs },
224         })
225     }
226 
227     #[cfg(feature = "native-tls")]
add_to_native_tls( self, tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()>228     pub(crate) fn add_to_native_tls(
229         self,
230         tls: &mut native_tls_crate::TlsConnectorBuilder,
231     ) -> crate::Result<()> {
232         match self.inner {
233             ClientCert::Pkcs12(id) => {
234                 tls.identity(id);
235                 Ok(())
236             }
237             #[cfg(feature = "__rustls")]
238             ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
239         }
240     }
241 
242     #[cfg(feature = "__rustls")]
add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()>243     pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> {
244         match self.inner {
245             ClientCert::Pem { key, certs } => {
246                 tls.set_single_client_cert(certs, key)
247                     .map_err(|e| crate::error::builder(e))?;
248                 Ok(())
249             }
250             #[cfg(feature = "native-tls")]
251             ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")),
252         }
253     }
254 }
255 
256 impl fmt::Debug for Certificate {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result257     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
258         f.debug_struct("Certificate").finish()
259     }
260 }
261 
262 impl fmt::Debug for Identity {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result263     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264         f.debug_struct("Identity").finish()
265     }
266 }
267 
268 pub(crate) enum TlsBackend {
269     #[cfg(feature = "default-tls")]
270     Default,
271     #[cfg(feature = "native-tls")]
272     BuiltNativeTls(native_tls_crate::TlsConnector),
273     #[cfg(feature = "__rustls")]
274     Rustls,
275     #[cfg(feature = "__rustls")]
276     BuiltRustls(rustls::ClientConfig),
277     #[cfg(any(feature = "native-tls", feature = "__rustls",))]
278     UnknownPreconfigured,
279 }
280 
281 impl fmt::Debug for TlsBackend {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result282     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283         match self {
284             #[cfg(feature = "default-tls")]
285             TlsBackend::Default => write!(f, "Default"),
286             #[cfg(feature = "native-tls")]
287             TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"),
288             #[cfg(feature = "__rustls")]
289             TlsBackend::Rustls => write!(f, "Rustls"),
290             #[cfg(feature = "__rustls")]
291             TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"),
292             #[cfg(any(feature = "native-tls", feature = "__rustls",))]
293             TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"),
294         }
295     }
296 }
297 
298 impl Default for TlsBackend {
default() -> TlsBackend299     fn default() -> TlsBackend {
300         #[cfg(feature = "default-tls")]
301         {
302             TlsBackend::Default
303         }
304 
305         #[cfg(all(feature = "__rustls", not(feature = "default-tls")))]
306         {
307             TlsBackend::Rustls
308         }
309     }
310 }
311 
312 #[cfg(feature = "__rustls")]
313 pub(crate) struct NoVerifier;
314 
315 #[cfg(feature = "__rustls")]
316 impl ServerCertVerifier for NoVerifier {
verify_server_cert( &self, _roots: &RootCertStore, _presented_certs: &[rustls::Certificate], _dns_name: DNSNameRef, _ocsp_response: &[u8], ) -> Result<ServerCertVerified, TLSError>317     fn verify_server_cert(
318         &self,
319         _roots: &RootCertStore,
320         _presented_certs: &[rustls::Certificate],
321         _dns_name: DNSNameRef,
322         _ocsp_response: &[u8],
323     ) -> Result<ServerCertVerified, TLSError> {
324         Ok(ServerCertVerified::assertion())
325     }
326 }
327 
328 #[cfg(test)]
329 mod tests {
330     use super::*;
331 
332     #[cfg(feature = "default-tls")]
333     #[test]
certificate_from_der_invalid()334     fn certificate_from_der_invalid() {
335         Certificate::from_der(b"not der").unwrap_err();
336     }
337 
338     #[cfg(feature = "default-tls")]
339     #[test]
certificate_from_pem_invalid()340     fn certificate_from_pem_invalid() {
341         Certificate::from_pem(b"not pem").unwrap_err();
342     }
343 
344     #[cfg(feature = "native-tls")]
345     #[test]
identity_from_pkcs12_der_invalid()346     fn identity_from_pkcs12_der_invalid() {
347         Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
348     }
349 
350     #[cfg(feature = "__rustls")]
351     #[test]
identity_from_pem_invalid()352     fn identity_from_pem_invalid() {
353         Identity::from_pem(b"not pem").unwrap_err();
354     }
355 
356     #[cfg(feature = "__rustls")]
357     #[test]
identity_from_pem_pkcs1_key()358     fn identity_from_pem_pkcs1_key() {
359         let pem = b"-----BEGIN CERTIFICATE-----\n\
360             -----END CERTIFICATE-----\n\
361             -----BEGIN RSA PRIVATE KEY-----\n\
362             -----END RSA PRIVATE KEY-----\n";
363 
364         Identity::from_pem(pem).unwrap();
365     }
366 }
367