diff --git a/Cargo.toml b/Cargo.toml index 568f59d92..7d7462271 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,7 +110,7 @@ ipnet = "2.3" ## default-tls hyper-tls = { version = "0.5", optional = true } -native-tls-crate = { version = "0.2.8", optional = true, package = "native-tls" } +native-tls-crate = { version = "0.2.10", optional = true, package = "native-tls" } tokio-native-tls = { version = "0.3.0", optional = true } # rustls-tls diff --git a/src/tls.rs b/src/tls.rs index 052fb76b4..db898e84b 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -45,6 +45,8 @@ pub struct Identity { enum ClientCert { #[cfg(feature = "native-tls")] Pkcs12(native_tls_crate::Identity), + #[cfg(feature = "native-tls")] + Pkcs8(native_tls_crate::Identity), #[cfg(feature = "__rustls")] Pem { key: rustls::PrivateKey, @@ -179,6 +181,39 @@ impl Identity { }) } + /// Parses a chain of PEM encoded X509 certificates, with the leaf certificate first. + /// `key` is a PEM encoded PKCS #8 formatted private key for the leaf certificate. + /// + /// The certificate chain should contain any intermediate cerficates that should be sent to + /// clients to allow them to build a chain to a trusted root. + /// + /// A certificate chain here means a series of PEM encoded certificates concatenated together. + /// + /// # Examples + /// + /// ``` + /// # use std::fs; + /// # fn pkcs8() -> Result<(), Box> { + /// let cert = fs::read("client.pem")?; + /// let key = fs::read("key.pem")?; + /// let pkcs8 = reqwest::Identity::from_pkcs8_pem(&cert, &key)?; + /// # drop(pkcs8); + /// # Ok(()) + /// # } + /// ``` + /// + /// # Optional + /// + /// This requires the `native-tls` Cargo feature enabled. + #[cfg(feature = "native-tls")] + pub fn from_pkcs8_pem(pem: &[u8], key: &[u8]) -> crate::Result { + Ok(Identity { + inner: ClientCert::Pkcs8( + native_tls_crate::Identity::from_pkcs8(pem, key).map_err(crate::error::builder)?, + ), + }) + } + /// Parses PEM encoded private key and certificate. /// /// The input should contain a PEM encoded private key @@ -253,7 +288,7 @@ impl Identity { tls: &mut native_tls_crate::TlsConnectorBuilder, ) -> crate::Result<()> { match self.inner { - ClientCert::Pkcs12(id) => { + ClientCert::Pkcs12(id) | ClientCert::Pkcs8(id) => { tls.identity(id); Ok(()) } @@ -275,7 +310,9 @@ impl Identity { .with_single_cert(certs, key) .map_err(crate::error::builder), #[cfg(feature = "native-tls")] - ClientCert::Pkcs12(..) => Err(crate::error::builder("incompatible TLS identity type")), + ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => { + Err(crate::error::builder("incompatible TLS identity type")) + } } } } @@ -443,6 +480,12 @@ mod tests { Identity::from_pkcs12_der(b"not der", "nope").unwrap_err(); } + #[cfg(feature = "native-tls")] + #[test] + fn identity_from_pkcs8_pem_invalid() { + Identity::from_pkcs8_pem(b"not pem", b"not key").unwrap_err(); + } + #[cfg(feature = "__rustls")] #[test] fn identity_from_pem_invalid() {