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/02/27 19:37:47 UTC

[knox] branch master updated: KNOX-1793 - DefaultKeystoreService should not validate the signing key on initialization (#61)

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 94e423c  KNOX-1793 - DefaultKeystoreService should not validate the signing key on initialization (#61)
94e423c is described below

commit 94e423c7288df44d2648afc87703bdb1596c2029
Author: Robert Levas <rl...@users.noreply.github.com>
AuthorDate: Wed Feb 27 14:37:43 2019 -0500

    KNOX-1793 - DefaultKeystoreService should not validate the signing key on initialization (#61)
---
 .../org/apache/knox/gateway/GatewayResources.java  |  18 ++
 .../gateway/services/DefaultGatewayServices.java   |   4 +
 .../security/impl/DefaultKeystoreService.java      |  28 ---
 .../token/impl/DefaultTokenAuthorityService.java   |  88 +++++++--
 .../impl/DefaultTokenAuthorityServiceTest.java     | 217 ++++++++++++++++++++-
 5 files changed, 307 insertions(+), 48 deletions(-)

diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayResources.java b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayResources.java
index 0d8d0c7..6e1422d 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/GatewayResources.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/GatewayResources.java
@@ -82,4 +82,22 @@ public interface GatewayResources {
 
   @Resource( text="Forward method: {0} to default context: {1}" )
   String forwardToDefaultTopology(String method, String context );
+
+  @Resource( text="The keystore used to acquire the signing key is not available. The file could be missing or the password could be incorrect: {0}")
+  String signingKeystoreNotAvailable( String path );
+
+  @Resource( text="Provisioned signing key passphrase cannot be acquired using the alias name {0}.")
+  String signingKeyPassphraseNotAvailable( String alias);
+
+  @Resource( text="The public signing key was not found in the signing keystore using the alias name {0}.")
+  String publicSigningKeyNotFound( String alias );
+
+  @Resource( text="The private signing key was not found in the signing keystore using the alias name {0}. The alias could be missing or the password could be incorrect.")
+  String privateSigningKeyNotFound( String alias );
+
+  @Resource( text="The private signing key found in the signing keystore using the alias name {0} is not a RSAPrivateKey")
+  String privateSigningKeyWrongType( String alias );
+
+  @Resource( text="The public signing key found in the signing keystore using the alias name {0} is not a RSAPublicKey")
+  String publicSigningKeyWrongType( String alias );
 }
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 171de72..1c9204e 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
@@ -162,6 +162,8 @@ public class DefaultGatewayServices implements GatewayServices {
     SSLService ssl = (SSLService) services.get(SSL_SERVICE);
     ssl.start();
 
+    (services.get(TOKEN_SERVICE)).start();
+
     ServerInfoService sis = (ServerInfoService) services.get(SERVER_INFO_SERVICE);
     sis.start();
 
@@ -192,6 +194,8 @@ public class DefaultGatewayServices implements GatewayServices {
     SSLService ssl = (SSLService) services.get(SSL_SERVICE);
     ssl.stop();
 
+    (services.get(TOKEN_SERVICE)).stop();
+
     ServerInfoService sis = (ServerInfoService) services.get(SERVER_INFO_SERVICE);
     sis.stop();
 
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
index d62d1ff..45305c1 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
@@ -66,8 +66,6 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
   private static GatewayResources RES = ResourcesFactory.get( GatewayResources.class );
 
   private GatewayConfig config;
-  private String signingKeystoreName;
-  private String signingKeyAlias;
   private Map<String, Map<String, String>> cache = new ConcurrentHashMap<>();
   private Lock readLock;
   private Lock writeLock;
@@ -88,32 +86,6 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
         throw new ServiceLifecycleException( RES.failedToCreateKeyStoreDirectory( ksd.getAbsolutePath() ) );
       }
     }
-
-    signingKeystoreName = config.getSigningKeystoreName();
-    // ensure that the keystore actually exists and fail to start if not
-    if (signingKeystoreName != null) {
-      File sks = new File(this.keyStoreDir, signingKeystoreName);
-      if (!sks.exists()) {
-        throw new ServiceLifecycleException("Configured signing keystore does not exist.");
-      }
-      signingKeyAlias = config.getSigningKeyAlias();
-      if (signingKeyAlias != null) {
-        // ensure that the signing key alias exists in the configured keystore
-        KeyStore ks;
-        try {
-          ks = getSigningKeystore();
-          if (ks != null) {
-            if (!ks.containsAlias(signingKeyAlias)) {
-              throw new ServiceLifecycleException("Configured signing key alias does not exist.");
-            }
-          }
-        } catch (KeystoreServiceException e) {
-          throw new ServiceLifecycleException("Unable to get the configured signing keystore.", e);
-        } catch (KeyStoreException e) {
-          throw new ServiceLifecycleException("Signing keystore has not been loaded.", e);
-        }
-      }
-    }
   }
 
   @Override
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
index 190d0dc..cb452c6 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
@@ -17,9 +17,14 @@
  */
 package org.apache.knox.gateway.services.token.impl;
 
+import java.security.Key;
+import java.security.KeyStore;
 import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.PublicKey;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.Certificate;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.util.Map;
@@ -30,7 +35,9 @@ import java.util.HashSet;
 
 import javax.security.auth.Subject;
 
+import org.apache.knox.gateway.GatewayResources;
 import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.i18n.resources.ResourcesFactory;
 import org.apache.knox.gateway.services.Service;
 import org.apache.knox.gateway.services.ServiceLifecycleException;
 import org.apache.knox.gateway.services.security.AliasService;
@@ -48,11 +55,12 @@ import com.nimbusds.jose.crypto.RSASSASigner;
 import com.nimbusds.jose.crypto.RSASSAVerifier;
 
 public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
+  private static final GatewayResources RESOURCES = ResourcesFactory.get(GatewayResources.class);
 
   private static final Set<String> SUPPORTED_SIG_ALGS = new HashSet<>();
   private AliasService as;
   private KeystoreService ks;
-  String signingKeyAlias;
+  private GatewayConfig config;
 
   static {
       // Only standard RSA signature algorithms are accepted
@@ -161,14 +169,18 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
     return as.getSigningKeyPassphrase();
   }
 
+  private String getSigningKeyAlias() {
+    String alias = config.getSigningKeyAlias();
+    return (alias == null) ? GatewayConfig.DEFAULT_SIGNING_KEY_ALIAS : alias;
+  }
+
   private String getSigningKeyAlias(String signingKeystoreAlias) {
     if(signingKeystoreAlias != null) {
      return signingKeystoreAlias;
     }
-    if(signingKeyAlias != null) {
-      return signingKeyAlias;
-    }
-    return GatewayConfig.DEFAULT_SIGNING_KEY_ALIAS;
+
+    // Fallback to defaults
+    return getSigningKeyAlias();
   }
 
   @Override
@@ -184,7 +196,7 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
     PublicKey key;
     try {
       if (publicKey == null) {
-        key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias(signingKeyAlias)).getPublicKey();
+        key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias()).getPublicKey();
       }
       else {
         key = publicKey;
@@ -205,26 +217,64 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
     if (as == null || ks == null) {
       throw new ServiceLifecycleException("Alias or Keystore service is not set");
     }
-    signingKeyAlias = config.getSigningKeyAlias();
+    this.config = config;
+  }
 
-    RSAPrivateKey key;
+  @Override
+  public void start() throws ServiceLifecycleException {
+    // Ensure that the default signing keystore is available
+    KeyStore keystore;
+    try {
+      keystore = ks.getSigningKeystore();
+      if (keystore == null) {
+        throw new ServiceLifecycleException(RESOURCES.signingKeystoreNotAvailable(config.getSigningKeystorePath()));
+      }
+    } catch (KeystoreServiceException e) {
+      throw new ServiceLifecycleException(RESOURCES.signingKeystoreNotAvailable(config.getSigningKeystorePath()), e);
+    }
+
+    // Ensure that the password for the signing key is available
     char[] passphrase;
     try {
       passphrase = as.getSigningKeyPassphrase();
-      if (passphrase != null) {
-        key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(signingKeyAlias),
-            passphrase);
-        if (key == null) {
-          throw new ServiceLifecycleException("Provisioned passphrase cannot be used to acquire signing key.");
-        }
+      if (passphrase == null) {
+        throw new ServiceLifecycleException(RESOURCES.signingKeyPassphraseNotAvailable(config.getSigningKeyPassphraseAlias()));
       }
-    } catch (AliasServiceException | KeystoreServiceException e) {
-      throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e);
+    } catch (AliasServiceException e) {
+      throw new ServiceLifecycleException(RESOURCES.signingKeyPassphraseNotAvailable(config.getSigningKeyPassphraseAlias()), e);
     }
-  }
 
-  @Override
-  public void start() throws ServiceLifecycleException {
+    String signingKeyAlias = getSigningKeyAlias();
+
+    // Ensure that the public signing keys is available
+    try {
+      Certificate certificate = keystore.getCertificate(signingKeyAlias);
+      if(certificate == null) {
+        throw new ServiceLifecycleException(RESOURCES.publicSigningKeyNotFound(signingKeyAlias));
+      }
+      PublicKey publicKey = certificate.getPublicKey();
+      if (publicKey == null) {
+        throw new ServiceLifecycleException(RESOURCES.publicSigningKeyNotFound(signingKeyAlias));
+      }
+      else if (! (publicKey instanceof  RSAPublicKey)) {
+        throw new ServiceLifecycleException(RESOURCES.publicSigningKeyWrongType(signingKeyAlias));
+      }
+    } catch (KeyStoreException e) {
+      throw new ServiceLifecycleException(RESOURCES.publicSigningKeyNotFound(signingKeyAlias), e);
+    }
+
+    // Ensure that the private signing keys is available
+    try {
+      Key key = keystore.getKey(signingKeyAlias, passphrase);
+      if (key == null) {
+        throw new ServiceLifecycleException(RESOURCES.privateSigningKeyNotFound(signingKeyAlias));
+      }
+      else if (! (key instanceof  RSAPrivateKey)) {
+        throw new ServiceLifecycleException(RESOURCES.privateSigningKeyWrongType(signingKeyAlias));
+      }
+    } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
+      throw new ServiceLifecycleException(RESOURCES.privateSigningKeyNotFound(signingKeyAlias), e);
+    }
   }
 
   @Override
diff --git a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
index 07faa11..1cbdf9b 100644
--- a/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
@@ -24,6 +24,7 @@ import java.util.Collections;
 import java.util.HashMap;
 
 import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.ServiceLifecycleException;
 import org.apache.knox.gateway.services.security.AliasService;
 import org.apache.knox.gateway.services.security.MasterService;
 import org.apache.knox.gateway.services.security.impl.DefaultKeystoreService;
@@ -301,7 +302,6 @@ public class DefaultTokenAuthorityServiceTest {
     EasyMock.expect(config.getSigningKeyAlias()).andReturn("server").anyTimes();
 
     MasterService ms = EasyMock.createNiceMock(MasterService.class);
-    EasyMock.expect(ms.getMasterSecret()).andReturn("horton".toCharArray());
 
     AliasService as = EasyMock.createNiceMock(AliasService.class);
     EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).anyTimes();
@@ -327,4 +327,219 @@ public class DefaultTokenAuthorityServiceTest {
     assertFalse(ta.verifyToken(token));
     assertTrue(ta.verifyToken(token, customPublicKey));
   }
+
+  @Test
+  public void testServiceStart() throws Exception {
+    /*
+     Generated testSigningKeyName.jks with the following commands:
+     cd gateway-server/src/test/resources/keystores/
+     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
+     */
+
+    String basedir = System.getProperty("basedir");
+    if (basedir == null) {
+      basedir = new File(".").getCanonicalPath();
+    }
+
+    GatewayConfig config = EasyMock.createMock(GatewayConfig.class);
+    EasyMock.expect(config.getGatewayKeystoreDir()).andReturn(basedir + "/target/test-classes/keystores").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePath()).andReturn(basedir + "/target/test-classes/keystores/server-keystore.jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystoreType()).andReturn("jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePasswordAlias()).andReturn(GatewayConfig.DEFAULT_SIGNING_KEYSTORE_PASSWORD_ALIAS).anyTimes();
+    EasyMock.expect(config.getSigningKeyAlias()).andReturn("server").anyTimes();
+
+    MasterService ms = EasyMock.createMock(MasterService.class);
+    EasyMock.expect(ms.getMasterSecret()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    AliasService as = EasyMock.createMock(AliasService.class);
+    EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    EasyMock.replay(config, ms, as);
+
+    DefaultKeystoreService ks = new DefaultKeystoreService();
+    ks.setMasterService(ms);
+    ks.init(config, new HashMap<>());
+    ks.start();
+
+    DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService();
+    ta.setAliasService(as);
+    ta.setKeystoreService(ks);
+    ta.init(config, new HashMap<>());
+    ta.start();
+
+    // Stop the started services...
+    ta.stop();
+    ks.stop();
+
+    EasyMock.verify(config, ms, as);
+  }
+
+  @Test(expected = ServiceLifecycleException.class)
+  public void testServiceStartMissingKeystore() throws Exception {
+    String basedir = System.getProperty("basedir");
+    if (basedir == null) {
+      basedir = new File(".").getCanonicalPath();
+    }
+
+    GatewayConfig config = EasyMock.createMock(GatewayConfig.class);
+    EasyMock.expect(config.getGatewayKeystoreDir()).andReturn(basedir + "/target/test-classes/keystores").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePath()).andReturn(basedir + "/target/test-classes/keystores/missing-server-keystore.jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystoreType()).andReturn("jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePasswordAlias()).andReturn(GatewayConfig.DEFAULT_SIGNING_KEYSTORE_PASSWORD_ALIAS).anyTimes();
+
+    MasterService ms = EasyMock.createMock(MasterService.class);
+    EasyMock.expect(ms.getMasterSecret()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    AliasService as = EasyMock.createMock(AliasService.class);
+    EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    EasyMock.replay(config, ms, as);
+
+    DefaultKeystoreService ks = new DefaultKeystoreService();
+    ks.setMasterService(ms);
+    ks.init(config, new HashMap<>());
+    ks.start();
+
+    DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService();
+    ta.setAliasService(as);
+    ta.setKeystoreService(ks);
+    ta.init(config, new HashMap<>());
+    ta.start();
+
+    EasyMock.verify(config, ms, as);
+  }
+
+  @Test(expected = ServiceLifecycleException.class)
+  public void testServiceStartInvalidKeystorePassword() throws Exception {
+    /*
+     Generated testSigningKeyName.jks with the following commands:
+     cd gateway-server/src/test/resources/keystores/
+     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
+     */
+
+    String basedir = System.getProperty("basedir");
+    if (basedir == null) {
+      basedir = new File(".").getCanonicalPath();
+    }
+
+    GatewayConfig config = EasyMock.createMock(GatewayConfig.class);
+    EasyMock.expect(config.getGatewayKeystoreDir()).andReturn(basedir + "/target/test-classes/keystores").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePath()).andReturn(basedir + "/target/test-classes/keystores/server-keystore.jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystoreType()).andReturn("jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePasswordAlias()).andReturn(GatewayConfig.DEFAULT_SIGNING_KEYSTORE_PASSWORD_ALIAS).anyTimes();
+    EasyMock.expect(config.getSigningKeyAlias()).andReturn("server").anyTimes();
+
+    MasterService ms = EasyMock.createMock(MasterService.class);
+    EasyMock.expect(ms.getMasterSecret()).andReturn("invalid_password".toCharArray()).atLeastOnce();
+
+    AliasService as = EasyMock.createMock(AliasService.class);
+    EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    EasyMock.replay(config, ms, as);
+
+    DefaultKeystoreService ks = new DefaultKeystoreService();
+    ks.setMasterService(ms);
+    ks.init(config, new HashMap<>());
+    ks.start();
+
+    DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService();
+    ta.setAliasService(as);
+    ta.setKeystoreService(ks);
+    ta.init(config, new HashMap<>());
+    ta.start();
+
+    EasyMock.verify(config, ms, as);
+  }
+
+  @Test(expected = ServiceLifecycleException.class)
+  public void testServiceStartMissingKey() throws Exception {
+    /*
+     Generated testSigningKeyName.jks with the following commands:
+     cd gateway-server/src/test/resources/keystores/
+     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
+     */
+
+    String basedir = System.getProperty("basedir");
+    if (basedir == null) {
+      basedir = new File(".").getCanonicalPath();
+    }
+
+    GatewayConfig config = EasyMock.createMock(GatewayConfig.class);
+    EasyMock.expect(config.getGatewayKeystoreDir()).andReturn(basedir + "/target/test-classes/keystores").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePath()).andReturn(basedir + "/target/test-classes/keystores/server-keystore.jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystoreType()).andReturn("jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePasswordAlias()).andReturn(GatewayConfig.DEFAULT_SIGNING_KEYSTORE_PASSWORD_ALIAS).anyTimes();
+    EasyMock.expect(config.getSigningKeyAlias()).andReturn("invalid_key").anyTimes();
+
+    MasterService ms = EasyMock.createMock(MasterService.class);
+    EasyMock.expect(ms.getMasterSecret()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    AliasService as = EasyMock.createMock(AliasService.class);
+    EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    EasyMock.replay(config, ms, as);
+
+    DefaultKeystoreService ks = new DefaultKeystoreService();
+    ks.setMasterService(ms);
+    ks.init(config, new HashMap<>());
+    ks.start();
+
+    DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService();
+    ta.setAliasService(as);
+    ta.setKeystoreService(ks);
+    ta.init(config, new HashMap<>());
+    ta.start();
+
+    EasyMock.verify(config, ms, as);
+  }
+
+  @Test(expected = ServiceLifecycleException.class)
+  public void testServiceInvalidKeyPassword() throws Exception {
+    /*
+     Generated testSigningKeyName.jks with the following commands:
+     cd gateway-server/src/test/resources/keystores/
+     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
+     */
+
+    String basedir = System.getProperty("basedir");
+    if (basedir == null) {
+      basedir = new File(".").getCanonicalPath();
+    }
+
+    GatewayConfig config = EasyMock.createMock(GatewayConfig.class);
+    EasyMock.expect(config.getGatewayKeystoreDir()).andReturn(basedir + "/target/test-classes/keystores").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePath()).andReturn(basedir + "/target/test-classes/keystores/server-keystore.jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystoreType()).andReturn("jks").atLeastOnce();
+    EasyMock.expect(config.getSigningKeystorePasswordAlias()).andReturn(GatewayConfig.DEFAULT_SIGNING_KEYSTORE_PASSWORD_ALIAS).anyTimes();
+    EasyMock.expect(config.getSigningKeyAlias()).andReturn("server").anyTimes();
+
+    MasterService ms = EasyMock.createMock(MasterService.class);
+    EasyMock.expect(ms.getMasterSecret()).andReturn("horton".toCharArray()).atLeastOnce();
+
+    AliasService as = EasyMock.createMock(AliasService.class);
+    EasyMock.expect(as.getSigningKeyPassphrase()).andReturn("invalid".toCharArray()).atLeastOnce();
+
+    EasyMock.replay(config, ms, as);
+
+    DefaultKeystoreService ks = new DefaultKeystoreService();
+    ks.setMasterService(ms);
+    ks.init(config, new HashMap<>());
+    ks.start();
+
+    DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService();
+    ta.setAliasService(as);
+    ta.setKeystoreService(ks);
+    ta.init(config, new HashMap<>());
+    ta.start();
+
+    EasyMock.verify(config, ms, as);
+  }
 }