diff --git a/benchmark/script/generate-certs.sh b/benchmark/script/generate-certs.sh deleted file mode 100755 index c363eb8b0..000000000 --- a/benchmark/script/generate-certs.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# Generate a new, self-signed root CA -openssl req -extensions v3_ca -new -x509 -days 36500 -nodes -subj "/CN=PushyTestRoot" -newkey rsa:2048 -sha512 -out ca.pem -keyout ca.key - -# Generate a certificate/key for the server -openssl req -new -keyout server_key.pem -nodes -newkey rsa:2048 -subj "/CN=com.eatthepath.pushy" | \ - openssl x509 -req -CAkey ca.key -CA ca.pem -days 36500 -set_serial 1 -sha512 -out server_certs.pem - -# Clean up intermediate files -rm ca.key diff --git a/benchmark/src/main/java/com/eatthepath/pushy/apns/ApnsClientBenchmark.java b/benchmark/src/main/java/com/eatthepath/pushy/apns/ApnsClientBenchmark.java index 6786a877c..6bce24a0c 100644 --- a/benchmark/src/main/java/com/eatthepath/pushy/apns/ApnsClientBenchmark.java +++ b/benchmark/src/main/java/com/eatthepath/pushy/apns/ApnsClientBenchmark.java @@ -28,6 +28,8 @@ import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.pkitesting.CertificateBuilder; +import io.netty.pkitesting.X509Bundle; import io.netty.util.concurrent.Future; import org.apache.commons.lang3.RandomStringUtils; import org.openjdk.jmh.annotations.*; @@ -35,6 +37,8 @@ import java.security.KeyPairGenerator; import java.security.SecureRandom; import java.security.interfaces.ECPrivateKey; +import java.time.Duration; +import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -57,9 +61,8 @@ public class ApnsClientBenchmark { @Param({"1", "4", "8"}) public int concurrentConnections; - private static final String CA_CERTIFICATE_FILENAME = "/ca.pem"; - private static final String SERVER_CERTIFICATES_FILENAME = "/server_certs.pem"; - private static final String SERVER_KEY_FILENAME = "/server_key.pem"; + protected static X509Bundle CA_BUNDLE; + protected static X509Bundle SERVER_CERTIFICATE_BUNDLE; private static final String TOPIC = "com.eatthepath.pushy"; private static final String TEAM_ID = "benchmark.team"; @@ -77,6 +80,27 @@ public void setUp() throws Exception { this.clientResources = new ApnsClientResources(new NioEventLoopGroup(this.concurrentConnections)); this.serverEventLoopGroup = new NioEventLoopGroup(this.concurrentConnections); + final Instant now = Instant.now(); + + final CertificateBuilder rootCertificateBuilderTemplate = new CertificateBuilder() + .notBefore(now) + .notAfter(now.plus(Duration.ofHours(8))); + + CA_BUNDLE = rootCertificateBuilderTemplate.copy() + .subject("CN=PushyTestRoot") + .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyCertSign) + .setIsCertificateAuthority(true) + .buildSelfSigned(); + + SERVER_CERTIFICATE_BUNDLE = rootCertificateBuilderTemplate.copy() + .subject("CN=com.eatthepath.pushy") + .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyEncipherment) + .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_CLIENT_AUTH) + .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_SERVER_AUTH) + .setIsCertificateAuthority(false) + .addSanDnsName("localhost") + .buildIssuedBy(CA_BUNDLE); + final ApnsSigningKey signingKey; { final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); @@ -89,14 +113,14 @@ public void setUp() throws Exception { .setApnsServer(HOST, PORT) .setConcurrentConnections(this.concurrentConnections) .setSigningKey(signingKey) - .setTrustedServerCertificateChain(ApnsClientBenchmark.class.getResourceAsStream(CA_CERTIFICATE_FILENAME)) + .setTrustedServerCertificateChain(CA_BUNDLE.getCertificate()) .setApnsClientResources(this.clientResources) .build(); this.server = new BenchmarkApnsServerBuilder() - .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), this.getClass().getResourceAsStream(SERVER_KEY_FILENAME), null) - .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) - .setEventLoopGroup(this.serverEventLoopGroup) + .setServerCredentials(SERVER_CERTIFICATE_BUNDLE.getCertificatePathWithRoot(), SERVER_CERTIFICATE_BUNDLE.getKeyPair().getPrivate()) + .setTrustedClientCertificateChain(CA_BUNDLE.getCertificate()) + .setIoEventLoopGroup(this.serverEventLoopGroup) .build(); this.pushNotifications = new ArrayList<>(this.notificationCount); diff --git a/benchmark/src/main/resources/ca.pem b/benchmark/src/main/resources/ca.pem deleted file mode 100644 index 82c0e479b..000000000 --- a/benchmark/src/main/resources/ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBTCCAe2gAwIBAgIJALwUzvevvXgfMA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNV -BAMMDVB1c2h5VGVzdFJvb3QwIBcNMTYwNjE3MTg1OTA0WhgPMjExNjA1MjQxODU5 -MDRaMBgxFjAUBgNVBAMMDVB1c2h5VGVzdFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQC0FXKF6gWRjzrbSWWIUyjxONvAzGLTa6asynwXDI+2WO4q -ZWK08Pjr768O9LGMGsluLMTXGWZ7aZwQ9bC7GnV8k3MXUGfi53NKWbD2Oj8UMzuH -k6HeszXIGxbJJE6H5y8/wut6EgthBiiz1sfa6rawLhlMWEH1Ug9/DxV/uycLQGFq -AEKcKssV5VLVl2yyPrvRT7wwnwy/kWNCtlkTjbjvCdG302fXAdjtBeQU2Fmp4nzU -upvJuF2oTsZC1dbKu1SIc7aHF/WWAfG9cwHS+siJkD7nTGKfv2ijcNmiwBTO8h9i -B4U+Y78w0uAltxTvgt/ucmGpqnvDuYSZ33FhorBPAgMBAAGjUDBOMB0GA1UdDgQW -BBRWe0bLUXw4OgBk/rAObIE8oSQldTAfBgNVHSMEGDAWgBRWe0bLUXw4OgBk/rAO -bIE8oSQldTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQARHA8w1jJ6 -chWHUQGClyVZzp+pXlCfX7K7jyhqRNErehSSE5b+jSNMutkQNQAiWa5EE8PA4mZp -yNJNHqLor9CRZTEkTbiayqs4M5O38Me93TF5ChXmq9LvHDc4Iae9jzx+XOzkmLSm -eyoRrUXdQYg2BBAl8NrAdtpl57p0RAic2eHR4ItAjRq/bkYUrCb2pjdLq5hoeC61 -nXy2+Y96hFHaUjDb3yy/+zW1+0h6zXV+8iXqjMRF5Jviw+Io3hBcLU9eFI1VckdD -tRNdumxnxj1EKTe+lOe/GLciWkAn0gRmRn2Z0yNzD/FUG+kPT4hoGnR5E0rqt47N -SAZGym9tacdr ------END CERTIFICATE----- diff --git a/benchmark/src/main/resources/server_certs.pem b/benchmark/src/main/resources/server_certs.pem deleted file mode 100644 index fdcd3aee5..000000000 --- a/benchmark/src/main/resources/server_certs.pem +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICrjCCAZYCAlpjMA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNVBAMMDVB1c2h5VGVz -dFJvb3QwIBcNMTYwNjE3MTg1OTA0WhgPMjExNjA1MjQxODU5MDRaMB8xHTAbBgNV -BAMMFGNvbS5yZWxheXJpZGVzLnB1c2h5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAwyrQU1UVPYWNYFbe5oXIFlZG/12U+PY3zDDM/FgaKQXQwNMeY8Mv -ue7Vmh17p/zRlGIaUYXb6pa5lXsDZFFqV7pfyL8EXaZFuO7VfHyLIwBvuCdHTgL2 -hgFJQyXSpCR99nXKqNGtyr8HyjLXcxvSA+OrhID2rY39dtAJTo9CjPiToyX2pqGb -MzMR9/LBuyTfu+DFf1Eagnq7kIbpVe4eTrgtzSyUHb72LsHqKAix4s4MpJcMBX1q -XN7wqqz2VFHHXnxl7asXFLZWoWlb9xERJBZACOolQ3WvJh/oljYoSdf372YN6TdV -k/nMRm3GCdN/Tet7qpDQqilFGuT34hKUNQIDAQABMA0GCSqGSIb3DQEBDQUAA4IB -AQA5fFrIv+OadpNkkI9j3kbSgPETRSoPBYOQfT+J2OOogNvcaXSQyY9dc1gnrMCe -CczhlBgFA6e/14ilgU6jzaNFdvOpynbb6dgtQVSuJ+ZIkdST9ZBBthrVYWtWT/5c -ePC3X7oZU9EGcaGCBXF5XYXmEHoMHg12UaBC4VJZCmZYHmY5TCN9ehSJ0S/wEjZY -lwA5DljOi3yr0nSzIR238lro03VShlULr3MzFS2A/cOmmR4umlBitW3w2FP4OkDC -SJJA4ab/Yo7MBwKaknTIJYsF41s17XM9aQmb2UWInMIxoWL+Fe91UF+p4Rvcu4YJ -qEjru8tjI4KTwAiVGXn46Ej3 ------END CERTIFICATE----- diff --git a/benchmark/src/main/resources/server_key.pem b/benchmark/src/main/resources/server_key.pem deleted file mode 100644 index 3c267e288..000000000 --- a/benchmark/src/main/resources/server_key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDKtBTVRU9hY1g -Vt7mhcgWVkb/XZT49jfMMMz8WBopBdDA0x5jwy+57tWaHXun/NGUYhpRhdvqlrmV -ewNkUWpXul/IvwRdpkW47tV8fIsjAG+4J0dOAvaGAUlDJdKkJH32dcqo0a3KvwfK -MtdzG9ID46uEgPatjf120AlOj0KM+JOjJfamoZszMxH38sG7JN+74MV/URqCeruQ -hulV7h5OuC3NLJQdvvYuweooCLHizgyklwwFfWpc3vCqrPZUUcdefGXtqxcUtlah -aVv3EREkFkAI6iVDda8mH+iWNihJ1/fvZg3pN1WT+cxGbcYJ039N63uqkNCqKUUa -5PfiEpQ1AgMBAAECggEAeFlFX62+zFI3rOnEyAhFKykG3N/7Rk6md768GPvHHfbd -FJrbSY+KNbDGfSOjJiLrpgGc0xqlUxKg3EGgBNODrRdbi3HnIkyu/+wfL4WpUuso -+TkXak6/cEEIyWRgcSGjn0BhuRqbfcOw7QnO+8dzCTzyfmH8O2esngCJTK1gz4ZA -0gbQfFd60+TPbbmQ82gMcrG2HpuSCK/jDcusNNxjWXUiGnPrfG7EmFIQIZAlCDFH -DUidywStC0fuhB+el2E/G57lgwmiqur+FFABtQjamsZ4kw39PhnJ/cdU2UQzLm8L -CPHcuMAqIo+0puscgeOlUdaXI0vob38LcuYrTOFZoQKBgQD0kZzRoUDT+aCrNhyt -5YjNrlSIZYs4l6mDFjWjRH0yvWbBf/pCQbqHn0+P9qMBW07CQr3SE7cbtAZo2Mm3 -Q+SutYSjmCHkab64kDKk0M42RbDnmZUbc03cGc25qGCjgu77uW7mCa5iq5htL8OR -V3BYP7xXpEH/h6cQOJEbjVquXQKBgQDMShZDc4/s8E7pG2nt0hgNWRL9RxE6escc -u/oviFgRd/BI6axwQCcqDrX74sh8igQ1M726Nap1wcdoz8pCF5Q1mOjaXJXc1j9T -ONlHr4c9PBPmO0Ib0mD5e5yKiUJms+EqU6TIZO+Yaddr7l87s/A1wuKoLxvU22MX -ZZuv+C2vuQKBgFVVlWUjoa4U1LXInLskctZpdH9fBvPJH1Fo53A/Tnvs0DTGBbkm -nAIEWUt/ICMyC8oGZZsuW96jsvTUZ5W1b85x8YTvaSv8pZpaXgFaBgmzyWUWFovM -cKdSrZCRKp8C6YYVjpdfW2R8QSQf3VoSK1XvbzUZUBnr6CPlyOPXL4PVAoGBAKbD -C62AFkr6V9AeGriSOiQbRE8QxlWQiB1CrZZDnHCWEcXTSnpPRCkm3RODKtnO6GRt -mkD5Me7ygk9i395X1RpzehFWZtdg5UyQKX5FIQaxliTo1qxTben6J+1G4rdnuzSM -FwWmr5chP0LR0VexKTMZpWBZJET72cXtocthshmxAoGBAIcDaq/2DJfN2W1KUpgg -CvcnotFNsOmTcMiymMC4/IkLQSsoOcTmQTul3Y90Fyk/C3LsqbMQ6ZXa/I92X/G+ -0AWNaDFSYrgU4q1ndVcdGk6sHKZiCBTH/uR3qTJ7qV6axZC0YMmiG8EEN1P3Moe/ -sGcha5KRdyBap/vsDCEAJL6P ------END PRIVATE KEY----- diff --git a/pom.xml b/pom.xml index 239225f76..96539c6b8 100644 --- a/pom.xml +++ b/pom.xml @@ -75,12 +75,16 @@ micrometer-metrics-listener + + UTF-8 + + io.netty netty-bom - 4.1.133.Final + 4.2.14.Final pom import @@ -112,10 +116,6 @@ - - UTF-8 - - diff --git a/pushy/pom.xml b/pushy/pom.xml index 9e265145c..1f2e7ca2e 100644 --- a/pushy/pom.xml +++ b/pushy/pom.xml @@ -66,12 +66,21 @@ slf4j-simple test + + io.netty + netty-pkitesting + io.netty netty-transport-native-epoll linux-x86_64 test + + io.netty + netty-transport-native-io_uring + test + io.netty netty-transport-native-kqueue diff --git a/pushy/script/apns-extensions.cnf b/pushy/script/apns-extensions.cnf deleted file mode 100644 index 7892e76a9..000000000 --- a/pushy/script/apns-extensions.cnf +++ /dev/null @@ -1,32 +0,0 @@ -[ apns_single_topic_client_extensions ] -basicConstraints = CA:FALSE -keyUsage = critical, digitalSignature -extendedKeyUsage = clientAuth - -[ apns_multi_topic_client_extensions ] -basicConstraints = CA:FALSE -keyUsage = critical, digitalSignature -extendedKeyUsage = clientAuth -1.2.840.113635.100.6.3.6 = ASN1:SEQUENCE:apns_topics - -[ apns_server_extensions ] -basicConstraints = CA:FALSE -keyUsage = critical, digitalSignature, keyEncipherment -extendedKeyUsage = clientAuth, serverAuth -subjectAltName = @alt_names - -[ apns_server_extensions_no_alt ] -basicConstraints = CA:FALSE -keyUsage = critical, digitalSignature, keyEncipherment -extendedKeyUsage = clientAuth, serverAuth - -[ apns_topics ] -aps_topics.0 = UTF8String:com.eatthepath.pushy -aps_topics.1 = SEQWRAP,UTF8String:app -aps_topics.2 = UTF8String:com.eatthepath.pushy.voip -aps_topics.3 = SEQWRAP,UTF8String:voip -aps_topics.4 = UTF8String:com.eatthepath.pushy.complication -aps_topics.5 = SEQWRAP,UTF8String:complication - -[alt_names] -DNS.1 = localhost diff --git a/pushy/script/generate-certs.sh b/pushy/script/generate-certs.sh deleted file mode 100755 index 9e21f424c..000000000 --- a/pushy/script/generate-certs.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/sh - -# Generate a new, self-signed root CA -openssl req -config openssl-custom.cnf -extensions v3_ca -new -x509 -days 36500 -nodes -subj "/CN=PushyTestRoot" -newkey rsa:2048 -sha512 -out ca.pem -keyout ca.key - -# Generate a certificate/key for the server -openssl req -new -keyout server-key.pem -nodes -newkey rsa:2048 -subj "/CN=com.eatthepath.pushy" | \ - openssl x509 -extfile ./apns-extensions.cnf -extensions apns_server_extensions -req -CAkey ca.key -CA ca.pem -days 36500 -set_serial 1 -sha512 -out server-certs.pem - -# Generate a certificate/key for the server the does NOT have `localhost` as an alternative name -openssl req -new -keyout server-key-no-alt.pem -nodes -newkey rsa:2048 -subj "/CN=com.eatthepath.pushy" | \ - openssl x509 -extfile ./apns-extensions.cnf -extensions apns_server_extensions_no_alt -req -CAkey ca.key -CA ca.pem -days 36500 -set_serial 1 -sha512 -out server-certs-no-alt.pem - -# Generate certificates/keys for clients and sign them with the intermediate certificate -openssl req -new -keyout single-topic-client.key -nodes -newkey rsa:2048 -subj "/CN=Apple Push Services: com.eatthepath.pushy/UID=com.eatthepath.pushy" | \ - openssl x509 -extfile ./apns-extensions.cnf -extensions apns_single_topic_client_extensions -req -CAkey ca.key -CA ca.pem -days 36500 -set_serial 1 -sha512 -out single-topic-client.pem - -openssl req -new -keyout multi-topic-client.key -nodes -newkey rsa:2048 -subj "/CN=Apple Push Services: com.eatthepath.pushy/UID=com.eatthepath.pushy" | \ - openssl x509 -extfile ./apns-extensions.cnf -extensions apns_multi_topic_client_extensions -req -CAkey ca.key -CA ca.pem -days 36500 -set_serial 1 -sha512 -out multi-topic-client.pem - -# For simplicity, squish everything down into PKCS#12 keystores -openssl pkcs12 -export -in server-certs.pem -inkey server-key.pem -out server.p12 -password pass:pushy-test -openssl pkcs12 -export -in single-topic-client.pem -inkey single-topic-client.key -out single-topic-client.p12 -password pass:pushy-test -openssl pkcs12 -export -in multi-topic-client.pem -inkey multi-topic-client.key -out multi-topic-client.p12 -password pass:pushy-test - -# We'll also want one keystore with an unprotected key to make sure no-password constructors behave correctly -openssl pkcs12 -export -in single-topic-client.pem -inkey single-topic-client.key -out single-topic-client-unprotected.p12 -nodes -password pass:pushy-test - -# Generate a PKCS#12 keystore with multiple keys -for i in `seq 1 4`; -do - # Couldn't find a way to get multiple keys into a PKCS#12 file using OpenSSL directly, so we'll take the long way - # around and construct a multi-key-pair keystore with keytool, then export to PKCS#12. - keytool -genkeypair -keyalg EC -validity 36500 -storepass pushy-test -keypass pushy-test -dname "CN=com.eatthepath.pushy.{$i}" -keystore multiple-keys.jks -alias "pair${i}" -done - -keytool -importkeystore -srckeystore multiple-keys.jks -destkeystore multiple-keys.p12 -srcstoretype JKS -deststoretype PKCS12 -srcstorepass pushy-test -deststorepass pushy-test - -# Generate a PKCS#12 with a certificate, but no private key -openssl pkcs12 -export -in ca.pem -nokeys -out no-keys.p12 -password pass:pushy-test - -# Generate a private key for token authentication testing -openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out token-auth-private-key.p8 - -# Clean up intermediate files -rm ca.key server.key multi-topic-client.key single-topic-client.key -rm multiple-keys.jks - -# Generate an elliptic key pair for token authentication testing -openssl ecparam -name prime256v1 -genkey -noout | openssl pkcs8 -topk8 -nocrypt -out token-auth-private-key.p8 -openssl ec -in token-auth-private-key.p8 -pubout -out token-auth-public-key.p8 diff --git a/pushy/script/openssl-custom.cnf b/pushy/script/openssl-custom.cnf deleted file mode 100644 index 9a90334f0..000000000 --- a/pushy/script/openssl-custom.cnf +++ /dev/null @@ -1,340 +0,0 @@ -# -# OpenSSL example configuration file. -# This is mostly being used for generation of certificate requests. -# - -# This definition stops the following lines choking if HOME isn't -# defined. -HOME = . -RANDFILE = $ENV::HOME/.rnd - -# Extra OBJECT IDENTIFIER info: -#oid_file = $ENV::HOME/.oid -oid_section = new_oids - -# To use this configuration file with the "-extfile" option of the -# "openssl x509" utility, name here the section containing the -# X.509v3 extensions to use: -# extensions = -# (Alternatively, use a configuration file that has only -# X.509v3 extensions in its main [= default] section.) - -[ new_oids ] - -# We can add new OIDs in here for use by 'ca', 'req' and 'ts'. -# Add a simple OID like this: -# testoid1=1.2.3.4 -# Or use config file substitution like this: -# testoid2=${testoid1}.5.6 - -# Policies used by the TSA examples. -tsa_policy1 = 1.2.3.4.1 -tsa_policy2 = 1.2.3.4.5.6 -tsa_policy3 = 1.2.3.4.5.7 - -#################################################################### -[ ca ] -default_ca = CA_default # The default ca section - -#################################################################### -[ CA_default ] - -dir = ./demoCA # Where everything is kept -certs = $dir/certs # Where the issued certs are kept -crl_dir = $dir/crl # Where the issued crl are kept -database = $dir/index.txt # database index file. -#unique_subject = no # Set to 'no' to allow creation of - # several ctificates with same subject. -new_certs_dir = $dir/newcerts # default place for new certs. - -certificate = $dir/cacert.pem # The CA certificate -serial = $dir/serial # The current serial number -crlnumber = $dir/crlnumber # the current crl number - # must be commented out to leave a V1 CRL -crl = $dir/crl.pem # The current CRL -private_key = $dir/private/cakey.pem# The private key -RANDFILE = $dir/private/.rand # private random number file - -x509_extensions = usr_cert # The extentions to add to the cert - -# Comment out the following two lines for the "traditional" -# (and highly broken) format. -name_opt = ca_default # Subject Name options -cert_opt = ca_default # Certificate field options - -# Extension copying option: use with caution. -# copy_extensions = copy - -# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs -# so this is commented out by default to leave a V1 CRL. -# crlnumber must also be commented out to leave a V1 CRL. -# crl_extensions = crl_ext - -default_days = 365 # how long to certify for -default_crl_days= 30 # how long before next CRL -default_md = default # use public key default MD -preserve = no # keep passed DN ordering - -# A few difference way of specifying how similar the request should look -# For type CA, the listed attributes must be the same, and the optional -# and supplied fields are just that :-) -policy = policy_match - -# For the CA policy -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -# For the 'anything' policy -# At this point in time, you must list all acceptable 'object' -# types. -[ policy_anything ] -countryName = optional -stateOrProvinceName = optional -localityName = optional -organizationName = optional -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -#################################################################### -[ req ] -default_bits = 1024 -default_keyfile = privkey.pem -distinguished_name = req_distinguished_name -attributes = req_attributes -x509_extensions = v3_ca # The extentions to add to the self signed cert - -# Passwords for private keys if not present they will be prompted for -# input_password = secret -# output_password = secret - -# This sets a mask for permitted string types. There are several options. -# default: PrintableString, T61String, BMPString. -# pkix : PrintableString, BMPString (PKIX recommendation before 2004) -# utf8only: only UTF8Strings (PKIX recommendation after 2004). -# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). -# MASK:XXXX a literal mask value. -# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. -string_mask = utf8only - -# req_extensions = v3_req # The extensions to add to a certificate request - -[ req_distinguished_name ] -countryName = Country Name (2 letter code) -countryName_default = AU -countryName_min = 2 -countryName_max = 2 - -stateOrProvinceName = State or Province Name (full name) -stateOrProvinceName_default = Some-State - -localityName = Locality Name (eg, city) - -0.organizationName = Organization Name (eg, company) -0.organizationName_default = Internet Widgits Pty Ltd - -# we can do this but it is not needed normally :-) -#1.organizationName = Second Organization Name (eg, company) -#1.organizationName_default = World Wide Web Pty Ltd - -organizationalUnitName = Organizational Unit Name (eg, section) -#organizationalUnitName_default = - -commonName = Common Name (e.g. server FQDN or YOUR name) -commonName_max = 64 - -emailAddress = Email Address -emailAddress_max = 64 - -# SET-ex3 = SET extension number 3 - -[ req_attributes ] -challengePassword = A challenge password -challengePassword_min = 4 -challengePassword_max = 20 - -unstructuredName = An optional company name - -[ usr_cert ] - -# These extensions are added when 'ca' signs a request. - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This is required for TSA certificates. -# extendedKeyUsage = critical,timeStamping - -[ v3_req ] - -# Extensions to add to a certificate request - -basicConstraints = CA:FALSE -keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -[ v3_ca ] - -# Extensions for a typical CA -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer -basicConstraints = critical, CA:true -keyUsage = critical, digitalSignature, cRLSign, keyCertSign - -# Key usage: this is typical for a CA certificate. However since it will -# prevent it being used as an test self-signed certificate it is best -# left out by default. -# keyUsage = cRLSign, keyCertSign - -# Some might want this also -# nsCertType = sslCA, emailCA - -# Include email address in subject alt name: another PKIX recommendation -# subjectAltName=email:copy -# Copy issuer details -# issuerAltName=issuer:copy - -# DER hex encoding of an extension: beware experts only! -# obj=DER:02:03 -# Where 'obj' is a standard or added object -# You can even override a supported extension: -# basicConstraints= critical, DER:30:03:01:01:FF - -[ crl_ext ] - -# CRL extensions. -# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. - -# issuerAltName=issuer:copy -authorityKeyIdentifier=keyid:always - -[ proxy_cert_ext ] -# These extensions should be added when creating a proxy certificate - -# This goes against PKIX guidelines but some CAs do it and some software -# requires this to avoid interpreting an end user certificate as a CA. - -basicConstraints=CA:FALSE - -# Here are some examples of the usage of nsCertType. If it is omitted -# the certificate can be used for anything *except* object signing. - -# This is OK for an SSL server. -# nsCertType = server - -# For an object signing certificate this would be used. -# nsCertType = objsign - -# For normal client use this is typical -# nsCertType = client, email - -# and for everything including object signing: -# nsCertType = client, email, objsign - -# This is typical in keyUsage for a client certificate. -# keyUsage = nonRepudiation, digitalSignature, keyEncipherment - -# This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Certificate" - -# PKIX recommendations harmless if included in all certificates. -subjectKeyIdentifier=hash -authorityKeyIdentifier=keyid,issuer - -# This stuff is for subjectAltName and issuerAltname. -# Import the email address. -# subjectAltName=email:copy -# An alternative to produce certificates that aren't -# deprecated according to PKIX. -# subjectAltName=email:move - -# Copy subject details -# issuerAltName=issuer:copy - -#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem -#nsBaseUrl -#nsRevocationUrl -#nsRenewalUrl -#nsCaPolicyUrl -#nsSslServerName - -# This really needs to be in place for it to be a proxy certificate. -proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo - -#################################################################### -[ tsa ] - -default_tsa = tsa_config1 # the default TSA section - -[ tsa_config1 ] - -# These are used by the TSA reply generation only. -dir = ./demoCA # TSA root directory -serial = $dir/tsaserial # The current serial number (mandatory) -crypto_device = builtin # OpenSSL engine to use for signing -signer_cert = $dir/tsacert.pem # The TSA signing certificate - # (optional) -certs = $dir/cacert.pem # Certificate chain to include in reply - # (optional) -signer_key = $dir/private/tsakey.pem # The TSA private key (optional) - -default_policy = tsa_policy1 # Policy if request did not specify it - # (optional) -other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) -digests = md5, sha1 # Acceptable message digests (mandatory) -accuracy = secs:1, millisecs:500, microsecs:100 # (optional) -clock_precision_digits = 0 # number of digits after dot. (optional) -ordering = yes # Is ordering defined for timestamps? - # (optional, default: no) -tsa_name = yes # Must the TSA name be included in the reply? - # (optional, default: no) -ess_cert_id_chain = no # Must the ESS cert id chain be included? - # (optional, default: no) diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsChannelFactory.java b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsChannelFactory.java index a3845743f..62c42da41 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsChannelFactory.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsChannelFactory.java @@ -37,8 +37,6 @@ import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.PromiseNotifier; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; import java.io.Closeable; import java.net.SocketAddress; import java.util.concurrent.TimeUnit; @@ -80,7 +78,7 @@ class ApnsChannelFactory implements PooledObjectFactory, Closeable { : clientResources.getRoundRobinDnsAddressResolverGroup(); this.bootstrapTemplate = new Bootstrap(); - this.bootstrapTemplate.group(clientResources.getEventLoopGroup()); + this.bootstrapTemplate.group(clientResources.getIoEventLoopGroup()); this.bootstrapTemplate.option(ChannelOption.TCP_NODELAY, true); this.bootstrapTemplate.remoteAddress(clientConfiguration.getApnsServerAddress()); this.bootstrapTemplate.resolver(this.addressResolverGroup); @@ -95,13 +93,6 @@ protected void initChannel(final SocketChannel channel) { final String authority = clientConfiguration.getApnsServerAddress().getHostName(); final SslHandler sslHandler = sslContext.newHandler(channel.alloc(), authority, clientConfiguration.getApnsServerAddress().getPort()); - if (clientConfiguration.isHostnameVerificationEnabled()) { - final SSLEngine sslEngine = sslHandler.engine(); - final SSLParameters sslParameters = sslEngine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - sslEngine.setSSLParameters(sslParameters); - } - final ApnsClientHandler apnsClientHandler; { final ApnsClientHandler.ApnsClientHandlerBuilder clientHandlerBuilder; @@ -157,11 +148,10 @@ public Future create(final Promise channelReadyPromise) { ApnsChannelFactory.this.currentDelaySeconds.compareAndSet(delay, updatedDelay); }); - this.bootstrapTemplate.config().group().schedule(() -> { final Bootstrap bootstrap = ApnsChannelFactory.this.bootstrapTemplate.clone() .channelFactory(new AugmentingReflectiveChannelFactory<>( - ClientChannelClassUtil.getSocketChannelClass(ApnsChannelFactory.this.bootstrapTemplate.config().group()), + ClientChannelClassUtil.getSocketChannelClass((IoEventLoopGroup) ApnsChannelFactory.this.bootstrapTemplate.config().group()), CHANNEL_READY_PROMISE_ATTRIBUTE_KEY, channelReadyPromise)); final ChannelFuture connectFuture = bootstrap.connect(); diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClient.java b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClient.java index 6e119448c..0ec53b85e 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClient.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClient.java @@ -25,7 +25,8 @@ import com.eatthepath.pushy.apns.util.concurrent.PushNotificationFuture; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; import org.slf4j.Logger; @@ -117,12 +118,11 @@ public void handleConnectionCreationFailed() { } ApnsClient(final ApnsClientConfiguration clientConfiguration, final ApnsClientResources clientResources) { - if (clientResources != null) { this.clientResources = clientResources; this.shouldShutDownClientResources = false; } else { - this.clientResources = new ApnsClientResources(new NioEventLoopGroup(1)); + this.clientResources = new ApnsClientResources(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())); this.shouldShutDownClientResources = true; } @@ -151,7 +151,7 @@ public void handleConnectionCreationFailed() { this.channelPool = new ApnsChannelPool(channelFactory, clientConfiguration.getConcurrentConnections(), - this.clientResources.getEventLoopGroup().next(), + this.clientResources.getIoEventLoopGroup().next(), channelPoolMetricsListener); } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientBuilder.java index 2b8377094..440cc4bb3 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientBuilder.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientBuilder.java @@ -614,7 +614,8 @@ public ApnsClient build() throws SSLException { final SslContextBuilder sslContextBuilder = SslContextBuilder.forClient() .sslProvider(sslProvider) - .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE); + .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE) + .endpointIdentificationAlgorithm(enableHostnameVerification ? "HTTPS" : null); if (useAlpn) { sslContextBuilder.applicationProtocolConfig( @@ -646,7 +647,6 @@ public ApnsClient build() throws SSLException { final ApnsClientConfiguration clientConfiguration = new ApnsClientConfiguration(this.apnsServerAddress, sslContext, - this.enableHostnameVerification, this.signingKey, this.tokenExpiration, this.proxyHandlerFactory, diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientConfiguration.java b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientConfiguration.java index afde7d2f4..8a35d5f75 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientConfiguration.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientConfiguration.java @@ -41,7 +41,6 @@ class ApnsClientConfiguration { private final InetSocketAddress apnsServerAddress; private final SslContext sslContext; - private final boolean hostnameVerificationEnabled; private final ApnsSigningKey signingKey; private final Duration tokenExpiration; private final ProxyHandlerFactory proxyHandlerFactory; @@ -54,7 +53,6 @@ class ApnsClientConfiguration { public ApnsClientConfiguration(final InetSocketAddress apnsServerAddress, final SslContext sslContext, - final boolean hostnameVerificationEnabled, final ApnsSigningKey signingKey, final Duration tokenExpiration, final ProxyHandlerFactory proxyHandlerFactory, @@ -67,7 +65,6 @@ public ApnsClientConfiguration(final InetSocketAddress apnsServerAddress, this.apnsServerAddress = Objects.requireNonNull(apnsServerAddress); this.sslContext = Objects.requireNonNull(sslContext); - this.hostnameVerificationEnabled = hostnameVerificationEnabled; this.signingKey = signingKey; this.tokenExpiration = tokenExpiration != null ? tokenExpiration : DEFAULT_TOKEN_EXPIRATION; this.proxyHandlerFactory = proxyHandlerFactory; @@ -87,10 +84,6 @@ public SslContext getSslContext() { return sslContext; } - public boolean isHostnameVerificationEnabled() { - return hostnameVerificationEnabled; - } - public Optional getSigningKey() { return Optional.ofNullable(signingKey); } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientResources.java b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientResources.java index 65792a552..4841c349f 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientResources.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/ApnsClientResources.java @@ -1,6 +1,6 @@ package com.eatthepath.pushy.apns; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; import io.netty.resolver.dns.DefaultDnsServerAddressStreamProvider; import io.netty.resolver.dns.RoundRobinDnsAddressResolverGroup; import io.netty.util.concurrent.Future; @@ -19,20 +19,20 @@ */ public class ApnsClientResources { - private final EventLoopGroup eventLoopGroup; + private final IoEventLoopGroup ioEventLoopGroup; private final RoundRobinDnsAddressResolverGroup roundRobinDnsAddressResolverGroup; /** * Constructs a new set of client resources that uses the given default event loop group. Clients that use this * resource set will use the given event loop group for IO operations. * - * @param eventLoopGroup the event loop group for this set of resources + * @param ioEventLoopGroup the event loop group for this set of resources */ - public ApnsClientResources(final EventLoopGroup eventLoopGroup) { - this.eventLoopGroup = Objects.requireNonNull(eventLoopGroup); + public ApnsClientResources(final IoEventLoopGroup ioEventLoopGroup) { + this.ioEventLoopGroup = Objects.requireNonNull(ioEventLoopGroup); this.roundRobinDnsAddressResolverGroup = new RoundRobinDnsAddressResolverGroup( - ClientChannelClassUtil.getDatagramChannelClass(eventLoopGroup), + ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup), DefaultDnsServerAddressStreamProvider.INSTANCE); } @@ -41,8 +41,8 @@ public ApnsClientResources(final EventLoopGroup eventLoopGroup) { * * @return the event loop group for this resource set */ - public EventLoopGroup getEventLoopGroup() { - return eventLoopGroup; + public IoEventLoopGroup getIoEventLoopGroup() { + return ioEventLoopGroup; } /** @@ -65,6 +65,6 @@ public RoundRobinDnsAddressResolverGroup getRoundRobinDnsAddressResolverGroup() */ public Future shutdownGracefully() { roundRobinDnsAddressResolverGroup.close(); - return eventLoopGroup.shutdownGracefully(); + return ioEventLoopGroup.shutdownGracefully(); } } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/ClientChannelClassUtil.java b/pushy/src/main/java/com/eatthepath/pushy/apns/ClientChannelClassUtil.java index 7641aa6e7..dbf1c0c61 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/ClientChannelClassUtil.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/ClientChannelClassUtil.java @@ -22,7 +22,9 @@ package com.eatthepath.pushy.apns; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.Channel; +import io.netty.channel.IoEventLoopGroup; +import io.netty.channel.IoHandler; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.SocketChannel; @@ -36,64 +38,66 @@ class ClientChannelClassUtil { private static final Map DATAGRAM_CHANNEL_CLASSES = new HashMap<>(); static { - SOCKET_CHANNEL_CLASSES.put("io.netty.channel.nio.NioEventLoopGroup", "io.netty.channel.socket.nio.NioSocketChannel"); - SOCKET_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollEventLoopGroup", "io.netty.channel.epoll.EpollSocketChannel"); - SOCKET_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueEventLoopGroup", "io.netty.channel.kqueue.KQueueSocketChannel"); + SOCKET_CHANNEL_CLASSES.put("io.netty.channel.nio.NioIoHandler", "io.netty.channel.socket.nio.NioSocketChannel"); + SOCKET_CHANNEL_CLASSES.put("io.netty.channel.uring.IoUringIoHandler", "io.netty.channel.uring.IoUringSocketChannel"); + SOCKET_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollIoHandler", "io.netty.channel.epoll.EpollSocketChannel"); + SOCKET_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueIoHandler", "io.netty.channel.kqueue.KQueueSocketChannel"); - DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.nio.NioEventLoopGroup", "io.netty.channel.socket.nio.NioDatagramChannel"); - DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollEventLoopGroup", "io.netty.channel.epoll.EpollDatagramChannel"); - DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueEventLoopGroup", "io.netty.channel.kqueue.KQueueDatagramChannel"); + DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.nio.NioIoHandler", "io.netty.channel.socket.nio.NioDatagramChannel"); + DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.uring.IoUringIoHandler", "io.netty.channel.uring.IoUringDatagramChannel"); + DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollIoHandler", "io.netty.channel.epoll.EpollDatagramChannel"); + DATAGRAM_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueIoHandler", "io.netty.channel.kqueue.KQueueDatagramChannel"); } /** * Returns a socket channel class suitable for specified event loop group. * - * @param eventLoopGroup the event loop group for which to identify an appropriate socket channel class; must not + * @param ioEventLoopGroup the event loop group for which to identify an appropriate socket channel class; must not * be {@code null} * * @return a socket channel class suitable for use with the given event loop group * - * @throws IllegalArgumentException in case of null or unrecognized event loop group + * @throws IllegalArgumentException if no suitable socket channel class could be found for the given event loop + * group + * @throws NullPointerException if the given {@code ioEventLoopGroup} was {@code null} */ - static Class getSocketChannelClass(final EventLoopGroup eventLoopGroup) { - Objects.requireNonNull(eventLoopGroup); - - final String socketChannelClassName = SOCKET_CHANNEL_CLASSES.get(eventLoopGroup.getClass().getName()); - - if (socketChannelClassName == null) { - throw new IllegalArgumentException("No socket channel class found for event loop group type: " + eventLoopGroup.getClass().getName()); - } - - try { - return Class.forName(socketChannelClassName).asSubclass(SocketChannel.class); - } catch (final ClassNotFoundException e) { - throw new IllegalArgumentException(e); - } + static Class getSocketChannelClass(final IoEventLoopGroup ioEventLoopGroup) { + return getChannelClass(Objects.requireNonNull(ioEventLoopGroup), SOCKET_CHANNEL_CLASSES, SocketChannel.class); } /** * Returns a datagram channel class suitable for specified event loop group. * - * @param eventLoopGroup the event loop group for which to identify an appropriate datagram channel class; must not - * be {@code null} + * @param ioEventLoopGroup the event loop group for which to identify an appropriate datagram channel class; must + * not be {@code null} * * @return a datagram channel class suitable for use with the given event loop group * - * @throws IllegalArgumentException in case of null or unrecognized event loop group + * @throws IllegalArgumentException if no suitable datagram channel class could be found for the given event loop + * group + * @throws NullPointerException if the given {@code ioEventLoopGroup} was {@code null} */ - static Class getDatagramChannelClass(final EventLoopGroup eventLoopGroup) { - Objects.requireNonNull(eventLoopGroup); + static Class getDatagramChannelClass(final IoEventLoopGroup ioEventLoopGroup) { + return getChannelClass(Objects.requireNonNull(ioEventLoopGroup), DATAGRAM_CHANNEL_CLASSES, DatagramChannel.class); + } - final String datagramChannelClassName = DATAGRAM_CHANNEL_CLASSES.get(eventLoopGroup.getClass().getName()); + private static Class getChannelClass(final IoEventLoopGroup ioEventLoopGroup, + final Map channelClassesByIoHandlerClass, + final Class channelType) { - if (datagramChannelClassName == null) { - throw new IllegalArgumentException("No datagram channel class found for event loop group type: " + eventLoopGroup.getClass().getName()); - } + for (final Map.Entry entry : channelClassesByIoHandlerClass.entrySet()) { + try { + final Class ioHandlerClass = + Class.forName(entry.getKey()).asSubclass(IoHandler.class); - try { - return Class.forName(datagramChannelClassName).asSubclass(DatagramChannel.class); - } catch (final ClassNotFoundException e) { - throw new IllegalArgumentException(e); + if (ioEventLoopGroup.isIoType(ioHandlerClass)) { + return Class.forName(entry.getValue()).asSubclass(channelType); + } + } catch (final ClassNotFoundException e) { + continue; + } } + + throw new IllegalArgumentException("No suitable channel class found for event loop group"); } } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java index 8b1f09905..20e4a5063 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsKey.java @@ -101,19 +101,4 @@ protected ECKey getKey() { public ECParameterSpec getParams() { return this.key.getParams(); } - - protected static byte[] decodeBase64EncodedString(final String base64EncodedString) { - final ByteBuf base64EncodedByteBuf = - Unpooled.wrappedBuffer(base64EncodedString.getBytes(StandardCharsets.US_ASCII)); - - final ByteBuf decodedByteBuf = Base64.decode(base64EncodedByteBuf); - final byte[] decodedBytes = new byte[decodedByteBuf.readableBytes()]; - - decodedByteBuf.readBytes(decodedBytes); - - base64EncodedByteBuf.release(); - decodedByteBuf.release(); - - return decodedBytes; - } } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java index 9beb1514b..790121199 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsSigningKey.java @@ -31,6 +31,7 @@ import java.security.interfaces.ECPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; /** * A private key used to sign authentication tokens. Signing keys are associated with a developer team (in Apple's @@ -154,7 +155,7 @@ public static ApnsSigningKey loadFromInputStream(final InputStream inputStream, base64EncodedPrivateKey = privateKeyBuilder.toString(); } - final byte[] keyBytes = decodeBase64EncodedString(base64EncodedPrivateKey); + final byte[] keyBytes = Base64.getDecoder().decode(base64EncodedPrivateKey); final PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); final KeyFactory keyFactory = KeyFactory.getInstance("EC"); diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java index 2e69131e9..60a10b8a0 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKey.java @@ -33,6 +33,7 @@ import java.security.spec.ECPoint; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; /** *

A public key used to verify authentication tokens. Signing keys are associated with a developer team (in Apple's @@ -161,7 +162,7 @@ public static ApnsVerificationKey loadFromInputStream(final InputStream inputStr base64EncodedPublicKey = publicKeyBuilder.toString(); } - final byte[] keyBytes = decodeBase64EncodedString(base64EncodedPublicKey); + final byte[] keyBytes = Base64.getDecoder().decode(base64EncodedPublicKey); final X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); final KeyFactory keyFactory = KeyFactory.getInstance("EC"); diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java index e009aa5eb..916d3a3dc 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2Server.java @@ -26,7 +26,7 @@ import io.netty.channel.*; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.socket.SocketChannel; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslHandler; @@ -76,13 +76,13 @@ public void exceptionCaught(final ChannelHandlerContext context, final Throwable this.bootstrap.group(eventLoopGroup); this.shouldShutDownEventLoopGroup = false; } else { - this.bootstrap.group(new NioEventLoopGroup(1)); + this.bootstrap.group(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())); this.shouldShutDownEventLoopGroup = true; } this.allChannels = new DefaultChannelGroup(this.bootstrap.config().group().next()); - this.bootstrap.channel(ServerChannelClassUtil.getServerSocketChannelClass(this.bootstrap.config().group())); + this.bootstrap.channel(ServerChannelClassUtil.getServerSocketChannelClass((IoEventLoopGroup) this.bootstrap.config().group())); this.bootstrap.childHandler(new ChannelInitializer() { @Override diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java index 05eea2f18..9958d9ed9 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BaseHttp2ServerBuilder.java @@ -22,7 +22,7 @@ package com.eatthepath.pushy.apns.server; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; import io.netty.handler.codec.http2.Http2SecurityUtil; import io.netty.handler.ssl.*; import io.netty.util.ReferenceCounted; @@ -53,7 +53,7 @@ abstract class BaseHttp2ServerBuilder { protected InputStream trustedClientCertificateInputStream; protected X509Certificate[] trustedClientCertificates; - protected EventLoopGroup eventLoopGroup; + protected IoEventLoopGroup ioEventLoopGroup; protected int maxConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS; @@ -219,7 +219,7 @@ public BaseHttp2ServerBuilder setTrustedClientCertificateChain(final InputStr * * @return a reference to this builder */ - public BaseHttp2ServerBuilder setTrustedServerCertificateChain(final X509Certificate... certificates) { + public BaseHttp2ServerBuilder setTrustedClientCertificateChain(final X509Certificate... certificates) { this.trustedClientCertificatePemFile = null; this.trustedClientCertificateInputStream = null; this.trustedClientCertificates = certificates; @@ -231,15 +231,15 @@ public BaseHttp2ServerBuilder setTrustedServerCertificateChain(final X509Cert *

Sets the event loop group to be used by the server under construction. If not set (or if {@code null}), the * server will create and manage its own event loop group.

* - * @param eventLoopGroup the event loop group to use for this server, or {@code null} to let the server manage its + * @param ioEventLoopGroup the event loop group to use for this server, or {@code null} to let the server manage its * own event loop group * * @return a reference to this builder * * @since 0.8 */ - public BaseHttp2ServerBuilder setEventLoopGroup(final EventLoopGroup eventLoopGroup) { - this.eventLoopGroup = eventLoopGroup; + public BaseHttp2ServerBuilder setIoEventLoopGroup(final IoEventLoopGroup ioEventLoopGroup) { + this.ioEventLoopGroup = ioEventLoopGroup; return this; } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java index 2b609b996..4d26d75b6 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServer.java @@ -23,7 +23,7 @@ package com.eatthepath.pushy.apns.server; import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.ssl.SslContext; @@ -42,8 +42,8 @@ public class BenchmarkApnsServer extends BaseHttp2Server { private final int maxConcurrentStreams; - BenchmarkApnsServer(final SslContext sslContext, final EventLoopGroup eventLoopGroup, final int maxConcurrentStreams) { - super(sslContext, eventLoopGroup); + BenchmarkApnsServer(final SslContext sslContext, final IoEventLoopGroup ioEventLoopGroup, final int maxConcurrentStreams) { + super(sslContext, ioEventLoopGroup); this.maxConcurrentStreams = maxConcurrentStreams; } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java index f506391f2..87eb8e51c 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/BenchmarkApnsServerBuilder.java @@ -22,7 +22,7 @@ package com.eatthepath.pushy.apns.server; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; import io.netty.handler.ssl.SslContext; import javax.net.ssl.SSLException; @@ -82,14 +82,14 @@ public BenchmarkApnsServerBuilder setTrustedClientCertificateChain(final InputSt } @Override - public BenchmarkApnsServerBuilder setTrustedServerCertificateChain(final X509Certificate... certificates) { - super.setTrustedServerCertificateChain(certificates); + public BenchmarkApnsServerBuilder setTrustedClientCertificateChain(final X509Certificate... certificates) { + super.setTrustedClientCertificateChain(certificates); return this; } @Override - public BenchmarkApnsServerBuilder setEventLoopGroup(final EventLoopGroup eventLoopGroup) { - super.setEventLoopGroup(eventLoopGroup); + public BenchmarkApnsServerBuilder setIoEventLoopGroup(final IoEventLoopGroup ioEventLoopGroup) { + super.setIoEventLoopGroup(ioEventLoopGroup); return this; } @@ -112,6 +112,6 @@ public BenchmarkApnsServer build() throws SSLException { @Override protected BenchmarkApnsServer constructServer(final SslContext sslContext) { - return new BenchmarkApnsServer(sslContext, this.eventLoopGroup, this.maxConcurrentStreams); + return new BenchmarkApnsServer(sslContext, this.ioEventLoopGroup, this.maxConcurrentStreams); } } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java index f192a2cb5..f5ac70fb5 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServer.java @@ -23,7 +23,7 @@ package com.eatthepath.pushy.apns.server; import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; import io.netty.handler.codec.http2.Http2Settings; import io.netty.handler.ssl.SslContext; @@ -55,7 +55,7 @@ public class MockApnsServer extends BaseHttp2Server { private final int maxConcurrentStreams; private final boolean generateApnsUniqueId; - MockApnsServer(final SslContext sslContext, final EventLoopGroup eventLoopGroup, + MockApnsServer(final SslContext sslContext, final IoEventLoopGroup eventLoopGroup, final PushNotificationHandlerFactory handlerFactory, final MockApnsServerListener listener, final int maxConcurrentStreams, boolean generateApnsUniqueId) { diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java index ac24a2740..b794c99ff 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilder.java @@ -22,7 +22,7 @@ package com.eatthepath.pushy.apns.server; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; import io.netty.handler.ssl.SslContext; import javax.net.ssl.SSLException; @@ -87,14 +87,14 @@ public MockApnsServerBuilder setTrustedClientCertificateChain(final InputStream } @Override - public MockApnsServerBuilder setTrustedServerCertificateChain(final X509Certificate... certificates) { - super.setTrustedServerCertificateChain(certificates); + public MockApnsServerBuilder setTrustedClientCertificateChain(final X509Certificate... certificates) { + super.setTrustedClientCertificateChain(certificates); return this; } @Override - public MockApnsServerBuilder setEventLoopGroup(final EventLoopGroup eventLoopGroup) { - super.setEventLoopGroup(eventLoopGroup); + public MockApnsServerBuilder setIoEventLoopGroup(final IoEventLoopGroup ioEventLoopGroup) { + super.setIoEventLoopGroup(ioEventLoopGroup); return this; } @@ -156,6 +156,6 @@ protected MockApnsServer constructServer(final SslContext sslContext) { throw new IllegalStateException("Must provide a push notification handler factory before building a mock server."); } - return new MockApnsServer(sslContext, this.eventLoopGroup, this.handlerFactory, this.listener, this.maxConcurrentStreams, generateApnsUniqueId); + return new MockApnsServer(sslContext, this.ioEventLoopGroup, this.handlerFactory, this.listener, this.maxConcurrentStreams, generateApnsUniqueId); } } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java b/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java index a43a1c2be..27e96ae74 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtil.java @@ -22,8 +22,10 @@ package com.eatthepath.pushy.apns.server; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoEventLoopGroup; +import io.netty.channel.IoHandler; import io.netty.channel.ServerChannel; +import io.netty.channel.socket.ServerSocketChannel; import java.util.HashMap; import java.util.Map; @@ -34,34 +36,40 @@ class ServerChannelClassUtil { private static final Map SERVER_SOCKET_CHANNEL_CLASSES = new HashMap<>(); static { - SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.nio.NioEventLoopGroup", "io.netty.channel.socket.nio.NioServerSocketChannel"); - SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollEventLoopGroup", "io.netty.channel.epoll.EpollServerSocketChannel"); - SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueEventLoopGroup", "io.netty.channel.kqueue.KQueueServerSocketChannel"); + SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.nio.NioIoHandler", "io.netty.channel.socket.nio.NioServerSocketChannel"); + SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.uring.IoUringIoHandler", "io.netty.channel.uring.IoUringServerSocketChannel"); + SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.epoll.EpollIoHandler", "io.netty.channel.epoll.EpollServerSocketChannel"); + SERVER_SOCKET_CHANNEL_CLASSES.put("io.netty.channel.kqueue.KQueueIoHandler", "io.netty.channel.kqueue.KQueueServerSocketChannel"); } /** * Returns a server socket channel class suitable for specified event loop group. * - * @param eventLoopGroup the event loop group for which to identify an appropriate socket channel class; must not + * @param ioEventLoopGroup the event loop group for which to identify an appropriate socket channel class; must not * be {@code null} * * @return a server socket channel class suitable for use with the given event loop group * - * @throws IllegalArgumentException in case of null or unrecognized event loop group + * @throws IllegalArgumentException if no suitable server socket channel class could be found for the given event + * loop group + * @throws NullPointerException if the given {@code ioEventLoopGroup} was {@code null} */ - static Class getServerSocketChannelClass(final EventLoopGroup eventLoopGroup) { - Objects.requireNonNull(eventLoopGroup); + static Class getServerSocketChannelClass(final IoEventLoopGroup ioEventLoopGroup) { + Objects.requireNonNull(ioEventLoopGroup); - final String serverSocketChannelClassName = SERVER_SOCKET_CHANNEL_CLASSES.get(eventLoopGroup.getClass().getName()); + for (final Map.Entry entry : SERVER_SOCKET_CHANNEL_CLASSES.entrySet()) { + try { + final Class ioHandlerClass = + Class.forName(entry.getKey()).asSubclass(IoHandler.class); - if (serverSocketChannelClassName == null) { - throw new IllegalArgumentException("No server socket channel class found for event loop group type: " + eventLoopGroup.getClass().getName()); + if (ioEventLoopGroup.isIoType(ioHandlerClass)) { + return Class.forName(entry.getValue()).asSubclass(ServerSocketChannel.class); + } + } catch (final ClassNotFoundException e) { + continue; + } } - try { - return Class.forName(serverSocketChannelClassName).asSubclass(ServerChannel.class); - } catch (final ClassNotFoundException e) { - throw new IllegalArgumentException(e); - } + throw new IllegalArgumentException("No suitable channel class found for event loop group"); } } diff --git a/pushy/src/main/resources/.gitignore b/pushy/src/main/resources/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/pushy/src/test/java/com/eatthepath/ApnsTestCertificates.java b/pushy/src/test/java/com/eatthepath/ApnsTestCertificates.java new file mode 100644 index 000000000..3bdff955f --- /dev/null +++ b/pushy/src/test/java/com/eatthepath/ApnsTestCertificates.java @@ -0,0 +1,90 @@ +package com.eatthepath; + +import io.netty.pkitesting.CertificateBuilder; +import io.netty.pkitesting.X509Bundle; + +import java.time.Duration; +import java.time.Instant; +import java.util.Base64; + +public class ApnsTestCertificates { + + private final X509Bundle caBundle; + private final X509Bundle trustedServerCertificateBundle; + private final X509Bundle untrustedServerCertificateBundle; + private final X509Bundle singleTopicClientCertificateBundle; + private final X509Bundle multiTopicClientCertificateBundle; + + // This is the value OpenSSL encodes for `ASN1:SEQUENCE:apns_topics`, where `apns_topics` is defined as: + // + // [ apns_topics ] + // aps_topics.0 = UTF8String:com.eatthepath.pushy + // aps_topics.1 = SEQWRAP,UTF8String:app + // aps_topics.2 = UTF8String:com.eatthepath.pushy.voip + // aps_topics.3 = SEQWRAP,UTF8String:voip + // aps_topics.4 = UTF8String:com.eatthepath.pushy.complication + // aps_topics.5 = SEQWRAP,UTF8String:complication + private static final byte[] MULTI_TOPIC_EXTENSION_VALUE = Base64.getDecoder() + .decode("BHUwcwwUY29tLmVhdHRoZXBhdGgucHVzaHkwBQwDYXBwDBljb20uZWF0dGhlcGF0aC5wdXNoeS52b2lwMAYMBHZvaXAMIWNvbS5lYXR0aGVwYXRoLnB1c2h5LmNvbXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24="); + + public ApnsTestCertificates() throws Exception { + final Instant now = Instant.now(); + + final CertificateBuilder rootCertificateBuilderTemplate = new CertificateBuilder() + .notBefore(now) + .notAfter(now.plus(Duration.ofHours(1))); + + caBundle = rootCertificateBuilderTemplate.copy() + .subject("CN=PushyTestRoot") + .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyCertSign) + .setIsCertificateAuthority(true) + .buildSelfSigned(); + + final CertificateBuilder serverCertificateBuilderTemplate = rootCertificateBuilderTemplate.copy() + .subject("CN=com.eatthepath.pushy") + .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyEncipherment) + .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_CLIENT_AUTH) + .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_SERVER_AUTH) + .setIsCertificateAuthority(false); + + trustedServerCertificateBundle = serverCertificateBuilderTemplate.copy() + .addSanDnsName("localhost") + .buildIssuedBy(caBundle); + + untrustedServerCertificateBundle = serverCertificateBuilderTemplate.copy() + .buildIssuedBy(caBundle); + + final CertificateBuilder clientCertificateBuilderTemplate = rootCertificateBuilderTemplate.copy() + .subject("CN=Apple Push Services: com.eatthepath.pushy, UID=com.eatthepath.pushy") + .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature) + .addExtendedKeyUsage(CertificateBuilder.ExtendedKeyUsage.PKIX_KP_CLIENT_AUTH) + .setIsCertificateAuthority(false); + + singleTopicClientCertificateBundle = clientCertificateBuilderTemplate.copy() + .buildIssuedBy(caBundle); + + multiTopicClientCertificateBundle = clientCertificateBuilderTemplate.copy() + .addExtensionOctetString("1.2.840.113635.100.6.3.6", false, MULTI_TOPIC_EXTENSION_VALUE) + .buildIssuedBy(caBundle); + } + + public X509Bundle getCaBundle() { + return caBundle; + } + + public X509Bundle getTrustedServerCertificateBundle() { + return trustedServerCertificateBundle; + } + + public X509Bundle getUntrustedServerCertificateBundle() { + return untrustedServerCertificateBundle; + } + + public X509Bundle getSingleTopicClientCertificateBundle() { + return singleTopicClientCertificateBundle; + } + + public X509Bundle getMultiTopicClientCertificateBundle() { + return multiTopicClientCertificateBundle; + } +} diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java index 69e78639b..3af7875b5 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/AbstractClientServerTest.java @@ -22,12 +22,15 @@ package com.eatthepath.pushy.apns; +import com.eatthepath.ApnsTestCertificates; import com.eatthepath.pushy.apns.auth.ApnsSigningKey; import com.eatthepath.pushy.apns.auth.ApnsVerificationKey; import com.eatthepath.pushy.apns.auth.KeyPairUtil; import com.eatthepath.pushy.apns.server.*; import com.eatthepath.pushy.apns.util.ApnsPayloadBuilder; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.IoEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.util.concurrent.*; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -36,7 +39,6 @@ import javax.net.ssl.SSLException; import java.io.IOException; -import java.io.InputStream; import java.security.KeyPair; import java.security.interfaces.ECPrivateKey; import java.security.interfaces.ECPublicKey; @@ -47,16 +49,8 @@ public class AbstractClientServerTest { protected static ApnsClientResources CLIENT_RESOURCES; - protected static NioEventLoopGroup SERVER_EVENT_LOOP_GROUP; - - protected static final String CA_CERTIFICATE_FILENAME = "/ca.pem"; - protected static final String SERVER_CERTIFICATES_FILENAME = "/server-certs.pem"; - protected static final String SERVER_KEY_FILENAME = "/server-key.pem"; - protected static final String UNTRUSTED_HOSTNAME_SERVER_CERTIFICATES_FILENAME = "/server-certs-no-alt.pem"; - protected static final String UNTRUSTED_HOSTNAME_SERVER_KEY_FILENAME = "/server-key-no-alt.pem"; - - protected static final String MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME = "/multi-topic-client.p12"; - protected static final String KEYSTORE_PASSWORD = "pushy-test"; + protected static IoEventLoopGroup SERVER_EVENT_LOOP_GROUP; + protected static ApnsTestCertificates TEST_CERTIFICATES; protected static final String HOST = "localhost"; protected static final int PORT = 8443; @@ -79,9 +73,11 @@ public class AbstractClientServerTest { protected Map> topicsByVerificationKey; @BeforeAll - public static void setUpBeforeClass() { - CLIENT_RESOURCES = new ApnsClientResources(new NioEventLoopGroup(2)); - SERVER_EVENT_LOOP_GROUP = new NioEventLoopGroup(2); + public static void setUpBeforeClass() throws Exception { + CLIENT_RESOURCES = new ApnsClientResources(new MultiThreadIoEventLoopGroup(2, NioIoHandler.newFactory())); + SERVER_EVENT_LOOP_GROUP = new MultiThreadIoEventLoopGroup(2, NioIoHandler.newFactory()); + + TEST_CERTIFICATES = new ApnsTestCertificates(); } @BeforeEach @@ -111,15 +107,14 @@ protected ApnsClient buildTlsAuthenticationClient() throws IOException { } protected ApnsClient buildTlsAuthenticationClient(final ApnsClientMetricsListener metricsListener) throws IOException { - try (final InputStream p12InputStream = getClass().getResourceAsStream(MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME)) { - return new ApnsClientBuilder() - .setApnsServer(HOST, PORT) - .setClientCredentials(p12InputStream, KEYSTORE_PASSWORD) - .setTrustedServerCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) - .setApnsClientResources(CLIENT_RESOURCES) - .setMetricsListener(metricsListener) - .build(); - } + return new ApnsClientBuilder() + .setApnsServer(HOST, PORT) + .setClientCredentials(TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getCertificate(), + TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getKeyPair().getPrivate()) + .setTrustedServerCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate()) + .setApnsClientResources(CLIENT_RESOURCES) + .setMetricsListener(metricsListener) + .build(); } protected ApnsClient buildTokenAuthenticationClient() throws SSLException { @@ -129,7 +124,7 @@ protected ApnsClient buildTokenAuthenticationClient() throws SSLException { protected ApnsClient buildTokenAuthenticationClient(final ApnsClientMetricsListener metricsListener) throws SSLException { return new ApnsClientBuilder() .setApnsServer(HOST, PORT) - .setTrustedServerCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) + .setTrustedServerCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate()) .setSigningKey(this.signingKey) .setApnsClientResources(CLIENT_RESOURCES) .setMetricsListener(metricsListener) @@ -150,9 +145,10 @@ protected MockApnsServer buildServer(final PushNotificationHandlerFactory handle protected MockApnsServer buildServer(final PushNotificationHandlerFactory handlerFactory, final MockApnsServerListener listener, final boolean generateApnsUniqueId) throws SSLException { return new MockApnsServerBuilder() - .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(SERVER_KEY_FILENAME), null) - .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) - .setEventLoopGroup(SERVER_EVENT_LOOP_GROUP) + .setServerCredentials(TEST_CERTIFICATES.getTrustedServerCertificateBundle().getCertificatePathWithRoot(), + TEST_CERTIFICATES.getTrustedServerCertificateBundle().getKeyPair().getPrivate()) + .setTrustedClientCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate()) + .setIoEventLoopGroup(SERVER_EVENT_LOOP_GROUP) .setHandlerFactory(handlerFactory) .setListener(listener) .generateApnsUniqueId(generateApnsUniqueId) diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java index 1ba7faa68..d11604f68 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientBuilderTest.java @@ -22,33 +22,39 @@ package com.eatthepath.pushy.apns; +import com.eatthepath.ApnsTestCertificates; import com.eatthepath.pushy.apns.auth.ApnsSigningKey; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; +import io.netty.pkitesting.X509Bundle; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.io.File; import java.io.InputStream; -import java.security.KeyStore.PrivateKeyEntry; -import java.security.cert.X509Certificate; - +import java.io.OutputStream; +import java.nio.file.Files; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; public class ApnsClientBuilderTest { - private static final String SIGNING_KEY_FILENAME = "/token-auth-private-key.p8"; - - private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME = "/single-topic-client.p12"; - private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME = "/single-topic-client-unprotected.p12"; - private static final String KEYSTORE_PASSWORD = "pushy-test"; + private static ApnsTestCertificates TEST_CERTIFICATES; private static ApnsClientResources CLIENT_RESOURCES; @BeforeAll - public static void setUpBeforeClass() { - CLIENT_RESOURCES = new ApnsClientResources(new NioEventLoopGroup(1)); + public static void setUpBeforeClass() throws Exception { + TEST_CERTIFICATES = new ApnsTestCertificates(); + CLIENT_RESOURCES = new ApnsClientResources(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())); } @AfterAll @@ -58,105 +64,95 @@ public static void tearDownAfterClass() throws Exception { @Test void testBuildClientWithPasswordProtectedP12File() throws Exception { - // We're happy here as long as nothing throws an exception - final ApnsClient client = new ApnsClientBuilder() + final File keystoreFile = File.createTempFile("pushy-test", ".p12"); + keystoreFile.deleteOnExit(); + + try (final OutputStream keystoreOutputStream = Files.newOutputStream(keystoreFile.toPath())) { + TEST_CERTIFICATES.getSingleTopicClientCertificateBundle().toKeyStore(KEYSTORE_PASSWORD.toCharArray()) + .store(keystoreOutputStream, KEYSTORE_PASSWORD.toCharArray()); + } + + final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder() .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials(new File(this.getClass().getResource(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME).toURI()), KEYSTORE_PASSWORD) - .build(); + .setClientCredentials(keystoreFile, KEYSTORE_PASSWORD) + .build()); client.close().get(); } @Test void testBuildClientWithPasswordProtectedP12InputStream() throws Exception { - // We're happy here as long as nothing throws an exception - try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) { - final ApnsClient client = new ApnsClientBuilder() + final File keystoreFile = File.createTempFile("pushy-test", ".p12"); + keystoreFile.deleteOnExit(); + + try (final OutputStream keystoreOutputStream = Files.newOutputStream(keystoreFile.toPath())) { + TEST_CERTIFICATES.getSingleTopicClientCertificateBundle().toKeyStore(KEYSTORE_PASSWORD.toCharArray()) + .store(keystoreOutputStream, KEYSTORE_PASSWORD.toCharArray()); + } + + try (final InputStream p12InputStream = Files.newInputStream(keystoreFile.toPath())) { + final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder() .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) .setApnsClientResources(CLIENT_RESOURCES) .setClientCredentials(p12InputStream, KEYSTORE_PASSWORD) - .build(); + .build()); client.close().get(); } } @Test - void testBuildClientWithNullPassword() { + void testBuildClientWithNullPassword() throws Exception { + final File keystoreFile = File.createTempFile("pushy-test", ".p12"); + keystoreFile.deleteOnExit(); + + try (final OutputStream keystoreOutputStream = Files.newOutputStream(keystoreFile.toPath())) { + TEST_CERTIFICATES.getSingleTopicClientCertificateBundle().toKeyStore(KEYSTORE_PASSWORD.toCharArray()) + .store(keystoreOutputStream, KEYSTORE_PASSWORD.toCharArray()); + } + assertThrows(NullPointerException.class, () -> new ApnsClientBuilder() .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials(new File(this.getClass().getResource(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME).toURI()), null) + .setClientCredentials(keystoreFile, null) .build()); } - @Test - void testBuildClientWithCertificateAndPasswordProtectedKey() throws Exception { - // We're happy here as long as nothing throws an exception - try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) { - final PrivateKeyEntry privateKeyEntry = - P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD); - - final ApnsClient client = new ApnsClientBuilder() - .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) - .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey(), KEYSTORE_PASSWORD) - .build(); - - client.close().get(); - } - } - @Test void testBuildClientWithCertificateAndUnprotectedKeyNoPassword() throws Exception { - // We DO need a password to unlock the keystore, but the key itself should be unprotected - try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME)) { + final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle(); - final PrivateKeyEntry privateKeyEntry = - P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD); + final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder() + .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) + .setApnsClientResources(CLIENT_RESOURCES) + .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate()) + .build()); - final ApnsClient client = new ApnsClientBuilder() - .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) - .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey()) - .build(); - - client.close().get(); - } + client.close().get(); } @Test void testBuildClientWithCertificateAndUnprotectedKey() throws Exception { - // We DO need a password to unlock the keystore, but the key itself should be unprotected - try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME)) { + final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle(); - final PrivateKeyEntry privateKeyEntry = - P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD); + final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder() + .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) + .setApnsClientResources(CLIENT_RESOURCES) + .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate(), null) + .build()); - final ApnsClient client = new ApnsClientBuilder() - .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) - .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey(), null) - .build(); - - client.close().get(); - } + client.close().get(); } @Test void testBuildWithSigningKey() throws Exception { - try (final InputStream p8InputStream = this.getClass().getResourceAsStream(SIGNING_KEY_FILENAME)) { - final ApnsSigningKey signingKey = ApnsSigningKey.loadFromInputStream(p8InputStream, "TEAM_ID", "KEY_ID"); - - // We're happy here as long as nothing explodes - final ApnsClient client = new ApnsClientBuilder() - .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) - .setApnsClientResources(CLIENT_RESOURCES) - .setSigningKey(signingKey) - .build(); + final ApnsClient client = assertDoesNotThrow(() -> new ApnsClientBuilder() + .setApnsServer(ApnsClientBuilder.PRODUCTION_APNS_HOST) + .setApnsClientResources(CLIENT_RESOURCES) + .setSigningKey(generateSigningKey()) + .build()); - client.close().get(); - } + client.close().get(); } @Test @@ -169,31 +165,31 @@ void testBuildWithoutClientCredentials() { @Test void testBuildWithClientCredentialsAndSigningCertificate() throws Exception { - try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_UNPROTECTED_FILENAME)) { - - final PrivateKeyEntry privateKeyEntry = - P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD); - - try (final InputStream p8InputStream = this.getClass().getResourceAsStream(SIGNING_KEY_FILENAME)) { + final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle(); - final ApnsSigningKey signingKey = ApnsSigningKey.loadFromInputStream(p8InputStream, "TEAM_ID", "KEY_ID"); - - assertThrows(IllegalStateException.class, () -> - new ApnsClientBuilder() - .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials((X509Certificate) privateKeyEntry.getCertificate(), privateKeyEntry.getPrivateKey(), null) - .setSigningKey(signingKey) - .build()); - } - } + assertThrows(IllegalStateException.class, () -> + new ApnsClientBuilder() + .setApnsClientResources(CLIENT_RESOURCES) + .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate()) + .setSigningKey(generateSigningKey()) + .build()); } @Test void testBuildWithoutApnsServerAddress() { + final X509Bundle clientCertificates = TEST_CERTIFICATES.getSingleTopicClientCertificateBundle(); + assertThrows(IllegalStateException.class, () -> - new ApnsClientBuilder() - .setApnsClientResources(CLIENT_RESOURCES) - .setClientCredentials(new File(this.getClass().getResource(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME).toURI()), KEYSTORE_PASSWORD) - .build()); + new ApnsClientBuilder() + .setApnsClientResources(CLIENT_RESOURCES) + .setClientCredentials(clientCertificates.getCertificate(), clientCertificates.getKeyPair().getPrivate()) + .build()); + } + + private static ApnsSigningKey generateSigningKey() throws NoSuchAlgorithmException, InvalidKeyException { + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC"); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + return new ApnsSigningKey("KEY_ID", "TEAM_ID", (ECPrivateKey) keyPair.getPrivate()); } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java index 365d19399..8c8bd71d3 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/ApnsClientTest.java @@ -32,7 +32,6 @@ import org.junit.jupiter.params.provider.ValueSource; import javax.net.ssl.SSLHandshakeException; -import java.io.InputStream; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -228,13 +227,12 @@ void testSendNotificationToUntrustedServer(final boolean useTokenAuthentication) .setApnsClientResources(CLIENT_RESOURCES) .build(); } else { - try (final InputStream p12InputStream = getClass().getResourceAsStream(MULTI_TOPIC_CLIENT_KEYSTORE_FILENAME)) { - cautiousClient = new ApnsClientBuilder() - .setApnsServer(HOST, PORT) - .setClientCredentials(p12InputStream, KEYSTORE_PASSWORD) - .setApnsClientResources(CLIENT_RESOURCES) - .build(); - } + cautiousClient = new ApnsClientBuilder() + .setApnsServer(HOST, PORT) + .setClientCredentials(TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getCertificate(), + TEST_CERTIFICATES.getMultiTopicClientCertificateBundle().getKeyPair().getPrivate()) + .setApnsClientResources(CLIENT_RESOURCES) + .build(); } final MockApnsServer server = this.buildServer(new AcceptAllPushNotificationHandlerFactory()); @@ -268,9 +266,10 @@ void testSendNotificationToUntrustedServer(final boolean useTokenAuthentication) @ValueSource(booleans = { true, false }) void testSendNotificationToServerWithUntrustedHostname(final boolean useTokenAuthentication) throws Exception { final MockApnsServer serverWithUntrustedHostname = new MockApnsServerBuilder() - .setServerCredentials(getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_KEY_FILENAME), null) - .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) - .setEventLoopGroup(SERVER_EVENT_LOOP_GROUP) + .setServerCredentials(TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getCertificatePathWithRoot(), + TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getKeyPair().getPrivate()) + .setTrustedClientCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate()) + .setIoEventLoopGroup(SERVER_EVENT_LOOP_GROUP) .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) .build(); @@ -304,15 +303,16 @@ void testSendNotificationToServerWithUntrustedHostname(final boolean useTokenAut @Test void testSendNotificationToServerWithUntrustedHostnameAndVerificationDisabled() throws Exception { final MockApnsServer serverWithUntrustedHostname = new MockApnsServerBuilder() - .setServerCredentials(getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(UNTRUSTED_HOSTNAME_SERVER_KEY_FILENAME), null) - .setTrustedClientCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) - .setEventLoopGroup(SERVER_EVENT_LOOP_GROUP) + .setServerCredentials(TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getCertificatePathWithRoot(), + TEST_CERTIFICATES.getUntrustedServerCertificateBundle().getKeyPair().getPrivate()) + .setTrustedClientCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate()) + .setIoEventLoopGroup(SERVER_EVENT_LOOP_GROUP) .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) .build(); final ApnsClient client = new ApnsClientBuilder() .setApnsServer(HOST, PORT) - .setTrustedServerCertificateChain(getClass().getResourceAsStream(CA_CERTIFICATE_FILENAME)) + .setTrustedServerCertificateChain(TEST_CERTIFICATES.getCaBundle().getCertificate()) .setSigningKey(this.signingKey) .setApnsClientResources(CLIENT_RESOURCES) .setHostnameVerificationEnabled(false) diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java index 5f4830eb2..1bc667a95 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/ClientChannelClassUtilTest.java @@ -22,107 +22,168 @@ package com.eatthepath.pushy.apns; -import io.netty.channel.epoll.Epoll; -import io.netty.channel.epoll.EpollDatagramChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.epoll.EpollSocketChannel; -import io.netty.channel.kqueue.KQueue; -import io.netty.channel.kqueue.KQueueDatagramChannel; -import io.netty.channel.kqueue.KQueueEventLoopGroup; -import io.netty.channel.kqueue.KQueueSocketChannel; +import io.netty.channel.IoEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; +import io.netty.channel.epoll.*; +import io.netty.channel.kqueue.*; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioSocketChannel; -import org.junit.jupiter.api.Test; +import io.netty.channel.uring.IoUring; +import io.netty.channel.uring.IoUringDatagramChannel; +import io.netty.channel.uring.IoUringIoHandler; +import io.netty.channel.uring.IoUringSocketChannel; +import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ClientChannelClassUtilTest { - @Test - void testGetCoreSocketChannelClass() { - final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1); + @Nested + class NioTransport { - try { - assertEquals(NioSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(nioEventLoopGroup)); - } finally { - nioEventLoopGroup.shutdownGracefully(); - } - } + private IoEventLoopGroup ioEventLoopGroup; + private NioEventLoopGroup legacyEventLoopGroup; - @Test - void testGetKqueueSocketChannelClass() { - final String unavailabilityMessage = - KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null; + @BeforeEach + void setUp() { + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); + legacyEventLoopGroup = new NioEventLoopGroup(); + } - assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage); + @Test + void getSocketChannelClass() { + assertEquals(NioSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup)); + assertEquals(NioSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(legacyEventLoopGroup)); + } - final KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1); + @Test + void getDatagramClass() { + assertEquals(NioDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup)); + assertEquals(NioDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(legacyEventLoopGroup)); + } - try { - assertEquals(KQueueSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(kQueueEventLoopGroup)); - } finally { - kQueueEventLoopGroup.shutdownGracefully(); + @AfterEach + void tearDown() { + ioEventLoopGroup.shutdownGracefully(); + legacyEventLoopGroup.shutdownGracefully(); } } - @Test - void testGetEpollSocketChannelClass() { - final String unavailabilityMessage = + @Nested + class EpollTransport { + + private IoEventLoopGroup ioEventLoopGroup; + private EpollEventLoopGroup legacyEventLoopGroup; + + @BeforeEach + void setUp() { + final String unavailabilityMessage = Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null; - assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage); + assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage); - final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1); + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory()); + legacyEventLoopGroup = new EpollEventLoopGroup(); + } - try { - assertEquals(EpollSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(epollEventLoopGroup)); - } finally { - epollEventLoopGroup.shutdownGracefully(); + @Test + void getSocketChannelClass() { + assertEquals(EpollSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup)); + assertEquals(EpollSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(legacyEventLoopGroup)); + } + + @Test + void getDatagramClass() { + assertEquals(EpollDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup)); + assertEquals(EpollDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(legacyEventLoopGroup)); } - } - @Test - void testGetCoreDatagramChannelClass() { - final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1); + @AfterEach + void tearDown() { + if (ioEventLoopGroup != null) { + ioEventLoopGroup.shutdownGracefully(); + } - try { - assertEquals(NioDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(nioEventLoopGroup)); - } finally { - nioEventLoopGroup.shutdownGracefully(); + if (legacyEventLoopGroup != null) { + legacyEventLoopGroup.shutdownGracefully(); + } } } - @Test - void testGetKqueueDatagramChannelClass() { - final String unavailabilityMessage = - KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null; + @Nested + class IoUringTransport { + + private IoEventLoopGroup ioEventLoopGroup; + + @BeforeEach + void setUp() { + final String unavailabilityMessage = + IoUring.unavailabilityCause() != null ? IoUring.unavailabilityCause().getMessage() : null; - assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage); + assumeTrue(IoUring.isAvailable(), "io_uring not available: " + unavailabilityMessage); - final KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1); + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(IoUringIoHandler.newFactory()); + } + + @Test + void getSocketChannelClass() { + assertEquals(IoUringSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup)); + } + + @Test + void getDatagramClass() { + assertEquals(IoUringDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup)); + } - try { - assertEquals(KQueueDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(kQueueEventLoopGroup)); - } finally { - kQueueEventLoopGroup.shutdownGracefully(); + @AfterEach + void tearDown() { + if (ioEventLoopGroup != null) { + ioEventLoopGroup.shutdownGracefully(); + } } } - @Test - void testGetEpollDatagramChannelClass() { - final String unavailabilityMessage = - Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null; + @Nested + class KQueueTransport { - assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage); + private IoEventLoopGroup ioEventLoopGroup; + private KQueueEventLoopGroup legacyEventLoopGroup; + + @BeforeEach + void setUp() { + final String unavailabilityMessage = + KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null; + + assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage); + + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(KQueueIoHandler.newFactory()); + legacyEventLoopGroup = new KQueueEventLoopGroup(); + } + + @Test + void getSocketChannelClass() { + assertEquals(KQueueSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(ioEventLoopGroup)); + assertEquals(KQueueSocketChannel.class, ClientChannelClassUtil.getSocketChannelClass(legacyEventLoopGroup)); + } + + @Test + void getDatagramClass() { + assertEquals(KQueueDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(ioEventLoopGroup)); + assertEquals(KQueueDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(legacyEventLoopGroup)); + } - final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1); + @AfterEach + void tearDown() { + if (ioEventLoopGroup != null) { + ioEventLoopGroup.shutdownGracefully(); + } - try { - assertEquals(EpollDatagramChannel.class, ClientChannelClassUtil.getDatagramChannelClass(epollEventLoopGroup)); - } finally { - epollEventLoopGroup.shutdownGracefully(); + if (legacyEventLoopGroup != null) { + legacyEventLoopGroup.shutdownGracefully(); + } } } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java index e9ce410b0..47ba72588 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/P12UtilTest.java @@ -22,26 +22,32 @@ package com.eatthepath.pushy.apns; +import io.netty.pkitesting.CertificateBuilder; +import io.netty.pkitesting.X509Bundle; import org.junit.jupiter.api.Test; -import java.io.InputStream; +import java.io.*; +import java.nio.file.Files; +import java.security.KeyStore; import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.time.Duration; +import java.time.Instant; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; public class P12UtilTest { - private static final String SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME = "/single-topic-client.p12"; - private static final String MULTIPLE_KEY_KEYSTORE_FILENAME = "/multiple-keys.p12"; - private static final String NO_KEY_KEYSTORE_FILENAME = "/no-keys.p12"; - private static final String KEYSTORE_PASSWORD = "pushy-test"; @Test void testGetPrivateKeyEntryFromP12InputStream() throws Exception { - try (final InputStream p12InputStream = P12UtilTest.class.getResourceAsStream(SINGLE_TOPIC_CLIENT_KEYSTORE_FILENAME)) { + final File keyStoreFile = writeTemporaryKeyStore(1); + + try (final InputStream p12InputStream = Files.newInputStream(keyStoreFile.toPath())) { final PrivateKeyEntry privateKeyEntry = P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD); @@ -51,7 +57,9 @@ void testGetPrivateKeyEntryFromP12InputStream() throws Exception { @Test void testGetPrivateKeyEntryFromP12InputStreamWithMultipleKeys() throws Exception { - try (final InputStream p12InputStream = P12UtilTest.class.getResourceAsStream(MULTIPLE_KEY_KEYSTORE_FILENAME)) { + final File keyStoreFile = writeTemporaryKeyStore(4); + + try (final InputStream p12InputStream = Files.newInputStream(keyStoreFile.toPath())) { final PrivateKeyEntry privateKeyEntry = P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD); @@ -61,9 +69,53 @@ void testGetPrivateKeyEntryFromP12InputStreamWithMultipleKeys() throws Exception @Test void testGetPrivateKeyEntryFromP12InputStreamWithNoKeys() throws Exception { - try (final InputStream p12InputStream = P12UtilTest.class.getResourceAsStream(NO_KEY_KEYSTORE_FILENAME)) { + final File keyStoreFile = writeTemporaryKeyStore(0); + + try (final InputStream p12InputStream = Files.newInputStream(keyStoreFile.toPath())) { assertThrows(KeyStoreException.class, () -> P12Util.getFirstPrivateKeyEntryFromP12InputStream(p12InputStream, KEYSTORE_PASSWORD)); } } + + private static File writeTemporaryKeyStore(final int keyCount) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException { + final KeyStore keyStore; + + try { + keyStore = KeyStore.getInstance("PKCS12"); + } catch (final KeyStoreException e) { + throw new AssertionError("Every implementation of the Java platform is required to support PKCS12"); + } + + // Keystores must be initialized before we can add any entries + keyStore.load(null); + + for (int i = 0; i < keyCount; i++) { + final String alias = "test-key-" + i; + + final X509Bundle x509Bundle; + + try { + x509Bundle = new CertificateBuilder() + .notBefore(Instant.now()) + .notAfter(Instant.now().plus(Duration.ofHours(1))) + .subject("CN=" + alias) + .setKeyUsage(true, CertificateBuilder.KeyUsage.digitalSignature, CertificateBuilder.KeyUsage.keyCertSign) + .setIsCertificateAuthority(true) + .buildSelfSigned(); + } catch (final Exception e) { + throw new AssertionError("Failed to build in-memory, self-signed certificate", e); + } + + keyStore.setKeyEntry(alias, x509Bundle.getKeyPair().getPrivate(), KEYSTORE_PASSWORD.toCharArray(), x509Bundle.getCertificatePath()); + } + + final File keystoreFile = File.createTempFile("pushy-test", ".p12"); + keystoreFile.deleteOnExit(); + + try (final OutputStream outputStream = Files.newOutputStream(keystoreFile.toPath())) { + keyStore.store(outputStream, KEYSTORE_PASSWORD.toCharArray()); + } + + return keystoreFile; + } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java index a23614dc3..0856c79c8 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsKeyTest.java @@ -23,21 +23,14 @@ package com.eatthepath.pushy.apns.auth; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; -import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.params.provider.Arguments.arguments; -public abstract class ApnsKeyTest { +abstract class ApnsKeyTest { protected abstract ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException, IOException; @@ -60,21 +53,4 @@ void testGetKey() throws Exception { void testGetParams() throws Exception { assertNotNull(this.getApnsKey().getParams()); } - - @ParameterizedTest - @MethodSource("argumentsForDecodeBase64EncodedString") - void testDecodeBase64EncodedString(final String base64EncodedString, final String decodedAsciiString) { - assertEquals(decodedAsciiString, new String(ApnsKey.decodeBase64EncodedString(base64EncodedString), StandardCharsets.US_ASCII)); - } - - private static Stream argumentsForDecodeBase64EncodedString() { - // Test vectors from https://tools.ietf.org/html/rfc4648#section-10 - return Stream.of( - arguments("Zg==", "f"), - arguments("Zm8=", "fo"), - arguments("Zm9v", "foo"), - arguments("Zm9vYg==", "foob"), - arguments("Zm9vYmE=", "fooba"), - arguments("Zm9vYmFy", "foobar")); - } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java index b20a55aa9..1ee61de3b 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsSigningKeyTest.java @@ -22,14 +22,38 @@ package com.eatthepath.pushy.apns.auth; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; +import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.util.Base64; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -public class ApnsSigningKeyTest extends ApnsKeyTest { +class ApnsSigningKeyTest extends ApnsKeyTest { @Override - protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException, IOException { - return ApnsSigningKey.loadFromInputStream(this.getClass().getResourceAsStream("/token-auth-private-key.p8"), "Team ID", "Key ID"); + protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException { + return new ApnsSigningKey("Key ID", "Team ID", + (ECPrivateKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPrivate()); + } + + @Test + void loadFromInputStream() throws IOException, NoSuchAlgorithmException { + final ECPrivateKey privateKey = + (ECPrivateKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPrivate(); + + final String pkcs8EncodedKey = "-----BEGIN PRIVATE KEY-----\r\n" + + Base64.getMimeEncoder().encodeToString(privateKey.getEncoded()) + + "\r\n-----END PRIVATE KEY-----\r\n"; + + try (final ByteArrayInputStream keyInputStream = new ByteArrayInputStream(pkcs8EncodedKey.getBytes(StandardCharsets.UTF_8))) { + assertDoesNotThrow(() -> ApnsSigningKey.loadFromInputStream(keyInputStream, "Team ID", "Key ID")); + } } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java index 819f32bc3..011a74adb 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/auth/ApnsVerificationKeyTest.java @@ -22,14 +22,38 @@ package com.eatthepath.pushy.apns.auth; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; +import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPublicKey; +import java.util.Base64; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; -public class ApnsVerificationKeyTest extends ApnsKeyTest { +class ApnsVerificationKeyTest extends ApnsKeyTest { @Override - protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException, IOException { - return ApnsVerificationKey.loadFromInputStream(this.getClass().getResourceAsStream("/token-auth-public-key.p8"), "Team ID", "Key ID"); + protected ApnsKey getApnsKey() throws NoSuchAlgorithmException, InvalidKeyException { + return new ApnsVerificationKey("Key ID", "Team ID", + (ECPublicKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPublic()); + } + + @Test + void loadFromInputStream() throws IOException, NoSuchAlgorithmException { + final ECPublicKey publicKey = + (ECPublicKey) KeyPairGenerator.getInstance("EC").generateKeyPair().getPublic(); + + final String pkcs8EncodedKey = "-----BEGIN PUBLIC KEY-----\r\n" + + Base64.getMimeEncoder().encodeToString(publicKey.getEncoded()) + + "\r\n-----END PUBLIC KEY-----\r\n"; + + try (final ByteArrayInputStream keyInputStream = new ByteArrayInputStream(pkcs8EncodedKey.getBytes(StandardCharsets.UTF_8))) { + assertDoesNotThrow(() -> ApnsVerificationKey.loadFromInputStream(keyInputStream, "Team ID", "Key ID")); + } } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java index 99391f1bf..3aa09c77e 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerBuilderTest.java @@ -22,66 +22,74 @@ package com.eatthepath.pushy.apns.server; +import com.eatthepath.ApnsTestCertificates; +import io.netty.pkitesting.X509Bundle; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.InputStream; -import java.security.KeyStore; -import java.security.cert.X509Certificate; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; public class MockApnsServerBuilderTest { - private static final String SERVER_CERTIFICATE_FILENAME = "/server-certs.pem"; - private static final String SERVER_KEY_FILENAME = "/server-key.pem"; - private static final String SERVER_KEYSTORE_FILENAME = "/server.p12"; - private static final String SERVER_KEYSTORE_ALIAS = "1"; - private static final String SERVER_KEYSTORE_PASSWORD = "pushy-test"; + private static X509Bundle CERTIFICATE_BUNDLE; + + @BeforeAll + static void setUpBeforeAll() throws Exception { + CERTIFICATE_BUNDLE = new ApnsTestCertificates().getTrustedServerCertificateBundle(); + } @Test void testSetServerCredentialsFileFileString() throws Exception { - final File certificateFile = new File(this.getClass().getResource(SERVER_CERTIFICATE_FILENAME).toURI()); - final File keyFile = new File(this.getClass().getResource(SERVER_KEY_FILENAME).toURI()); + final File certificateFile = File.createTempFile("pushy-test", ".pem"); + certificateFile.deleteOnExit(); - // We're happy here as long as nothing explodes - new MockApnsServerBuilder() - .setServerCredentials(certificateFile, keyFile, null) - .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) - .build(); + try (final OutputStream certificateOutputStream = Files.newOutputStream(certificateFile.toPath())) { + certificateOutputStream.write(CERTIFICATE_BUNDLE.getCertificatePathPEM().getBytes(StandardCharsets.UTF_8)); + } + + final File keyFile = File.createTempFile("pushy-test", ".p8"); + keyFile.deleteOnExit(); + + try (final OutputStream keyOutputStream = Files.newOutputStream(keyFile.toPath())) { + keyOutputStream.write(CERTIFICATE_BUNDLE.getPrivateKeyPEM().getBytes(StandardCharsets.UTF_8)); + } + + assertDoesNotThrow(() -> new MockApnsServerBuilder() + .setServerCredentials(certificateFile, keyFile, null) + .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) + .build()); } @Test void testSetServerCredentialsInputStreamInputStreamString() throws Exception { - try (final InputStream certificateInputStream = this.getClass().getResourceAsStream(SERVER_CERTIFICATE_FILENAME); - final InputStream keyInputStream = this.getClass().getResourceAsStream(SERVER_KEY_FILENAME)) { - - // We're happy here as long as nothing explodes - new MockApnsServerBuilder() - .setServerCredentials(certificateInputStream, keyInputStream, null) - .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) - .build(); + + try (final InputStream certificateInputStream = + new ByteArrayInputStream(CERTIFICATE_BUNDLE.getCertificatePathPEM().getBytes(StandardCharsets.UTF_8)); + final InputStream keyInputStream = + new ByteArrayInputStream(CERTIFICATE_BUNDLE.getPrivateKeyPEM().getBytes(StandardCharsets.UTF_8))) { + + assertDoesNotThrow(() -> new MockApnsServerBuilder() + .setServerCredentials(certificateInputStream, keyInputStream, null) + .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) + .build()); } } @Test - void testSetServerCredentialsX509CertificateArrayPrivateKeyString() throws Exception { - try (final InputStream p12InputStream = this.getClass().getResourceAsStream(SERVER_KEYSTORE_FILENAME)) { - final KeyStore keyStore = KeyStore.getInstance("PKCS12"); - keyStore.load(p12InputStream, SERVER_KEYSTORE_PASSWORD.toCharArray()); - - final KeyStore.PasswordProtection passwordProtection = - new KeyStore.PasswordProtection(SERVER_KEYSTORE_PASSWORD.toCharArray()); - - final KeyStore.PrivateKeyEntry privateKeyEntry = - (KeyStore.PrivateKeyEntry) keyStore.getEntry(SERVER_KEYSTORE_ALIAS, passwordProtection); - - // We're happy here as long as nothing explodes - new MockApnsServerBuilder() - .setServerCredentials(new X509Certificate[] { (X509Certificate) privateKeyEntry.getCertificate() }, privateKeyEntry.getPrivateKey()) - .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) - .build(); - } + void testSetServerCredentialsX509CertificateArrayPrivateKeyString() { + assertDoesNotThrow(() -> new MockApnsServerBuilder() + .setServerCredentials(CERTIFICATE_BUNDLE.getCertificatePathWithRoot(), + CERTIFICATE_BUNDLE.getKeyPair().getPrivate()) + .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) + .build()); } @Test @@ -92,19 +100,16 @@ void testBuildWithoutServerCredentials() { } @Test - void testBuildWithoutHandlerFactory() throws Exception { - final File certificateFile = new File(this.getClass().getResource(SERVER_CERTIFICATE_FILENAME).toURI()); - final File keyFile = new File(this.getClass().getResource(SERVER_KEY_FILENAME).toURI()); - + void testBuildWithoutHandlerFactory() { assertThrows(IllegalStateException.class, () -> new MockApnsServerBuilder() - .setServerCredentials(certificateFile, keyFile, null) - .build()); + .setServerCredentials(CERTIFICATE_BUNDLE.getCertificatePathWithRoot(), + CERTIFICATE_BUNDLE.getKeyPair().getPrivate()) + .build()); } @Test void testSetMaxConcurrentStreams() { - // We're happy here as long as nothing explodes - new MockApnsServerBuilder().setMaxConcurrentStreams(1); + assertDoesNotThrow(() -> new MockApnsServerBuilder().setMaxConcurrentStreams(1)); } @Test diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java index 30bcf9ad7..99bc34203 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/server/MockApnsServerTest.java @@ -24,7 +24,9 @@ import com.eatthepath.pushy.apns.*; import com.eatthepath.pushy.apns.util.SimpleApnsPushNotification; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.IoEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import org.junit.jupiter.api.Test; import java.time.Instant; @@ -105,22 +107,23 @@ void testShutdownBeforeStart() throws Exception { @Test void testShutdownWithProvidedEventLoopGroup() throws Exception { - final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1); + final IoEventLoopGroup ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); try { final MockApnsServer providedGroupServer = new MockApnsServerBuilder() - .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(SERVER_KEY_FILENAME), null) + .setServerCredentials(TEST_CERTIFICATES.getTrustedServerCertificateBundle().getCertificatePathWithRoot(), + TEST_CERTIFICATES.getTrustedServerCertificateBundle().getKeyPair().getPrivate()) .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) - .setEventLoopGroup(eventLoopGroup) + .setIoEventLoopGroup(ioEventLoopGroup) .build(); assertDoesNotThrow(() -> providedGroupServer.start(PORT).get()); assertDoesNotThrow(() -> providedGroupServer.shutdown().get()); - assertFalse(eventLoopGroup.isShutdown()); + assertFalse(ioEventLoopGroup.isShutdown()); } finally { - eventLoopGroup.shutdownGracefully().await(); + ioEventLoopGroup.shutdownGracefully().await(); } } @@ -136,14 +139,15 @@ void testRestartWithProvidedEventLoopGroup() throws Exception { // TODO Remove this assumption when https://github.com/netty/netty/issues/8697 gets resolved assumeTrue(javaVersion < 11); - final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(1); + final IoEventLoopGroup ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); try { final MockApnsServer providedGroupServer = new MockApnsServerBuilder() - .setServerCredentials(getClass().getResourceAsStream(SERVER_CERTIFICATES_FILENAME), getClass().getResourceAsStream(SERVER_KEY_FILENAME), null) - .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) - .setEventLoopGroup(eventLoopGroup) - .build(); + .setServerCredentials(TEST_CERTIFICATES.getTrustedServerCertificateBundle().getCertificatePathWithRoot(), + TEST_CERTIFICATES.getTrustedServerCertificateBundle().getKeyPair().getPrivate()) + .setHandlerFactory(new AcceptAllPushNotificationHandlerFactory()) + .setIoEventLoopGroup(ioEventLoopGroup) + .build(); assertDoesNotThrow(() -> providedGroupServer.start(PORT).get()); assertDoesNotThrow(() -> providedGroupServer.shutdown().get()); @@ -151,7 +155,7 @@ void testRestartWithProvidedEventLoopGroup() throws Exception { assertDoesNotThrow(() -> providedGroupServer.start(PORT).get()); assertDoesNotThrow(() -> providedGroupServer.shutdown().get()); } finally { - eventLoopGroup.shutdownGracefully().await(); + ioEventLoopGroup.shutdownGracefully().await(); } } diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java index 6fe593f04..03830c9ab 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/server/ServerChannelClassUtilTest.java @@ -22,61 +22,152 @@ package com.eatthepath.pushy.apns.server; +import io.netty.channel.IoEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollIoHandler; import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.kqueue.KQueue; import io.netty.channel.kqueue.KQueueEventLoopGroup; +import io.netty.channel.kqueue.KQueueIoHandler; import io.netty.channel.kqueue.KQueueServerSocketChannel; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.channel.uring.IoUring; +import io.netty.channel.uring.IoUringIoHandler; +import io.netty.channel.uring.IoUringServerSocketChannel; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ServerChannelClassUtilTest { - @Test - void testGetCoreSocketChannelClass() { - final NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup(1); + @Nested + class NioTransport { - try { - assertEquals(NioServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(nioEventLoopGroup)); - } finally { - nioEventLoopGroup.shutdownGracefully(); + private IoEventLoopGroup ioEventLoopGroup; + private NioEventLoopGroup legacyEventLoopGroup; + + @BeforeEach + void setUp() { + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); + legacyEventLoopGroup = new NioEventLoopGroup(); + } + + @Test + void getServerSocketChannelClass() { + assertEquals(NioServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup)); + assertEquals(NioServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(legacyEventLoopGroup)); + } + + @AfterEach + void tearDown() { + ioEventLoopGroup.shutdownGracefully(); + legacyEventLoopGroup.shutdownGracefully(); } } - @Test - void testGetKqueueSocketChannelClass() { - final String unavailabilityMessage = - KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null; + @Nested + class EpollTransport { + + private IoEventLoopGroup ioEventLoopGroup; + private EpollEventLoopGroup legacyEventLoopGroup; + + @BeforeEach + void setUp() { + final String unavailabilityMessage = + Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null; + + assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage); + + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory()); + legacyEventLoopGroup = new EpollEventLoopGroup(); + } - assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage); + @Test + void getServerSocketChannelClass() { + assertEquals(EpollServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup)); + assertEquals(EpollServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(legacyEventLoopGroup)); + } - final KQueueEventLoopGroup kQueueEventLoopGroup = new KQueueEventLoopGroup(1); + @AfterEach + void tearDown() { + if (ioEventLoopGroup != null) { + ioEventLoopGroup.shutdownGracefully(); + } - try { - assertEquals(KQueueServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(kQueueEventLoopGroup)); - } finally { - kQueueEventLoopGroup.shutdownGracefully(); + if (legacyEventLoopGroup != null) { + legacyEventLoopGroup.shutdownGracefully(); + } } } - @Test - void testGetEpollSocketChannelClass() { - final String unavailabilityMessage = - Epoll.unavailabilityCause() != null ? Epoll.unavailabilityCause().getMessage() : null; + @Nested + class IoUringTransport { + + private IoEventLoopGroup ioEventLoopGroup; + + @BeforeEach + void setUp() { + final String unavailabilityMessage = + IoUring.unavailabilityCause() != null ? IoUring.unavailabilityCause().getMessage() : null; - assumeTrue(Epoll.isAvailable(), "Epoll not available: " + unavailabilityMessage); + assumeTrue(IoUring.isAvailable(), "io_uring not available: " + unavailabilityMessage); + + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(IoUringIoHandler.newFactory()); + } + + @Test + void getServerSocketChannelClass() { + assertEquals(IoUringServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup)); + } + + @AfterEach + void tearDown() { + if (ioEventLoopGroup != null) { + ioEventLoopGroup.shutdownGracefully(); + } + } + } + + @Nested + class KQueueTransport { + + private IoEventLoopGroup ioEventLoopGroup; + private KQueueEventLoopGroup legacyEventLoopGroup; + + @BeforeEach + void setUp() { + final String unavailabilityMessage = + KQueue.unavailabilityCause() != null ? KQueue.unavailabilityCause().getMessage() : null; + + assumeTrue(KQueue.isAvailable(), "KQueue not available: " + unavailabilityMessage); + + ioEventLoopGroup = new MultiThreadIoEventLoopGroup(KQueueIoHandler.newFactory()); + legacyEventLoopGroup = new KQueueEventLoopGroup(); + } + + @Test + void getServerSocketChannelClass() { + assertEquals(KQueueServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(ioEventLoopGroup)); + assertEquals(KQueueServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(legacyEventLoopGroup)); + } - final EpollEventLoopGroup epollEventLoopGroup = new EpollEventLoopGroup(1); + @AfterEach + void tearDown() { + if (ioEventLoopGroup != null) { + ioEventLoopGroup.shutdownGracefully(); + } - try { - assertEquals(EpollServerSocketChannel.class, ServerChannelClassUtil.getServerSocketChannelClass(epollEventLoopGroup)); - } finally { - epollEventLoopGroup.shutdownGracefully(); + if (legacyEventLoopGroup != null) { + legacyEventLoopGroup.shutdownGracefully(); + } } } } diff --git a/pushy/src/test/resources/.gitignore b/pushy/src/test/resources/.gitignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/pushy/src/test/resources/ca.pem b/pushy/src/test/resources/ca.pem deleted file mode 100644 index b6aa12e40..000000000 --- a/pushy/src/test/resources/ca.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDGDCCAgCgAwIBAgIJAJ+d4CU73eboMA0GCSqGSIb3DQEBDQUAMBgxFjAUBgNV -BAMMDVB1c2h5VGVzdFJvb3QwIBcNMjEwNTMwMTc0MDUyWhgPMjEyMTA1MDYxNzQw -NTJaMBgxFjAUBgNVBAMMDVB1c2h5VGVzdFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQDbxgGLLg0OMkBTiISlp+CFzldy+a4aV0iXQgZuyN0IiNgZ -oYcW85NUt68GGMA74e6TMNKnTPfLtDIff7/GsrG4A44BYDmfmsHin793Z08gaBmv -DjyhTyCFYSFW/fYH854Cc7aGL1EkB/YtZE+geIyhh8PBe2GjHv5JhGYamDsNplmN -L5+tNO3Eh5RCkF6H1mLvYi6YLaPd0ebm6DMr8P2cI5jw7bGa+EudRHlT5cKfUC24 -yX1hVO78JFD9xktgCBzqLvAYOxQftrt8kydc+X5vrwcg2kL3cuAddCb65EMSnxyg -RmqZZOcslfObbiTG5BxlhCU4dOhjbA3taeRIt7PZAgMBAAGjYzBhMB0GA1UdDgQW -BBSEi2xZ6B06Ce5hbSF1TAVzNyFGPDAfBgNVHSMEGDAWgBSEi2xZ6B06Ce5hbSF1 -TAVzNyFGPDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG -9w0BAQ0FAAOCAQEAL0zcVcy3K51SveofHYh156woBF0+JX7q+nslx8aRzdFCrJ/+ -gcFwPO3ntKA+k+Qw2iEgM5Tk3dUjywI05x8pFkeNIuIzCWjPYkw5xKwdYTMA9Uui -0b7QDiAMz/Nu/YLgUMCSv3sI9E2v86dNx/EgdB1he2AVJk8Dhyf/ZIvxrOhAPyaJ -59L2NA8ZyWYH57jUOjN39pTvKfVQ4U/W/2kt7PcuJS0jDcBxoZol2H2LRGWQbVxT -rBwmmhBkqpmKAgm1+Kv3gyUSFOh+MR5TBzDm9bjzsLka5UxYzQS5/jknmKLZ98Y0 -2uMNWxdJ2Zt05oKoaNUeuUdwqLqrTYXgVWSnAg== ------END CERTIFICATE----- diff --git a/pushy/src/test/resources/multi-topic-client.p12 b/pushy/src/test/resources/multi-topic-client.p12 deleted file mode 100644 index 3f95df783..000000000 Binary files a/pushy/src/test/resources/multi-topic-client.p12 and /dev/null differ diff --git a/pushy/src/test/resources/multi-topic-client.pem b/pushy/src/test/resources/multi-topic-client.pem deleted file mode 100644 index 05301d6f0..000000000 --- a/pushy/src/test/resources/multi-topic-client.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDqTCCApGgAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo -eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1M1oYDzIxMjEwNTA2MTc0MDUzWjBaMTIw -MAYDVQQDDClBcHBsZSBQdXNoIFNlcnZpY2VzOiBjb20uZWF0dGhlcGF0aC5wdXNo -eTEkMCIGCgmSJomT8ixkAQEMFGNvbS5lYXR0aGVwYXRoLnB1c2h5MIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8bWSCyRhDo1YPhBSdGgQadrLvyfzZVWg -jtL6oHVxqre4lM15woYCs34e7IYZ7o7XtOiI97gQgIWTfBvXbKcTg4FphrYrd+q8 -YXPbagakbM2IStuJep59DCcEx3mKw6rIFb+YgimQQ34l+qA2AgaEqQ0Qyywmgtcp -wQShTfU2Sd2ELhROUnOyJyVeBqL+/sNkTtqt+zbU16GEVTi+X26h4UVFnoLHJgTc -WRK8UALC+vu5TQpCCGWuCWBcbwdCdoCxQB3l1yLbfGsCzCnK7NBSMn0zM8WOpJqr -5yz7dn8mR6/hTXSIjzZbKGPR+4265z8eVJVLCFrdvtEy+iUciH3pZQIDAQABo4G5 -MIG2MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMIGDBgoqhkiG92NkBgMGBHUwcwwUY29tLmVhdHRoZXBhdGgucHVzaHkwBQwD -YXBwDBljb20uZWF0dGhlcGF0aC5wdXNoeS52b2lwMAYMBHZvaXAMIWNvbS5lYXR0 -aGVwYXRoLnB1c2h5LmNvbXBsaWNhdGlvbjAODAxjb21wbGljYXRpb24wDQYJKoZI -hvcNAQENBQADggEBAMk6+dc7eYt5xkC75dPwoh2y/ZZN4+i2YE8NfaQWLWjfIbl9 -2p/t7m0T8+J+3HsRSMf8Ywgl1KsqeNCtdWHQg+EJpB9r18LgMT12sGgJqrRdcw2R -0gqTZw/q53erUcvnNVBeQFeLV5f3nz2kFgrktfel/+aWfzYoU9CqzZLFcdCRa+zp -A5pD2zEzrs6lk2X1eq5aVHbJfoiBslUHitw9UNXIfPHCnoNsGuTj85ZFpvxBdNK8 -f/7mIR6qgJhMOOcGcNHZZG7+A1iZoiRYEyeywsC1+7unLqrhmRr0UzwMKOdgbSHd -gn3vFW9onFYKPGq9Hg3Lvyz+8QzNPZTlw6o8aA8= ------END CERTIFICATE----- diff --git a/pushy/src/test/resources/multiple-keys.p12 b/pushy/src/test/resources/multiple-keys.p12 deleted file mode 100644 index 795cb0602..000000000 Binary files a/pushy/src/test/resources/multiple-keys.p12 and /dev/null differ diff --git a/pushy/src/test/resources/no-keys.p12 b/pushy/src/test/resources/no-keys.p12 deleted file mode 100644 index 651fc5315..000000000 Binary files a/pushy/src/test/resources/no-keys.p12 and /dev/null differ diff --git a/pushy/src/test/resources/server-certs-no-alt.pem b/pushy/src/test/resources/server-certs-no-alt.pem deleted file mode 100644 index 10596456f..000000000 --- a/pushy/src/test/resources/server-certs-no-alt.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC8DCCAdigAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo -eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1MloYDzIxMjEwNTA2MTc0MDUyWjAfMR0w -GwYDVQQDDBRjb20uZWF0dGhlcGF0aC5wdXNoeTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAO1UVO09VFQW1ihZaEY3FV/Cm6npyuJo6oCBnVtlSUwCPIYo -46lxSZXi3QrQcHHJ6XtZFZEwZgwzCCjMRthJEdjADwaG9on9TervvuDbc9s6ZDh7 -yHG0WqG9hhTjjnFGY25vrCno3Y2hcZVz6rtJKkvLAZOKUD2gbOJiZ0O1rrJks5ZP -PxyHouh2jGBVxIXAJXlJcpa7xrrOvW+3TIzmuO03wtsIfv5sCn71RsLLJcYHQwZI -O78ob1FcvY+lXhrC9qiT772RraqpfAnaXQ1GLaD/0rbc8l6KTD5ZzrydhMXwb2Gw -c9SnxEQN0ogu3nQGLTd5JY7ALzKri3NsrhpxQS8CAwEAAaM8MDowCQYDVR0TBAIw -ADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB -MA0GCSqGSIb3DQEBDQUAA4IBAQBHt27vREjaYL0lKkfK6I1xhiLCUiZeZdbAu1O4 -L3Wvs0v/y+2xY37spL3v4+sJLTINcjNFuUS+L9Wnh7yCGltWtHvgVFuPGF19yrCp -9288FHNba6NJvtY92wBGHZVyCBGeDET8BX4xv0q46Uu4j1RfArEEAjiaEfhwD3B6 -XkPD8vA+JYPLNSPMf8YYrd2eVUt/k2dzFyDxW/rf9AhKNqy7ThGIQlTevbAqk1sW -JXGewZYwNWx/zgAIsRGkgrYZ99EYZmihxLTj/HtwdqE0urRsioItzYlGUp3FqcNU -ap4t1Bw5PmtdOZzZr02lP0v1C6mYhjxiJb9qTxPQ4C2cR3+2 ------END CERTIFICATE----- diff --git a/pushy/src/test/resources/server-certs.pem b/pushy/src/test/resources/server-certs.pem deleted file mode 100644 index 8b112ed15..000000000 --- a/pushy/src/test/resources/server-certs.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo -eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1MloYDzIxMjEwNTA2MTc0MDUyWjAfMR0w -GwYDVQQDDBRjb20uZWF0dGhlcGF0aC5wdXNoeTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBALhgEuJJfNuPxJXoBOVB+ZE/msSgo5Dg6T80uqTQ0Y38d+Hg -ERpXdO0eSm0M5INx//IVf25wRVH5ueHGBHXp7i2HrnZNgMXD3JMo9rrI0SJ78h91 -oe4+KRX+q2saIJJZ6arsDZUEKQHTLz+HEPODgtD5wzG6rV9P/dbgYF6OEpxDy99X -/ZGwYH7p2T21XmRBYy0CWYIuJVYrHsLxuhagzw+BD3sCmz19dwzc/uPEawJaEvtu -DRXvpN+Hih20bMn005U8xq4U7JuSUPvsDYYf8dI5SA6NqVanh1YH8ipVO6wyO/1W -5QyB4+pV1r2FLHuFnlue3H1mtrKXuKuXTuMjzucCAwEAAaNSMFAwCQYDVR0TBAIw -ADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMB -MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQ0FAAOCAQEAMcgC8aq8 -qpFPFWwN8Yp9P0dfn7G7zUAUy6qsVC63A+qCNpxsdcB87wawuiuxvSQbWS6XyNwm -ZTIYjkyq9P+OtWOk99gQEyadkc25FRDqsYFC8TDfGWh11qi/VCGwvSZtat+j5hMU -JG93tuGkuUiVFmpotZhE2x1hmS0WIvVzeaheCEWUNCUx5M7Ldt2yiBEQXGUL75Vm -+a4DwMYw2e901gowdhuVSahDrX7nb4gz8Ixm4ljfU3fSFh5G4kncvZ7/Ikkw5dzx -x1rnMXw90bqA7TkHmUSBjN2guFclxXg9KyRwq4Ps08moE/LCxPei76PgX82zJQl8 -B4L2nXfdSQzmAA== ------END CERTIFICATE----- diff --git a/pushy/src/test/resources/server-key-no-alt.pem b/pushy/src/test/resources/server-key-no-alt.pem deleted file mode 100644 index fa206f15b..000000000 --- a/pushy/src/test/resources/server-key-no-alt.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDtVFTtPVRUFtYo -WWhGNxVfwpup6criaOqAgZ1bZUlMAjyGKOOpcUmV4t0K0HBxyel7WRWRMGYMMwgo -zEbYSRHYwA8GhvaJ/U3q777g23PbOmQ4e8hxtFqhvYYU445xRmNub6wp6N2NoXGV -c+q7SSpLywGTilA9oGziYmdDta6yZLOWTz8ch6LodoxgVcSFwCV5SXKWu8a6zr1v -t0yM5rjtN8LbCH7+bAp+9UbCyyXGB0MGSDu/KG9RXL2PpV4awvaok++9ka2qqXwJ -2l0NRi2g/9K23PJeikw+Wc68nYTF8G9hsHPUp8REDdKILt50Bi03eSWOwC8yq4tz -bK4acUEvAgMBAAECggEAFyTNL2F2ssiTU9X8NDS08eSxd21kKpYeoC+Dn+ENt8rU -CiU8pk505Zf9BEv1WzNcgHncf48ftHrZhdj946OkiOWZ0YIh0q0QByQgEh90eeGE -2nk/v87ds74esDTMtEEv+xoKyP97c49V20Q1lNP6uu9uFOw9DPVzCNSdy12RTrYR -LePuJhRsuTygiZPyMLst2jMjAs6M5687xm/AF6igKPU+onFX0/hzlnPV0pmVu3qj -f2zz/plZeuRpI+Fnx3DwzH5n29SeIBGWQzC1CRu1LDBTkFz6Qc/16qLrclR8wY+6 -LHu6ZjzA8434y2GnamdcPXjSzkUTixZOXcz3CvimeQKBgQD/uvrFUxe4wAvbD7ya -Tj7dVb9z23rOm/IpGKeIY/jBeGwwv9W40l9EwYEUaORoE4lobrqNiiQwUueASr94 -ny86viDKHdEs/P9pP9YClydxR+ogx8WBHOhx+cYlRvnVtJSZfGq/UqzKliuNfFEd -ALcfE2eVOTD+utgUreFnk/z8JQKBgQDtlGLGNsSaBMrqv1Wt5RgmjnMnjRYe2Hok -hBqZrwyoM5E5mCKKWvwWLVB0lj/5I2VCnZ+6pHrFA0Tj2gvDZqOL83RLYz9xY7Ec -K9X1ht+3Q9W8KSbMl91jHc8P6eKwY0ErKsYhYdlcSRrveM0uM3fOicTihqxRWQdb -2XqUCWkdwwKBgQDWJV2Zn9tdenRzHNpy3NMHxaZs/n34Rd6jS2H/dLf6Sz1OFVaD -Tqc4jFHrJWsfPDz0lsThgayMSuBRLkboW2TRbCVJG27unW3EVRCBWtJMqkwE50Uc -uXhs+RxUWvsbWfyWCvnY/QJ1IwuVj2TdRJwUCcvTyfCdXxlTN8hpVCOlgQKBgQDq -Nyw9XsbpVCo7zQ8JlV3+vNaXukaBeEbJ8xZKRkGDHPthvTLoFRSKRHgZx/ofgh6U -0tIibX6+9R8YReDs7SX0lbkjjR+BiJeVPz36hNHOWWi/zA39CwZtbXixppEd9WvM -w6l6RX3EtimAxiX5EzJcgoOAEuaUd+GCUl++y5w+gwKBgQCDkskEgatuhstW9QtK -9qQiWk1MCC8iBjOBiNRAuhw1DsYsp/vjKXeS2gMk9SZaPYyFqt/t6xbnT96NNpZL -8BXZVO2oZdKbxCl5+EVyQuSd+dHeujFns9qoB5bBtu+S0ckEG9S1/cV2JASkbOmH -F3ZfBaeqR47s3+rCqSu91gtInw== ------END PRIVATE KEY----- diff --git a/pushy/src/test/resources/server-key.pem b/pushy/src/test/resources/server-key.pem deleted file mode 100644 index 740b67ee7..000000000 --- a/pushy/src/test/resources/server-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4YBLiSXzbj8SV -6ATlQfmRP5rEoKOQ4Ok/NLqk0NGN/Hfh4BEaV3TtHkptDOSDcf/yFX9ucEVR+bnh -xgR16e4th652TYDFw9yTKPa6yNEie/IfdaHuPikV/qtrGiCSWemq7A2VBCkB0y8/ -hxDzg4LQ+cMxuq1fT/3W4GBejhKcQ8vfV/2RsGB+6dk9tV5kQWMtAlmCLiVWKx7C -8boWoM8PgQ97Aps9fXcM3P7jxGsCWhL7bg0V76Tfh4odtGzJ9NOVPMauFOybklD7 -7A2GH/HSOUgOjalWp4dWB/IqVTusMjv9VuUMgePqVda9hSx7hZ5bntx9Zrayl7ir -l07jI87nAgMBAAECggEBAI7HH3iLDhx9HfA0Z64dxCT9y11fRKsJ8LZYn/zIFK9O -houtV7E9bre9EEeYh1FfM6QFj9Q3LwdHSvISxRuG10H841aLuB/uB98SBtcocgOx -VhOUpZx4GJsGxzo+VmDfLfuFpxLx0Muv/dPFRZQ+EEzCTa0x8dZwfJMs2JQAk1rc -Npql/AY0EbpN/LgtzLxv+KlH+AUCHvsl7a9Y5M3VzWsvU0Pmn/Hbfk7SnkYLmuyA -2urGVmIiI5fzFlxC2v39oLmijlaQEbQTetuUSOTCFehZfTb//SIKp8/rv7BsUfU5 -RMnRQ0VP03t1n1szA3OLswiE8WPlEymZ29lDkDcGIAkCgYEA9Oev4ERmkv+5Z27t -o6/z6CtIx9rw5L9G+SaQOAbTlJzLgtuaxoA2PcLnjH9WzID7AyPe9W3uG367EdYc -aTcb75y8GYKw9xdgAnMpNNFXMoU0UCQJaf4q78bfTGBPlA0+DGGzLgcMLDWvNZWT -v+Q6TZRQqk9q/LaOYfCSAkeEGp0CgYEAwLpi7eui70lJuJpNaX2s0Oim3536Mwd2 -v5WzDB+yswu1CZ4hRJpa0d7R0FpcStieF7AL0gc/pc6WjjSH0hhyGwWtQKMabtRb -y8MaHpa8WvLaCLnec+02RWVS+IDvpRS3JpF5oWOE6rtRn/8rLB9yi/OMbqfhROob -8dhgRlCVhlMCgYEAoiLxMUyXjCJ5IJ0z/vZyR/bADHBKo2ZvGmwJds4uLWlQ4qV4 -5onjXyg2G7ICSQnrJL0O0vWgeduBBeH2lUHC4POnJEx91FhJW9XaJfmh/PRrGdOB -2AZJbsz+8JWimaXaul/EPGi2Cl7QTG1mj9gNMWdLsDU742sJAJZAU/n835ECgYA1 -pq1LlkExY7GGFk5eg4HMVje+IHg6JGXGR6IkSd6xQR0QpFiWhHGr4t99pOn0XAEG -jxd7TFFHkw8OX0lAD1YUd6wXRlBhcfRr8NAgm82rc9eGfleS5gIpp19Fln8f8Xha -4Dx/1Ph3b9X1OE+IZOi6VP5O/6USTimhVZ7XdC9ryQKBgQCxCRPwzGGN8olgrAIF -0VGsznrvoKE01mSKWrJ5J+FMxnD9YIWuvW9OZoO+tluDfezIInnBjY5iSNGNAhy8 -0rzdgTLT1RKsNUQB6Y9BHq3qkF+FxJg6/gbabKQdx7hrrxKiw/1LoEM8MK2XPuny -NnbPOKIZgik5JR+uduVCM/5cqg== ------END PRIVATE KEY----- diff --git a/pushy/src/test/resources/server.p12 b/pushy/src/test/resources/server.p12 deleted file mode 100644 index aa276f66a..000000000 Binary files a/pushy/src/test/resources/server.p12 and /dev/null differ diff --git a/pushy/src/test/resources/single-topic-client-unprotected.p12 b/pushy/src/test/resources/single-topic-client-unprotected.p12 deleted file mode 100644 index b95de08b1..000000000 Binary files a/pushy/src/test/resources/single-topic-client-unprotected.p12 and /dev/null differ diff --git a/pushy/src/test/resources/single-topic-client.p12 b/pushy/src/test/resources/single-topic-client.p12 deleted file mode 100644 index 61a352c38..000000000 Binary files a/pushy/src/test/resources/single-topic-client.p12 and /dev/null differ diff --git a/pushy/src/test/resources/single-topic-client.pem b/pushy/src/test/resources/single-topic-client.pem deleted file mode 100644 index 1796700f4..000000000 --- a/pushy/src/test/resources/single-topic-client.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDITCCAgmgAwIBAgIBATANBgkqhkiG9w0BAQ0FADAYMRYwFAYDVQQDDA1QdXNo -eVRlc3RSb290MCAXDTIxMDUzMDE3NDA1MloYDzIxMjEwNTA2MTc0MDUyWjBaMTIw -MAYDVQQDDClBcHBsZSBQdXNoIFNlcnZpY2VzOiBjb20uZWF0dGhlcGF0aC5wdXNo -eTEkMCIGCgmSJomT8ixkAQEMFGNvbS5lYXR0aGVwYXRoLnB1c2h5MIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqw4S2wls3RZvUP3ZD3SKwRYWF60a54DN -oqfTl3aDL9lOSL/RMRUQS/4XwJN1nNwklVJteAtZbRbKOSf+DQHR4NsTwOWBYa42 -Nx6RIWVIdzYOWdhtOApoB2B5eilG/3jiOcZ2NkHmNQ2m47c84m4xWliaLJ9EULYJ -n8qQqbMEK+vi+wLclkQuNsg1L7qCAjLf8brIxIX0RKGiDsbG/joWyO0hbzCxRlax -JV0aOzHvLJF2VUZg5MMEOl1dTeb/EHIbUaI4h/zMAr4T/Ea3OxzEf7nrlKSY6yik -oboX4D2Q780KYe2KqvAZ87QXXgJc4UyaCplAmbNmq/LFYxZFJHw9hwIDAQABozIw -MDAJBgNVHRMEAjAAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcD -AjANBgkqhkiG9w0BAQ0FAAOCAQEAEwESey8ATqtJAYqCgGkBivL10RIG/8+BC36E -cpRsxvzQaU2h8yGOyD6Y2TACHYHASDdGnMyNdeckue0vf8Ou3royYAe1nvDiG1ho -dc8hSK2AgJwS7+BbxQfQgrwCDUOkPkKXtRfyww01fc3pYg6FO3cmNFagGjnw/Unw -OjKUDyvZEadVzOEDhWzfMXrKOe8doxC4CyGLFsdUPbMVtO/KdsY1gzXal9IvS9P6 -eIjzGWLpeBz1WxM4PVZviwlC70yYZ/sbKw20tbiTNuloDjL5EePhgrkBbppIZ/Pg -Fc1vlv26E+59Muqa1oCyWJxoCw3rB50ZNamPC20SjI1wv6NQAA== ------END CERTIFICATE----- diff --git a/pushy/src/test/resources/token-auth-private-key.p8 b/pushy/src/test/resources/token-auth-private-key.p8 deleted file mode 100644 index 418d96906..000000000 --- a/pushy/src/test/resources/token-auth-private-key.p8 +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgupTFGBOZFMLlNJYr -obN5w/Gpp4Un37JmkLIQ1eSVRM6hRANCAARWvXN7hpi86fvQUY7rE8IJ/C6bUSPs -FwT2Z60gp37DKGeuTwMb61g4KflNiEbYDHFbBOAk9cTK3+yb/RoeSBz6 ------END PRIVATE KEY----- diff --git a/pushy/src/test/resources/token-auth-public-key.p8 b/pushy/src/test/resources/token-auth-public-key.p8 deleted file mode 100644 index a4f3a2dc8..000000000 --- a/pushy/src/test/resources/token-auth-public-key.p8 +++ /dev/null @@ -1,4 +0,0 @@ ------BEGIN PUBLIC KEY----- -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVr1ze4aYvOn70FGO6xPCCfwum1Ej -7BcE9metIKd+wyhnrk8DG+tYOCn5TYhG2AxxWwTgJPXEyt/sm/0aHkgc+g== ------END PUBLIC KEY-----