1 #[cfg(feature = "__rustls")] 2 use rustls::{RootCertStore, ServerCertVerified, ServerCertVerifier, TLSError}; 3 use std::fmt; 4 #[cfg(feature = "__rustls")] 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")] 13 original: Cert, 14 } 15 16 #[cfg(feature = "__rustls")] 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")), 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")] 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).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; 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")] from_pem(buf: &[u8]) -> crate::Result<Identity>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")] add_to_native_tls( self, tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()>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")] 240 ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")), 241 } 242 } 243 244 #[cfg(feature = "__rustls")] add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()>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).map_err(|e| crate::error::builder(e))?; 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 { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result258 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 259 f.debug_struct("Certificate").finish() 260 } 261 } 262 263 impl fmt::Debug for Identity { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result264 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 265 f.debug_struct("Identity").finish() 266 } 267 } 268 269 pub(crate) enum TlsBackend { 270 #[cfg(feature = "default-tls")] 271 Default, 272 #[cfg(feature = "native-tls")] 273 BuiltNativeTls(native_tls_crate::TlsConnector), 274 #[cfg(feature = "__rustls")] 275 Rustls, 276 #[cfg(feature = "__rustls")] 277 BuiltRustls(rustls::ClientConfig), 278 #[cfg(any( 279 feature = "native-tls", 280 feature = "__rustls", 281 ))] 282 UnknownPreconfigured, 283 } 284 285 impl fmt::Debug for TlsBackend { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result286 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 287 match self { 288 #[cfg(feature = "default-tls")] 289 TlsBackend::Default => write!(f, "Default"), 290 #[cfg(feature = "native-tls")] 291 TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"), 292 #[cfg(feature = "__rustls")] 293 TlsBackend::Rustls => write!(f, "Rustls"), 294 #[cfg(feature = "__rustls")] 295 TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"), 296 #[cfg(any( 297 feature = "native-tls", 298 feature = "__rustls", 299 ))] 300 TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"), 301 } 302 } 303 } 304 305 impl Default for TlsBackend { default() -> TlsBackend306 fn default() -> TlsBackend { 307 #[cfg(feature = "default-tls")] 308 { 309 TlsBackend::Default 310 } 311 312 #[cfg(all(feature = "__rustls", not(feature = "default-tls")))] 313 { 314 TlsBackend::Rustls 315 } 316 } 317 } 318 319 #[cfg(feature = "__rustls")] 320 pub(crate) struct NoVerifier; 321 322 #[cfg(feature = "__rustls")] 323 impl ServerCertVerifier for NoVerifier { verify_server_cert( &self, _roots: &RootCertStore, _presented_certs: &[rustls::Certificate], _dns_name: DNSNameRef, _ocsp_response: &[u8], ) -> Result<ServerCertVerified, TLSError>324 fn verify_server_cert( 325 &self, 326 _roots: &RootCertStore, 327 _presented_certs: &[rustls::Certificate], 328 _dns_name: DNSNameRef, 329 _ocsp_response: &[u8], 330 ) -> Result<ServerCertVerified, TLSError> { 331 Ok(ServerCertVerified::assertion()) 332 } 333 } 334 335 #[cfg(test)] 336 mod tests { 337 use super::*; 338 339 #[cfg(feature = "default-tls")] 340 #[test] certificate_from_der_invalid()341 fn certificate_from_der_invalid() { 342 Certificate::from_der(b"not der").unwrap_err(); 343 } 344 345 #[cfg(feature = "default-tls")] 346 #[test] certificate_from_pem_invalid()347 fn certificate_from_pem_invalid() { 348 Certificate::from_pem(b"not pem").unwrap_err(); 349 } 350 351 #[cfg(feature = "native-tls")] 352 #[test] identity_from_pkcs12_der_invalid()353 fn identity_from_pkcs12_der_invalid() { 354 Identity::from_pkcs12_der(b"not der", "nope").unwrap_err(); 355 } 356 357 #[cfg(feature = "__rustls")] 358 #[test] identity_from_pem_invalid()359 fn identity_from_pem_invalid() { 360 Identity::from_pem(b"not pem").unwrap_err(); 361 } 362 363 #[cfg(feature = "__rustls")] 364 #[test] identity_from_pem_pkcs1_key()365 fn identity_from_pem_pkcs1_key() { 366 let pem = b"-----BEGIN CERTIFICATE-----\n\ 367 -----END CERTIFICATE-----\n\ 368 -----BEGIN RSA PRIVATE KEY-----\n\ 369 -----END RSA PRIVATE KEY-----\n"; 370 371 Identity::from_pem(pem).unwrap(); 372 } 373 } 374