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