From 61ffe5e9b4f1935416030d1d07b124a0919252ce Mon Sep 17 00:00:00 2001 From: Jens Deppe Date: Fri, 9 Aug 2019 16:57:44 -0700 Subject: [PATCH 1/5] GEODE-7071: Add CA to CertStores so that all certificates can be signed - Future work should convert all other tests, which utilize key/trust stores to use the CertStores class. --- ...erHostNameVerificationDistributedTest.java | 2 - .../CustomSSLProviderDistributedTest.java | 2 - ...shHostNameVerificationDistributedTest.java | 3 +- .../TCPClientSSLIntegrationTest.java | 1 - ...etHostNameVerificationIntegrationTest.java | 1 - .../apache/geode/cache/ssl/CertStores.java | 39 ++++++-- .../apache/geode/cache/ssl/TestSSLUtils.java | 91 ++++++++++++++----- ...ANHostNameVerificationDistributedTest.java | 6 -- 8 files changed, 100 insertions(+), 45 deletions(-) diff --git a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java index 5565360709a8..5a9bbce3ce6d 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java @@ -159,13 +159,11 @@ private void validateClientConnection(CertificateBuilder locatorCertificate, clientStore.withCertificate(clientCertificate); Properties locatorSSLProps = locatorStore - .trustSelf() .trust(clientStore.alias(), clientStore.certificate()) .trust(serverStore.alias(), serverStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerficiationForLocator); Properties serverSSLProps = serverStore - .trustSelf() .trust(locatorStore.alias(), locatorStore.certificate()) .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerificationForServer); diff --git a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java index 9fddeec6885c..9b29267cff39 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java @@ -194,13 +194,11 @@ private void validateClientSSLConnection(CertificateBuilder locatorCertificate, clientStore.withCertificate(clientCertificate); Properties locatorSSLProps = locatorStore - .trustSelf() .trust(serverStore.alias(), serverStore.certificate()) .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(ALL, false, enableHostNameVerficationForLocator); Properties serverSSLProps = serverStore - .trustSelf() .trust(locatorStore.alias(), locatorStore.certificate()) .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerificationForServer); diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java index c79424783bf3..bd91527f3289 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java @@ -70,7 +70,6 @@ public void setupCluster() throws Exception { gfshStore.withCertificate(gfshCertificate); locatorStore - .trustSelf() .trust(gfshStore.alias(), gfshStore.certificate()); gfshStore @@ -128,7 +127,7 @@ private void validateGfshConnection(CertificateBuilder locatorCertificate) lstore.withCertificate(locatorCertificate); gstore.withCertificate(gfshCertificate); - lstore.trustSelf().trust(gstore.alias(), gstore.certificate()); + lstore.trust(gstore.alias(), gstore.certificate()); gstore.trust(lstore.alias(), lstore.certificate()); diff --git a/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java index 858a46954c65..ee24e590b614 100644 --- a/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java @@ -75,7 +75,6 @@ private void startServerAndClient(CertificateBuilder serverCertificate, clientStore.withCertificate(clientCertificate); Properties serverProperties = serverStore - .trustSelf() .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(LOCATOR, true, enableHostNameValidation); diff --git a/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java index b14f78fb4e50..f1cc22f2a92c 100755 --- a/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java @@ -113,7 +113,6 @@ public void setUp() throws Exception { CertStores certStores = CertStores.locatorStore(); certStores.withCertificate(certBuilder); - certStores.trustSelf(); this.distributionConfig = new DistributionConfigImpl( diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java index 7fca0a4ef9b1..6778898c93b1 100644 --- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java @@ -33,11 +33,17 @@ import java.io.IOException; import java.security.GeneralSecurityException; import java.security.KeyPair; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.Properties; +/** + * The {@code CertStores} class encapsulates the key and trust stores typically used by various + * components in a Geode cluster. It currently supports certificate collections for servers, + * locators and clients. All certificates are signed by a single Root Certificate Authority. + */ public class CertStores { private final String alias; private final String storePrefix; @@ -51,6 +57,10 @@ public class CertStores { private X509Certificate cert; + private final X509Certificate caCertificate; + + private final KeyPair rootCAKeyPair; + public static CertStores locatorStore() { return new CertStores("locator", "locator"); } @@ -66,6 +76,20 @@ public static CertStores clientStore() { public CertStores(String alias, String storePrefix) { this.alias = alias; this.storePrefix = storePrefix; + try { + this.rootCAKeyPair = TestSSLUtils.generateKeyPair("RSA"); + caCertificate = createCACertificate(rootCAKeyPair); + trustedCerts.put("ca", caCertificate); + } catch (Exception ex) { + throw new RuntimeException("Unable to create default root CA", ex); + } + } + + private X509Certificate createCACertificate(KeyPair caKeyPair) throws CertificateException { + return new TestSSLUtils.CertificateBuilder() + .isCA() + .commonName("Test CA") + .generate(caKeyPair.getPublic(), caKeyPair.getPrivate()); } public String alias() { @@ -78,21 +102,18 @@ public X509Certificate certificate() { public CertStores withCertificate(TestSSLUtils.CertificateBuilder certificateBuilder) throws GeneralSecurityException, IOException { - keyStoreFile = File.createTempFile(storePrefix + "KS", ".jks"); + keyStoreFile = File.createTempFile(storePrefix + "-KS-", ".jks"); withCertificate(certificateBuilder, keyStoreFile); return this; } private void withCertificate(TestSSLUtils.CertificateBuilder certificateBuilder, File keyStoreFile) throws GeneralSecurityException, IOException { + certificateBuilder.issuedBy(caCertificate); KeyPair keyPair = generateKeyPair("RSA"); - cert = certificateBuilder.generate(keyPair); - createKeyStore(keyStoreFile.getPath(), keyStorePassword, alias, keyPair.getPrivate(), cert); - } - - public CertStores trustSelf() { - this.trustedCerts.put(alias, cert); - return this; + cert = certificateBuilder.generate(keyPair.getPublic(), rootCAKeyPair.getPrivate()); + createKeyStore(keyStoreFile.getPath(), keyStorePassword, alias, keyPair.getPrivate(), cert, + caCertificate); } public CertStores trust(String alias, X509Certificate certificate) { @@ -114,7 +135,7 @@ public Properties propertiesWith(String components, boolean requireAuth, public Properties propertiesWith(String components, String protocols, String ciphers, boolean requireAuth, boolean endPointIdentification) throws GeneralSecurityException, IOException { - File trustStoreFile = File.createTempFile(storePrefix + "TS", ".jks"); + File trustStoreFile = File.createTempFile(storePrefix + "-TS-", ".jks"); trustStoreFile.deleteOnExit(); createTrustStore(trustStoreFile.getPath(), trustStorePassword, trustedCerts); diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java index 42a945b3c490..e01b53d8d405 100644 --- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java @@ -31,8 +31,9 @@ import java.security.KeyPairGenerator; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; import java.security.SecureRandom; -import java.security.Security; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -44,15 +45,20 @@ import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.util.PrivateKeyFactory; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; @@ -63,7 +69,7 @@ public class TestSSLUtils { public static KeyPair generateKeyPair(String algorithm) throws NoSuchAlgorithmException { KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm); - keyGen.initialize(1024); + keyGen.initialize(2048); return keyGen.genKeyPair(); } @@ -75,16 +81,24 @@ private static KeyStore createEmptyKeyStore() throws GeneralSecurityException, I public static void createKeyStore(String filename, String password, String alias, - Key privateKey, Certificate cert) throws GeneralSecurityException, IOException { + Key privateKey, Certificate cert, Certificate issuer) + throws GeneralSecurityException, IOException { KeyStore ks = createEmptyKeyStore(); - ks.setKeyEntry(alias, privateKey, password.toCharArray(), new Certificate[] {cert}); + + List chain = new ArrayList<>(); + chain.add(cert); + if (issuer != null) { + chain.add(issuer); + } + + ks.setKeyEntry(alias, privateKey, password.toCharArray(), chain.toArray(new Certificate[] {})); try (OutputStream out = Files.newOutputStream(Paths.get(filename))) { ks.store(out, password.toCharArray()); } } - public static void createTrustStore( - String filename, String password, Map certs) + public static void createTrustStore(String filename, String password, + Map certs) throws GeneralSecurityException, IOException { KeyStore ks = KeyStore.getInstance("JKS"); try (InputStream in = Files.newInputStream(Paths.get(filename))) { @@ -92,7 +106,7 @@ public static void createTrustStore( } catch (EOFException e) { ks = createEmptyKeyStore(); } - for (Map.Entry cert : certs.entrySet()) { + for (Map.Entry cert : certs.entrySet()) { ks.setCertificateEntry(cert.getKey(), cert.getValue()); } try (OutputStream out = Files.newOutputStream(Paths.get(filename))) { @@ -103,12 +117,14 @@ public static void createTrustStore( public static class CertificateBuilder { private final int days; private final String algorithm; - private String name; + private X500Name name; private List dnsNames; private List ipAddresses; + private boolean isCA; + private X509Certificate issuer; public CertificateBuilder() { - this(30, "SHA1withRSA"); + this(30, "SHA256withRSA"); } public CertificateBuilder(int days, String algorithm) { @@ -128,7 +144,7 @@ private static GeneralName ipGeneralName(InetAddress hostAddress) { } public CertificateBuilder commonName(String cn) { - this.name = "CN=" + cn + ", O=Geode"; + this.name = new X500Name("CN=" + cn + ", O=Geode"); return this; } @@ -142,6 +158,16 @@ public CertificateBuilder sanIpAddress(InetAddress hostAddress) { return this; } + public CertificateBuilder isCA() { + this.isCA = true; + return this; + } + + public CertificateBuilder issuedBy(X509Certificate issuer) { + this.issuer = issuer; + return this; + } + private byte[] san() throws IOException { List names = dnsNames.stream() .map(CertificateBuilder::dnsGeneralName) @@ -155,35 +181,56 @@ private byte[] san() throws IOException { : new GeneralNames(names.toArray(new GeneralName[] {})).getEncoded(); } - public X509Certificate generate(KeyPair keyPair) throws CertificateException { - return this.generate(this.name, keyPair); - } - - public X509Certificate generate(String dn, KeyPair keyPair) throws CertificateException { + public X509Certificate generate(PublicKey publicKey, PrivateKey privateKey) + throws CertificateException { try { - Security.addProvider(new BouncyCastleProvider()); AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm); AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); + AsymmetricKeyParameter publicKeyAsymKeyParam = + PublicKeyFactory.createKey(publicKey.getEncoded()); AsymmetricKeyParameter privateKeyAsymKeyParam = - PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded()); + PrivateKeyFactory.createKey(privateKey.getEncoded()); SubjectPublicKeyInfo subPubKeyInfo = - SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); + SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKeyAsymKeyParam); - X500Name name = new X500Name(dn); + Date from = new Date(); Date to = new Date(from.getTime() + days * 86400000L); BigInteger sn = new BigInteger(64, new SecureRandom()); - X509v3CertificateBuilder v3CertGen = - new X509v3CertificateBuilder(name, sn, from, to, name, subPubKeyInfo); + + X509v3CertificateBuilder v3CertGen; + if (issuer == null) { + // This is a self-signed certificate + v3CertGen = new X509v3CertificateBuilder(name, sn, from, to, name, subPubKeyInfo); + } else { + v3CertGen = new X509v3CertificateBuilder(new X500Name(issuer.getIssuerDN().getName()), + sn, from, to, name, subPubKeyInfo); + } byte[] subjectAltName = san(); if (subjectAltName != null) { v3CertGen.addExtension(Extension.subjectAlternativeName, false, subjectAltName); } + + if (isCA) { + v3CertGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign)); + v3CertGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); + } + + // Not strictly necessary, but this makes the certs look like those generated + // by `keytool`. + SubjectKeyIdentifier subjectKeyIdentifier = + new SubjectKeyIdentifier( + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKeyAsymKeyParam) + .getEncoded()); + v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, subjectKeyIdentifier); + X509CertificateHolder certificateHolder = v3CertGen.build(sigGen); - return new JcaX509CertificateConverter().setProvider("BC") + return new JcaX509CertificateConverter() + .setProvider(new BouncyCastleProvider()) .getCertificate(certificateHolder); } catch (CertificateException ce) { throw ce; diff --git a/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java b/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java index 177570a510ca..8d896823c165 100644 --- a/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java +++ b/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java @@ -91,14 +91,12 @@ private int setupWanSite1(CertStores locator_ln_store, CertStores server_ln_stor throws GeneralSecurityException, IOException { Properties locatorSSLProps = locator_ln_store - .trustSelf() .trust(server_ln_store.alias(), server_ln_store.certificate()) .trust(locator_ny_store.alias(), locator_ny_store.certificate()) .trust(server_ny_store.alias(), server_ny_store.certificate()) .propertiesWith(ALL, true, true); Properties serverSSLProps = server_ln_store - .trustSelf() .trust(locator_ln_store.alias(), locator_ln_store.certificate()) .trust(locator_ny_store.alias(), locator_ny_store.certificate()) .trust(server_ny_store.alias(), server_ny_store.certificate()) @@ -125,7 +123,6 @@ private void setupWanSite2(int site1Port, CertStores locator_ny_store, throws GeneralSecurityException, IOException { Properties locator_ny_props = locator_ny_store - .trustSelf() .trust(server_ln_store.alias(), server_ln_store.certificate()) .trust(server_ny_store.alias(), server_ny_store.certificate()) .trust(locator_ln_store.alias(), locator_ln_store.certificate()) @@ -136,7 +133,6 @@ private void setupWanSite2(int site1Port, CertStores locator_ny_store, locator_ny_props.setProperty(REMOTE_LOCATORS, "localhost[" + site1Port + "]"); Properties server_ny_props = server_ny_store - .trustSelf() .trust(locator_ln_store.alias(), locator_ln_store.certificate()) .trust(locator_ny_store.alias(), locator_ny_store.certificate()) .trust(server_ln_store.alias(), server_ln_store.certificate()) @@ -252,12 +248,10 @@ public void gwsenderFailsToConnectIfGWReceiverHasNoHostName() throws Exception { gwReceiver.withCertificate(gwReceiverCertificate); Properties ln_SSLProps = gwSender - .trustSelf() .trust(gwReceiver.alias(), gwReceiver.certificate()) .propertiesWith(GATEWAY, true, true); Properties ny_SSLProps = gwReceiver - .trustSelf() .trust(gwSender.alias(), gwSender.certificate()) .propertiesWith(GATEWAY, true, true); From 5118d1e095eabf9505df6f478ddc4e0dd46a7600 Mon Sep 17 00:00:00 2001 From: Jens Deppe Date: Fri, 9 Aug 2019 20:35:38 -0700 Subject: [PATCH 2/5] Trigger CI From bb04a93b3be6aaec2778cddd4b4c05c9b666d8d3 Mon Sep 17 00:00:00 2001 From: Jens Deppe Date: Mon, 12 Aug 2019 22:35:15 -0700 Subject: [PATCH 3/5] GEODE-7071: Provide clearer abstractions for CertificateBuilder and CertStores - CAs need to be explicitly created and added as trusted. - Certificates need to be explicitly signed. - Introduce CertificateMaterial which includes the generated X509Certificate, the certificate's KeyPair and the issuer if relevant. --- ...erHostNameVerificationDistributedTest.java | 105 +++++--- .../CustomSSLProviderDistributedTest.java | 122 ++++++--- ...shHostNameVerificationDistributedTest.java | 66 +++-- .../TCPClientSSLIntegrationTest.java | 71 +++-- ...etHostNameVerificationIntegrationTest.java | 21 +- .../apache/geode/cache/ssl/CertStores.java | 128 ++++----- .../geode/cache/ssl/CertificateBuilder.java | 199 ++++++++++++++ .../geode/cache/ssl/CertificateMaterial.java | 44 ++++ .../apache/geode/cache/ssl/TestSSLUtils.java | 242 ------------------ ...ANHostNameVerificationDistributedTest.java | 110 ++++---- 10 files changed, 626 insertions(+), 482 deletions(-) create mode 100644 geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java create mode 100644 geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java delete mode 100644 geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java diff --git a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java index 5a9bbce3ce6d..a1bb2c37036c 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/ClientServerHostNameVerificationDistributedTest.java @@ -23,6 +23,7 @@ import java.security.GeneralSecurityException; import java.util.Properties; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -36,7 +37,8 @@ import org.apache.geode.cache.client.ClientRegionShortcut; import org.apache.geode.cache.client.NoAvailableServersException; import org.apache.geode.cache.ssl.CertStores; -import org.apache.geode.cache.ssl.TestSSLUtils.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateMaterial; import org.apache.geode.distributed.internal.tcpserver.LocatorCancelException; import org.apache.geode.internal.net.SocketCreatorFactory; import org.apache.geode.test.dunit.IgnoredException; @@ -49,6 +51,16 @@ public class ClientServerHostNameVerificationDistributedTest { @Rule public ClusterStartupRule cluster = new ClusterStartupRule(); + private CertificateMaterial ca; + + @Before + public void setup() { + ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); + } + private static void createServerRegion() { RegionFactory factory = ClusterStartupRule.getCache().createRegionFactory(RegionShortcut.REPLICATE); @@ -64,23 +76,29 @@ private static void doServerRegionTest() { @Test public void connectionSuccessfulWhenHostNameOfLocatorAndServer() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() + CertificateMaterial locatorCertificate = new CertificateBuilder() .commonName("locator") + .issuedBy(ca) // ClusterStartupRule uses 'localhost' as locator host .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) .sanIpAddress(InetAddress.getLocalHost()) - .sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows + .sanIpAddress(InetAddress.getByName("0.0.0.0")) // to pass on windows + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() + CertificateMaterial serverCertificate = new CertificateBuilder() .commonName("server") + .issuedBy(ca) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) - .sanIpAddress(InetAddress.getLocalHost()); + .sanIpAddress(InetAddress.getLocalHost()) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientConnection(locatorCertificate, serverCertificate, clientCertificate, true, true, true, @@ -90,14 +108,20 @@ public void connectionSuccessfulWhenHostNameOfLocatorAndServer() throws Exceptio @Test public void expectConnectionFailureWhenNoHostNameInLocatorKey() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() - .commonName("locator"); + CertificateMaterial locatorCertificate = new CertificateBuilder() + .commonName("locator") + .issuedBy(ca) + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() - .commonName("server"); + CertificateMaterial serverCertificate = new CertificateBuilder() + .commonName("server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientConnection(locatorCertificate, serverCertificate, clientCertificate, false, false, true, @@ -107,16 +131,22 @@ public void expectConnectionFailureWhenNoHostNameInLocatorKey() throws Exception @Test public void expectConnectionFailureWhenWrongHostNameInLocatorKey() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() + CertificateMaterial locatorCertificate = new CertificateBuilder() .commonName("locator") - .sanDnsName("example.com");; + .sanDnsName("example.com") + .issuedBy(ca) + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() + CertificateMaterial serverCertificate = new CertificateBuilder() .commonName("server") - .sanDnsName("example.com");; + .sanDnsName("example.com") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientConnection(locatorCertificate, serverCertificate, clientCertificate, false, false, true, @@ -125,52 +155,55 @@ public void expectConnectionFailureWhenWrongHostNameInLocatorKey() throws Except @Test public void expectConnectionFailureWhenNoHostNameInServerKey() throws Exception { - CertificateBuilder locatorCertificateWithSan = new CertificateBuilder() + CertificateMaterial locatorCertificateWithSan = new CertificateBuilder() .commonName("locator") + .issuedBy(ca) .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) - .sanIpAddress(InetAddress.getLocalHost()); + .sanIpAddress(InetAddress.getLocalHost()) + .generate(); - CertificateBuilder serverCertificateWithNoSan = new CertificateBuilder() - .commonName("server"); + CertificateMaterial serverCertificateWithNoSan = new CertificateBuilder() + .commonName("server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientConnection(locatorCertificateWithSan, serverCertificateWithNoSan, clientCertificate, false, false, true, NoAvailableServersException.class); } - private void validateClientConnection(CertificateBuilder locatorCertificate, - CertificateBuilder serverCertificate, CertificateBuilder clientCertificate, + private void validateClientConnection(CertificateMaterial locatorCertificate, + CertificateMaterial serverCertificate, CertificateMaterial clientCertificate, boolean enableHostNameVerficiationForLocator, boolean enableHostNameVerificationForServer, boolean enableHostNameVerificationForClient, Class expectedExceptionOnClient) throws GeneralSecurityException, IOException { CertStores locatorStore = CertStores.locatorStore(); - locatorStore.withCertificate(locatorCertificate); + locatorStore.withCertificate("locator", locatorCertificate); + locatorStore.trust("ca", ca); CertStores serverStore = CertStores.serverStore(); - serverStore.withCertificate(serverCertificate); + serverStore.withCertificate("server", serverCertificate); + serverStore.trust("ca", ca); CertStores clientStore = CertStores.clientStore(); - clientStore.withCertificate(clientCertificate); + clientStore.withCertificate("client", clientCertificate); + clientStore.trust("ca", ca); Properties locatorSSLProps = locatorStore - .trust(clientStore.alias(), clientStore.certificate()) - .trust(serverStore.alias(), serverStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerficiationForLocator); Properties serverSSLProps = serverStore - .trust(locatorStore.alias(), locatorStore.certificate()) - .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerificationForServer); Properties clientSSLProps = clientStore - .trust(locatorStore.alias(), locatorStore.certificate()) - .trust(serverStore.alias(), serverStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerificationForClient); // create a cluster diff --git a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java index 9b29267cff39..db9c224c4165 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/cache/client/internal/CustomSSLProviderDistributedTest.java @@ -33,6 +33,7 @@ import javax.net.ssl.SSLContext; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -48,7 +49,8 @@ import org.apache.geode.cache.client.internal.provider.CustomKeyManagerFactory; import org.apache.geode.cache.client.internal.provider.CustomTrustManagerFactory; import org.apache.geode.cache.ssl.CertStores; -import org.apache.geode.cache.ssl.TestSSLUtils.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateMaterial; import org.apache.geode.distributed.internal.tcpserver.LocatorCancelException; import org.apache.geode.internal.net.SocketCreatorFactory; import org.apache.geode.test.dunit.IgnoredException; @@ -60,10 +62,19 @@ public class CustomSSLProviderDistributedTest { private static MemberVM locator; private static MemberVM server; + private CertificateMaterial ca; @Rule public ClusterStartupRule cluster = new ClusterStartupRule(); + @Before + public void setup() { + ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); + } + private CustomKeyManagerFactory.PKIXFactory keyManagerFactory; private CustomTrustManagerFactory.PKIXFactory trustManagerFactory; @@ -86,23 +97,29 @@ private static void createServerRegion() { @Test public void hostNameIsValidatedWhenUsingDefaultContext() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() + CertificateMaterial locatorCertificate = new CertificateBuilder() .commonName("locator") + .issuedBy(ca) // ClusterStartupRule uses 'localhost' as locator host .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) .sanIpAddress(InetAddress.getLocalHost()) - .sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows + .sanIpAddress(InetAddress.getByName("0.0.0.0")) // to pass on windows + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() + CertificateMaterial serverCertificate = new CertificateBuilder() .commonName("server") + .issuedBy(ca) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) - .sanIpAddress(InetAddress.getLocalHost()); + .sanIpAddress(InetAddress.getLocalHost()) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientSSLConnection(locatorCertificate, serverCertificate, clientCertificate, true, true, false, null); @@ -110,14 +127,20 @@ public void hostNameIsValidatedWhenUsingDefaultContext() throws Exception { @Test public void clientCanChooseNotToValidateHostName() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() - .commonName("locator"); + CertificateMaterial locatorCertificate = new CertificateBuilder() + .commonName("locator") + .issuedBy(ca) + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() - .commonName("server"); + CertificateMaterial serverCertificate = new CertificateBuilder() + .commonName("server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientSSLConnection(locatorCertificate, serverCertificate, clientCertificate, false, false, true, null); @@ -125,14 +148,20 @@ public void clientCanChooseNotToValidateHostName() throws Exception { @Test public void clientConnectionFailsIfNoHostNameInLocatorKey() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() - .commonName("locator"); + CertificateMaterial locatorCertificate = new CertificateBuilder() + .commonName("locator") + .issuedBy(ca) + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() - .commonName("server"); + CertificateMaterial serverCertificate = new CertificateBuilder() + .commonName("server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientSSLConnection(locatorCertificate, serverCertificate, clientCertificate, false, false, false, LocatorCancelException.class); @@ -140,16 +169,22 @@ public void clientConnectionFailsIfNoHostNameInLocatorKey() throws Exception { @Test public void clientConnectionFailsWhenWrongHostNameInLocatorKey() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() + CertificateMaterial locatorCertificate = new CertificateBuilder() .commonName("locator") - .sanDnsName("example.com");; + .sanDnsName("example.com") + .issuedBy(ca) + .generate(); - CertificateBuilder serverCertificate = new CertificateBuilder() + CertificateMaterial serverCertificate = new CertificateBuilder() .commonName("server") - .sanDnsName("example.com");; + .sanDnsName("example.com") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientSSLConnection(locatorCertificate, serverCertificate, clientCertificate, false, false, @@ -159,54 +194,57 @@ public void clientConnectionFailsWhenWrongHostNameInLocatorKey() throws Exceptio @Test public void expectConnectionFailureWhenNoHostNameInServerKey() throws Exception { - CertificateBuilder locatorCertificateWithSan = new CertificateBuilder() + CertificateMaterial locatorCertificateWithSan = new CertificateBuilder() .commonName("locator") + .issuedBy(ca) .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) - .sanIpAddress(InetAddress.getLocalHost()); + .sanIpAddress(InetAddress.getLocalHost()) + .generate(); - CertificateBuilder serverCertificateWithNoSan = new CertificateBuilder() - .commonName("server"); + CertificateMaterial serverCertificateWithNoSan = new CertificateBuilder() + .commonName("server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("client") + .issuedBy(ca) + .generate(); validateClientSSLConnection(locatorCertificateWithSan, serverCertificateWithNoSan, clientCertificate, false, false, false, NoAvailableServersException.class); } - private void validateClientSSLConnection(CertificateBuilder locatorCertificate, - CertificateBuilder serverCertificate, CertificateBuilder clientCertificate, + private void validateClientSSLConnection(CertificateMaterial locatorCertificate, + CertificateMaterial serverCertificate, CertificateMaterial clientCertificate, boolean enableHostNameVerficationForLocator, boolean enableHostNameVerificationForServer, boolean disableHostNameVerificationForClient, Class expectedExceptionOnClient) throws GeneralSecurityException, IOException { CertStores locatorStore = CertStores.locatorStore(); - locatorStore.withCertificate(locatorCertificate); + locatorStore.withCertificate("locator", locatorCertificate); + locatorStore.trust("ca", ca); CertStores serverStore = CertStores.serverStore(); - serverStore.withCertificate(serverCertificate); + serverStore.withCertificate("server", serverCertificate); + serverStore.trust("ca", ca); CertStores clientStore = CertStores.clientStore(); - clientStore.withCertificate(clientCertificate); + clientStore.withCertificate("client", clientCertificate); + clientStore.trust("ca", ca); Properties locatorSSLProps = locatorStore - .trust(serverStore.alias(), serverStore.certificate()) - .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(ALL, false, enableHostNameVerficationForLocator); Properties serverSSLProps = serverStore - .trust(locatorStore.alias(), locatorStore.certificate()) - .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(ALL, true, enableHostNameVerificationForServer); // this props is only to create temp keystore and truststore and get paths Properties clientSSLProps = clientStore - .trust(locatorStore.alias(), locatorStore.certificate()) - .trust(serverStore.alias(), serverStore.certificate()) .propertiesWith(ALL, true, true); setupCluster(locatorSSLProps, serverSSLProps); diff --git a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java index bd91527f3289..f23274884ad3 100644 --- a/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java +++ b/geode-core/src/distributedTest/java/org/apache/geode/management/internal/cli/commands/GfshHostNameVerificationDistributedTest.java @@ -30,7 +30,8 @@ import org.junit.experimental.categories.Category; import org.apache.geode.cache.ssl.CertStores; -import org.apache.geode.cache.ssl.TestSSLUtils.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateMaterial; import org.apache.geode.test.dunit.IgnoredException; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.rules.ClusterStartupRule; @@ -41,6 +42,7 @@ @Category({GfshTest.class}) public class GfshHostNameVerificationDistributedTest { private static MemberVM locator; + private CertificateMaterial ca; @Rule public ClusterStartupRule cluster = new ClusterStartupRule(); @@ -53,27 +55,32 @@ public class GfshHostNameVerificationDistributedTest { @Before public void setupCluster() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() + ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); + + CertificateMaterial locatorCertificate = new CertificateBuilder() .commonName("locator") + .issuedBy(ca) .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanIpAddress(InetAddress.getLocalHost()) - .sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows + .sanIpAddress(InetAddress.getByName("0.0.0.0")) // to pass on windows + .generate(); - CertificateBuilder gfshCertificate = new CertificateBuilder() - .commonName("gfsh"); + CertificateMaterial gfshCertificate = new CertificateBuilder() + .commonName("gfsh") + .issuedBy(ca) + .generate(); locatorStore = CertStores.locatorStore(); - gfshStore = CertStores.clientStore(); - - locatorStore.withCertificate(locatorCertificate); - gfshStore.withCertificate(gfshCertificate); + locatorStore.withCertificate("locator", locatorCertificate); + locatorStore.trust("ca", ca); - locatorStore - .trust(gfshStore.alias(), gfshStore.certificate()); - - gfshStore - .trust(locatorStore.alias(), locatorStore.certificate()); + gfshStore = CertStores.clientStore(); + gfshStore.withCertificate("gfsh", gfshCertificate); + gfshStore.trust("ca", ca); } private File gfshSecurityProperties(Properties clientSSLProps) throws IOException { @@ -102,34 +109,39 @@ public void gfshConnectsToLocator() throws Exception { @Test public void expectConnectionFailureWhenNoHostNameInLocatorKey() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() - .commonName("locator"); + CertificateMaterial locatorCertificate = new CertificateBuilder() + .commonName("locator") + .issuedBy(ca) + .generate(); validateGfshConnection(locatorCertificate); } @Test public void expectConnectionFailureWhenWrongHostNameInLocatorKey() throws Exception { - CertificateBuilder locatorCertificate = new CertificateBuilder() + CertificateMaterial locatorCertificate = new CertificateBuilder() .commonName("locator") - .sanDnsName("example.com"); + .issuedBy(ca) + .sanDnsName("example.com") + .generate(); validateGfshConnection(locatorCertificate); } - private void validateGfshConnection(CertificateBuilder locatorCertificate) + private void validateGfshConnection(CertificateMaterial locatorCertificate) throws Exception { - CertificateBuilder gfshCertificate = new CertificateBuilder().commonName("gfsh"); + CertificateMaterial gfshCertificate = new CertificateBuilder() + .commonName("gfsh") + .issuedBy(ca) + .generate(); CertStores lstore = CertStores.locatorStore(); - CertStores gstore = CertStores.clientStore(); - - lstore.withCertificate(locatorCertificate); - gstore.withCertificate(gfshCertificate); + lstore.withCertificate("locator", locatorCertificate); + lstore.trust("ca", ca); - lstore.trust(gstore.alias(), gstore.certificate()); - - gstore.trust(lstore.alias(), lstore.certificate()); + CertStores gstore = CertStores.clientStore(); + gstore.withCertificate("gfsh", gfshCertificate); + gstore.trust("ca", ca); Properties locatorSSLProps = lstore.propertiesWith(ALL, false, false); diff --git a/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java index ee24e590b614..f40a2910d7e1 100644 --- a/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/distributed/internal/tcpserver/TCPClientSSLIntegrationTest.java @@ -36,7 +36,8 @@ import org.mockito.Mockito; import org.apache.geode.cache.ssl.CertStores; -import org.apache.geode.cache.ssl.TestSSLUtils.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateMaterial; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.distributed.internal.DistributionConfigImpl; import org.apache.geode.distributed.internal.PoolStatHelper; @@ -55,6 +56,7 @@ public class TCPClientSSLIntegrationTest { private int port; private FakeTcpServer server; private TcpClient client; + private CertificateMaterial ca; @Rule public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); @@ -62,24 +64,29 @@ public class TCPClientSSLIntegrationTest { @Before public void setup() { SocketCreatorFactory.setDistributionConfig(new DistributionConfigImpl(new Properties())); + + ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); } - private void startServerAndClient(CertificateBuilder serverCertificate, - CertificateBuilder clientCertificate, boolean enableHostNameValidation) + private void startServerAndClient(CertificateMaterial serverCertificate, + CertificateMaterial clientCertificate, boolean enableHostNameValidation) throws GeneralSecurityException, IOException { CertStores serverStore = CertStores.locatorStore(); - serverStore.withCertificate(serverCertificate); + serverStore.withCertificate("server", serverCertificate); + serverStore.trust("ca", ca); CertStores clientStore = CertStores.clientStore(); - clientStore.withCertificate(clientCertificate); + clientStore.withCertificate("client", clientCertificate); + clientStore.trust("ca", ca); Properties serverProperties = serverStore - .trust(clientStore.alias(), clientStore.certificate()) .propertiesWith(LOCATOR, true, enableHostNameValidation); Properties clientProperties = clientStore - .trust(serverStore.alias(), serverStore.certificate()) .propertiesWith(LOCATOR, true, enableHostNameValidation); startTcpServer(serverProperties); @@ -107,12 +114,16 @@ private void startTcpServer(Properties sslProperties) throws IOException { @Test public void clientConnectsIfServerCertificateHasHostname() throws Exception { - CertificateBuilder serverCertificate = new CertificateBuilder() + CertificateMaterial serverCertificate = new CertificateBuilder() .commonName("tcp-server") - .sanDnsName(InetAddress.getLocalHost().getHostName()); + .issuedBy(ca) + .sanDnsName(InetAddress.getLocalHost().getHostName()) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("tcp-client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("tcp-client") + .issuedBy(ca) + .generate(); startServerAndClient(serverCertificate, clientCertificate, true); String response = @@ -123,11 +134,15 @@ public void clientConnectsIfServerCertificateHasHostname() throws Exception { @Test public void clientChooseToDisableHasHostnameValidation() throws Exception { // no host name in server cert - CertificateBuilder serverCertificate = new CertificateBuilder() - .commonName("tcp-server"); + CertificateMaterial serverCertificate = new CertificateBuilder() + .commonName("tcp-server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("tcp-client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("tcp-client") + .issuedBy(ca) + .generate(); startServerAndClient(serverCertificate, clientCertificate, false); String response = @@ -137,11 +152,15 @@ public void clientChooseToDisableHasHostnameValidation() throws Exception { @Test public void clientFailsToConnectIfServerCertificateNoHostname() throws Exception { - CertificateBuilder serverCertificate = new CertificateBuilder() - .commonName("tcp-server"); + CertificateMaterial serverCertificate = new CertificateBuilder() + .commonName("tcp-server") + .issuedBy(ca) + .generate(); - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("tcp-client"); + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("tcp-client") + .issuedBy(ca) + .generate(); startServerAndClient(serverCertificate, clientCertificate, true); @@ -153,12 +172,16 @@ public void clientFailsToConnectIfServerCertificateNoHostname() throws Exception @Test public void clientFailsToConnectIfServerCertificateWrongHostname() throws Exception { - CertificateBuilder serverCertificate = new CertificateBuilder() + CertificateMaterial serverCertificate = new CertificateBuilder() .commonName("tcp-server") - .sanDnsName("example.com"); - - CertificateBuilder clientCertificate = new CertificateBuilder() - .commonName("tcp-client"); + .issuedBy(ca) + .sanDnsName("example.com") + .generate(); + + CertificateMaterial clientCertificate = new CertificateBuilder() + .commonName("tcp-client") + .issuedBy(ca) + .generate(); startServerAndClient(serverCertificate, clientCertificate, true); diff --git a/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java b/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java index f1cc22f2a92c..2715c24830fc 100755 --- a/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java +++ b/geode-core/src/integrationTest/java/org/apache/geode/internal/net/SSLSocketHostNameVerificationIntegrationTest.java @@ -49,7 +49,8 @@ import org.junit.runners.Parameterized.Parameters; import org.apache.geode.cache.ssl.CertStores; -import org.apache.geode.cache.ssl.TestSSLUtils; +import org.apache.geode.cache.ssl.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateMaterial; import org.apache.geode.distributed.internal.DMStats; import org.apache.geode.distributed.internal.DistributionConfig; import org.apache.geode.distributed.internal.DistributionConfigImpl; @@ -104,15 +105,25 @@ public void setUp() throws Exception { IgnoredException.addIgnoredException("javax.net.ssl.SSLException: Read timed out"); this.localHost = InetAddress.getLoopbackAddress(); - TestSSLUtils.CertificateBuilder certBuilder = new TestSSLUtils.CertificateBuilder() - .commonName("iAmTheServer"); + + CertificateMaterial ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); + + CertStores certStores = CertStores.locatorStore(); + certStores.trust("ca", ca); + + CertificateBuilder certBuilder = new CertificateBuilder() + .commonName("iAmTheServer") + .issuedBy(ca); if (addCertificateSAN) { certBuilder.sanDnsName(this.localHost.getHostName()); } - CertStores certStores = CertStores.locatorStore(); - certStores.withCertificate(certBuilder); + CertificateMaterial locatorCert = certBuilder.generate(); + certStores.withCertificate("locator", locatorCert); this.distributionConfig = new DistributionConfigImpl( diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java index 6778898c93b1..a72c2fe704b9 100644 --- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertStores.java @@ -14,9 +14,6 @@ */ package org.apache.geode.cache.ssl; -import static org.apache.geode.cache.ssl.TestSSLUtils.createKeyStore; -import static org.apache.geode.cache.ssl.TestSSLUtils.createTrustStore; -import static org.apache.geode.cache.ssl.TestSSLUtils.generateKeyPair; import static org.apache.geode.distributed.ConfigurationProperties.SSL_CIPHERS; import static org.apache.geode.distributed.ConfigurationProperties.SSL_ENABLED_COMPONENTS; import static org.apache.geode.distributed.ConfigurationProperties.SSL_ENDPOINT_IDENTIFICATION_ENABLED; @@ -29,13 +26,19 @@ import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD; import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_TYPE; +import java.io.EOFException; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -45,79 +48,40 @@ * locators and clients. All certificates are signed by a single Root Certificate Authority. */ public class CertStores { - private final String alias; private final String storePrefix; - private Map trustedCerts = new HashMap<>(); + // Contents of keystore + private Map keyStoreEntries = new HashMap<>(); - private File keyStoreFile; + // Contents of truststore + private Map trustedCerts = new HashMap<>(); private String trustStorePassword = "password"; private String keyStorePassword = "password"; - private X509Certificate cert; - - private final X509Certificate caCertificate; - - private final KeyPair rootCAKeyPair; - public static CertStores locatorStore() { - return new CertStores("locator", "locator"); + return new CertStores("locator"); } public static CertStores serverStore() { - return new CertStores("server", "server"); + return new CertStores("server"); } public static CertStores clientStore() { - return new CertStores("client", "client"); + return new CertStores("client"); } - public CertStores(String alias, String storePrefix) { - this.alias = alias; + public CertStores(String storePrefix) { this.storePrefix = storePrefix; - try { - this.rootCAKeyPair = TestSSLUtils.generateKeyPair("RSA"); - caCertificate = createCACertificate(rootCAKeyPair); - trustedCerts.put("ca", caCertificate); - } catch (Exception ex) { - throw new RuntimeException("Unable to create default root CA", ex); - } } - private X509Certificate createCACertificate(KeyPair caKeyPair) throws CertificateException { - return new TestSSLUtils.CertificateBuilder() - .isCA() - .commonName("Test CA") - .generate(caKeyPair.getPublic(), caKeyPair.getPrivate()); - } - - public String alias() { - return alias; - } - - public X509Certificate certificate() { - return cert; - } - - public CertStores withCertificate(TestSSLUtils.CertificateBuilder certificateBuilder) - throws GeneralSecurityException, IOException { - keyStoreFile = File.createTempFile(storePrefix + "-KS-", ".jks"); - withCertificate(certificateBuilder, keyStoreFile); + public CertStores withCertificate(String alias, CertificateMaterial material) { + keyStoreEntries.put(alias, material); return this; } - private void withCertificate(TestSSLUtils.CertificateBuilder certificateBuilder, - File keyStoreFile) throws GeneralSecurityException, IOException { - certificateBuilder.issuedBy(caCertificate); - KeyPair keyPair = generateKeyPair("RSA"); - cert = certificateBuilder.generate(keyPair.getPublic(), rootCAKeyPair.getPrivate()); - createKeyStore(keyStoreFile.getPath(), keyStorePassword, alias, keyPair.getPrivate(), cert, - caCertificate); - } - - public CertStores trust(String alias, X509Certificate certificate) { - this.trustedCerts.put(alias, certificate); + public CertStores trust(String alias, CertificateMaterial material) { + this.trustedCerts.put(alias, material); return this; } @@ -137,8 +101,11 @@ public Properties propertiesWith(String components, String protocols, throws GeneralSecurityException, IOException { File trustStoreFile = File.createTempFile(storePrefix + "-TS-", ".jks"); trustStoreFile.deleteOnExit(); + createTrustStore(trustStoreFile.getPath(), trustStorePassword); - createTrustStore(trustStoreFile.getPath(), trustStorePassword, trustedCerts); + File keyStoreFile = File.createTempFile(storePrefix + "-KS-", ".jks"); + keyStoreFile.deleteOnExit(); + createKeyStore(keyStoreFile.getPath(), keyStorePassword); return propertiesWith(components, protocols, ciphers, trustStoreFile, keyStoreFile, requireAuth, endPointIdentification); @@ -163,4 +130,49 @@ private Properties propertiesWith(String components, String protocols, String ci return sslConfigs; } + + private void createTrustStore(String filename, String password) + throws GeneralSecurityException, IOException { + KeyStore ks = KeyStore.getInstance("JKS"); + try (InputStream in = Files.newInputStream(Paths.get(filename))) { + ks.load(in, password.toCharArray()); + } catch (EOFException e) { + ks = createEmptyKeyStore(); + } + for (Map.Entry cert : trustedCerts.entrySet()) { + ks.setCertificateEntry(cert.getKey(), cert.getValue().getCertificate()); + } + + try (OutputStream out = Files.newOutputStream(Paths.get(filename))) { + ks.store(out, password.toCharArray()); + } + } + + private void createKeyStore(String filename, String password) + throws GeneralSecurityException, IOException { + KeyStore ks = createEmptyKeyStore(); + + for (Map.Entry entry : keyStoreEntries.entrySet()) { + CertificateMaterial cert = entry.getValue(); + + List chain = new ArrayList<>(); + chain.add(cert.getCertificate()); + + cert.getIssuer().ifPresent(chain::add); + + ks.setKeyEntry(entry.getKey(), cert.getPrivateKey(), password.toCharArray(), + chain.toArray(new Certificate[] {})); + } + try (OutputStream out = Files.newOutputStream(Paths.get(filename))) { + ks.store(out, password.toCharArray()); + } + } + + + private KeyStore createEmptyKeyStore() throws GeneralSecurityException, IOException { + KeyStore ks = KeyStore.getInstance("JKS"); + ks.load(null, null); // initialize + return ks; + } + } diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java new file mode 100644 index 000000000000..520fc933606e --- /dev/null +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java @@ -0,0 +1,199 @@ +package org.apache.geode.cache.ssl; + +import static java.util.stream.Collectors.toList; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.InetAddress; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.util.PrivateKeyFactory; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; +import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; +import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; + +/** + * Class which allows easily building certificates. It can also be used to build + * Certificate Authorities. The class is intended to be used in conjunction with {@link CertStores} + * to facilitate building key and trust stores. + */ +public class CertificateBuilder { + private final int days; + private final String algorithm; + private X500Name name; + private List dnsNames; + private List ipAddresses; + private boolean isCA; + private CertificateMaterial issuer; + + public CertificateBuilder() { + this(30, "SHA256withRSA"); + } + + public CertificateBuilder(int days, String algorithm) { + this.days = days; + this.algorithm = algorithm; + this.dnsNames = new ArrayList<>(); + this.ipAddresses = new ArrayList<>(); + } + + private static GeneralName dnsGeneralName(String name) { + return new GeneralName(GeneralName.dNSName, name); + } + + private static GeneralName ipGeneralName(InetAddress hostAddress) { + return new GeneralName(GeneralName.iPAddress, + new DEROctetString(hostAddress.getAddress())); + } + + public CertificateBuilder commonName(String cn) { + this.name = new X500Name("CN=" + cn + ", O=Geode"); + return this; + } + + public CertificateBuilder sanDnsName(String hostName) { + this.dnsNames.add(hostName); + return this; + } + + public CertificateBuilder sanIpAddress(InetAddress hostAddress) { + this.ipAddresses.add(hostAddress); + return this; + } + + public CertificateBuilder isCA() { + this.isCA = true; + return this; + } + + public CertificateBuilder issuedBy(CertificateMaterial issuer) { + this.issuer = issuer; + return this; + } + + private byte[] san() throws IOException { + List names = dnsNames.stream() + .map(CertificateBuilder::dnsGeneralName) + .collect(toList()); + + names.addAll(ipAddresses.stream() + .map(CertificateBuilder::ipGeneralName) + .collect(toList())); + + return names.isEmpty() ? null + : new GeneralNames(names.toArray(new GeneralName[] {})).getEncoded(); + } + + public CertificateMaterial generate() { + KeyPair keyPair = generateKeyPair("RSA"); + PrivateKey privateKey; + X509Certificate issuerCertificate = null; + + if (issuer == null) { + privateKey = keyPair.getPrivate(); + } else { + privateKey = issuer.getPrivateKey(); + } + + X509Certificate cert = generate(keyPair.getPublic(), privateKey); + + if (issuer != null) { + issuerCertificate = issuer.getCertificate(); + } + + return new CertificateMaterial(cert, keyPair, issuerCertificate); + } + + private X509Certificate generate(PublicKey publicKey, PrivateKey privateKey) { + try { + AlgorithmIdentifier sigAlgId = + new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm); + AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); + AsymmetricKeyParameter publicKeyAsymKeyParam = + PublicKeyFactory.createKey(publicKey.getEncoded()); + AsymmetricKeyParameter privateKeyAsymKeyParam = + PrivateKeyFactory.createKey(privateKey.getEncoded()); + SubjectPublicKeyInfo subPubKeyInfo = + SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + + ContentSigner sigGen = + new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKeyAsymKeyParam); + + Date from = new Date(); + Date to = new Date(from.getTime() + days * 86400000L); + BigInteger sn = new BigInteger(64, new SecureRandom()); + + X509v3CertificateBuilder v3CertGen; + if (issuer == null) { + // This is a self-signed certificate + v3CertGen = new X509v3CertificateBuilder(name, sn, from, to, name, subPubKeyInfo); + } else { + v3CertGen = new X509v3CertificateBuilder( + new X500Name(issuer.getCertificate().getIssuerDN().getName()), + sn, from, to, name, subPubKeyInfo); + } + + byte[] subjectAltName = san(); + if (subjectAltName != null) { + v3CertGen.addExtension(Extension.subjectAlternativeName, false, subjectAltName); + } + + if (isCA) { + v3CertGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign)); + v3CertGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); + } + + // Not strictly necessary, but this makes the certs look like those generated + // by `keytool`. + SubjectKeyIdentifier subjectKeyIdentifier = + new SubjectKeyIdentifier( + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKeyAsymKeyParam) + .getEncoded()); + v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, subjectKeyIdentifier); + + X509CertificateHolder certificateHolder = v3CertGen.build(sigGen); + return new JcaX509CertificateConverter() + .setProvider(new BouncyCastleProvider()) + .getCertificate(certificateHolder); + } catch (Exception e) { + throw new RuntimeException("Unable to create certificate", e); + } + } + + private KeyPair generateKeyPair(String algorithm) { + try { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm); + keyGen.initialize(2048); + return keyGen.genKeyPair(); + } catch (NoSuchAlgorithmException nex) { + throw new RuntimeException("Unable to generate " + algorithm + " keypair"); + } + } +} diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java new file mode 100644 index 000000000000..7a67b38ea42a --- /dev/null +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java @@ -0,0 +1,44 @@ +package org.apache.geode.cache.ssl; + +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.util.Optional; + +/** + * Class which encapsulates a {@link X509Certificate} as well as the associated + * {@link KeyPair}. If the certificate is not self-signed it will also hold an issuer. + *

+ * {@code CertificateMaterial} is produced by {@link CertificateBuilder}s. + * + * @see CertificateBuilder + * @see CertStores + */ +public class CertificateMaterial { + private final X509Certificate certificate; + private final KeyPair keyPair; + private final Optional issuer; + + public CertificateMaterial(X509Certificate certificate, KeyPair keyPair, X509Certificate issuer) { + this.certificate = certificate; + this.keyPair = keyPair; + this.issuer = Optional.ofNullable(issuer); + } + + public X509Certificate getCertificate() { + return certificate; + } + + public PublicKey getPublicKey() { + return keyPair.getPublic(); + } + + public PrivateKey getPrivateKey() { + return keyPair.getPrivate(); + } + + public Optional getIssuer() { + return issuer; + } +} diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java deleted file mode 100644 index e01b53d8d405..000000000000 --- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license - * agreements. See the NOTICE file distributed with this work for additional information regarding - * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance with the License. You may obtain a - * copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package org.apache.geode.cache.ssl; - -import static java.util.stream.Collectors.toList; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigInteger; -import java.net.InetAddress; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.security.GeneralSecurityException; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.KeyStore; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.cert.Certificate; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.AlgorithmIdentifier; -import org.bouncycastle.asn1.x509.BasicConstraints; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.asn1.x509.GeneralName; -import org.bouncycastle.asn1.x509.GeneralNames; -import org.bouncycastle.asn1.x509.KeyUsage; -import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.X509v3CertificateBuilder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; -import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.util.PrivateKeyFactory; -import org.bouncycastle.crypto.util.PublicKeyFactory; -import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.operator.ContentSigner; -import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; -import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; -import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; - -public class TestSSLUtils { - - public static KeyPair generateKeyPair(String algorithm) throws NoSuchAlgorithmException { - KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algorithm); - keyGen.initialize(2048); - return keyGen.genKeyPair(); - } - - private static KeyStore createEmptyKeyStore() throws GeneralSecurityException, IOException { - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(null, null); // initialize - return ks; - } - - public static void createKeyStore(String filename, - String password, String alias, - Key privateKey, Certificate cert, Certificate issuer) - throws GeneralSecurityException, IOException { - KeyStore ks = createEmptyKeyStore(); - - List chain = new ArrayList<>(); - chain.add(cert); - if (issuer != null) { - chain.add(issuer); - } - - ks.setKeyEntry(alias, privateKey, password.toCharArray(), chain.toArray(new Certificate[] {})); - try (OutputStream out = Files.newOutputStream(Paths.get(filename))) { - ks.store(out, password.toCharArray()); - } - } - - public static void createTrustStore(String filename, String password, - Map certs) - throws GeneralSecurityException, IOException { - KeyStore ks = KeyStore.getInstance("JKS"); - try (InputStream in = Files.newInputStream(Paths.get(filename))) { - ks.load(in, password.toCharArray()); - } catch (EOFException e) { - ks = createEmptyKeyStore(); - } - for (Map.Entry cert : certs.entrySet()) { - ks.setCertificateEntry(cert.getKey(), cert.getValue()); - } - try (OutputStream out = Files.newOutputStream(Paths.get(filename))) { - ks.store(out, password.toCharArray()); - } - } - - public static class CertificateBuilder { - private final int days; - private final String algorithm; - private X500Name name; - private List dnsNames; - private List ipAddresses; - private boolean isCA; - private X509Certificate issuer; - - public CertificateBuilder() { - this(30, "SHA256withRSA"); - } - - public CertificateBuilder(int days, String algorithm) { - this.days = days; - this.algorithm = algorithm; - this.dnsNames = new ArrayList<>(); - this.ipAddresses = new ArrayList<>(); - } - - private static GeneralName dnsGeneralName(String name) { - return new GeneralName(GeneralName.dNSName, name); - } - - private static GeneralName ipGeneralName(InetAddress hostAddress) { - return new GeneralName(GeneralName.iPAddress, - new DEROctetString(hostAddress.getAddress())); - } - - public CertificateBuilder commonName(String cn) { - this.name = new X500Name("CN=" + cn + ", O=Geode"); - return this; - } - - public CertificateBuilder sanDnsName(String hostName) { - this.dnsNames.add(hostName); - return this; - } - - public CertificateBuilder sanIpAddress(InetAddress hostAddress) { - this.ipAddresses.add(hostAddress); - return this; - } - - public CertificateBuilder isCA() { - this.isCA = true; - return this; - } - - public CertificateBuilder issuedBy(X509Certificate issuer) { - this.issuer = issuer; - return this; - } - - private byte[] san() throws IOException { - List names = dnsNames.stream() - .map(CertificateBuilder::dnsGeneralName) - .collect(toList()); - - names.addAll(ipAddresses.stream() - .map(CertificateBuilder::ipGeneralName) - .collect(toList())); - - return names.isEmpty() ? null - : new GeneralNames(names.toArray(new GeneralName[] {})).getEncoded(); - } - - public X509Certificate generate(PublicKey publicKey, PrivateKey privateKey) - throws CertificateException { - try { - AlgorithmIdentifier sigAlgId = - new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm); - AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); - AsymmetricKeyParameter publicKeyAsymKeyParam = - PublicKeyFactory.createKey(publicKey.getEncoded()); - AsymmetricKeyParameter privateKeyAsymKeyParam = - PrivateKeyFactory.createKey(privateKey.getEncoded()); - SubjectPublicKeyInfo subPubKeyInfo = - SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); - - ContentSigner sigGen = - new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(privateKeyAsymKeyParam); - - Date from = new Date(); - Date to = new Date(from.getTime() + days * 86400000L); - BigInteger sn = new BigInteger(64, new SecureRandom()); - - X509v3CertificateBuilder v3CertGen; - if (issuer == null) { - // This is a self-signed certificate - v3CertGen = new X509v3CertificateBuilder(name, sn, from, to, name, subPubKeyInfo); - } else { - v3CertGen = new X509v3CertificateBuilder(new X500Name(issuer.getIssuerDN().getName()), - sn, from, to, name, subPubKeyInfo); - } - - byte[] subjectAltName = san(); - if (subjectAltName != null) { - v3CertGen.addExtension(Extension.subjectAlternativeName, false, subjectAltName); - } - - if (isCA) { - v3CertGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign)); - v3CertGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); - } - - // Not strictly necessary, but this makes the certs look like those generated - // by `keytool`. - SubjectKeyIdentifier subjectKeyIdentifier = - new SubjectKeyIdentifier( - SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKeyAsymKeyParam) - .getEncoded()); - v3CertGen.addExtension(Extension.subjectKeyIdentifier, false, subjectKeyIdentifier); - - X509CertificateHolder certificateHolder = v3CertGen.build(sigGen); - return new JcaX509CertificateConverter() - .setProvider(new BouncyCastleProvider()) - .getCertificate(certificateHolder); - } catch (CertificateException ce) { - throw ce; - } catch (Exception e) { - throw new CertificateException(e); - } - } - } -} diff --git a/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java b/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java index 8d896823c165..329e8007ee4e 100644 --- a/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java +++ b/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/serial/WANHostNameVerificationDistributedTest.java @@ -14,7 +14,6 @@ */ package org.apache.geode.internal.cache.wan.serial; -import static org.apache.geode.cache.ssl.TestSSLUtils.CertificateBuilder; import static org.apache.geode.distributed.ConfigurationProperties.DISTRIBUTED_SYSTEM_ID; import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT; import static org.apache.geode.distributed.ConfigurationProperties.REMOTE_LOCATORS; @@ -37,6 +36,8 @@ import org.apache.geode.cache.RegionFactory; import org.apache.geode.cache.RegionShortcut; import org.apache.geode.cache.ssl.CertStores; +import org.apache.geode.cache.ssl.CertificateBuilder; +import org.apache.geode.cache.ssl.CertificateMaterial; import org.apache.geode.cache.wan.GatewayReceiverFactory; import org.apache.geode.cache.wan.GatewaySenderFactory; import org.apache.geode.internal.AvailablePortHelper; @@ -66,40 +67,38 @@ public void setupCluster() throws Exception { IgnoredException.addIgnoredException("Unexpected IOException"); } - private void setupWanSites(CertificateBuilder locator_ln_cert, CertificateBuilder server_ln_cert, - CertificateBuilder locator_ny_cert, CertificateBuilder server_ny_cert) + private void setupWanSites(CertificateMaterial ca, CertificateMaterial locator_ln_cert, + CertificateMaterial server_ln_cert, + CertificateMaterial locator_ny_cert, CertificateMaterial server_ny_cert) throws GeneralSecurityException, IOException { - CertStores locator_ln_store = new CertStores("ln_locator", "ln_locator"); - locator_ln_store.withCertificate(locator_ln_cert); + CertStores locator_ln_store = new CertStores("ln_locator"); + locator_ln_store.withCertificate("ln_locator", locator_ln_cert); + locator_ln_store.trust("ca", ca); - CertStores server_ln_store = new CertStores("ln_server", "ln_server"); - server_ln_store.withCertificate(server_ln_cert); + CertStores server_ln_store = new CertStores("ln_server"); + server_ln_store.withCertificate("ln_server", server_ln_cert); + server_ln_store.trust("ca", ca); - CertStores locator_ny_store = new CertStores("ny_locator", "ny_locator"); - locator_ny_store.withCertificate(locator_ny_cert); + CertStores locator_ny_store = new CertStores("ny_locator"); + locator_ny_store.withCertificate("ny_locator", locator_ny_cert); + locator_ny_store.trust("ca", ca); - CertStores server_ny_store = new CertStores("ny_server", "ny_server"); - server_ny_store.withCertificate(server_ny_cert); + CertStores server_ny_store = new CertStores("ny_server"); + server_ny_store.withCertificate("ny_server", server_ny_cert); + server_ny_store.trust("ca", ca); int site1Port = - setupWanSite1(locator_ln_store, server_ln_store, locator_ny_store, server_ny_store); - setupWanSite2(site1Port, locator_ny_store, server_ny_store, locator_ln_store, server_ln_store); + setupWanSite1(locator_ln_store, server_ln_store); + setupWanSite2(site1Port, locator_ny_store, server_ny_store); } - private int setupWanSite1(CertStores locator_ln_store, CertStores server_ln_store, - CertStores locator_ny_store, CertStores server_ny_store) + private int setupWanSite1(CertStores locator_ln_store, CertStores server_ln_store) throws GeneralSecurityException, IOException { Properties locatorSSLProps = locator_ln_store - .trust(server_ln_store.alias(), server_ln_store.certificate()) - .trust(locator_ny_store.alias(), locator_ny_store.certificate()) - .trust(server_ny_store.alias(), server_ny_store.certificate()) .propertiesWith(ALL, true, true); Properties serverSSLProps = server_ln_store - .trust(locator_ln_store.alias(), locator_ln_store.certificate()) - .trust(locator_ny_store.alias(), locator_ny_store.certificate()) - .trust(server_ny_store.alias(), server_ny_store.certificate()) .propertiesWith(ALL, true, true); // create a cluster @@ -118,14 +117,10 @@ private int setupWanSite1(CertStores locator_ln_store, CertStores server_ln_stor } private void setupWanSite2(int site1Port, CertStores locator_ny_store, - CertStores server_ny_store, - CertStores locator_ln_store, CertStores server_ln_store) + CertStores server_ny_store) throws GeneralSecurityException, IOException { Properties locator_ny_props = locator_ny_store - .trust(server_ln_store.alias(), server_ln_store.certificate()) - .trust(server_ny_store.alias(), server_ny_store.certificate()) - .trust(locator_ln_store.alias(), locator_ln_store.certificate()) .propertiesWith(ALL, true, true); locator_ny_props.setProperty(MCAST_PORT, "0"); @@ -133,9 +128,6 @@ private void setupWanSite2(int site1Port, CertStores locator_ny_store, locator_ny_props.setProperty(REMOTE_LOCATORS, "localhost[" + site1Port + "]"); Properties server_ny_props = server_ny_store - .trust(locator_ln_store.alias(), locator_ln_store.certificate()) - .trust(locator_ny_store.alias(), locator_ny_store.certificate()) - .trust(server_ln_store.alias(), server_ln_store.certificate()) .propertiesWith(ALL, true, true); // create a cluster @@ -199,35 +191,48 @@ public void enableHostNameValidationAcrossAllComponents() throws Exception { // server-ln -> locator-ny // server-ln -> server-ny - CertificateBuilder locator_ln_cert = new CertificateBuilder() + CertificateMaterial ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); + + CertificateMaterial locator_ln_cert = new CertificateBuilder() .commonName("locator_ln") + .issuedBy(ca) // ClusterStartupRule uses 'localhost' as locator host .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanIpAddress(InetAddress.getLocalHost()) - .sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows + .sanIpAddress(InetAddress.getByName("0.0.0.0")) // to pass on windows + .generate(); - CertificateBuilder server_ln_cert = new CertificateBuilder() + CertificateMaterial server_ln_cert = new CertificateBuilder() .commonName("server_ln") + .issuedBy(ca) .sanDnsName(InetAddress.getLocalHost().getHostName()) - .sanIpAddress(InetAddress.getLocalHost()); + .sanIpAddress(InetAddress.getLocalHost()) + .generate(); - CertificateBuilder locator_ny_cert = new CertificateBuilder() + CertificateMaterial locator_ny_cert = new CertificateBuilder() .commonName("locator_ny") + .issuedBy(ca) // ClusterStartupRule uses 'localhost' as locator host .sanDnsName(InetAddress.getLoopbackAddress().getHostName()) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) .sanIpAddress(InetAddress.getLocalHost()) - .sanIpAddress(InetAddress.getByName("0.0.0.0")); // to pass on windows + .sanIpAddress(InetAddress.getByName("0.0.0.0")) // to pass on windows + .generate(); - CertificateBuilder server_ny_cert = new CertificateBuilder() + CertificateMaterial server_ny_cert = new CertificateBuilder() .commonName("server_ny") + .issuedBy(ca) .sanDnsName(InetAddress.getLocalHost().getHostName()) .sanDnsName(InetAddress.getLocalHost().getCanonicalHostName()) - .sanIpAddress(InetAddress.getLocalHost()); + .sanIpAddress(InetAddress.getLocalHost()) + .generate(); - setupWanSites(locator_ln_cert, server_ln_cert, locator_ny_cert, server_ny_cert); + setupWanSites(ca, locator_ln_cert, server_ln_cert, locator_ny_cert, server_ny_cert); server_ln.invoke(WANHostNameVerificationDistributedTest::doPutOnSite1); server_ny.invoke(WANHostNameVerificationDistributedTest::verifySite2Received); @@ -235,24 +240,33 @@ public void enableHostNameValidationAcrossAllComponents() throws Exception { @Test public void gwsenderFailsToConnectIfGWReceiverHasNoHostName() throws Exception { - CertificateBuilder gwSenderCertificate = new CertificateBuilder() - .commonName("gwsender-ln"); + CertificateMaterial ca = new CertificateBuilder() + .commonName("Test CA") + .isCA() + .generate(); + + CertificateMaterial gwSenderCertificate = new CertificateBuilder() + .commonName("gwsender-ln") + .issuedBy(ca) + .generate(); - CertificateBuilder gwReceiverCertificate = new CertificateBuilder() - .commonName("gwreceiver-ny"); + CertificateMaterial gwReceiverCertificate = new CertificateBuilder() + .commonName("gwreceiver-ny") + .issuedBy(ca) + .generate(); - CertStores gwSender = new CertStores("gwsender", "gwsender"); - gwSender.withCertificate(gwSenderCertificate); + CertStores gwSender = new CertStores("gwsender"); + gwSender.withCertificate("gwsender", gwSenderCertificate); + gwSender.trust("ca", ca); - CertStores gwReceiver = new CertStores("gwreceiver", "gwreceiver"); - gwReceiver.withCertificate(gwReceiverCertificate); + CertStores gwReceiver = new CertStores("gwreceiver"); + gwReceiver.withCertificate("gwreceiver", gwReceiverCertificate); + gwReceiver.trust("ca", ca); Properties ln_SSLProps = gwSender - .trust(gwReceiver.alias(), gwReceiver.certificate()) .propertiesWith(GATEWAY, true, true); Properties ny_SSLProps = gwReceiver - .trust(gwSender.alias(), gwSender.certificate()) .propertiesWith(GATEWAY, true, true); // create a ln cluster From d365eea52429a6e4944e3a4dc3b2c30998b68864 Mon Sep 17 00:00:00 2001 From: Jens Deppe Date: Tue, 13 Aug 2019 06:53:22 -0700 Subject: [PATCH 4/5] Add license headers --- .../apache/geode/cache/ssl/CertificateBuilder.java | 14 ++++++++++++++ .../geode/cache/ssl/CertificateMaterial.java | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java index 520fc933606e..0dd226ec3a7d 100644 --- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java @@ -1,3 +1,17 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ package org.apache.geode.cache.ssl; import static java.util.stream.Collectors.toList; diff --git a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java index 7a67b38ea42a..66568187bef3 100644 --- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java +++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java @@ -1,3 +1,17 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ package org.apache.geode.cache.ssl; import java.security.KeyPair; From ef4be52b3549d40409bf23a3db55125b1c8bfc45 Mon Sep 17 00:00:00 2001 From: Jens Deppe Date: Tue, 13 Aug 2019 07:59:32 -0700 Subject: [PATCH 5/5] Trigger CI