From 9c580784dbbf7f40ef16316fe11190ea1e833177 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Thu, 20 Aug 2020 12:28:23 +0200 Subject: [PATCH 01/10] fixes compatibility with jdk 9+ and jdk8u252+ --- .../com/clevertap/apns/clients/SyncOkHttpApnsClient.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java index 86e8c22..c3021eb 100644 --- a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java +++ b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java @@ -43,6 +43,7 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; +import java.util.Arrays; import java.util.UUID; /** @@ -180,9 +181,14 @@ public SyncOkHttpApnsClient(InputStream certificate, String password, boolean pr tmf.init((KeyStore) null); sslContext.init(keyManagers, tmf.getTrustManagers(), null); + TrustManager[] trustManagers = tmf.getTrustManagers(); + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); + } + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); - builder.sslSocketFactory(sslSocketFactory); + builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0]); client = builder.build(); From 7eaf2d4cdcacf475738bcdec268e12c0cf92946c Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Thu, 20 Aug 2020 12:36:28 +0200 Subject: [PATCH 02/10] Update to OkHttp for jdk9+ and jdk8u252+ compatibility --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1b5b052..3f70f44 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,7 @@ com.squareup.okhttp3 okhttp - 3.2.0 + 4.8.1 From d2594ce84d5be738106909fd33a64a2838fefe41 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Thu, 20 Aug 2020 15:08:47 +0200 Subject: [PATCH 03/10] Update Version and added sait maven repo --- pom.xml | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index 3f70f44..65a80f8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.clevertap.apns apns-http2 - 1.0.7 + 1.0.7-fix_jdk8u252 apns-http2 A library for communicating with the Apple Push Gateway in HTTP/2. @@ -46,6 +46,17 @@ + + + + + org.apache.maven.wagon + wagon-ssh + 3.3.3 + + + + org.apache.maven.plugins @@ -105,12 +116,11 @@ - - bintray-clevertap-Maven - clevertap-Maven - https://api.bintray.com/maven/clevertap/Maven/apns-http2/;publish=1 - - + + maven.sait-srv.local + scp://vlx-svnam.sait-srv.local/maven + + scm:git:git://github.com/CleverTap/apns-http2.git From 764e87c0206769b7c2c7b86b2600a24599a52ab0 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Mon, 18 Jan 2021 15:45:37 +0100 Subject: [PATCH 04/10] changed maven repository --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 65a80f8..c1e62f9 100644 --- a/pom.xml +++ b/pom.xml @@ -117,8 +117,8 @@ - maven.sait-srv.local - scp://vlx-svnam.sait-srv.local/maven + gitam.sait-srv.local + https://gitam.sait-srv.local/api/v4/projects/134/packages/maven From 068be1806b88cdf219ee9f3fe9ac357e0d33e5af Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Tue, 18 May 2021 16:39:11 +0200 Subject: [PATCH 05/10] added first unit test --- pom.xml | 18 +- .../clients/SyncOkHttpApnsClientTest.java | 181 ++++++++++++++++++ 2 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/clevertap/apns/clients/SyncOkHttpApnsClientTest.java diff --git a/pom.xml b/pom.xml index 3f70f44..aed2cec 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,20 @@ 4.8.1 + + com.squareup.okhttp3 + mockwebserver + 4.8.1 + test + + + + com.squareup.okhttp3 + okhttp-tls + 4.8.1 + test + + junit junit @@ -105,12 +119,12 @@ - + bintray-clevertap-Maven clevertap-Maven https://api.bintray.com/maven/clevertap/Maven/apns-http2/;publish=1 - + scm:git:git://github.com/CleverTap/apns-http2.git diff --git a/src/test/java/com/clevertap/apns/clients/SyncOkHttpApnsClientTest.java b/src/test/java/com/clevertap/apns/clients/SyncOkHttpApnsClientTest.java new file mode 100644 index 0000000..b7aa4fc --- /dev/null +++ b/src/test/java/com/clevertap/apns/clients/SyncOkHttpApnsClientTest.java @@ -0,0 +1,181 @@ +package com.clevertap.apns.clients; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import com.clevertap.apns.ApnsClient; +import com.clevertap.apns.Notification; +import com.clevertap.apns.NotificationResponse; + +import org.junit.Before; +import org.junit.Test; + +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import okhttp3.tls.HandshakeCertificates; +import okhttp3.tls.HeldCertificate; + + +public class SyncOkHttpApnsClientTest { + + protected static final String DEFAULT_TOPIC = "com.clevertap.testTopic"; + protected static final String CERT_PASSWD = "cert-password"; + protected static final String DEVICE_TOKEN = "vaild-device-token"; + protected static final String INVALID_DEVICE_TOKEN = "invaild-device-token"; + + protected HeldCertificate rootCertificate; + protected HeldCertificate serverCertificate; + protected HeldCertificate clientCertificate; + protected HandshakeCertificates serverCertificateChain; + protected HandshakeCertificates clientCertificateChain; + + @Before + public void initCertificates() { + rootCertificate = new HeldCertificate.Builder() + .certificateAuthority(0) + .build(); + + serverCertificate = new HeldCertificate.Builder() + .addSubjectAlternativeName("localhost") + .commonName("localhost") + .signedBy(rootCertificate) + .build(); + + clientCertificate = new HeldCertificate.Builder() + .commonName("push") + .signedBy(rootCertificate) + .build(); + + serverCertificateChain = new HandshakeCertificates.Builder() + .heldCertificate(serverCertificate) + .addTrustedCertificate(rootCertificate.certificate()) + .build(); + + // Don't add client cert to client cert chain b/c it will be added via the apns api + clientCertificateChain = new HandshakeCertificates.Builder() + .addTrustedCertificate(rootCertificate.certificate()) + .build(); + } + + /** + * Convert client cert to PKCS12 Format and return as InputStream. + * @return + */ + protected InputStream getClientCertPKCS12() { + try { + KeyStore pkcs12 = KeyStore.getInstance("PKCS12"); + pkcs12.load(null, null); + Certificate chain[] = {clientCertificate.certificate()}; + pkcs12.setKeyEntry("privatekeyalias", clientCertificate.keyPair().getPrivate(), CERT_PASSWD.toCharArray(), chain); + + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + pkcs12.store(outStream, CERT_PASSWD.toCharArray()); + + return new ByteArrayInputStream(outStream.toByteArray()); + } catch(Exception e) { + fail(e.getMessage()); + } + return null; + } + + /** + * Changes Gateway-URL of the ApnsClient instance to the given URL via reflection. + * + * @param client ApnsClient instance which gatewayUrl shall be changed + * @param gatewayUrl URL to set + */ + protected void setClientGatewayUrl(ApnsClient client, HttpUrl gatewayUrl) { + try { + String url = gatewayUrl.toString(); + + // strip trailling slash + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + + Field field = client.getClass().getDeclaredField("gateway"); + field.setAccessible(true); + + Field modifiers = Field.class.getDeclaredField("modifiers"); + modifiers.setAccessible(true); + modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL); + + field.set(client, url); + } catch (Exception e) { + fail(e.getMessage()); + } + } + + /** + * Build ApnsClient with valid client cert in synchronous mode. + * @return apnsClient + */ + private ApnsClient buildClientWithCert() { + try { + return new ApnsClientBuilder() + .withOkHttpClientBuilder(new OkHttpClient.Builder().sslSocketFactory(clientCertificateChain.sslSocketFactory(), clientCertificateChain.trustManager())) + .withDefaultTopic(DEFAULT_TOPIC) + .withCertificate(getClientCertPKCS12()) + .withPassword(CERT_PASSWD) + .inSynchronousMode() + .withProductionGateway() + .build(); + } catch (Exception e) { + fail(e.getMessage()); + } + return null; + } + + @Test + public void pushTestWithCert() { + MockWebServer server = new MockWebServer(); + try { + server.useHttps(serverCertificateChain.sslSocketFactory(), false); + server.requestClientAuth(); + server.enqueue(new MockResponse().setResponseCode(200).setBody("Hello world!")); + + ApnsClient client = buildClientWithCert(); + setClientGatewayUrl(client, server.url("")); + + NotificationResponse response = client.push( + new Notification.Builder(DEVICE_TOKEN) + .alertBody("Notification Body") + .alertTitle("Alert Title") + .badge(10) + .sound("sound") + .build() + ); + assertEquals("HTTP-Response-Code 200", 200, response.getHttpStatusCode()); + + RecordedRequest request = server.takeRequest(); + assertEquals("/3/device/" + DEVICE_TOKEN, request.getPath()); + assertEquals(DEFAULT_TOPIC, request.getHeader("apns-topic")); + + X509Certificate clientCert = (X509Certificate) request.getHandshake().peerCertificates().get(0); + X509Certificate clientChain[] = {clientCert}; + serverCertificateChain.trustManager().checkClientTrusted(clientChain, "RSA"); + + } catch (Exception e) { + fail(e.getMessage()); + } + + try { + server.close(); + } catch (IOException e) { + fail(e.getMessage()); + } + } +} \ No newline at end of file From 574093e8ead94d9a0a02254b6bf36dd6d9a8b8e6 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Tue, 18 May 2021 16:39:25 +0200 Subject: [PATCH 06/10] add existing TrustManager from builder --- .../clevertap/apns/clients/SyncOkHttpApnsClient.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java index c3021eb..e08508b 100644 --- a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java +++ b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java @@ -179,7 +179,15 @@ public SyncOkHttpApnsClient(InputStream certificate, String password, boolean pr final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init((KeyStore) null); - sslContext.init(keyManagers, tmf.getTrustManagers(), null); + + // check if there is an existing TrustManager configured in the builder + TrustManager[] trustManagers = (builder.getX509TrustManagerOrNull$okhttp() != null) ? + new TrustManager[] {builder.getX509TrustManagerOrNull$okhttp()} : tmf.getTrustManagers(); + sslContext.init(keyManagers, trustManagers, null); + + if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); + } TrustManager[] trustManagers = tmf.getTrustManagers(); if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { From b647761dd8668b6f78b6a5082b4616c0564e41fc Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Tue, 18 May 2021 17:05:33 +0200 Subject: [PATCH 07/10] whitespace --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index aed2cec..abc5712 100644 --- a/pom.xml +++ b/pom.xml @@ -119,12 +119,12 @@ - + bintray-clevertap-Maven clevertap-Maven https://api.bintray.com/maven/clevertap/Maven/apns-http2/;publish=1 - + scm:git:git://github.com/CleverTap/apns-http2.git From 3002210e53e19428d1c0894a0fe9625bd2cd3957 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Tue, 18 May 2021 17:23:01 +0200 Subject: [PATCH 08/10] Revert "changed maven repository" This reverts commit 764e87c0206769b7c2c7b86b2600a24599a52ab0. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index c1e62f9..65a80f8 100644 --- a/pom.xml +++ b/pom.xml @@ -117,8 +117,8 @@ - gitam.sait-srv.local - https://gitam.sait-srv.local/api/v4/projects/134/packages/maven + maven.sait-srv.local + scp://vlx-svnam.sait-srv.local/maven From 7c6569441e9278d6cbaeedfcf6ee8d1f456b1eb0 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Tue, 18 May 2021 17:23:24 +0200 Subject: [PATCH 09/10] Revert "Update Version and added sait maven repo" This reverts commit d2594ce84d5be738106909fd33a64a2838fefe41. --- pom.xml | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/pom.xml b/pom.xml index 65a80f8..3f70f44 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.clevertap.apns apns-http2 - 1.0.7-fix_jdk8u252 + 1.0.7 apns-http2 A library for communicating with the Apple Push Gateway in HTTP/2. @@ -46,17 +46,6 @@ - - - - - org.apache.maven.wagon - wagon-ssh - 3.3.3 - - - - org.apache.maven.plugins @@ -116,11 +105,12 @@ - - maven.sait-srv.local - scp://vlx-svnam.sait-srv.local/maven - - + + bintray-clevertap-Maven + clevertap-Maven + https://api.bintray.com/maven/clevertap/Maven/apns-http2/;publish=1 + + scm:git:git://github.com/CleverTap/apns-http2.git From b357d496574f73dfc73996ad4c5a86ca2a3842c7 Mon Sep 17 00:00:00 2001 From: Simon Apold Date: Tue, 18 May 2021 17:33:07 +0200 Subject: [PATCH 10/10] removed duplicate trustManager --- .../com/clevertap/apns/clients/SyncOkHttpApnsClient.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java index e08508b..edde700 100644 --- a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java +++ b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java @@ -189,11 +189,6 @@ public SyncOkHttpApnsClient(InputStream certificate, String password, boolean pr throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); } - TrustManager[] trustManagers = tmf.getTrustManagers(); - if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { - throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers)); - } - final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0]);