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(not(any(feature = "native-tls", feature = "__rustls")), allow(unused))] 26 inner: ClientCert, 27 } 28 29 enum ClientCert { 30 #[cfg(feature = "native-tls")] 31 Pkcs12(native_tls_crate::Identity), 32 #[cfg(feature = "__rustls")] 33 Pem { 34 key: rustls::PrivateKey, 35 certs: Vec<rustls::Certificate>, 36 }, 37 } 38 39 impl Certificate { 40 /// Create a `Certificate` from a binary DER encoded certificate 41 /// 42 /// # Examples 43 /// 44 /// ``` 45 /// # use std::fs::File; 46 /// # use std::io::Read; 47 /// # fn cert() -> Result<(), Box<std::error::Error>> { 48 /// let mut buf = Vec::new(); 49 /// File::open("my_cert.der")? 50 /// .read_to_end(&mut buf)?; 51 /// let cert = reqwest::Certificate::from_der(&buf)?; 52 /// # drop(cert); 53 /// # Ok(()) 54 /// # } 55 /// ``` from_der(der: &[u8]) -> crate::Result<Certificate>56 pub fn from_der(der: &[u8]) -> crate::Result<Certificate> { 57 Ok(Certificate { 58 #[cfg(feature = "native-tls-crate")] 59 native: native_tls_crate::Certificate::from_der(der).map_err(crate::error::builder)?, 60 #[cfg(feature = "__rustls")] 61 original: Cert::Der(der.to_owned()), 62 }) 63 } 64 65 /// Create a `Certificate` from a PEM encoded certificate 66 /// 67 /// # Examples 68 /// 69 /// ``` 70 /// # use std::fs::File; 71 /// # use std::io::Read; 72 /// # fn cert() -> Result<(), Box<std::error::Error>> { 73 /// let mut buf = Vec::new(); 74 /// File::open("my_cert.pem")? 75 /// .read_to_end(&mut buf)?; 76 /// let cert = reqwest::Certificate::from_pem(&buf)?; 77 /// # drop(cert); 78 /// # Ok(()) 79 /// # } 80 /// ``` from_pem(pem: &[u8]) -> crate::Result<Certificate>81 pub fn from_pem(pem: &[u8]) -> crate::Result<Certificate> { 82 Ok(Certificate { 83 #[cfg(feature = "native-tls-crate")] 84 native: native_tls_crate::Certificate::from_pem(pem).map_err(crate::error::builder)?, 85 #[cfg(feature = "__rustls")] 86 original: Cert::Pem(pem.to_owned()), 87 }) 88 } 89 90 #[cfg(feature = "native-tls-crate")] add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder)91 pub(crate) fn add_to_native_tls(self, tls: &mut native_tls_crate::TlsConnectorBuilder) { 92 tls.add_root_certificate(self.native); 93 } 94 95 #[cfg(feature = "__rustls")] add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()>96 pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> { 97 use rustls::internal::pemfile; 98 use std::io::Cursor; 99 100 match self.original { 101 Cert::Der(buf) => tls 102 .root_store 103 .add(&::rustls::Certificate(buf)) 104 .map_err(|e| crate::error::builder(TLSError::WebPKIError(e)))?, 105 Cert::Pem(buf) => { 106 let mut pem = Cursor::new(buf); 107 let certs = pemfile::certs(&mut pem).map_err(|_| { 108 crate::error::builder(TLSError::General(String::from( 109 "No valid certificate was found", 110 ))) 111 })?; 112 for c in certs { 113 tls.root_store 114 .add(&c) 115 .map_err(|e| crate::error::builder(TLSError::WebPKIError(e)))?; 116 } 117 } 118 } 119 Ok(()) 120 } 121 } 122 123 impl Identity { 124 /// Parses a DER-formatted PKCS #12 archive, using the specified password to decrypt the key. 125 /// 126 /// The archive should contain a leaf certificate and its private key, as well any intermediate 127 /// certificates that allow clients to build a chain to a trusted root. 128 /// The chain certificates should be in order from the leaf certificate towards the root. 129 /// 130 /// PKCS #12 archives typically have the file extension `.p12` or `.pfx`, and can be created 131 /// with the OpenSSL `pkcs12` tool: 132 /// 133 /// ```bash 134 /// openssl pkcs12 -export -out identity.pfx -inkey key.pem -in cert.pem -certfile chain_certs.pem 135 /// ``` 136 /// 137 /// # Examples 138 /// 139 /// ``` 140 /// # use std::fs::File; 141 /// # use std::io::Read; 142 /// # fn pkcs12() -> Result<(), Box<std::error::Error>> { 143 /// let mut buf = Vec::new(); 144 /// File::open("my-ident.pfx")? 145 /// .read_to_end(&mut buf)?; 146 /// let pkcs12 = reqwest::Identity::from_pkcs12_der(&buf, "my-privkey-password")?; 147 /// # drop(pkcs12); 148 /// # Ok(()) 149 /// # } 150 /// ``` 151 /// 152 /// # Optional 153 /// 154 /// This requires the `native-tls` Cargo feature enabled. 155 #[cfg(feature = "native-tls")] from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity>156 pub fn from_pkcs12_der(der: &[u8], password: &str) -> crate::Result<Identity> { 157 Ok(Identity { 158 inner: ClientCert::Pkcs12( 159 native_tls_crate::Identity::from_pkcs12(der, password) 160 .map_err(crate::error::builder)?, 161 ), 162 }) 163 } 164 165 /// Parses PEM encoded private key and certificate. 166 /// 167 /// The input should contain a PEM encoded private key 168 /// and at least one PEM encoded certificate. 169 /// 170 /// # Examples 171 /// 172 /// ``` 173 /// # use std::fs::File; 174 /// # use std::io::Read; 175 /// # fn pem() -> Result<(), Box<std::error::Error>> { 176 /// let mut buf = Vec::new(); 177 /// File::open("my-ident.pem")? 178 /// .read_to_end(&mut buf)?; 179 /// let id = reqwest::Identity::from_pem(&buf)?; 180 /// # drop(id); 181 /// # Ok(()) 182 /// # } 183 /// ``` 184 /// 185 /// # Optional 186 /// 187 /// This requires the `rustls-tls(-...)` Cargo feature enabled. 188 #[cfg(feature = "__rustls")] from_pem(buf: &[u8]) -> crate::Result<Identity>189 pub fn from_pem(buf: &[u8]) -> crate::Result<Identity> { 190 use rustls::internal::pemfile; 191 use std::io::Cursor; 192 193 let (key, certs) = { 194 let mut pem = Cursor::new(buf); 195 let certs = pemfile::certs(&mut pem) 196 .map_err(|_| TLSError::General(String::from("No valid certificate was found"))) 197 .map_err(crate::error::builder)?; 198 pem.set_position(0); 199 let mut sk = pemfile::pkcs8_private_keys(&mut pem) 200 .and_then(|pkcs8_keys| { 201 if pkcs8_keys.is_empty() { 202 Err(()) 203 } else { 204 Ok(pkcs8_keys) 205 } 206 }) 207 .or_else(|_| { 208 pem.set_position(0); 209 pemfile::rsa_private_keys(&mut pem) 210 }) 211 .map_err(|_| TLSError::General(String::from("No valid private key was found"))) 212 .map_err(crate::error::builder)?; 213 if let (Some(sk), false) = (sk.pop(), certs.is_empty()) { 214 (sk, certs) 215 } else { 216 return Err(crate::error::builder(TLSError::General(String::from( 217 "private key or certificate not found", 218 )))); 219 } 220 }; 221 222 Ok(Identity { 223 inner: ClientCert::Pem { key, certs }, 224 }) 225 } 226 227 #[cfg(feature = "native-tls")] add_to_native_tls( self, tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()>228 pub(crate) fn add_to_native_tls( 229 self, 230 tls: &mut native_tls_crate::TlsConnectorBuilder, 231 ) -> crate::Result<()> { 232 match self.inner { 233 ClientCert::Pkcs12(id) => { 234 tls.identity(id); 235 Ok(()) 236 } 237 #[cfg(feature = "__rustls")] 238 ClientCert::Pem { .. } => Err(crate::error::builder("incompatible TLS identity type")), 239 } 240 } 241 242 #[cfg(feature = "__rustls")] add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()>243 pub(crate) fn add_to_rustls(self, tls: &mut rustls::ClientConfig) -> crate::Result<()> { 244 match self.inner { 245 ClientCert::Pem { key, certs } => { 246 tls.set_single_client_cert(certs, key) 247 .map_err(|e| crate::error::builder(e))?; 248 Ok(()) 249 } 250 #[cfg(feature = "native-tls")] 251 ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")), 252 } 253 } 254 } 255 256 impl fmt::Debug for Certificate { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result257 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 258 f.debug_struct("Certificate").finish() 259 } 260 } 261 262 impl fmt::Debug for Identity { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result263 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 264 f.debug_struct("Identity").finish() 265 } 266 } 267 268 pub(crate) enum TlsBackend { 269 #[cfg(feature = "default-tls")] 270 Default, 271 #[cfg(feature = "native-tls")] 272 BuiltNativeTls(native_tls_crate::TlsConnector), 273 #[cfg(feature = "__rustls")] 274 Rustls, 275 #[cfg(feature = "__rustls")] 276 BuiltRustls(rustls::ClientConfig), 277 #[cfg(any(feature = "native-tls", feature = "__rustls",))] 278 UnknownPreconfigured, 279 } 280 281 impl fmt::Debug for TlsBackend { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 283 match self { 284 #[cfg(feature = "default-tls")] 285 TlsBackend::Default => write!(f, "Default"), 286 #[cfg(feature = "native-tls")] 287 TlsBackend::BuiltNativeTls(_) => write!(f, "BuiltNativeTls"), 288 #[cfg(feature = "__rustls")] 289 TlsBackend::Rustls => write!(f, "Rustls"), 290 #[cfg(feature = "__rustls")] 291 TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"), 292 #[cfg(any(feature = "native-tls", feature = "__rustls",))] 293 TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"), 294 } 295 } 296 } 297 298 impl Default for TlsBackend { default() -> TlsBackend299 fn default() -> TlsBackend { 300 #[cfg(feature = "default-tls")] 301 { 302 TlsBackend::Default 303 } 304 305 #[cfg(all(feature = "__rustls", not(feature = "default-tls")))] 306 { 307 TlsBackend::Rustls 308 } 309 } 310 } 311 312 #[cfg(feature = "__rustls")] 313 pub(crate) struct NoVerifier; 314 315 #[cfg(feature = "__rustls")] 316 impl ServerCertVerifier for NoVerifier { verify_server_cert( &self, _roots: &RootCertStore, _presented_certs: &[rustls::Certificate], _dns_name: DNSNameRef, _ocsp_response: &[u8], ) -> Result<ServerCertVerified, TLSError>317 fn verify_server_cert( 318 &self, 319 _roots: &RootCertStore, 320 _presented_certs: &[rustls::Certificate], 321 _dns_name: DNSNameRef, 322 _ocsp_response: &[u8], 323 ) -> Result<ServerCertVerified, TLSError> { 324 Ok(ServerCertVerified::assertion()) 325 } 326 } 327 328 #[cfg(test)] 329 mod tests { 330 use super::*; 331 332 #[cfg(feature = "default-tls")] 333 #[test] certificate_from_der_invalid()334 fn certificate_from_der_invalid() { 335 Certificate::from_der(b"not der").unwrap_err(); 336 } 337 338 #[cfg(feature = "default-tls")] 339 #[test] certificate_from_pem_invalid()340 fn certificate_from_pem_invalid() { 341 Certificate::from_pem(b"not pem").unwrap_err(); 342 } 343 344 #[cfg(feature = "native-tls")] 345 #[test] identity_from_pkcs12_der_invalid()346 fn identity_from_pkcs12_der_invalid() { 347 Identity::from_pkcs12_der(b"not der", "nope").unwrap_err(); 348 } 349 350 #[cfg(feature = "__rustls")] 351 #[test] identity_from_pem_invalid()352 fn identity_from_pem_invalid() { 353 Identity::from_pem(b"not pem").unwrap_err(); 354 } 355 356 #[cfg(feature = "__rustls")] 357 #[test] identity_from_pem_pkcs1_key()358 fn identity_from_pem_pkcs1_key() { 359 let pem = b"-----BEGIN CERTIFICATE-----\n\ 360 -----END CERTIFICATE-----\n\ 361 -----BEGIN RSA PRIVATE KEY-----\n\ 362 -----END RSA PRIVATE KEY-----\n"; 363 364 Identity::from_pem(pem).unwrap(); 365 } 366 } 367