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