1 #[cfg(feature = "rustls-tls")]
2 use rustls::{RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError};
3 use std::fmt;
4 #[cfg(feature = "rustls-tls")]
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-tls")]
13     original: Cert,
14 }
15 
16 #[cfg(feature = "rustls-tls")]
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(
26         not(any(feature = "native-tls", feature = "rustls-tls")),
27         allow(unused)
28     )]
29     inner: ClientCert,
30 }
31 
32 enum ClientCert {
33     #[cfg(feature = "native-tls")]
34     Pkcs12(native_tls_crate::Identity),
35     #[cfg(feature = "rustls-tls")]
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     /// ```
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-tls")]
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)?;
into_proxy_scheme(self) -> crate::Result<ProxyScheme>80     /// # drop(cert);
81     /// # Ok(())
82     /// # }
83     /// ```
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-tls")]
89             original: Cert::Pem(pem.to_owned()),
90         })
91     }
92 
93     #[cfg(feature = "native-tls-crate")]
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-tls")]
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);
http<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy>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
https<U: IntoProxyScheme>(proxy_scheme: U) -> crate::Result<Proxy>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")]
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).map_err(crate::error::builder)?,
163             ),
164         })
165     }
166 
167     /// Parses PEM encoded private key and certificate.
168     ///
169     /// The input should contain a PEM encoded private key
170     /// and at least one PEM encoded certificate.
171     ///
172     /// # Examples
173     ///
174     /// ```
175     /// # use std::fs::File;
custom<F, U: IntoProxyScheme>(fun: F) -> Proxy where F: Fn(&Url) -> Option<U> + Send + Sync + 'static,176     /// # use std::io::Read;
177     /// # fn pem() -> Result<(), Box<std::error::Error>> {
178     /// let mut buf = Vec::new();
179     /// File::open("my-ident.pem")?
180     ///     .read_to_end(&mut buf)?;
181     /// let id = reqwest::Identity::from_pem(&buf)?;
182     /// # drop(id);
183     /// # Ok(())
184     /// # }
185     /// ```
186     ///
187     /// # Optional
188     ///
189     /// This requires the `rustls-tls` Cargo feature enabled.
190     #[cfg(feature = "rustls-tls")]
191     pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> {
192         use rustls::internal::pemfile;
193         use std::io::Cursor;
194 
195         let (key, certs) = {
196             let mut pem = Cursor::new(buf);
197             let certs = pemfile::certs(&mut pem)
198                 .map_err(|_| TLSError::General(String::from("No valid certificate was found")))
199                 .map_err(crate::error::builder)?;
200             pem.set_position(0);
201             let mut sk = pemfile::pkcs8_private_keys(&mut pem)
202                 .and_then(|pkcs8_keys| {
203                     if pkcs8_keys.is_empty() {
204                         Err(())
205                     } else {
206                         Ok(pkcs8_keys)
207                     }
208                 })
209                 .or_else(|_| {
210                     pem.set_position(0);
211                     pemfile::rsa_private_keys(&mut pem)
212                 })
213                 .map_err(|_| TLSError::General(String::from("No valid private key was found")))
214                 .map_err(crate::error::builder)?;
215             if let (Some(sk), false) = (sk.pop(), certs.is_empty()) {
216                 (sk, certs)
217             } else {
218                 return Err(crate::error::builder(TLSError::General(String::from(
219                     "private key or certificate not found",
220                 ))));
221             }
222         };
223 
224         Ok(Identity {
225             inner: ClientCert::Pem { key, certs },
226         })
227     }
228 
229     #[cfg(feature = "native-tls")]
230     pub(crate) fn add_to_native_tls(
231         self,
232         tls: &mut native_tls_crate::TlsConnectorBuilder,
233     ) -> crate::Result<()> {
234         match self.inner {
235             ClientCert::Pkcs12(id) => {
236                 tls.identity(id);
237                 Ok(())
238             }
239             #[cfg(feature = "rustls-tls")]
intercept<D: Dst>(&self, uri: &D) -> Option<ProxyScheme>240             ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")),
241         }
242     }
243 
244     #[cfg(feature = "rustls-tls")]
245     pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> {
246         match self.inner {
247             ClientCert::Pem { key, certs } => {
248                 tls.set_single_client_cert(certs, key);
249                 Ok(())
250             }
251             #[cfg(feature = "native-tls")]
252             ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")),
253         }
254     }
255 }
256 
257 impl fmt::Debug for Certificate {
258     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259         f.debug_struct("Certificate").finish()
260     }
261 }
262 
263 impl fmt::Debug for Identity {
is_match<D: Dst>(&self, uri: &D) -> bool264     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
265         f.debug_struct("Identity").finish()
266     }
267 }
268 
269 #[derive(Debug)]
270 pub(crate) enum TlsBackend {
271     #[cfg(feature = "default-tls")]
272     Default,
273     #[cfg(feature = "rustls-tls")]
274     Rustls,
275 }
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result276 
277 impl Default for TlsBackend {
278     fn default() -> TlsBackend {
279         #[cfg(feature = "default-tls")]
280         {
281             TlsBackend::Default
282         }
283 
284         #[cfg(all(feature = "rustls-tls", not(feature = "default-tls")))]
285         {
286             TlsBackend::Rustls
287         }
288     }
289 }
290 
291 #[cfg(feature = "rustls-tls")]
292 pub(crate) struct NoVerifier;
https(host: &str) -> crate::Result<Self>293 
294 #[cfg(feature = "rustls-tls")]
295 impl ServerCertVerifier for NoVerifier {
296     fn verify_server_cert(
297         &self,
298         _roots: &RootCertStore,
299         _presented_certs: &[rustls::Certificate],
300         _dns_name: DNSNameRef,
301         _ocsp_response: &[u8],
302     ) -> Result<ServerCertVerified, TLSError> {
303         Ok(ServerCertVerified::assertion())
304     }
305 }
socks5(addr: SocketAddr) -> crate::Result<Self>306 
307 #[cfg(test)]
308 mod tests {
309     use super::*;
310 
311     #[cfg(feature = "default-tls")]
312     #[test]
313     fn certificate_from_der_invalid() {
314         Certificate::from_der(b"not der").unwrap_err();
315     }
316 
317     #[cfg(feature = "default-tls")]
318     #[test]
319     fn certificate_from_pem_invalid() {
320         Certificate::from_pem(b"not pem").unwrap_err();
321     }
322 
323     #[cfg(feature = "native-tls")]
324     #[test]
325     fn identity_from_pkcs12_der_invalid() {
326         Identity::from_pkcs12_der(b"not der", "nope").unwrap_err();
327     }
328 
329     #[cfg(feature = "rustls-tls")]
330     #[test]
331     fn identity_from_pem_invalid() {
332         Identity::from_pem(b"not pem").unwrap_err();
333     }
334 
335     #[cfg(feature = "rustls-tls")]
336     #[test]
337     fn identity_from_pem_pkcs1_key() {
338         let pem = b"-----BEGIN CERTIFICATE-----\n\
339             -----END CERTIFICATE-----\n\
340             -----BEGIN RSA PRIVATE KEY-----\n\
341             -----END RSA PRIVATE KEY-----\n";
342 
343         Identity::from_pem(pem).unwrap();
344     }
345 }
346