You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by kr...@apache.org on 2019/03/05 20:17:25 UTC
[knox] branch master updated: KNOX-1801 - Master secret is
incorrectly assumed when a custom truststore is not specified when
clientauth is enabled (#63)
This is an automated email from the ASF dual-hosted git repository.
krisden pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/master by this push:
new 1262230 KNOX-1801 - Master secret is incorrectly assumed when a custom truststore is not specified when clientauth is enabled (#63)
1262230 is described below
commit 1262230a1d4b96d098524fd301fc2bb062415137
Author: Robert Levas <rl...@apache.org>
AuthorDate: Tue Mar 5 15:17:21 2019 -0500
KNOX-1801 - Master secret is incorrectly assumed when a custom truststore is not specified when clientauth is enabled (#63)
---
.../org/apache/knox/gateway/GatewayMessages.java | 9 +
.../org/apache/knox/gateway/GatewayServer.java | 7 +-
.../gateway/services/DefaultGatewayServices.java | 1 -
.../services/security/impl/JettySSLService.java | 131 +++---
.../security/impl/JettySSLServiceTest.java | 478 +++++++++++++++++++++
.../src/test/resources/keystores/readme.txt | 40 ++
.../{ => keystores}/server-truststore.jks | Bin
.../knox/gateway/services/security/SSLService.java | 8 +-
8 files changed, 586 insertions(+), 88 deletions(-)
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
index 6f0ade5..74d662d 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayMessages.java
@@ -169,6 +169,12 @@ public interface GatewayMessages {
@Message( level = MessageLevel.INFO, text = "Creating keystore for the gateway instance." )
void creatingKeyStoreForGateway();
+ @Message( level = MessageLevel.ERROR, text = "Unable to obtain the password for the keystore for the gateway instance: {0}" )
+ void failedToGetPasswordForGatewayIdentityKeystore(Exception e);
+
+ @Message( level = MessageLevel.ERROR, text = "Unable to obtain the password for the gateway identity key: {0}" )
+ void failedToGetPassphraseForGatewayIdentityKey(Exception e);
+
@Message( level = MessageLevel.INFO, text = "Keystore for the gateway instance found - no need to create one." )
void keyStoreForGatewayFoundNotCreating();
@@ -178,6 +184,9 @@ public interface GatewayMessages {
@Message( level = MessageLevel.INFO, text = "Credential store found for the cluster: {0} - no need to create one." )
void credentialStoreForClusterFoundNotCreating(String clusterName);
+ @Message( level = MessageLevel.ERROR, text = "Unable to obtain the password for the gateway truststore using the alias {0}: {1}" )
+ void failedToGetPasswordForGatewayTruststore(String alias, Exception e);
+
@Message( level = MessageLevel.DEBUG, text = "Received request: {0} {1}" )
void receivedRequest( String method, String uri );
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
index e4a9e76..e13c14c 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayServer.java
@@ -41,6 +41,7 @@ import org.apache.knox.gateway.i18n.messages.MessagesFactory;
import org.apache.knox.gateway.i18n.resources.ResourcesFactory;
import org.apache.knox.gateway.services.GatewayServices;
import org.apache.knox.gateway.services.registry.ServiceRegistry;
+import org.apache.knox.gateway.services.security.AliasServiceException;
import org.apache.knox.gateway.services.security.SSLService;
import org.apache.knox.gateway.services.topology.TopologyService;
import org.apache.knox.gateway.topology.Application;
@@ -377,7 +378,7 @@ public class GatewayServer {
private Connector createConnector(final Server server,
final GatewayConfig config, final int port, final String topologyName)
throws IOException, CertificateException, NoSuchAlgorithmException,
- KeyStoreException {
+ KeyStoreException, AliasServiceException {
ServerConnector connector;
@@ -401,7 +402,7 @@ public class GatewayServer {
httpsConfig.setSecurePort( connectorPort );
httpsConfig.addCustomizer( new SecureRequestCustomizer() );
SSLService ssl = services.getService(GatewayServices.SSL_SERVICE);
- SslContextFactory sslContextFactory = (SslContextFactory)ssl.buildSslContextFactory( config.getIdentityKeystorePath(), config.getIdentityKeystoreType(), config.getIdentityKeyAlias() );
+ SslContextFactory sslContextFactory = (SslContextFactory)ssl.buildSslContextFactory( config );
connector = new ServerConnector( server, sslContextFactory, new HttpConnectionFactory( httpsConfig ) );
} else {
connector = new ServerConnector( server );
@@ -693,7 +694,7 @@ public class GatewayServer {
return errorHandler;
}
- private WebAppContext createWebAppContext( Topology topology, File warFile, String warPath ) throws IOException, ZipException, TransformerException, SAXException, ParserConfigurationException {
+ private WebAppContext createWebAppContext( Topology topology, File warFile, String warPath ) {
String topoName = topology.getName();
WebAppContext context = new WebAppContext();
String contextPath;
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/DefaultGatewayServices.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/DefaultGatewayServices.java
index 9bd669a..34ea43a 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/DefaultGatewayServices.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/DefaultGatewayServices.java
@@ -101,7 +101,6 @@ public class DefaultGatewayServices implements GatewayServices {
JettySSLService ssl = new JettySSLService();
ssl.setAliasService(alias);
ssl.setKeystoreService(ks);
- ssl.setMasterService(ms);
ssl.init(config, options);
services.put(SSL_SERVICE, ssl);
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java
index 1e07823..180c1b8 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/JettySSLService.java
@@ -17,15 +17,7 @@
*/
package org.apache.knox.gateway.services.security.impl;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
-import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
@@ -42,7 +34,6 @@ import org.apache.knox.gateway.services.security.AliasService;
import org.apache.knox.gateway.services.security.AliasServiceException;
import org.apache.knox.gateway.services.security.KeystoreService;
import org.apache.knox.gateway.services.security.KeystoreServiceException;
-import org.apache.knox.gateway.services.security.MasterService;
import org.apache.knox.gateway.services.security.SSLService;
import org.apache.knox.gateway.util.X500PrincipalParser;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -53,21 +44,8 @@ public class JettySSLService implements SSLService {
private static final String GATEWAY_CREDENTIAL_STORE_NAME = "__gateway";
private static GatewayMessages log = MessagesFactory.get( GatewayMessages.class );
- private MasterService ms;
private KeystoreService ks;
private AliasService as;
- private List<String> sslIncludeCiphers;
- private List<String> sslExcludeCiphers;
- private List<String> sslExcludeProtocols;
- private boolean clientAuthNeeded;
- private boolean trustAllCerts;
- private String truststorePath;
- private String trustStoreType;
- private boolean clientAuthWanted;
-
- public void setMasterService(MasterService ms) {
- this.ms = ms;
- }
public void setAliasService(AliasService as) {
this.as = as;
@@ -95,7 +73,7 @@ public class JettySSLService implements SSLService {
log.credentialStoreForGatewayFoundNotCreating();
}
} catch (KeystoreServiceException e) {
- throw new ServiceLifecycleException("Keystore was not loaded properly - the provided (or persisted) master secret may not match the password for the keystore.", e);
+ throw new ServiceLifecycleException("Keystore was not loaded properly - the provided password may not match the password for the keystore.", e);
}
try {
@@ -108,9 +86,6 @@ public class JettySSLService implements SSLService {
} catch (AliasServiceException e) {
throw new ServiceLifecycleException("Error accessing credential store for the gateway.", e);
}
- if (passphrase == null) {
- passphrase = ms.getMasterSecret();
- }
ks.addSelfSignedCertForGateway(config.getIdentityKeyAlias(), passphrase);
}
else {
@@ -120,15 +95,6 @@ public class JettySSLService implements SSLService {
} catch (KeystoreServiceException e) {
throw new ServiceLifecycleException("The identity keystore was not loaded properly - the provided password may not match the password for the keystore.", e);
}
-
- sslIncludeCiphers = config.getIncludedSSLCiphers();
- sslExcludeCiphers = config.getExcludedSSLCiphers();
- sslExcludeProtocols = config.getExcludedSSLProtocols();
- clientAuthNeeded = config.isClientAuthNeeded();
- clientAuthWanted = config.isClientAuthWanted();
- truststorePath = config.getTruststorePath();
- trustAllCerts = config.getTrustAllCerts();
- trustStoreType = config.getTruststoreType();
}
private void logAndValidateCertificate(GatewayConfig config) throws ServiceLifecycleException {
@@ -165,64 +131,74 @@ public class JettySSLService implements SSLService {
}
@Override
- public Object buildSslContextFactory(String keystoreFileName, String keystoreType, String alias) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException {
+ public Object buildSslContextFactory(GatewayConfig config) throws AliasServiceException {
+ String identityKeystorePath = config.getIdentityKeystorePath();
+ String identityKeystoreType = config.getIdentityKeystoreType();
+ String identityKeyAlias = config.getIdentityKeyAlias();
+
SslContextFactory sslContextFactory = new SslContextFactory( true );
- sslContextFactory.setCertAlias( alias );
- sslContextFactory.setKeyStoreType(keystoreType);
- sslContextFactory.setKeyStorePath(keystoreFileName);
- char[] master = ms.getMasterSecret();
+ sslContextFactory.setCertAlias( identityKeyAlias );
+ sslContextFactory.setKeyStoreType(identityKeystoreType);
+ sslContextFactory.setKeyStorePath(identityKeystorePath );
- char[] keystorePasswordChars = null;
+ char[] keystorePasswordChars;
try {
keystorePasswordChars = as.getGatewayIdentityKeystorePassword();
} catch (AliasServiceException e) {
- log.creatingKeyStoreForGateway();
- // nop - default passphrase will be used
+ log.failedToGetPasswordForGatewayIdentityKeystore(e);
+ throw e;
}
- if(keystorePasswordChars == null) {
- // If a keystore password was not set, use the master password
- keystorePasswordChars = master;
+ if(keystorePasswordChars != null) {
+ sslContextFactory.setKeyStorePassword(new String(keystorePasswordChars));
}
- sslContextFactory.setKeyStorePassword(new String(keystorePasswordChars));
- char[] keypass = null;
+ char[] keypass;
try {
keypass = as.getGatewayIdentityPassphrase();
} catch (AliasServiceException e) {
- // nop - default passphrase will be used
+ log.failedToGetPassphraseForGatewayIdentityKey(e);
+ throw e;
}
- if (keypass == null) {
- // there has been no alias created for the key - let's assume it is the same as the keystore password
- keypass = master;
+ if(keypass != null) {
+ sslContextFactory.setKeyManagerPassword(new String(keypass));
}
- sslContextFactory.setKeyManagerPassword(new String(keypass));
- String truststorePassword;
+ boolean clientAuthNeeded = config.isClientAuthNeeded();
+ boolean clientAuthWanted = config.isClientAuthWanted();
if (clientAuthNeeded || clientAuthWanted) {
+ String truststorePath = config.getTruststorePath();
+ String trustStoreType;
+ char[] truststorePassword;
+
if (truststorePath != null) {
- char[] truststorePwd = null;
+ trustStoreType = config.getTruststoreType();
+
try {
- truststorePwd = as.getPasswordFromAliasForGateway(GATEWAY_TRUSTSTORE_PASSWORD);
+ truststorePassword = as.getPasswordFromAliasForGateway(GATEWAY_TRUSTSTORE_PASSWORD);
} catch (AliasServiceException e) {
- // nop - master secret will be used
- }
- if (truststorePwd != null) {
- truststorePassword = new String(truststorePwd);
- }
- else {
- truststorePassword = new String(master);
+ log.failedToGetPasswordForGatewayTruststore(GATEWAY_TRUSTSTORE_PASSWORD, e);
+ throw e;
}
- sslContextFactory.setTrustStore(loadKeyStore(truststorePath, trustStoreType, truststorePassword.toCharArray()));
- sslContextFactory.setTrustStorePassword(truststorePassword);
- sslContextFactory.setTrustStoreType(trustStoreType);
}
else {
// when clientAuthIsNeeded but no truststore provided
// default to the server's keystore and details
- sslContextFactory.setTrustStore(loadKeyStore(keystoreFileName, keystoreType, master));
- sslContextFactory.setTrustStorePassword(new String(master));
- sslContextFactory.setTrustStoreType(keystoreType);
+ truststorePath = identityKeystorePath;
+ trustStoreType = identityKeystoreType;
+
+ try {
+ truststorePassword = as.getGatewayIdentityKeystorePassword();
+ } catch (AliasServiceException e) {
+ log.failedToGetPasswordForGatewayTruststore(config.getIdentityKeystorePasswordAlias(), e);
+ throw e;
+ }
+ }
+
+ sslContextFactory.setTrustStorePath(truststorePath);
+ if(truststorePassword != null) {
+ sslContextFactory.setTrustStorePassword(new String(truststorePassword));
}
+ sslContextFactory.setTrustStoreType(trustStoreType);
}
if (clientAuthNeeded) {
sslContextFactory.setNeedClientAuth( clientAuthNeeded );
@@ -230,13 +206,20 @@ public class JettySSLService implements SSLService {
else {
sslContextFactory.setWantClientAuth( clientAuthWanted );
}
- sslContextFactory.setTrustAll( trustAllCerts );
+
+ sslContextFactory.setTrustAll( config.getTrustAllCerts() );
+
+ List<String> sslIncludeCiphers = config.getIncludedSSLCiphers();
if (sslIncludeCiphers != null && !sslIncludeCiphers.isEmpty()) {
sslContextFactory.setIncludeCipherSuites( sslIncludeCiphers.toArray(new String[0]) );
}
+
+ List<String> sslExcludeCiphers = config.getExcludedSSLCiphers();
if (sslExcludeCiphers != null && !sslExcludeCiphers.isEmpty()) {
sslContextFactory.setExcludeCipherSuites( sslExcludeCiphers.toArray(new String[0]) );
}
+
+ List<String> sslExcludeProtocols = config.getExcludedSSLProtocols();
if (sslExcludeProtocols != null && !sslExcludeProtocols.isEmpty()) {
sslContextFactory.setExcludeProtocols( sslExcludeProtocols.toArray(new String[0]) );
}
@@ -250,12 +233,4 @@ public class JettySSLService implements SSLService {
@Override
public void stop() throws ServiceLifecycleException {
}
-
- private static KeyStore loadKeyStore( String fileName, String storeType, char[] storePass ) throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException {
- KeyStore keystore = KeyStore.getInstance(storeType);
- try (InputStream is = Files.newInputStream(Paths.get(fileName))) {
- keystore.load(is, storePass);
- }
- return keystore;
- }
}
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java
new file mode 100644
index 0000000..628076b
--- /dev/null
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/security/impl/JettySSLServiceTest.java
@@ -0,0 +1,478 @@
+/*
+ * 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.knox.gateway.services.security.impl;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.AliasServiceException;
+import org.apache.knox.gateway.services.security.KeystoreService;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.UnrecoverableKeyException;
+
+public class JettySSLServiceTest {
+ @Test
+ public void TestBuildSslContextFactoryOnlyIdentityKeystore() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeystorePassword = "horton".toCharArray();
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ assertEquals(identityKeystorePath.toUri().toString(), sslContextFactory.getKeyStorePath());
+ assertEquals(identityKeystoreType, sslContextFactory.getKeyStoreType());
+ assertNotNull(sslContextFactory.getKeyStore());
+
+ assertNull(sslContextFactory.getTrustStorePath());
+ assertNull(sslContextFactory.getTrustStoreType());
+
+ // If the truststore is not set, by default the identity keystore is used by Jetty.
+ assertEquals(sslContextFactory.getKeyStore().size(), sslContextFactory.getTrustStore().size());
+ assertTrue(sslContextFactory.getTrustStore().containsAlias(identityKeyAlias));
+
+ verify(config, aliasService, keystoreService);
+ }
+
+ @Test(expected = AliasServiceException.class)
+ public void TestBuildSslContextFactoryOnlyIdentityKeystoreErrorGettingPassword() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andThrow(new AliasServiceException(null)).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ sslService.buildSslContextFactory(config);
+
+ fail("AliasServiceException should have been thrown");
+ }
+
+ @Test
+ public void TestBuildSslContextFactoryOnlyIdentityKeystoreNullKeystorePassword() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(null).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ assertEquals(identityKeystorePath.toUri().toString(), sslContextFactory.getKeyStorePath());
+ assertEquals(identityKeystoreType, sslContextFactory.getKeyStoreType());
+ assertNotNull(sslContextFactory.getKeyStore());
+
+ assertNull(sslContextFactory.getTrustStorePath());
+ assertNull(sslContextFactory.getTrustStoreType());
+
+ // If the truststore is not set, by default the identity keystore is used by Jetty.
+ assertEquals(sslContextFactory.getKeyStore().size(), sslContextFactory.getTrustStore().size());
+ assertTrue(sslContextFactory.getTrustStore().containsAlias(identityKeyAlias));
+
+ verify(config, aliasService, keystoreService);
+
+ // Note: The key password is used if the keystore password is not set; and vice versa
+ }
+
+ @Test(expected = UnrecoverableKeyException.class)
+ public void TestBuildSslContextFactoryOnlyIdentityKeystoreNullKeyPassword() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(false, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(null).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(null).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ fail("UnrecoverableKeyException should have been thrown");
+ }
+
+ @Test
+ public void TestBuildSslContextFactoryImplicitTrustStore() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeystorePassword = "horton".toCharArray();
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(true, false, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ assertEquals(identityKeystorePath.toUri().toString(), sslContextFactory.getKeyStorePath());
+ assertEquals(identityKeystoreType, sslContextFactory.getKeyStoreType());
+ assertNotNull(sslContextFactory.getKeyStore());
+
+ assertEquals(identityKeystorePath.toUri().toString(), sslContextFactory.getTrustStorePath());
+ assertEquals(identityKeystoreType, sslContextFactory.getTrustStoreType());
+
+ // The truststore is expected to be the same as the identity keystore
+ assertEquals(sslContextFactory.getKeyStore().size(), sslContextFactory.getTrustStore().size());
+ assertTrue(sslContextFactory.getTrustStore().containsAlias(identityKeyAlias));
+
+ verify(config, aliasService, keystoreService);
+ }
+
+ @Test
+ public void TestBuildSslContextFactoryExplicitTrustStore() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeystorePassword = "horton".toCharArray();
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+ char[] truststorePassword = "horton".toCharArray();
+
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andReturn(truststorePassword).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ assertEquals(identityKeystorePath.toUri().toString(), sslContextFactory.getKeyStorePath());
+ assertEquals(identityKeystoreType, sslContextFactory.getKeyStoreType());
+ assertNotNull(sslContextFactory.getKeyStore());
+
+ assertEquals(truststorePath.toUri().toString(), sslContextFactory.getTrustStorePath());
+ assertEquals(truststoreType, sslContextFactory.getTrustStoreType());
+ assertNotNull(sslContextFactory.getTrustStore());
+
+ // The truststore is expected to be different than the identity keystore
+ assertTrue(sslContextFactory.getKeyStore().containsAlias(identityKeyAlias));
+ assertFalse(sslContextFactory.getTrustStore().containsAlias(identityKeyAlias));
+
+ verify(config, aliasService, keystoreService);
+ }
+
+ @Test(expected = AliasServiceException.class)
+ public void TestBuildSslContextFactoryExplicitTrustStoreErrorGettingPassword() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeystorePassword = "horton".toCharArray();
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andThrow(new AliasServiceException(null)).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ sslService.buildSslContextFactory(config);
+
+ fail("AliasServiceException should have been thrown");
+ }
+
+ @Test
+ public void TestBuildSslContextFactoryExplicitTrustStoreNullPassword() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ char[] identityKeystorePassword = "horton".toCharArray();
+ char[] identityKeyPassphrase = "horton".toCharArray();
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(identityKeystorePassword).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(identityKeyPassphrase).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andReturn(null).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ assertEquals(identityKeystorePath.toUri().toString(), sslContextFactory.getKeyStorePath());
+ assertEquals(identityKeystoreType, sslContextFactory.getKeyStoreType());
+ assertNotNull(sslContextFactory.getKeyStore());
+
+ assertEquals(truststorePath.toUri().toString(), sslContextFactory.getTrustStorePath());
+ assertEquals(truststoreType, sslContextFactory.getTrustStoreType());
+ assertNotNull(sslContextFactory.getTrustStore());
+
+ // The truststore is expected to be different than the identity keystore
+ assertTrue(sslContextFactory.getKeyStore().containsAlias(identityKeyAlias));
+ assertFalse(sslContextFactory.getTrustStore().containsAlias(identityKeyAlias));
+
+ verify(config, aliasService, keystoreService);
+
+ // Note: The keystore password is used if the truststore password is not set
+ }
+
+ @Test(expected = UnrecoverableKeyException.class)
+ public void TestBuildSslContextFactoryExplicitTrustStoreNullPasswords() throws Exception {
+ String basedir = System.getProperty("basedir");
+ if (basedir == null) {
+ basedir = new File(".").getCanonicalPath();
+ }
+
+ Path identityKeystorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-keystore.jks");
+ String identityKeystoreType = "jks";
+ String identityKeyAlias = "server";
+ Path truststorePath = Paths.get(basedir, "target", "test-classes", "keystores", "server-truststore.jks");
+ String truststoreType = "jks";
+
+ GatewayConfig config = createGatewayConfig(true, true, identityKeystorePath, identityKeystoreType, identityKeyAlias, truststorePath, truststoreType);
+
+ AliasService aliasService = createMock(AliasService.class);
+ expect(aliasService.getGatewayIdentityKeystorePassword()).andReturn(null).atLeastOnce();
+ expect(aliasService.getGatewayIdentityPassphrase()).andReturn(null).atLeastOnce();
+ expect(aliasService.getPasswordFromAliasForGateway(eq("gateway-truststore-password"))).andReturn(null).atLeastOnce();
+
+ KeystoreService keystoreService = createMock(KeystoreService.class);
+
+ replay(config, aliasService, keystoreService);
+
+ JettySSLService sslService = new JettySSLService();
+ sslService.setAliasService(aliasService);
+ sslService.setKeystoreService(keystoreService);
+
+ Object result = sslService.buildSslContextFactory(config);
+ assertNotNull(result);
+ assertTrue(result instanceof SslContextFactory);
+
+ SslContextFactory sslContextFactory = (SslContextFactory) result;
+ sslContextFactory.start();
+
+ fail("UnrecoverableKeyException should have been thrown");
+ }
+
+ private GatewayConfig createGatewayConfig(boolean isClientAuthNeeded, boolean isExplicitTruststore,
+ Path identityKeystorePath, String identityKeystoreType,
+ String identityKeyAlias, Path truststorePath, String truststoreType) {
+ GatewayConfig config = createMock(GatewayConfig.class);
+ expect(config.getIdentityKeystorePath()).andReturn(identityKeystorePath.toString()).atLeastOnce();
+ expect(config.getIdentityKeystoreType()).andReturn(identityKeystoreType).atLeastOnce();
+ expect(config.getIdentityKeyAlias()).andReturn(identityKeyAlias).atLeastOnce();
+
+ if (isClientAuthNeeded) {
+ expect(config.isClientAuthNeeded()).andReturn(true).atLeastOnce();
+
+ if (isExplicitTruststore) {
+ expect(config.getTruststorePath()).andReturn(truststorePath.toString()).atLeastOnce();
+ expect(config.getTruststoreType()).andReturn(truststoreType).atLeastOnce();
+ } else {
+ expect(config.getTruststorePath()).andReturn(null).atLeastOnce();
+ }
+ } else {
+ expect(config.isClientAuthNeeded()).andReturn(false).atLeastOnce();
+ }
+
+ expect(config.isClientAuthWanted()).andReturn(false).atLeastOnce();
+ expect(config.getTrustAllCerts()).andReturn(false).atLeastOnce();
+ expect(config.getIncludedSSLCiphers()).andReturn(null).atLeastOnce();
+ expect(config.getExcludedSSLCiphers()).andReturn(null).atLeastOnce();
+ expect(config.getExcludedSSLProtocols()).andReturn(null).atLeastOnce();
+ return config;
+ }
+
+}
\ No newline at end of file
diff --git a/gateway-server/src/test/resources/keystores/readme.txt b/gateway-server/src/test/resources/keystores/readme.txt
new file mode 100644
index 0000000..865c0fb
--- /dev/null
+++ b/gateway-server/src/test/resources/keystores/readme.txt
@@ -0,0 +1,40 @@
+##########################################################################
+# 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.
+##########################################################################
+
+server-keystore.jks
+ Keystore password: horton
+ identity key alias: server
+ identity key password: horton
+
+----
+
+server-truststore.jks
+ Keystore password: horton
+ trusted cert alias: client
+
+----
+
+testSigningKeyName.jks
+ Keystore password: testSigningKeyPassphrase
+ Signing key alias: testSigningKeyAlias
+ Signing key password: testSigningKeyPassphrase
+
+ keytool -genkey -alias testSigningKeyAlias -keyalg RSA -keystore testSigningKeyName.jks \
+ -storepass testSigningKeyPassphrase -keypass testSigningKeyPassphrase -keysize 2048 \
+ -dname 'CN=testSigningKey,OU=example,O=Apache,L=US,ST=CA,C=US' -noprompt
+
diff --git a/gateway-server/src/test/resources/server-truststore.jks b/gateway-server/src/test/resources/keystores/server-truststore.jks
similarity index 100%
rename from gateway-server/src/test/resources/server-truststore.jks
rename to gateway-server/src/test/resources/keystores/server-truststore.jks
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/SSLService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/SSLService.java
index 3a6874d..6b135de 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/SSLService.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/SSLService.java
@@ -17,13 +17,9 @@
*/
package org.apache.knox.gateway.services.security;
-import java.io.IOException;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-
+import org.apache.knox.gateway.config.GatewayConfig;
import org.apache.knox.gateway.services.Service;
public interface SSLService extends Service {
- Object buildSslContextFactory(String keystoreFileName, String keystoreType, String alias) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException;
+ Object buildSslContextFactory(GatewayConfig config) throws AliasServiceException;
}