You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by je...@apache.org on 2019/08/19 13:35:44 UTC
[geode] branch develop updated: GEODE-7071: Add CA to CertStores so
that all certificates can be signed (#3905)
This is an automated email from the ASF dual-hosted git repository.
jensdeppe pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new 87b3110 GEODE-7071: Add CA to CertStores so that all certificates can be signed (#3905)
87b3110 is described below
commit 87b31108a3cb9e2a7acca1a02643b71cd7b73d3f
Author: Jens Deppe <jd...@pivotal.io>
AuthorDate: Mon Aug 19 06:35:05 2019 -0700
GEODE-7071: Add CA to CertStores so that all certificates can be signed (#3905)
- 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.
- Future work should convert all other tests, which utilize key/trust
stores to use the CertStores class.
---
...tServerHostNameVerificationDistributedTest.java | 107 +++++++----
.../internal/CustomSSLProviderDistributedTest.java | 124 +++++++-----
.../GfshHostNameVerificationDistributedTest.java | 67 ++++---
.../tcpserver/TCPClientSSLIntegrationTest.java | 72 ++++---
...LSocketHostNameVerificationIntegrationTest.java | 22 ++-
.../org/apache/geode/cache/ssl/CertStores.java | 119 +++++++-----
.../apache/geode/cache/ssl/CertificateBuilder.java | 213 +++++++++++++++++++++
.../geode/cache/ssl/CertificateMaterial.java | 58 ++++++
.../org/apache/geode/cache/ssl/TestSSLUtils.java | 195 -------------------
.../WANHostNameVerificationDistributedTest.java | 116 +++++------
10 files changed, 660 insertions(+), 433 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 5565360..a1bb2c3 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.net.InetAddress;
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.ClientRegionFactory;
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 @@ public class ClientServerHostNameVerificationDistributedTest {
@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 class ClientServerHostNameVerificationDistributedTest {
@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 class ClientServerHostNameVerificationDistributedTest {
@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,54 +155,55 @@ public class ClientServerHostNameVerificationDistributedTest {
@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<? extends Throwable> 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
- .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);
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 9fddeec6..db9c224 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 java.util.Properties;
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.NoAvailableServersException;
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 @@ import org.apache.geode.test.junit.categories.ClientServerTest;
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 @@ public class CustomSSLProviderDistributedTest {
@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 class CustomSSLProviderDistributedTest {
@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 class CustomSSLProviderDistributedTest {
@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 class CustomSSLProviderDistributedTest {
@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,56 +194,57 @@ public class CustomSSLProviderDistributedTest {
@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
- .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);
// 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 c794247..f232748 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.Test;
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 @@ import org.apache.geode.test.junit.rules.GfshCommandRule;
@Category({GfshTest.class})
public class GfshHostNameVerificationDistributedTest {
private static MemberVM locator;
+ private CertificateMaterial ca;
@Rule
public ClusterStartupRule cluster = new ClusterStartupRule();
@@ -53,28 +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
- .trustSelf()
- .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 {
@@ -103,34 +109,39 @@ public class GfshHostNameVerificationDistributedTest {
@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.trustSelf().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 858a469..f40a291 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.junit.experimental.categories.Category;
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,25 +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
- .trustSelf()
- .trust(clientStore.alias(), clientStore.certificate())
.propertiesWith(LOCATOR, true, enableHostNameValidation);
Properties clientProperties = clientStore
- .trust(serverStore.alias(), serverStore.certificate())
.propertiesWith(LOCATOR, true, enableHostNameValidation);
startTcpServer(serverProperties);
@@ -108,12 +114,16 @@ public class TCPClientSSLIntegrationTest {
@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 =
@@ -124,11 +134,15 @@ public class TCPClientSSLIntegrationTest {
@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 =
@@ -138,11 +152,15 @@ public class TCPClientSSLIntegrationTest {
@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);
@@ -154,12 +172,16 @@ public class TCPClientSSLIntegrationTest {
@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 b14f78f..2715c24 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.Parameter;
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,16 +105,25 @@ public class SSLSocketHostNameVerificationIntegrationTest {
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);
- certStores.trustSelf();
+ 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 7fca0a4..a72c2fe 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,74 +26,62 @@ import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTOR
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.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;
+/**
+ * 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;
- private Map<String, X509Certificate> trustedCerts = new HashMap<>();
+ // Contents of keystore
+ private Map<String, CertificateMaterial> keyStoreEntries = new HashMap<>();
- private File keyStoreFile;
+ // Contents of truststore
+ private Map<String, CertificateMaterial> trustedCerts = new HashMap<>();
private String trustStorePassword = "password";
private String keyStorePassword = "password";
- private X509Certificate cert;
-
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;
}
- 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 {
- 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;
- }
-
- 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;
}
@@ -114,10 +99,13 @@ public class CertStores {
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);
- 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);
@@ -142,4 +130,49 @@ public class CertStores {
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<String, CertificateMaterial> 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<String, CertificateMaterial> entry : keyStoreEntries.entrySet()) {
+ CertificateMaterial cert = entry.getValue();
+
+ List<Certificate> 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 0000000..0dd226e
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateBuilder.java
@@ -0,0 +1,213 @@
+/*
+ * 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.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<String> dnsNames;
+ private List<InetAddress> 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<GeneralName> 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 0000000..6656818
--- /dev/null
+++ b/geode-junit/src/main/java/org/apache/geode/cache/ssl/CertificateMaterial.java
@@ -0,0 +1,58 @@
+/*
+ * 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;
+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.
+ * <p/>
+ * {@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<X509Certificate> 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<X509Certificate> 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 42a945b..0000000
--- a/geode-junit/src/main/java/org/apache/geode/cache/ssl/TestSSLUtils.java
+++ /dev/null
@@ -1,195 +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.SecureRandom;
-import java.security.Security;
-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.Extension;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-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.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(1024);
- 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) throws GeneralSecurityException, IOException {
- KeyStore ks = createEmptyKeyStore();
- ks.setKeyEntry(alias, privateKey, password.toCharArray(), new Certificate[] {cert});
- try (OutputStream out = Files.newOutputStream(Paths.get(filename))) {
- ks.store(out, password.toCharArray());
- }
- }
-
- public static <T extends Certificate> void createTrustStore(
- String filename, String password, Map<String, T> 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<String, T> 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 String name;
- private List<String> dnsNames;
- private List<InetAddress> ipAddresses;
-
- public CertificateBuilder() {
- this(30, "SHA1withRSA");
- }
-
- 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 = "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;
- }
-
- private byte[] san() throws IOException {
- List<GeneralName> 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(KeyPair keyPair) throws CertificateException {
- return this.generate(this.name, keyPair);
- }
-
- public X509Certificate generate(String dn, KeyPair keyPair) throws CertificateException {
- try {
- Security.addProvider(new BouncyCastleProvider());
- AlgorithmIdentifier sigAlgId =
- new DefaultSignatureAlgorithmIdentifierFinder().find(algorithm);
- AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
- AsymmetricKeyParameter privateKeyAsymKeyParam =
- PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
- SubjectPublicKeyInfo subPubKeyInfo =
- SubjectPublicKeyInfo.getInstance(keyPair.getPublic().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);
-
- byte[] subjectAltName = san();
- if (subjectAltName != null) {
- v3CertGen.addExtension(Extension.subjectAlternativeName, false, subjectAltName);
- }
- X509CertificateHolder certificateHolder = v3CertGen.build(sigGen);
- return new JcaX509CertificateConverter().setProvider("BC")
- .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 177570a..329e800 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.Region;
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,42 +67,38 @@ public class WANHostNameVerificationDistributedTest {
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
- .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())
.propertiesWith(ALL, true, true);
// create a cluster
@@ -120,15 +117,10 @@ public class WANHostNameVerificationDistributedTest {
}
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
- .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())
.propertiesWith(ALL, true, true);
locator_ny_props.setProperty(MCAST_PORT, "0");
@@ -136,10 +128,6 @@ public class WANHostNameVerificationDistributedTest {
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())
.propertiesWith(ALL, true, true);
// create a cluster
@@ -203,35 +191,48 @@ public class WANHostNameVerificationDistributedTest {
// 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);
@@ -239,26 +240,33 @@ public class WANHostNameVerificationDistributedTest {
@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
- .trustSelf()
- .trust(gwReceiver.alias(), gwReceiver.certificate())
.propertiesWith(GATEWAY, true, true);
Properties ny_SSLProps = gwReceiver
- .trustSelf()
- .trust(gwSender.alias(), gwSender.certificate())
.propertiesWith(GATEWAY, true, true);
// create a ln cluster