1 //! TLS configuration 2 //! 3 //! By default, a `Client` will make use of system-native transport layer 4 //! security to connect to HTTPS destinations. This means schannel on Windows, 5 //! Security-Framework on macOS, and OpenSSL on Linux. 6 //! 7 //! - Additional X509 certificates can be configured on a `ClientBuilder` with the 8 //! [`Certificate`](Certificate) type. 9 //! - Client certificates can be add to a `ClientBuilder` with the 10 //! [`Identity`][Identity] type. 11 //! - Various parts of TLS can also be configured or even disabled on the 12 //! `ClientBuilder`. 13 14 #[cfg(feature = "__rustls")] 15 use rustls::{ 16 client::HandshakeSignatureValid, client::ServerCertVerified, client::ServerCertVerifier, 17 internal::msgs::handshake::DigitallySignedStruct, Error as TLSError, ServerName, 18 }; 19 use std::fmt; 20 21 /// Represents a server X509 certificate. 22 #[derive(Clone)] 23 pub struct Certificate { 24 #[cfg(feature = "native-tls-crate")] 25 native: native_tls_crate::Certificate, 26 #[cfg(feature = "__rustls")] 27 original: Cert, 28 } 29 30 #[cfg(feature = "__rustls")] 31 #[derive(Clone)] 32 enum Cert { 33 Der(Vec<u8>), 34 Pem(Vec<u8>), 35 } 36 37 /// Represents a private key and X509 cert as a client certificate. 38 #[derive(Clone)] 39 pub struct Identity { 40 #[cfg_attr(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))] 41 inner: ClientCert, 42 } 43 44 #[derive(Clone)] 45 enum ClientCert { 46 #[cfg(feature = "native-tls")] 47 Pkcs12(native_tls_crate::Identity), 48 #[cfg(feature = "__rustls")] 49 Pem { 50 key: rustls::PrivateKey, 51 certs: Vec<rustls::Certificate>, 52 }, 53 } 54 55 impl Certificate { 56 /// Create a `Certificate` from a binary DER encoded certificate 57 /// 58 /// # Examples 59 /// 60 /// ``` 61 /// # use std::fs::File; 62 /// # use std::io::Read; 63 /// # fn cert() -> Result<(), Box<std::error::Error>> { 64 /// let mut buf = Vec::new(); 65 /// File::open("my_cert.der")? 66 /// .read_to_end(&mut buf)?; 67 /// let cert = reqwest::Certificate::from_der(&buf)?; 68 /// # drop(cert); 69 /// # Ok(()) 70 /// # } 71 /// ``` from_der(der: &[u8]) -> crate::Result<Certificate>72 pub fn from_der(der: &[u8]) -> crate::Result<Certificate> { 73 Ok(Certificate { 74 #[cfg(feature = "native-tls-crate")] 75 native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?, 76 #[cfg(feature = "__rustls")] 77 original: Cert::Der(der.to_owned()), 78 }) 79 } 80 81 /// Create a `Certificate` from a PEM encoded certificate 82 /// 83 /// # Examples 84 /// 85 /// ``` 86 /// # use std::fs::File; 87 /// # use std::io::Read; 88 /// # fn cert() -> Result<(), Box<std::error::Error>> { 89 /// let mut buf = Vec::new(); 90 /// File::open("my_cert.pem")? 91 /// .read_to_end(&mut buf)?; 92 /// let cert = reqwest::Certificate::from_pem(&buf)?; 93 /// # drop(cert); 94 /// # Ok(()) 95 /// # } 96 /// ``` from_pem(pem: &[u8]) -> crate::Result<Certificate>97 pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> { 98 Ok(Certificate { 99 #[cfg(feature = "native-tls-crate")] 100 native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?, 101 #[cfg(feature = "__rustls")] 102 original: Cert::Pem(pem.to_owned()), 103 }) 104 } 105 106 #[cfg(feature = "native-tls-crate")] add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder)107 pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) { 108 tls.add_root_certificate(self.native); 109 } 110 111 #[cfg(feature = "__rustls")] add_to_rustls( self, root_cert_store: &mut rustls::RootCertStore, ) -> crate::Result<()>112 pub(crate) fn add_to_rustls( 113 self, 114 root_cert_store: &mut rustls::RootCertStore, 115 ) -> crate::Result<()> { 116 use std::io::Cursor; 117 118 match self.original { 119 Cert::Der(buf) => root_cert_store 120 .add(&rustls::Certificate(buf)) 121 .map_err(crate::error::builder)?, 122 Cert::Pem(buf) => { 123 let mut pem = Cursor::new(buf); 124 let certs = rustls_pemfile::certs(&mut pem).map_err(|_| { 125 crate::error::builder(TLSError::General(String::from( 126 "No valid certificate was found", 127 ))) 128 })?; 129 for c in certs { 130 root_cert_store 131 .add(&rustls::Certificate(c)) 132 .map_err(crate::error::builder)?; 133 } 134 } 135 } 136 Ok(()) 137 } 138 } 139 140 impl Identity { 141 /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key. 142 /// 143 /// The archive should contain a leaf certificate and its private key, as well any intermediate 144 /// certificates that allow clients to build a chain to a trusted root. 145 /// The chain certificates should be in order from the leaf certificate towards the root. 146 /// 147 /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created 148 /// with the OpenSSL `pkcs12` tool: 149 /// 150 /// ```bash 151 /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem 152 /// ``` 153 /// 154 /// # Examples 155 /// 156 /// ``` 157 /// # use std::fs::File; 158 /// # use std::io::Read; 159 /// # fn pkcs12() -> Result<(), Box<std::error::Error>> { 160 /// let mut buf = Vec::new(); 161 /// File::open("my-ident.pfx")? 162 /// .read_to_end(&mut buf)?; 163 /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?; 164 /// # drop(pkcs12); 165 /// # Ok(()) 166 /// # } 167 /// ``` 168 /// 169 /// # Optional 170 /// 171 /// This requires the `native-tls` Cargo feature enabled. 172 #[cfg(feature = "native-tls")] from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity>173 pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> { 174 Ok(Identity { 175 inner: ClientCert::Pkcs12( 176 native_tls_crate::Identity::from_pkcs12(der, password) 177 .map_err(crate::error::builder)?, 178 ), 179 }) 180 } 181 182 /// Parses PEM encoded private key and certificate. 183 /// 184 /// The input should contain a PEM encoded private key 185 /// and at least one PEM encoded certificate. 186 /// 187 /// Note: The private key must be in RSA or PKCS#8 format. 188 /// 189 /// # Examples 190 /// 191 /// ``` 192 /// # use std::fs::File; 193 /// # use std::io::Read; 194 /// # fn pem() -> Result<(), Box<std::error::Error>> { 195 /// let mut buf = Vec::new(); 196 /// File::open("my-ident.pem")? 197 /// .read_to_end(&mut buf)?; 198 /// let id = reqwest::Identity::from_pem(&buf)?; 199 /// # drop(id); 200 /// # Ok(()) 201 /// # } 202 /// ``` 203 /// 204 /// # Optional 205 /// 206 /// This requires the `rustls-tls(-...)` Cargo feature enabled. 207 #[cfg(feature = "__rustls")] from_pem(buf: &[u8]) -> crate::Result<Identity>208 pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> { 209 use std::io::Cursor; 210 211 let (key, certs) = { 212 let mut pem = Cursor::new(buf); 213 let certs = rustls_pemfile::certs(&mut pem) 214 .map_err(|_| TLSError::General(String::from("No valid certificate was found"))) 215 .map_err(crate::error::builder)? 216 .into_iter() 217 .map(rustls::Certificate) 218 .collect::<Vec<_>>(); 219 pem.set_position(0); 220 let mut sk = rustls_pemfile::pkcs8_private_keys(&mut pem) 221 .and_then(|pkcs8_keys| { 222 if pkcs8_keys.is_empty() { 223 Err(std::io::Error::new( 224 std::io::ErrorKind::NotFound, 225 "No valid private key was found", 226 )) 227 } else { 228 Ok(pkcs8_keys) 229 } 230 }) 231 .or_else(|_| { 232 pem.set_position(0); 233 rustls_pemfile::rsa_private_keys(&mut pem) 234 }) 235 .map_err(|_| TLSError::General(String::from("No valid private key was found"))) 236 .map_err(crate::error::builder)? 237 .into_iter() 238 .map(rustls::PrivateKey) 239 .collect::<Vec<_>>(); 240 if let (Some(sk), false) = (sk.pop(), certs.is_empty()) { 241 (sk, certs) 242 } else { 243 return Err(crate::error::builder(TLSError::General(String::from( 244 "private key or certificate not found", 245 )))); 246 } 247 }; 248 249 Ok(Identity { 250 inner: ClientCert::Pem { key, certs }, 251 }) 252 } 253 254 #[cfg(feature = "native-tls")] add_to_native_tls( self, tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()>255 pub(crate) fn add_to_native_tls( 256 self, 257 tls: &mut native_tls_crate::TlsConnectorBuilder, 258 ) -> crate::Result<()> { 259 match self.inner { 260 ClientCert::Pkcs12(id) => { 261 tls.identity(id); 262 Ok(()) 263 } 264 #[cfg(feature = "__rustls")] 265 ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")), 266 } 267 } 268 269 #[cfg(feature = "__rustls")] add_to_rustls( self, config_builder: rustls::ConfigBuilder< rustls::ClientConfig, rustls::client::WantsTransparencyPolicyOrClientCert, >, ) -> crate::Result<rustls::ClientConfig>270 pub(crate) fn add_to_rustls( 271 self, 272 config_builder: rustls::ConfigBuilder< 273 rustls::ClientConfig, 274 rustls::client::WantsTransparencyPolicyOrClientCert, 275 >, 276 ) -> crate::Result<rustls::ClientConfig> { 277 match self.inner { 278 ClientCert::Pem { key, certs } => config_builder 279 .with_single_cert(certs, key) 280 .map_err(crate::error::builder), 281 #[cfg(feature = "native-tls")] 282 ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")), 283 } 284 } 285 } 286 287 impl fmt::Debug for Certificate { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result288 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 289 f.debug_struct("Certificate").finish() 290 } 291 } 292 293 impl fmt::Debug for Identity { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result294 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 295 f.debug_struct("Identity").finish() 296 } 297 } 298 299 /// A TLS protocol version. 300 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 301 pub struct Version(InnerVersion); 302 303 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 304 #[non_exhaustive] 305 enum InnerVersion { 306 Tls1_0, 307 Tls1_1, 308 Tls1_2, 309 Tls1_3, 310 } 311 312 // These could perhaps be From/TryFrom implementations, but those would be 313 // part of the public API so let's be careful 314 impl Version { 315 /// Version 1.0 of the TLS protocol. 316 pub const TLS_1_0: Version = Version(InnerVersion::Tls1_0); 317 /// Version 1.1 of the TLS protocol. 318 pub const TLS_1_1: Version = Version(InnerVersion::Tls1_1); 319 /// Version 1.2 of the TLS protocol. 320 pub const TLS_1_2: Version = Version(InnerVersion::Tls1_2); 321 /// Version 1.3 of the TLS protocol. 322 pub const TLS_1_3: Version = Version(InnerVersion::Tls1_3); 323 324 #[cfg(feature = "default-tls")] to_native_tls(self) -> Option<native_tls_crate::Protocol>325 pub(crate) fn to_native_tls(self) -> Option<native_tls_crate::Protocol> { 326 match self.0 { 327 InnerVersion::Tls1_0 => Some(native_tls_crate::Protocol::Tlsv10), 328 InnerVersion::Tls1_1 => Some(native_tls_crate::Protocol::Tlsv11), 329 InnerVersion::Tls1_2 => Some(native_tls_crate::Protocol::Tlsv12), 330 InnerVersion::Tls1_3 => None, 331 } 332 } 333 334 #[cfg(feature = "__rustls")] from_rustls(version: rustls::ProtocolVersion) -> Option<Self>335 pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option<Self> { 336 match version { 337 rustls::ProtocolVersion::SSLv2 => None, 338 rustls::ProtocolVersion::SSLv3 => None, 339 rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)), 340 rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)), 341 rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)), 342 rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)), 343 _ => None, 344 } 345 } 346 } 347 348 pub(crate) enum TlsBackend { 349 #[cfg(feature = "default-tls")] 350 Default, 351 #[cfg(feature = "native-tls")] 352 BuiltNativeTls(native_tls_crate::TlsConnector), 353 #[cfg(feature = "__rustls")] 354 Rustls, 355 #[cfg(feature = "__rustls")] 356 BuiltRustls(rustls::ClientConfig), 357 #[cfg(any(feature = "native-tls", feature = "__rustls",))] 358 UnknownPreconfigured, 359 } 360 361 impl fmt::Debug for TlsBackend { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result362 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 363 match self { 364 #[cfg(feature = "default-tls")] 365 TlsBackend::Default => write!(f, "Default"), 366 #[cfg(feature = "native-tls")] 367 TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"), 368 #[cfg(feature = "__rustls")] 369 TlsBackend::Rustls => write!(f, "Rustls"), 370 #[cfg(feature = "__rustls")] 371 TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"), 372 #[cfg(any(feature = "native-tls", feature = "__rustls",))] 373 TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"), 374 } 375 } 376 } 377 378 impl Default for TlsBackend { default() -> TlsBackend379 fn default() -> TlsBackend { 380 #[cfg(feature = "default-tls")] 381 { 382 TlsBackend::Default 383 } 384 385 #[cfg(all(feature = "__rustls", not(feature = "default-tls")))] 386 { 387 TlsBackend::Rustls 388 } 389 } 390 } 391 392 #[cfg(feature = "__rustls")] 393 pub(crate) struct NoVerifier; 394 395 #[cfg(feature = "__rustls")] 396 impl ServerCertVerifier for NoVerifier { verify_server_cert( &self, _end_entity: &rustls::Certificate, _intermediates: &[rustls::Certificate], _server_name: &ServerName, _scts: &mut dyn Iterator<Item = &[u8]>, _ocsp_response: &[u8], _now: std::time::SystemTime, ) -> Result<ServerCertVerified, TLSError>397 fn verify_server_cert( 398 &self, 399 _end_entity: &rustls::Certificate, 400 _intermediates: &[rustls::Certificate], 401 _server_name: &ServerName, 402 _scts: &mut dyn Iterator<Item = &[u8]>, 403 _ocsp_response: &[u8], 404 _now: std::time::SystemTime, 405 ) -> Result<ServerCertVerified, TLSError> { 406 Ok(ServerCertVerified::assertion()) 407 } 408 verify_tls12_signature( &self, _message: &[u8], _cert: &rustls::Certificate, _dss: &DigitallySignedStruct, ) -> Result<HandshakeSignatureValid, TLSError>409 fn verify_tls12_signature( 410 &self, 411 _message: &[u8], 412 _cert: &rustls::Certificate, 413 _dss: &DigitallySignedStruct, 414 ) -> Result<HandshakeSignatureValid, TLSError> { 415 Ok(HandshakeSignatureValid::assertion()) 416 } 417 verify_tls13_signature( &self, _message: &[u8], _cert: &rustls::Certificate, _dss: &DigitallySignedStruct, ) -> Result<HandshakeSignatureValid, TLSError>418 fn verify_tls13_signature( 419 &self, 420 _message: &[u8], 421 _cert: &rustls::Certificate, 422 _dss: &DigitallySignedStruct, 423 ) -> Result<HandshakeSignatureValid, TLSError> { 424 Ok(HandshakeSignatureValid::assertion()) 425 } 426 } 427 428 #[cfg(test)] 429 mod tests { 430 use super::*; 431 432 #[cfg(feature = "default-tls")] 433 #[test] certificate_from_der_invalid()434 fn certificate_from_der_invalid() { 435 Certificate::from_der(b"not der").unwrap_err(); 436 } 437 438 #[cfg(feature = "default-tls")] 439 #[test] certificate_from_pem_invalid()440 fn certificate_from_pem_invalid() { 441 Certificate::from_pem(b"not pem").unwrap_err(); 442 } 443 444 #[cfg(feature = "native-tls")] 445 #[test] identity_from_pkcs12_der_invalid()446 fn identity_from_pkcs12_der_invalid() { 447 Identity::from_pkcs12_der(b"not der", "nope").unwrap_err(); 448 } 449 450 #[cfg(feature = "__rustls")] 451 #[test] identity_from_pem_invalid()452 fn identity_from_pem_invalid() { 453 Identity::from_pem(b"not pem").unwrap_err(); 454 } 455 456 #[cfg(feature = "__rustls")] 457 #[test] identity_from_pem_pkcs1_key()458 fn identity_from_pem_pkcs1_key() { 459 let pem = b"-----BEGIN CERTIFICATE-----\n\ 460 -----END CERTIFICATE-----\n\ 461 -----BEGIN RSA PRIVATE KEY-----\n\ 462 -----END RSA PRIVATE KEY-----\n"; 463 464 Identity::from_pem(pem).unwrap(); 465 } 466 } 467