From 1b23557101d16d7162ccfa5c3bd972149c9262b9 Mon Sep 17 00:00:00 2001 From: Paul Vorobyev Date: Sun, 28 Jul 2024 21:22:26 +0000 Subject: [PATCH 1/4] Add TLS 1.2 support via configuration --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/client.rs | 25 +++++++++++++++++++------ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3afe4a81..4529d9cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "a2" -version = "0.10.0" +version = "0.10.1" dependencies = [ "argparse", "base64", diff --git a/Cargo.toml b/Cargo.toml index 29bdaeeb..6edc63b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a2" -version = "0.10.0" +version = "0.10.1" authors = [ "Harry Bairstow ", "Julius de Bruijn ", diff --git a/src/client.rs b/src/client.rs index 9da3c8d4..535317d4 100644 --- a/src/client.rs +++ b/src/client.rs @@ -69,6 +69,9 @@ pub struct ClientConfig { pub request_timeout_secs: Option, /// The timeout for idle sockets being kept alive pub pool_idle_timeout_secs: Option, + /// We use TLS 1.3 by default, setting this to `true` overrides it to use TLS 1.2. + /// Defaults to `false`. + pub use_tls_12_override: bool, } impl Default for ClientConfig { @@ -77,6 +80,7 @@ impl Default for ClientConfig { endpoint: Endpoint::Production, request_timeout_secs: Some(DEFAULT_REQUEST_TIMEOUT_SECS), pool_idle_timeout_secs: Some(600), + use_tls_12_override: false, } } } @@ -130,6 +134,7 @@ impl ClientBuilder { endpoint, request_timeout_secs, pool_idle_timeout_secs, + use_tls_12_override: _, }, signer, connector, @@ -188,7 +193,7 @@ impl Client { let Some((cert, pkey)) = pkcs.cert.zip(pkcs.pkey) else { return Err(Error::InvalidCertificate); }; - let connector = client_cert_connector(&cert.to_pem()?, &pkey.private_key_to_pem_pkcs8()?)?; + let connector = client_cert_connector(&cert.to_pem()?, &pkey.private_key_to_pem_pkcs8()?, &config)?; Ok(Self::builder().connector(connector).config(config).build()) } @@ -197,7 +202,7 @@ impl Client { /// key, extracted from the provider client certificate you obtain from your /// [Apple developer account](https://developer.apple.com/account/) pub fn certificate_parts(cert_pem: &[u8], key_pem: &[u8], config: ClientConfig) -> Result { - let connector = client_cert_connector(cert_pem, key_pem)?; + let connector = client_cert_connector(cert_pem, key_pem, &config)?; Ok(Self::builder().config(config).connector(connector).build()) } @@ -309,7 +314,11 @@ fn default_connector() -> HyperConnector { .build() } -fn client_cert_connector(mut cert_pem: &[u8], mut key_pem: &[u8]) -> Result { +fn client_cert_connector( + mut cert_pem: &[u8], + mut key_pem: &[u8], + client_config: &ClientConfig, +) -> Result { let private_key_error = || io::Error::new(io::ErrorKind::InvalidData, "private key"); let key = rustls_pemfile::pkcs8_private_keys(&mut key_pem) @@ -320,9 +329,13 @@ fn client_cert_connector(mut cert_pem: &[u8], mut key_pem: &[u8]) -> Result, _> = rustls_pemfile::certs(&mut cert_pem).collect(); let cert_chain = cert_chain.map_err(|_| private_key_error())?; - let config = rustls::client::ClientConfig::builder() - .with_webpki_roots() - .with_client_auth_cert(cert_chain, key.into())?; + let config = if client_config.use_tls_12_override { + rustls::client::ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS12]) + } else { + rustls::client::ClientConfig::builder() + } + .with_webpki_roots() + .with_client_auth_cert(cert_chain, key.into())?; Ok(HttpsConnectorBuilder::new() .with_tls_config(config) From 4ea1a63cd15832b47c45cf6ddcdcda8e86f99d21 Mon Sep 17 00:00:00 2001 From: Paul Vorobyev Date: Sun, 28 Jul 2024 21:38:39 +0000 Subject: [PATCH 2/4] Add certificate_resolver method --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/client.rs | 25 +++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4529d9cc..c18bd858 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "a2" -version = "0.10.1" +version = "0.10.2" dependencies = [ "argparse", "base64", diff --git a/Cargo.toml b/Cargo.toml index 6edc63b3..8c639b6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a2" -version = "0.10.1" +version = "0.10.2" authors = [ "Harry Bairstow ", "Julius de Bruijn ", diff --git a/src/client.rs b/src/client.rs index 535317d4..c838633a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -16,8 +16,10 @@ use hyper_rustls::{ConfigBuilderExt, HttpsConnector, HttpsConnectorBuilder}; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client as HttpClient; use hyper_util::rt::TokioExecutor; +use rustls::client::ResolvesClientCert; use std::convert::Infallible; use std::io::Read; +use std::sync::Arc; use std::time::Duration; use std::{fmt, io}; @@ -198,6 +200,29 @@ impl Client { Ok(Self::builder().connector(connector).config(config).build()) } + /// Create a connection to APNs using the provider client certificate which + /// you obtain from your [Apple developer + /// account](https://developer.apple.com/account/), chosen dynamically via + /// the rustls `ResolvesClientCert` trait. Prefer certificate() over this; use + /// this if you're using a key management service and don't have the private + /// key available. + pub fn certificate_resolver( + client_auth_cert_resolver: Arc, + config: ClientConfig, + ) -> Result { + let tls_config = rustls::client::ClientConfig::builder() + .with_webpki_roots() + .with_client_cert_resolver(client_auth_cert_resolver); + + let connector = HttpsConnectorBuilder::new() + .with_tls_config(tls_config) + .https_only() + .enable_http2() + .build(); + + Ok(Self::builder().connector(connector).config(config).build()) + } + /// Create a connection to APNs using the raw PEM-formatted certificate and /// key, extracted from the provider client certificate you obtain from your /// [Apple developer account](https://developer.apple.com/account/) From f7379d9196d46ab1e51757211983b8ec25aca97b Mon Sep 17 00:00:00 2001 From: Paul Vorobyev Date: Sun, 28 Jul 2024 22:03:24 +0000 Subject: [PATCH 3/4] Use TLS 1.2 support --- src/client.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/client.rs b/src/client.rs index c838633a..26ee2e93 100644 --- a/src/client.rs +++ b/src/client.rs @@ -94,6 +94,14 @@ impl ClientConfig { ..Default::default() } } + + pub fn get_tls_config_builder(&self) -> rustls::ConfigBuilder { + if self.use_tls_12_override { + rustls::client::ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS12]) + } else { + rustls::client::ClientConfig::builder() + } + } } #[derive(Debug, Clone)] @@ -210,7 +218,8 @@ impl Client { client_auth_cert_resolver: Arc, config: ClientConfig, ) -> Result { - let tls_config = rustls::client::ClientConfig::builder() + let tls_config = config + .get_tls_config_builder() .with_webpki_roots() .with_client_cert_resolver(client_auth_cert_resolver); @@ -354,13 +363,10 @@ fn client_cert_connector( let cert_chain: Result, _> = rustls_pemfile::certs(&mut cert_pem).collect(); let cert_chain = cert_chain.map_err(|_| private_key_error())?; - let config = if client_config.use_tls_12_override { - rustls::client::ClientConfig::builder_with_protocol_versions(&[&rustls::version::TLS12]) - } else { - rustls::client::ClientConfig::builder() - } - .with_webpki_roots() - .with_client_auth_cert(cert_chain, key.into())?; + let config = client_config + .get_tls_config_builder() + .with_webpki_roots() + .with_client_auth_cert(cert_chain, key.into())?; Ok(HttpsConnectorBuilder::new() .with_tls_config(config) From c021479dc2373ca4fb35373dea46c96b74d7cf43 Mon Sep 17 00:00:00 2001 From: Paul Vorobyev Date: Sun, 28 Jul 2024 22:10:55 +0000 Subject: [PATCH 4/4] pub use ResolvesClientcert --- src/client.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index 26ee2e93..c060d766 100644 --- a/src/client.rs +++ b/src/client.rs @@ -16,7 +16,7 @@ use hyper_rustls::{ConfigBuilderExt, HttpsConnector, HttpsConnectorBuilder}; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client as HttpClient; use hyper_util::rt::TokioExecutor; -use rustls::client::ResolvesClientCert; +pub use rustls::client::ResolvesClientCert; use std::convert::Infallible; use std::io::Read; use std::sync::Arc; diff --git a/src/lib.rs b/src/lib.rs index 513fac04..c87b841b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,6 +131,6 @@ pub use crate::request::notification::{ pub use crate::response::{ErrorBody, ErrorReason, Response}; -pub use crate::client::{Client, ClientConfig, Endpoint}; +pub use crate::client::{Client, ClientConfig, Endpoint, ResolvesClientCert}; pub use crate::error::Error;