You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by pi...@apache.org on 2018/03/30 23:46:53 UTC

[geode] branch develop updated: GEODE-4817: Add support for SSL to the experimental driver. (#1683)

This is an automated email from the ASF dual-hosted git repository.

pivotalsarge 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 5809567  GEODE-4817: Add support for SSL to the experimental driver. (#1683)
5809567 is described below

commit 5809567ed4609b2e2ef48ce5a86ed7c57d9c77d7
Author: Michael "Sarge" Dodge <md...@pivotal.io>
AuthorDate: Fri Mar 30 16:46:49 2018 -0700

    GEODE-4817: Add support for SSL to the experimental driver. (#1683)
    
    * GEODE-4817: Add support for SSL to the experimental driver.
    - Adding a test that a locator can shut itself down
      with SSL. In order to use a locator with SSL, the
      locator must trust itself. Modifying the truststore
      and adding a test of shutting down a locator with SSL.
    - The locator needs to trust itself. Fixing the
      truststore so that this test can shutdown.
    
        Signed-off-by: Dan Smith <ds...@pivotal.io>
    
    * GEODE-4817: Adding ssl tests with bad certificates
    
    Adding tests that the client cannot connect if the server or the client
    has a bad ssl certificate.
    
    * GEODE-4817: Adding protocols and cipher parameters to the experimental driver
    
    Adding parameters that let the user restrict the protocols and ciphers
    used.
---
 .../cache/client/internal/LocatorSSLJUnitTest.java |  57 ++++++
 .../cache/client/internal/cacheserver.truststore   | Bin 844 -> 1658 bytes
 .../geode/experimental/driver/DriverFactory.java   |  85 ++++++++-
 .../geode/experimental/driver/ProtobufChannel.java |  65 ++++---
 .../geode/experimental/driver/ProtobufDriver.java  |  13 +-
 .../geode/experimental/driver/SocketFactory.java   | 203 +++++++++++++++++++++
 .../apache/geode/experimental/driver/SSLTest.java  | 187 +++++++++++++++++++
 .../geode/experimental/driver/bogusclient.keystore | Bin 0 -> 1115 bytes
 .../geode/experimental/driver/bogusserver.keystore | Bin 0 -> 1299 bytes
 .../geode/experimental/driver/cacheserver.keystore | Bin 0 -> 1253 bytes
 .../experimental/driver/cacheserver.truststore     | Bin 0 -> 2519 bytes
 .../geode/experimental/driver/client.keystore      | Bin 844 -> 1251 bytes
 .../geode/experimental/driver/client.truststore    | Bin 0 -> 846 bytes
 13 files changed, 572 insertions(+), 38 deletions(-)

diff --git a/geode-core/src/test/java/org/apache/geode/cache/client/internal/LocatorSSLJUnitTest.java b/geode-core/src/test/java/org/apache/geode/cache/client/internal/LocatorSSLJUnitTest.java
new file mode 100644
index 0000000..a93edb3
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/cache/client/internal/LocatorSSLJUnitTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.client.internal;
+
+import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_ENABLED_COMPONENTS;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_TYPE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import org.apache.geode.distributed.Locator;
+import org.apache.geode.test.junit.categories.ClientServerTest;
+import org.apache.geode.test.junit.categories.UnitTest;
+import org.apache.geode.util.test.TestUtil;
+
+@Category({UnitTest.class, ClientServerTest.class})
+public class LocatorSSLJUnitTest {
+  private final String SERVER_KEY_STORE =
+      TestUtil.getResourcePath(LocatorSSLJUnitTest.class, "cacheserver.keystore");
+  private final String SERVER_TRUST_STORE =
+      TestUtil.getResourcePath(LocatorSSLJUnitTest.class, "cacheserver.truststore");
+
+  @Test
+  public void canStopLocatorWithSSL() throws IOException {
+    Properties properties = new Properties();
+    properties.setProperty(MCAST_PORT, "0");
+    properties.put(SSL_ENABLED_COMPONENTS, "all");
+    properties.put(SSL_KEYSTORE_TYPE, "jks");
+    properties.put(SSL_KEYSTORE, SERVER_KEY_STORE);
+    properties.put(SSL_KEYSTORE_PASSWORD, "password");
+    properties.put(SSL_TRUSTSTORE, SERVER_TRUST_STORE);
+    properties.put(SSL_TRUSTSTORE_PASSWORD, "password");
+
+    Locator locator = Locator.startLocatorAndDS(0, null, properties);
+    locator.stop();
+  }
+}
diff --git a/geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore b/geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore
index 3920963..4b887ee 100644
Binary files a/geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore and b/geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore differ
diff --git a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java
index b6cf205..91c2843 100644
--- a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java
+++ b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/DriverFactory.java
@@ -47,6 +47,26 @@ public class DriverFactory {
   private String password = null;
 
   /**
+   * Path to SSL key store; SSL is <em>not</em> used if <code>null</code>.
+   */
+  private String keyStorePath;
+
+  /**
+   * Path to SSL trust store; SSL is <em>not</em> used if <code>null</code>.
+   */
+  private String trustStorePath;
+
+  /**
+   * Space-separated list of the SSL protocols to enable.
+   */
+  private String protocols;
+
+  /**
+   * Space-separated list of the SSL cipher suites to enable.
+   */
+  private String ciphers;
+
+  /**
    * Adds a locator at <code>host</code> and <code>port</code> to the set of locators to use.
    *
    * @param host Internet address or host name.
@@ -59,16 +79,6 @@ public class DriverFactory {
   }
 
   /**
-   * Creates a driver configured to use all the locators about which this driver factory knows.
-   *
-   * @return New driver.
-   * @throws Exception
-   */
-  public Driver create() throws Exception {
-    return new ProtobufDriver(locators, username, password);
-  }
-
-  /**
    * Specifies the user name with which to authenticate with the server.
    *
    * @param username User identity as a string; may be <code>null</code>.
@@ -89,4 +99,59 @@ public class DriverFactory {
     this.password = password;
     return this;
   }
+
+  /**
+   * Specifies the key store to use with SSL.
+   *
+   * @param keyStorePath Path to the SSL key store.
+   * @return This driver factory.
+   */
+  public DriverFactory setKeyStorePath(String keyStorePath) {
+    this.keyStorePath = keyStorePath;
+    return this;
+  }
+
+  /**
+   * Specifies the trust store to use with SSL.
+   *
+   * @param trustStorePath Path to the SSL trust store.
+   * @return This driver factory.
+   */
+  public DriverFactory setTrustStorePath(String trustStorePath) {
+    this.trustStorePath = trustStorePath;
+    return this;
+  }
+
+  /**
+   * Specifies the protocols to enable.
+   *
+   * @param protocols Space-separated list of the SSL protocols to enable.
+   * @return This driver factory.
+   */
+  public DriverFactory setProtocols(String protocols) {
+    this.protocols = protocols;
+    return this;
+  }
+
+  /**
+   * Specifies the cipher suites to enable.
+   *
+   * @param ciphers Space-separated list of the SSL cipher suites to enable.
+   * @return This driver factory.
+   */
+  public DriverFactory setCiphers(String ciphers) {
+    this.ciphers = ciphers;
+    return this;
+  }
+
+  /**
+   * Creates a driver configured to use all the locators about which this driver factory knows.
+   *
+   * @return New driver.
+   * @throws Exception
+   */
+  public Driver create() throws Exception {
+    return new ProtobufDriver(locators, username, password, keyStorePath, trustStorePath, protocols,
+        ciphers);
+  }
 }
diff --git a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufChannel.java b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufChannel.java
index 8ddbe6f..890d43c 100644
--- a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufChannel.java
+++ b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufChannel.java
@@ -17,8 +17,10 @@ package org.apache.geode.experimental.driver;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
+import java.security.GeneralSecurityException;
 import java.util.Objects;
 import java.util.Set;
 
@@ -32,42 +34,33 @@ import org.apache.geode.internal.protocol.protobuf.v1.ConnectionAPI;
 import org.apache.geode.internal.protocol.protobuf.v1.LocatorAPI;
 
 class ProtobufChannel {
-
-  private final Set<InetSocketAddress> locators;
   /**
    * Socket to a GemFire server that has Protobuf enabled.
    */
   final Socket socket;
 
-  public ProtobufChannel(final Set<InetSocketAddress> locators, String username, String password)
-      throws IOException {
-    this.locators = locators;
-    this.socket = connectToAServer(username, password);
-  }
-
-  Message sendRequest(final Message request, MessageTypeCase expectedResult) throws IOException {
-    final OutputStream outputStream = socket.getOutputStream();
-    request.writeDelimitedTo(outputStream);
-    Message response = readResponse();
-
-    if (!response.getMessageTypeCase().equals(expectedResult)) {
-      throw new RuntimeException(
-          "Got invalid response for request " + request + ", response " + response);
-    }
-    return response;
+  public ProtobufChannel(final Set<InetSocketAddress> locators, String username, String password,
+      String keyStorePath, String trustStorePath, String protocols, String ciphers)
+      throws GeneralSecurityException, IOException {
+    socket = connectToAServer(locators, username, password, keyStorePath, trustStorePath, protocols,
+        ciphers);
   }
 
   public void close() throws IOException {
-    this.socket.close();
+    socket.close();
   }
 
   public boolean isClosed() {
-    return this.socket.isClosed();
+    return socket.isClosed();
   }
 
-  private Socket connectToAServer(String username, String password) throws IOException {
-    InetSocketAddress server = findAServer(username, password);
-    Socket socket = new Socket(server.getAddress(), server.getPort());
+  private Socket connectToAServer(final Set<InetSocketAddress> locators, String username,
+      String password, String keyStorePath, String trustStorePath, String protocols, String ciphers)
+      throws GeneralSecurityException, IOException {
+    InetSocketAddress server =
+        findAServer(locators, username, password, keyStorePath, trustStorePath, protocols, ciphers);
+    Socket socket = createSocket(server.getAddress(), server.getPort(), keyStorePath,
+        trustStorePath, protocols, ciphers);
     socket.setTcpNoDelay(true);
     socket.setSendBufferSize(65535);
     socket.setReceiveBufferSize(65535);
@@ -96,13 +89,16 @@ class ProtobufChannel {
    *
    * @return The server chosen by the Locator service for this client
    */
-  private InetSocketAddress findAServer(String username, String password) throws IOException {
+  private InetSocketAddress findAServer(final Set<InetSocketAddress> locators, String username,
+      String password, String keyStorePath, String trustStorePath, String protocols, String ciphers)
+      throws GeneralSecurityException, IOException {
     IOException lastException = null;
 
     for (InetSocketAddress locator : locators) {
       Socket locatorSocket = null;
       try {
-        locatorSocket = new Socket(locator.getAddress(), locator.getPort());
+        locatorSocket = createSocket(locator.getAddress(), locator.getPort(), keyStorePath,
+            trustStorePath, protocols, ciphers);
 
         final OutputStream outputStream = locatorSocket.getOutputStream();
         final InputStream inputStream = locatorSocket.getInputStream();
@@ -179,6 +175,18 @@ class ProtobufChannel {
     }
   }
 
+  Message sendRequest(final Message request, MessageTypeCase expectedResult) throws IOException {
+    final OutputStream outputStream = socket.getOutputStream();
+    request.writeDelimitedTo(outputStream);
+    Message response = readResponse();
+
+    if (!response.getMessageTypeCase().equals(expectedResult)) {
+      throw new RuntimeException(
+          "Got invalid response for request " + request + ", response " + response);
+    }
+    return response;
+  }
+
   private Message readResponse() throws IOException {
     final InputStream inputStream = socket.getInputStream();
     Message response = ClientProtocol.Message.parseDelimitedFrom(inputStream);
@@ -192,4 +200,11 @@ class ProtobufChannel {
     return response;
   }
 
+  private Socket createSocket(InetAddress host, int port, String keyStorePath,
+      String trustStorePath, String protocols, String ciphers)
+      throws GeneralSecurityException, IOException {
+    return new SocketFactory().setHost(host).setPort(port).setTimeout(5000)
+        .setKeyStorePath(keyStorePath).setTrustStorePath(trustStorePath).setProtocols(protocols)
+        .setCiphers(ciphers).connect();
+  }
 }
diff --git a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java
index bded2b0..c780ea8 100644
--- a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java
+++ b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/ProtobufDriver.java
@@ -16,6 +16,7 @@ package org.apache.geode.experimental.driver;
 
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.security.GeneralSecurityException;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Set;
@@ -47,11 +48,17 @@ public class ProtobufDriver implements Driver {
    *        GemFire servers that have Protobuf enabled.
    * @param username User identity as a string; may be <code>null</code>.
    * @param password User proof as a string; may be <code>null</code>.
+   * @param keyStorePath Path to SSL key store; SSL is <em>not</em> used if <code>null</code>.
+   * @param trustStorePath Path to SSL trust store; SSL is <em>not</em> used if <code>null</code>.
+   * @param protocols Space-separated list of the SSL protocols to enable.
+   * @param ciphers Space-separated list of the SSL cipher suites to enable.
    * @throws IOException
    */
-  ProtobufDriver(Set<InetSocketAddress> locators, String username, String password)
-      throws IOException {
-    this.channel = new ProtobufChannel(locators, username, password);
+  ProtobufDriver(Set<InetSocketAddress> locators, String username, String password,
+      String keyStorePath, String trustStorePath, String protocols, String ciphers)
+      throws GeneralSecurityException, IOException {
+    this.channel = new ProtobufChannel(locators, username, password, keyStorePath, trustStorePath,
+        protocols, ciphers);
   }
 
   @Override
diff --git a/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/SocketFactory.java b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/SocketFactory.java
new file mode 100644
index 0000000..eefc8e1
--- /dev/null
+++ b/geode-experimental-driver/src/main/java/org/apache/geode/experimental/driver/SocketFactory.java
@@ -0,0 +1,203 @@
+/*
+ * 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.experimental.driver;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Objects;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+public class SocketFactory {
+  private InetAddress host;
+  private int port;
+  private int timeout = -1;
+  private String keyStorePath;
+  private String trustStorePath;
+  private String protocols;
+  private String ciphers;
+
+  public SocketFactory() {
+    // Do nothing.
+  }
+
+  public InetAddress getHost() {
+    return host;
+  }
+
+  public SocketFactory setHost(InetAddress host) {
+    this.host = host;
+    return this;
+  }
+
+  public int getPort() {
+    return port;
+  }
+
+  public SocketFactory setPort(int port) {
+    this.port = port;
+    return this;
+  }
+
+  public int getTimeout() {
+    return timeout;
+  }
+
+  public SocketFactory setTimeout(int timeout) {
+    this.timeout = timeout;
+    return this;
+  }
+
+  public String getKeyStorePath() {
+    return keyStorePath;
+  }
+
+  public SocketFactory setKeyStorePath(String keyStorePath) {
+    this.keyStorePath = keyStorePath;
+    return this;
+  }
+
+  public String getTrustStorePath() {
+    return trustStorePath;
+  }
+
+  public SocketFactory setTrustStorePath(String trustStorePath) {
+    this.trustStorePath = trustStorePath;
+    return this;
+  }
+
+  public String getProtocols() {
+    return protocols;
+  }
+
+  public SocketFactory setProtocols(String protocols) {
+    this.protocols = protocols;
+    return this;
+  }
+
+  public String getCiphers() {
+    return ciphers;
+  }
+
+  public SocketFactory setCiphers(String ciphers) {
+    this.ciphers = ciphers;
+    return this;
+  }
+
+  public boolean isSsl() {
+    return (!Objects.isNull(keyStorePath) && !keyStorePath.isEmpty())
+        || (!Objects.isNull(trustStorePath) && !trustStorePath.isEmpty());
+  }
+
+  public Socket connect() throws GeneralSecurityException, IOException {
+    Socket socket;
+
+    SocketAddress sockaddr = new InetSocketAddress(host, port);
+    if (isSsl()) {
+      final SSLContext sslContext = getSSLContextInstance();
+      final KeyManager[] keyManagers = getKeyManagers();
+      final TrustManager[] trustManagers = getTrustManagers();
+      sslContext.init(keyManagers, trustManagers, null /* use the default secure random */);
+
+      javax.net.SocketFactory socketFactory = sslContext.getSocketFactory();
+      socket = socketFactory.createSocket();
+
+      socket.connect(sockaddr, Math.max(timeout, 0));
+      if (socket instanceof SSLSocket) {
+        SSLSocket sslSocket = (SSLSocket) socket;
+        sslSocket.setUseClientMode(true); // Should this depend on clientSide?
+        sslSocket.setEnableSessionCreation(true);
+        if (timeout > 0) {
+          sslSocket.setSoTimeout(timeout);
+        }
+        if (protocols != null) {
+          sslSocket.setEnabledProtocols(protocols.split(" "));
+        }
+        if (ciphers != null) {
+          sslSocket.setEnabledCipherSuites(ciphers.split(" "));
+        }
+        sslSocket.startHandshake();
+      }
+    } else {
+      socket = new Socket();
+      socket.connect(sockaddr, Math.max(timeout, 0));
+    }
+
+    return socket;
+  }
+
+  private SSLContext getSSLContextInstance() throws IOException {
+    String[] knownAlgorithms = {"SSL", "SSLv2", "SSLv3", "TLS", "TLSv1", "TLSv1.1", "TLSv1.2"};
+    for (String algo : knownAlgorithms) {
+      try {
+        return SSLContext.getInstance(algo);
+      } catch (NoSuchAlgorithmException e) {
+        // continue
+      }
+    }
+    throw new IOException("SSL not configured correctly, unable create an SSLContext");
+  }
+
+  private TrustManager[] getTrustManagers()
+      throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException {
+    if (Objects.isNull(trustStorePath)) {
+      return new TrustManager[0];
+    }
+
+    String trustStoreType = "jks";
+    KeyStore keyStore = KeyStore.getInstance(trustStoreType);
+    FileInputStream fileInputStream = new FileInputStream(trustStorePath);
+    char[] password = "password".toCharArray();
+    keyStore.load(fileInputStream, password);
+
+    TrustManagerFactory tmf =
+        TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+    tmf.init(keyStore);
+    return tmf.getTrustManagers();
+  }
+
+  private KeyManager[] getKeyManagers() throws KeyStoreException, IOException,
+      NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
+    if (Objects.isNull(keyStorePath)) {
+      return new KeyManager[0];
+    }
+
+    String keyStoreType = "jks";
+    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+    FileInputStream fileInputStream = new FileInputStream(keyStorePath);
+    char[] password = "password".toCharArray();
+    keyStore.load(fileInputStream, password);
+
+    KeyManagerFactory keyManagerFactory =
+        KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+    keyManagerFactory.init(keyStore, password);
+    return keyManagerFactory.getKeyManagers();
+  }
+}
diff --git a/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/SSLTest.java b/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/SSLTest.java
new file mode 100644
index 0000000..6cd3e7a
--- /dev/null
+++ b/geode-experimental-driver/src/test/java/org/apache/geode/experimental/driver/SSLTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.experimental.driver;
+
+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_KEYSTORE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_PASSWORD;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_KEYSTORE_TYPE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_PROTOCOLS;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_REQUIRE_AUTHENTICATION;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE;
+import static org.apache.geode.distributed.ConfigurationProperties.SSL_TRUSTSTORE_PASSWORD;
+import static org.apache.geode.internal.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.net.ssl.SSLException;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.contrib.java.lang.system.RestoreSystemProperties;
+import org.junit.experimental.categories.Category;
+import org.junit.rules.ExpectedException;
+
+import org.apache.geode.cache.Cache;
+import org.apache.geode.cache.CacheFactory;
+import org.apache.geode.cache.RegionShortcut;
+import org.apache.geode.cache.server.CacheServer;
+import org.apache.geode.distributed.ConfigurationProperties;
+import org.apache.geode.distributed.Locator;
+import org.apache.geode.internal.net.SocketCreatorFactory;
+import org.apache.geode.test.junit.categories.IntegrationTest;
+import org.apache.geode.util.test.TestUtil;
+
+@Category(IntegrationTest.class)
+public class SSLTest {
+  @Rule
+  public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private final String SERVER_KEY_STORE =
+      TestUtil.getResourcePath(SSLTest.class, "cacheserver.keystore");
+  private final String SERVER_TRUST_STORE =
+      TestUtil.getResourcePath(SSLTest.class, "cacheserver.truststore");
+  private final String BOGUSSERVER_KEY_STORE =
+      TestUtil.getResourcePath(SSLTest.class, "bogusserver.keystore");
+  private final String BOGUSCLIENT_KEY_STORE =
+      TestUtil.getResourcePath(SSLTest.class, "bogusclient.keystore");
+  private final String CLIENT_KEY_STORE =
+      TestUtil.getResourcePath(SSLTest.class, "client.keystore");
+  private final String CLIENT_TRUST_STORE =
+      TestUtil.getResourcePath(SSLTest.class, "client.truststore");
+  private Locator locator;
+  private Cache cache;
+  private Driver driver;
+  private int locatorPort;
+
+  @Before
+  public void enableProtobuf() throws Exception {
+    System.setProperty("geode.feature-protobuf-protocol", "true");
+  }
+
+  private void startLocator(String keyStore, boolean twoWayAuthentication, String protocols,
+      String ciphers) throws IOException {
+    // Create a cache
+    Properties properties = new Properties();
+    properties.put(SSL_ENABLED_COMPONENTS, "all");
+    properties.put(SSL_KEYSTORE_TYPE, "jks");
+    properties.put(SSL_KEYSTORE, keyStore);
+    properties.put(SSL_PROTOCOLS, protocols);
+    properties.put(SSL_CIPHERS, ciphers);
+    properties.put(SSL_KEYSTORE_PASSWORD, "password");
+    properties.put(SSL_TRUSTSTORE, SERVER_TRUST_STORE);
+    properties.put(SSL_TRUSTSTORE_PASSWORD, "password");
+    properties.put(SSL_REQUIRE_AUTHENTICATION, String.valueOf(twoWayAuthentication));
+
+    CacheFactory cf = new CacheFactory(properties);
+    cf.set(ConfigurationProperties.MCAST_PORT, "0");
+    cache = cf.create();
+    cache.createRegionFactory(RegionShortcut.REPLICATE).create("region");
+
+    // Start a locator
+    locator = Locator.startLocatorAndDS(0, null, properties);
+    locatorPort = locator.getPort();
+  }
+
+  private void startServer() throws IOException {
+    CacheServer server = cache.addCacheServer();
+    server.setPort(0);
+    server.start();
+  }
+
+  @After
+  public void cleanup() {
+    cache.close();
+    locator.stop();
+    SocketCreatorFactory.close();
+  }
+
+  @Test
+  public void driverFailsToConnectWhenThereAreNoServers() throws Exception {
+    startLocator(SERVER_KEY_STORE, true, "any", "any");
+    expectedException.expect(IOException.class);
+    driver = new DriverFactory().addLocator("localhost", locatorPort).create();
+  }
+
+  @Test
+  public void driverCanConnectWithTwoWayAuthentication() throws Exception {
+    startLocator(SERVER_KEY_STORE, true, "any", "any");
+    startServer();
+    driver = new DriverFactory().addLocator("localhost", locatorPort)
+        .setTrustStorePath(CLIENT_TRUST_STORE).setKeyStorePath(CLIENT_KEY_STORE).create();
+    Set<String> regionsOnServer = driver.getRegionNames();
+    assertEquals(Collections.singleton("region"), regionsOnServer);
+    assertTrue(driver.isConnected());
+  }
+
+  @Test
+  public void driverCannotConnectWithBogusClientKeystore() throws Exception {
+    startLocator(SERVER_KEY_STORE, true, "any", "any");
+    startServer();
+    expectedException.expect(SSLException.class);
+    driver = new DriverFactory().addLocator("localhost", locatorPort)
+        .setTrustStorePath(CLIENT_TRUST_STORE).setKeyStorePath(BOGUSCLIENT_KEY_STORE).create();
+  }
+
+  @Test
+  public void driverCannotConnectWithBogusServerKeystore() throws Exception {
+    startLocator(BOGUSSERVER_KEY_STORE, true, "any", "any");
+    startServer();
+    expectedException.expect(SSLException.class);
+    driver = new DriverFactory().addLocator("localhost", locatorPort)
+        .setTrustStorePath(CLIENT_TRUST_STORE).setKeyStorePath(CLIENT_KEY_STORE).create();
+  }
+
+  @Test
+  public void driverCanConnectWithOneWayAuthentication() throws Exception {
+    startLocator(SERVER_KEY_STORE, false, "any", "any");
+    startServer();
+    driver = new DriverFactory().addLocator("localhost", locatorPort)
+        .setTrustStorePath(CLIENT_TRUST_STORE).create();
+    Set<String> regionsOnServer = driver.getRegionNames();
+    assertEquals(Collections.singleton("region"), regionsOnServer);
+    assertTrue(driver.isConnected());
+  }
+
+  @Test
+  public void driverCannotConnectIfProtocolsMismatch() throws Exception {
+    startLocator(SERVER_KEY_STORE, true, "TLSv1.1", "any");
+    startServer();
+    expectedException.expect(SSLException.class);
+    driver = new DriverFactory().addLocator("localhost", locatorPort)
+        .setTrustStorePath(CLIENT_TRUST_STORE).setKeyStorePath(CLIENT_KEY_STORE)
+        .setProtocols("TLSv1.2").create();
+  }
+
+  @Test
+  public void driverCannotConnectIfCiphersMismatch() throws Exception {
+    startLocator(SERVER_KEY_STORE, true, "any", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256");
+    startServer();
+    expectedException.expect(SSLException.class);
+    driver = new DriverFactory().addLocator("localhost", locatorPort)
+        .setTrustStorePath(CLIENT_TRUST_STORE).setKeyStorePath(CLIENT_KEY_STORE)
+        .setCiphers("TLS_DHE_DSS_WITH_AES_128_CBC_SHA").create();
+  }
+}
diff --git a/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/bogusclient.keystore b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/bogusclient.keystore
new file mode 100644
index 0000000..8dce373
Binary files /dev/null and b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/bogusclient.keystore differ
diff --git a/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/bogusserver.keystore b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/bogusserver.keystore
new file mode 100644
index 0000000..d6e2523
Binary files /dev/null and b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/bogusserver.keystore differ
diff --git a/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/cacheserver.keystore b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/cacheserver.keystore
new file mode 100644
index 0000000..adbea7b
Binary files /dev/null and b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/cacheserver.keystore differ
diff --git a/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/cacheserver.truststore b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/cacheserver.truststore
new file mode 100644
index 0000000..0cfb813
Binary files /dev/null and b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/cacheserver.truststore differ
diff --git a/geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/client.keystore
similarity index 61%
copy from geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore
copy to geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/client.keystore
index 3920963..38a315d 100644
Binary files a/geode-core/src/test/resources/org/apache/geode/cache/client/internal/cacheserver.truststore and b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/client.keystore differ
diff --git a/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/client.truststore b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/client.truststore
new file mode 100644
index 0000000..d598b86
Binary files /dev/null and b/geode-experimental-driver/src/test/resources/org/apache/geode/experimental/driver/client.truststore differ

-- 
To stop receiving notification emails like this one, please contact
pivotalsarge@apache.org.