From 8b38de0b2f3ce4acc7be0164856859e0e31025b5 Mon Sep 17 00:00:00 2001 From: JR Conlin Date: Mon, 5 Jun 2023 12:27:24 -0700 Subject: [PATCH] feat: add Client::certificate_parts() method (#72) --- Cargo.lock | 2 +- Cargo.toml | 3 ++- examples/certificate_client.rs | 1 - examples/token_client.rs | 1 - src/client.rs | 24 +++++++++++++++++++++++- src/error.rs | 2 +- src/request/notification.rs | 2 +- src/request/payload.rs | 2 +- src/signer.rs | 2 +- test_cert/README.md | 11 +++++++++++ test_cert/test.crt | 21 +++++++++++++++++++++ test_cert/test.key | 28 ++++++++++++++++++++++++++++ 12 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 test_cert/README.md create mode 100644 test_cert/test.crt create mode 100644 test_cert/test.key diff --git a/Cargo.lock b/Cargo.lock index 00df152f..ca51654a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 3 [[package]] name = "a2" -version = "0.7.0" +version = "0.7.1" dependencies = [ "argparse", "base64 0.20.0", diff --git a/Cargo.toml b/Cargo.toml index 5c066df2..76b4d457 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "a2" -version = "0.7.0" +version = "0.7.1" authors = [ "Harry Bairstow ", "Julius de Bruijn ", @@ -14,6 +14,7 @@ repository = "https://github.com/walletconnect/a2.git" homepage = "https://github.com/walletconnect/a2" documentation = "https://docs.rs/a2" edition = "2021" +rust-version = "1.60" # set the minimum rust version we can work with. [features] default = ["openssl"] diff --git a/examples/certificate_client.rs b/examples/certificate_client.rs index 709ba03f..9f08d8bb 100644 --- a/examples/certificate_client.rs +++ b/examples/certificate_client.rs @@ -1,6 +1,5 @@ use a2::{Client, DefaultNotificationBuilder, NotificationBuilder, NotificationOptions}; use argparse::{ArgumentParser, Store, StoreOption, StoreTrue}; -use tokio; // An example client connectiong to APNs with a certificate and key #[tokio::main] diff --git a/examples/token_client.rs b/examples/token_client.rs index 32c3c0af..b60cff02 100644 --- a/examples/token_client.rs +++ b/examples/token_client.rs @@ -1,6 +1,5 @@ use argparse::{ArgumentParser, Store, StoreOption, StoreTrue}; use std::fs::File; -use tokio; use a2::{Client, DefaultNotificationBuilder, Endpoint, NotificationBuilder, NotificationOptions}; diff --git a/src/client.rs b/src/client.rs index 8339749c..19c7f721 100644 --- a/src/client.rs +++ b/src/client.rs @@ -80,6 +80,15 @@ impl Client { Ok(Self::new(connector, None, endpoint)) } + /// 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/) + pub fn certificate_parts(cert_pem: &[u8], key_pem: &[u8], endpoint: Endpoint) -> Result { + let connector = AlpnConnector::with_client_cert(cert_pem, key_pem)?; + + Ok(Self::new(connector, None, endpoint)) + } + /// Create a connection to APNs using system certificates, signing every /// request with a signature using a private key, key id and team id /// provisioned from your [Apple developer @@ -181,7 +190,7 @@ mod tests { use hyper::Method; use hyper_alpn::AlpnConnector; - const PRIVATE_KEY: &'static str = "-----BEGIN PRIVATE KEY----- + const PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8g/n6j9roKvnUkwu lCEIvbDqlUhA5FOzcakkG90E8L+hRANCAATKS2ZExEybUvchRDuKBftotMwVEus3 jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ @@ -458,4 +467,17 @@ jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ assert_eq!(payload.to_json_string().unwrap(), body_str,); } + + #[tokio::test] + /// Try to create a test client using the unencrypted key & cert provided. + /// These are test values that do not work with Apple, but mimic the sort + /// of values you should get from the Apple Developer Console. + async fn test_cert_parts() -> Result<(), Error> { + let key: Vec = include_str!("../test_cert/test.key").bytes().collect(); + let cert: Vec = include_str!("../test_cert/test.crt").bytes().collect(); + + let c = Client::certificate_parts(&cert, &key, Endpoint::Sandbox)?; + assert!(c.signer.is_none()); + Ok(()) + } } diff --git a/src/error.rs b/src/error.rs index a4ed649d..af7ffb12 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -///! Error and result module +/// Error and result module use crate::{response::Response, signer::SignerError}; use std::io; use thiserror::Error; diff --git a/src/request/notification.rs b/src/request/notification.rs index 354f556e..f478729a 100644 --- a/src/request/notification.rs +++ b/src/request/notification.rs @@ -1,4 +1,4 @@ -///! The `aps` notification content builders +/// The `aps` notification content builders mod default; mod options; mod web; diff --git a/src/request/payload.rs b/src/request/payload.rs index 71c7c3f7..f6268dfe 100644 --- a/src/request/payload.rs +++ b/src/request/payload.rs @@ -1,4 +1,4 @@ -///! Payload with `aps` and custom data +/// Payload with `aps` and custom data use crate::error::Error; use crate::request::notification::{DefaultAlert, NotificationOptions, WebPushAlert}; use erased_serde::Serialize; diff --git a/src/signer.rs b/src/signer.rs index df93fb5f..20738fc4 100644 --- a/src/signer.rs +++ b/src/signer.rs @@ -248,7 +248,7 @@ fn get_time() -> i64 { mod tests { use super::*; - const PRIVATE_KEY: &'static str = "-----BEGIN PRIVATE KEY----- + const PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY----- MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8g/n6j9roKvnUkwu lCEIvbDqlUhA5FOzcakkG90E8L+hRANCAATKS2ZExEybUvchRDuKBftotMwVEus3 jDwmlD1Gg0yJt1e38djFwsxsfr5q2hv0Rj9fTEqAPr8H7mGm0wKxZ7iQ diff --git a/test_cert/README.md b/test_cert/README.md new file mode 100644 index 00000000..8764bd59 --- /dev/null +++ b/test_cert/README.md @@ -0,0 +1,11 @@ +This contains a self signed cert for test purposes. The password is +"test". + +These values are not encrypted + +Key and Cert generation (unencrypted): + +``` +$ openssl req -newkey rsa:2048 -nodes \ + -keyout test.key -x509 -days 3650 -out test.crt +``` diff --git a/test_cert/test.crt b/test_cert/test.crt new file mode 100644 index 00000000..9abdc107 --- /dev/null +++ b/test_cert/test.crt @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhTCCAm2gAwIBAgIUJgWPx+sbai7lWxPHuIS6aFXM9jwwDQYJKoZIhvcNAQEL +BQAwUjELMAkGA1UEBhMCVVMxDTALBgNVBAgMBFRlc3QxDTALBgNVBAcMBFRlc3Qx +FjAUBgNVBAoMDVRlc3R5IFRlc3RpbmcxDTALBgNVBAMMBFRlc3QwHhcNMjMwNTIy +MjIwOTUxWhcNMzMwNTE5MjIwOTUxWjBSMQswCQYDVQQGEwJVUzENMAsGA1UECAwE +VGVzdDENMAsGA1UEBwwEVGVzdDEWMBQGA1UECgwNVGVzdHkgVGVzdGluZzENMAsG +A1UEAwwEVGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOUGcm/w +hdvnCJIxFgR3Quj/DLmeush7lHh5J77bMXUWTJy/CltQZdQtJVYg35bmVTDMtjUw +1crkhFjgsF90uYPWUf5JgIHpg+5WXMkP686GvPC8sscWjMFVnqs1ot2gdMQkTSPL +0Kaxfch02TXJjK1PI6bnbJGX6qrFLR2/7a6IfUtAn2eFwHDHhZOKwNichEnlCxc+ +TwjHnbIDGtupa5OMF4ILAH60/YfRop9uE7Ueekib1zWFhrFrVKcUvjbIXlrXEybp +ojSktmesgKg78Nr7ZiBrMR/wuZAdcnWUWHZwrOKyJlYO3VmRNhbAazahtHzXy1pZ +D7aIWMjdmT0pCAUCAwEAAaNTMFEwHQYDVR0OBBYEFGiyrdtyFdw0USMw47NVcZZx ++jzyMB8GA1UdIwQYMBaAFGiyrdtyFdw0USMw47NVcZZx+jzyMA8GA1UdEwEB/wQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGAagQG+vrBTtaUCEogWBY93+gvTEzB7 +kIE3vP/BX8cNwRbnNM4O4AKB85/oh5RWsEc6aiKfF5bIUf8ixXBmGwet/sb7OIAU +VGZY5IyUGCNiQ5Q3elqqROFHz4Vkx6oJkly1vjYJFsZgjm5JIFD1IMqHMdGPdpZb +Bez9Asr6xFuikF2V5X1Q1QD25tshZqHquS7kZ5WLqv1OJf8z+hcyRQKfKbvOgPsd +9Pntn/5ftTN+Kp73Lgyfsf5G4JgBVyPNPOgIwMYkfE0o6QoKtIkxCVBGw1QlNcyC +j4hNPXkc4nde8MWlIOCFSg+ri7qehR10dcMQyb7RQG9Nq3Qq+4KI15g= +-----END CERTIFICATE----- diff --git a/test_cert/test.key b/test_cert/test.key new file mode 100644 index 00000000..2881aaaf --- /dev/null +++ b/test_cert/test.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDlBnJv8IXb5wiS +MRYEd0Lo/wy5nrrIe5R4eSe+2zF1FkycvwpbUGXULSVWIN+W5lUwzLY1MNXK5IRY +4LBfdLmD1lH+SYCB6YPuVlzJD+vOhrzwvLLHFozBVZ6rNaLdoHTEJE0jy9CmsX3I +dNk1yYytTyOm52yRl+qqxS0dv+2uiH1LQJ9nhcBwx4WTisDYnIRJ5QsXPk8Ix52y +AxrbqWuTjBeCCwB+tP2H0aKfbhO1HnpIm9c1hYaxa1SnFL42yF5a1xMm6aI0pLZn +rICoO/Da+2YgazEf8LmQHXJ1lFh2cKzisiZWDt1ZkTYWwGs2obR818taWQ+2iFjI +3Zk9KQgFAgMBAAECggEAFD+0AdD6iiJsxT17xzH4LiYmrN6rgFWUyetZp+vfUo0s +gYtOfMZTSnrMsJxqyFUIYS59nvRODV3o6abBUUnpAQ6uOdUBJ1CgboPKOP35s6KB +X/4psaUnwAw0NspLjQbGbB+9hkBbQUcIv569JElJRnrBxIb04rvTbqfM67LFbpx4 +h3/NiWMBvXoOxN7/v/2wO7rBPJMO00NEEmLfX9/zOpDEjD2XOxTLqGyQ5Y6OxWkt +tYAEo57As0OXZ9Yh11Jv4+McDUCZRlx8ePPOoedTIUKVf2P4yJc2J6yr4CFLBa5a +G5l4CMOI/GJ9l9Ydp+Sr1+30TPpMuBFGOVCG+Buf/QKBgQDpvjQWXuW+9qkxO8h5 +ZQmpeJRr5XMd3+/ZoqEeK2hf6KhvRm73gyUdEcKwFwcqmkD2Okc0Xg6QfRZfrYYc +IR0dPUAvpksGtnUvvfg/+GcIJiKrw5fXUfWlyGiCpBwpL+cG7S1c4FuT7eSwJ7GA +2DpFFIztvv3CBWL5Rz7iQ/lgJwKBgQD61T2thNaX4KgxbZv9p51FhqY/Dn6nGBvg +2iHchWYYHllwX3LwyTHEkryb7rSRAlya1pJG/YvUnH99DGYTDOTi91BZUUIVZ7F8 +FZE4fyVgQ5u9Q9Pq3tfrnBuTGwnMgKJPhztfyHSxeHropj72eVcOPcjQ125fqE9g +XSq2TB4F8wKBgQDR2be91dkCB0WDNB8aDcIM6nqmG8usKJ6Xj9CC24nLgX+m119M +y+sIHCfkG+iNMQvdhBjlRQRiaEsr/wgGPRx8Yb88iFmXXzv6bt1v4T3vLP23o9Sw +tZ6LBk/96gR2XdFWgJ1XYv4U42GLXTeZa4d0+axEzlHYXSmsj3A/h7NdxQKBgQDk +FU09YGJlvnISaBIFSAnZc8Pt6LdAJ8sJ3jAWPvMEEWzQoup4iuqHTcrVm+xzQ4uD +fIOMq/cfgKLoyYJz5jCnNa3JWftWTXD1XQMtNWh+LVwLcQbdNn9ujggA7wAtBfcR +i+1wfm2mBuD0dl8gblu75nCZfputvfVXscAp/fL7AwKBgQCzpRvFcfJGLgL/WtIZ +4kocABWdhfsCIAfEZgPNno0M6COCxaKDP2a4z5m8monn9bn0M4oCb0L8N780oa21 +nNLXE5GwAfI/g4sV+PLNUW12xxEclQ4cLkdDCpe9T74CCbCZTc6bAbmG8lEjY915 +yQSmE2K4xWj3jxI7UrCurwPvGg== +-----END PRIVATE KEY-----