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