diff --git a/pom.xml b/pom.xml index e9a0c78..f788872 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.clevertap.apns apns-http2 - 1.0.3 + 1.0.4 apns-http2 A library for communicating with the Apple Push Gateway in HTTP/2. diff --git a/src/main/java/com/clevertap/apns/clients/ApnsClientBuilder.java b/src/main/java/com/clevertap/apns/clients/ApnsClientBuilder.java index ceaaf93..2ed9a3a 100644 --- a/src/main/java/com/clevertap/apns/clients/ApnsClientBuilder.java +++ b/src/main/java/com/clevertap/apns/clients/ApnsClientBuilder.java @@ -50,6 +50,10 @@ public class ApnsClientBuilder { private InputStream certificate; private boolean production; + /** + * gateway field should be filled for development purposes only, and contain the address of local APNS mock instance. + */ + private String customGateway; private String password; private int connectionPort = 443; @@ -138,18 +142,25 @@ public ApnsClientBuilder withKeyID(String keyID) { return this; } + public ApnsClientBuilder withCustomGateway(String customGateway) { + this.customGateway = customGateway; + this.production = false; + return this; + } + public ApnsClientBuilder withProductionGateway() { + this.customGateway = null; this.production = true; return this; } public ApnsClientBuilder withProductionGateway(boolean production) { if (production) return withProductionGateway(); - return withDevelopmentGateway(); } public ApnsClientBuilder withDevelopmentGateway() { + this.customGateway = null; this.production = false; return this; } @@ -183,15 +194,31 @@ public ApnsClient build() throws CertificateException, if (certificate != null) { if (asynchronous) { - return new AsyncOkHttpApnsClient(certificate, password, production, defaultTopic, builder, connectionPort); + if (customGateway != null){ + return new AsyncOkHttpApnsClient(certificate, password, customGateway, defaultTopic, builder, connectionPort); + } else { + return new AsyncOkHttpApnsClient(certificate, password, production, defaultTopic, builder, connectionPort); + } } else { - return new SyncOkHttpApnsClient(certificate, password, production, defaultTopic, builder, connectionPort); + if (customGateway != null){ + return new SyncOkHttpApnsClient(certificate, password, customGateway, defaultTopic, builder, connectionPort); + } else { + return new SyncOkHttpApnsClient(certificate, password, production, defaultTopic, builder, connectionPort); + } } } else if (keyID != null && teamID != null && apnsAuthKey != null) { if (asynchronous) { - return new AsyncOkHttpApnsClient(apnsAuthKey, teamID, keyID, production, defaultTopic, builder, connectionPort); + if (customGateway != null) { + return new AsyncOkHttpApnsClient(apnsAuthKey, teamID, keyID, customGateway, defaultTopic, builder, connectionPort); + } else { + return new AsyncOkHttpApnsClient(apnsAuthKey, teamID, keyID, production, defaultTopic, builder, connectionPort); + } } else { - return new SyncOkHttpApnsClient(apnsAuthKey, teamID, keyID, production, defaultTopic, builder, connectionPort); + if (customGateway != null) { + return new SyncOkHttpApnsClient(apnsAuthKey, teamID, keyID, customGateway, defaultTopic, builder, connectionPort); + } else { + return new SyncOkHttpApnsClient(apnsAuthKey, teamID, keyID, production, defaultTopic, builder, connectionPort); + } } } else { throw new IllegalArgumentException("Either the token credentials (team ID, key ID, and the private key) " + diff --git a/src/main/java/com/clevertap/apns/clients/AsyncOkHttpApnsClient.java b/src/main/java/com/clevertap/apns/clients/AsyncOkHttpApnsClient.java index 4e79ee1..bdbddce 100644 --- a/src/main/java/com/clevertap/apns/clients/AsyncOkHttpApnsClient.java +++ b/src/main/java/com/clevertap/apns/clients/AsyncOkHttpApnsClient.java @@ -60,6 +60,13 @@ public AsyncOkHttpApnsClient(InputStream certificate, String password, boolean p super(certificate, password, production, defaultTopic, connectionPool); } + public AsyncOkHttpApnsClient(InputStream certificate, String password, String gateway, + String defaultTopic, OkHttpClient.Builder builder, int connectionPort) + throws CertificateException, NoSuchAlgorithmException, KeyStoreException, + IOException, UnrecoverableKeyException, KeyManagementException { + super(certificate, password, gateway, defaultTopic, builder, connectionPort); + } + public AsyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, boolean production, String defaultTopic, OkHttpClient.Builder builder) { this(apnsAuthKey, teamID, keyID, production, defaultTopic, builder, 443); @@ -70,6 +77,11 @@ public AsyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, super(apnsAuthKey, teamID, keyID, production, defaultTopic, builder); } + public AsyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, String gateway, + String defaultTopic, OkHttpClient.Builder builder, int connectionPort) { + super(apnsAuthKey, teamID, keyID, gateway, defaultTopic, builder, connectionPort); + } + public AsyncOkHttpApnsClient(InputStream certificate, String password, boolean production, String defaultTopic, OkHttpClient.Builder builder) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, diff --git a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java index 3ba261c..cc80f53 100644 --- a/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java +++ b/src/main/java/com/clevertap/apns/clients/SyncOkHttpApnsClient.java @@ -89,6 +89,23 @@ public SyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, boo */ public SyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, boolean production, String defaultTopic, OkHttpClient.Builder clientBuilder, int connectionPort) { + this (apnsAuthKey, teamID, keyID, production ? Constants.ENDPOINT_PRODUCTION : Constants.ENDPOINT_SANDBOX, + defaultTopic,clientBuilder, connectionPort); + } + + /** + * Creates a new client which uses token authentication API. + * + * @param apnsAuthKey The private key - exclude -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- + * @param teamID The team ID + * @param keyID The key ID (retrieved from the file name) + * @param gateway Endpoint address + * @param defaultTopic A default topic (can be changed per message) + * @param clientBuilder An OkHttp client builder, possibly pre-initialized, to build the actual client + * @param connectionPort The port to establish a connection with APNs. Either 443 or 2197 + */ + public SyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, String gateway, + String defaultTopic, OkHttpClient.Builder clientBuilder, int connectionPort) { this.apnsAuthKey = apnsAuthKey; this.teamID = teamID; this.keyID = keyID; @@ -96,7 +113,7 @@ public SyncOkHttpApnsClient(String apnsAuthKey, String teamID, String keyID, boo this.defaultTopic = defaultTopic; - gateway = (production ? Constants.ENDPOINT_PRODUCTION : Constants.ENDPOINT_SANDBOX) + ":" + connectionPort; + this.gateway = gateway + ":" + connectionPort; } /** @@ -161,7 +178,6 @@ public SyncOkHttpApnsClient(InputStream certificate, String password, boolean pr String defaultTopic, OkHttpClient.Builder builder, int connectionPort) throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException, KeyManagementException { - teamID = keyID = apnsAuthKey = null; password = password == null ? "" : password; @@ -190,6 +206,57 @@ public SyncOkHttpApnsClient(InputStream certificate, String password, boolean pr gateway = (production ? Constants.ENDPOINT_PRODUCTION : Constants.ENDPOINT_SANDBOX) + ":" + connectionPort; } + /** + * Creates a new client for custom endpoint and automatically loads the key store + * with the push certificate read from the input stream. + * Certificate will not be validated + * + * @param certificate The client certificate to be used + * @param password The password (if required, else null) + * @param gateway Address of custom gateway. + * @param defaultTopic A default topic (can be changed per message) + * @param builder An OkHttp client builder, possibly pre-initialized, to build the actual client + * @param connectionPort The port to establish a connection with APNs. Either 443 or 2197 + * @throws UnrecoverableKeyException If the key cannot be recovered + * @throws KeyManagementException if the key failed to be loaded + * @throws CertificateException if any of the certificates in the keystore could not be loaded + * @throws NoSuchAlgorithmException if the algorithm used to check the integrity of the keystore cannot be found + * @throws IOException if there is an I/O or format problem with the keystore data, + * if a password is required but not given, or if the given password was incorrect + * @throws KeyStoreException if no Provider supports a KeyStoreSpi implementation for the specified type + */ + + public SyncOkHttpApnsClient(InputStream certificate, String password, String gateway, + String defaultTopic, OkHttpClient.Builder builder, int connectionPort) + throws CertificateException, NoSuchAlgorithmException, KeyStoreException, + IOException, UnrecoverableKeyException, KeyManagementException { + + teamID = keyID = apnsAuthKey = null; + + password = password == null ? "" : password; + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(certificate, password.toCharArray()); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, password.toCharArray()); + KeyManager[] keyManagers = kmf.getKeyManagers(); + SSLContext sslContext = SSLContext.getInstance("TLS"); + + final TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore) null); + sslContext.init(keyManagers, tmf.getTrustManagers(), null); + + final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + + builder.sslSocketFactory(sslSocketFactory); + + client = builder.build(); + + this.defaultTopic = defaultTopic; + this.gateway = gateway + ":" + connectionPort; + + } + /** * Creates a new client and automatically loads the key store * with the push certificate read from the input stream.