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 2018/11/07 17:09:46 UTC

knox git commit: KNOX-1549 - KnoxSSO should support signing keys per topology

Repository: knox
Updated Branches:
  refs/heads/master 44e2bfc8a -> 2135bc22c


KNOX-1549 - KnoxSSO should support signing keys per topology

Signed-off-by: Kevin Risden <kr...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/knox/repo
Commit: http://git-wip-us.apache.org/repos/asf/knox/commit/2135bc22
Tree: http://git-wip-us.apache.org/repos/asf/knox/tree/2135bc22
Diff: http://git-wip-us.apache.org/repos/asf/knox/diff/2135bc22

Branch: refs/heads/master
Commit: 2135bc22c0b5beb383450d6a574bcc21078e4b19
Parents: 44e2bfc
Author: Kevin Risden <kr...@apache.org>
Authored: Thu Nov 1 16:50:21 2018 -0400
Committer: Kevin Risden <kr...@apache.org>
Committed: Wed Nov 7 11:37:14 2018 -0500

----------------------------------------------------------------------
 .../federation/AbstractJWTFilterTest.java       |  34 ++--
 .../security/impl/DefaultKeystoreService.java   |  32 ++-
 .../impl/DefaultTokenAuthorityService.java      |  51 +++--
 .../impl/DefaultTokenAuthorityServiceTest.java  |  56 ++++++
 .../resources/keystores/testSigningKeyName.jks  | Bin 0 -> 2238 bytes
 .../gateway/service/knoxsso/WebSSOResource.java |  76 ++++---
 .../service/knoxsso/WebSSOResourceTest.java     | 199 ++++++++++++++-----
 .../knoxtoken/TokenServiceResourceTest.java     |  52 +++--
 .../services/security/KeystoreService.java      |  36 ++--
 .../security/token/JWTokenAuthority.java        |   6 +-
 10 files changed, 370 insertions(+), 172 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
index ea8607d..387b274 100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/AbstractJWTFilterTest.java
@@ -31,7 +31,6 @@ import org.apache.knox.gateway.provider.federation.jwt.filter.SSOCookieFederatio
 import org.apache.knox.gateway.security.PrimaryPrincipal;
 import org.apache.knox.gateway.services.security.impl.X509CertificateUtil;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
-import org.apache.knox.gateway.services.security.token.TokenServiceException;
 import org.apache.knox.gateway.services.security.token.impl.JWT;
 import org.easymock.EasyMock;
 import org.junit.After;
@@ -48,7 +47,6 @@ import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
 import java.security.AccessController;
@@ -107,7 +105,7 @@ public abstract class AbstractJWTFilterTest  {
   }
 
   @After
-  public void teardown() throws Exception {
+  public void teardown() {
     handler.destroy();
   }
 
@@ -675,7 +673,7 @@ public abstract class AbstractJWTFilterTest  {
       TestFilterChain chain = new TestFilterChain();
       handler.doFilter(request, response, chain);
       Assert.assertTrue("doFilterCalled should not be false.", !chain.doFilterCalled);
-      Assert.assertTrue("No Subject should be returned.", chain.subject == null);
+      Assert.assertNull("No Subject should be returned.", chain.subject);
     } catch (ServletException se) {
       fail("Should NOT have thrown a ServletException.");
     }
@@ -765,51 +763,56 @@ public abstract class AbstractJWTFilterTest  {
 
     private PublicKey verifyingKey;
 
-    public TestJWTokenAuthority(PublicKey verifyingKey) {
+    TestJWTokenAuthority(PublicKey verifyingKey) {
       this.verifyingKey = verifyingKey;
     }
 
     @Override
-    public JWT issueToken(Subject subject, String algorithm) throws TokenServiceException {
+    public JWT issueToken(Subject subject, String algorithm) {
       return null;
     }
 
     @Override
-    public JWT issueToken(Principal p, String algorithm) throws TokenServiceException {
+    public JWT issueToken(Principal p, String algorithm) {
       return null;
     }
 
     @Override
-    public JWT issueToken(Principal p, String audience, String algorithm)
-        throws TokenServiceException {
+    public JWT issueToken(Principal p, String audience, String algorithm) {
       return null;
     }
 
     @Override
-    public boolean verifyToken(JWT token) throws TokenServiceException {
+    public boolean verifyToken(JWT token) {
       JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) verifyingKey);
       return token.verify(verifier);
     }
 
     @Override
     public JWT issueToken(Principal p, String audience, String algorithm,
-        long expires) throws TokenServiceException {
+        long expires) {
       return null;
     }
 
     @Override
     public JWT issueToken(Principal p, List<String> audiences, String algorithm,
-        long expires) throws TokenServiceException {
+        long expires) {
+      return null;
+    }
+
+    @Override
+    public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires,
+                          String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase) {
       return null;
     }
 
     @Override
-    public JWT issueToken(Principal p, String algorithm, long expires) throws TokenServiceException {
+    public JWT issueToken(Principal p, String algorithm, long expires) {
       return null;
     }
 
     @Override
-    public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
+    public boolean verifyToken(JWT token, RSAPublicKey publicKey) {
       JWSVerifier verifier = new RSASSAVerifier(publicKey);
       return token.verify(verifier);
     }
@@ -820,8 +823,7 @@ public abstract class AbstractJWTFilterTest  {
     Subject subject = null;
 
     @Override
-    public void doFilter(ServletRequest request, ServletResponse response)
-        throws IOException, ServletException {
+    public void doFilter(ServletRequest request, ServletResponse response) {
       doFilterCalled = true;
 
       subject = Subject.getSubject( AccessController.getContext() );

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/main/java/org/apache/knox/gateway/services/security/impl/DefaultKeystoreService.java
----------------------------------------------------------------------
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 26ca369..08257ca 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
@@ -144,16 +144,23 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
 
   @Override
   public KeyStore getSigningKeystore() throws KeystoreServiceException {
-    File  keyStoreFile = null;
-    if (signingKeystoreName == null) {
+    return getSigningKeystore(null);
+  }
+
+  @Override
+  public KeyStore getSigningKeystore(String keystoreName) throws KeystoreServiceException {
+    File  keyStoreFile;
+    if(keystoreName != null) {
+      keyStoreFile = new File(keyStoreDir + keystoreName + ".jks");
+    } else if (signingKeystoreName != null) {
+      keyStoreFile = new File(keyStoreDir + signingKeystoreName);
+    } else {
       keyStoreFile = new File(keyStoreDir + GATEWAY_KEYSTORE);
     }
-    else {
-      keyStoreFile = new File(keyStoreDir + signingKeystoreName);
-      // make sure the keystore exists
-      if (!keyStoreFile.exists()) {
-        throw new KeystoreServiceException("Configured signing keystore does not exist.");
-      }
+      
+    // make sure the keystore exists
+    if (!keyStoreFile.exists()) {
+      throw new KeystoreServiceException("Configured signing keystore does not exist.");
     }
     readLock.lock();
     try {
@@ -305,10 +312,15 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
 
   @Override
   public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException {
+    return getSigningKey(null, alias, passphrase);
+  }
+
+  @Override
+  public Key getSigningKey(String keystoreName, String alias, char[] passphrase) throws KeystoreServiceException {
     Key key = null;
     readLock.lock();
     try {
-      KeyStore ks = getSigningKeystore();
+      KeyStore ks = getSigningKeystore(keystoreName);
       if (passphrase == null) {
         passphrase = masterService.getMasterSecret();
         LOG.assumingKeyPassphraseIsMaster();
@@ -331,7 +343,7 @@ public class DefaultKeystoreService extends BaseKeystoreService implements
     }
   }
 
-  public KeyStore getCredentialStoreForCluster(String clusterName) 
+  public KeyStore getCredentialStoreForCluster(String clusterName)
       throws KeystoreServiceException {
     final File  keyStoreFile = new File( keyStoreDir + clusterName + CREDENTIALS_SUFFIX  );
     readLock.lock();

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/main/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityService.java
----------------------------------------------------------------------
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 ad8f999..b32e914 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
@@ -110,6 +110,13 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
   @Override
   public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires)
       throws TokenServiceException {
+    return issueToken(p, audiences, algorithm, expires, null, null, null);
+  }
+
+  @Override
+  public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires,
+                        String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase)
+      throws TokenServiceException {
     String[] claimArray = new String[4];
     claimArray[0] = "KNOXSSO";
     claimArray[1] = p.getName();
@@ -121,19 +128,18 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
       claimArray[3] = String.valueOf(expires);
     }
 
-    JWT token = null;
+    JWT token;
     if (SUPPORTED_SIG_ALGS.contains(algorithm)) {
       token = new JWTToken(algorithm, claimArray, audiences);
-      RSAPrivateKey key;
-      char[] passphrase = null;
+      char[] passphrase;
       try {
-        passphrase = getSigningKeyPassphrase();
+        passphrase = getSigningKeyPassphrase(signingKeystorePassphrase);
       } catch (AliasServiceException e) {
         throw new TokenServiceException(e);
       }
       try {
-        key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(),
-            passphrase);
+        RSAPrivateKey key = (RSAPrivateKey) ks.getSigningKey(signingKeystoreName,
+            getSigningKeyAlias(signingKeystoreAlias), passphrase);
         JWSSigner signer = new RSASSASigner(key);
         token.sign(signer);
       } catch (KeystoreServiceException e) {
@@ -147,7 +153,10 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
     return token;
   }
 
-  private char[] getSigningKeyPassphrase() throws AliasServiceException {
+  private char[] getSigningKeyPassphrase(char[] signingKeyPassphrase) throws AliasServiceException {
+    if(signingKeyPassphrase != null) {
+      return signingKeyPassphrase;
+    }
     char[] phrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE);
     if (phrase == null) {
       phrase = as.getGatewayIdentityPassphrase();
@@ -155,11 +164,14 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
     return phrase;
   }
 
-  private String getSigningKeyAlias() {
-    if (signingKeyAlias == null) {
-      return "gateway-identity";
+  private String getSigningKeyAlias(String signingKeystoreAlias) {
+    if(signingKeystoreAlias != null) {
+     return signingKeystoreAlias;
+    }
+    if(signingKeyAlias != null) {
+      return signingKeyAlias;
     }
-    return signingKeyAlias;
+    return "gateway-identity";
   }
 
   @Override
@@ -171,11 +183,11 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
   @Override
   public boolean verifyToken(JWT token, RSAPublicKey publicKey)
       throws TokenServiceException {
-    boolean rc = false;
+    boolean rc;
     PublicKey key;
     try {
       if (publicKey == null) {
-        key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias()).getPublicKey();
+        key = ks.getSigningKeystore().getCertificate(getSigningKeyAlias(signingKeyAlias)).getPublicKey();
       }
       else {
         key = publicKey;
@@ -184,9 +196,7 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
       // TODO: interrogate the token for issuer claim in order to determine the public key to use for verification
       // consider jwk for specifying the key too
       rc = token.verify(verifier);
-    } catch (KeyStoreException e) {
-      throw new TokenServiceException("Cannot verify token.", e);
-    } catch (KeystoreServiceException e) {
+    } catch (KeyStoreException | KeystoreServiceException e) {
       throw new TokenServiceException("Cannot verify token.", e);
     }
     return rc;
@@ -200,21 +210,18 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
     }
     signingKeyAlias = config.getSigningKeyAlias();
 
-    @SuppressWarnings("unused")
     RSAPrivateKey key;
-    char[] passphrase = null;
+    char[] passphrase;
     try {
       passphrase = as.getPasswordFromAliasForGateway(SIGNING_KEY_PASSPHRASE);
       if (passphrase != null) {
-        key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(),
+        key = (RSAPrivateKey) ks.getSigningKey(getSigningKeyAlias(signingKeyAlias),
             passphrase);
         if (key == null) {
           throw new ServiceLifecycleException("Provisioned passphrase cannot be used to acquire signing key.");
         }
       }
-    } catch (AliasServiceException e) {
-      throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e);
-    } catch (KeystoreServiceException e) {
+    } catch (AliasServiceException | KeystoreServiceException e) {
       throw new ServiceLifecycleException("Provisioned signing key passphrase cannot be acquired.", e);
     }
   }

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/test/java/org/apache/knox/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
----------------------------------------------------------------------
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 f4d2d68..a51c79f 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
@@ -19,6 +19,8 @@ package org.apache.knox.gateway.services.token.impl;
 
 import java.io.File;
 import java.security.Principal;
+import java.security.interfaces.RSAPublicKey;
+import java.util.Collections;
 import java.util.HashMap;
 
 import org.apache.knox.gateway.config.GatewayConfig;
@@ -251,4 +253,58 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
     }
   }
 
+  @Test
+  public void testTokenCreationCustomSigningKey() 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 customSigningKeyName = "testSigningKeyName";
+    String customSigningKeyAlias = "testSigningKeyAlias";
+    String customSigningKeyPassphrase = "testSigningKeyPassphrase";
+
+    Principal principal = EasyMock.createNiceMock(Principal.class);
+    EasyMock.expect(principal.getName()).andReturn("john.doe@example.com");
+
+    GatewayConfig config = EasyMock.createNiceMock(GatewayConfig.class);
+    String basedir = System.getProperty("basedir");
+    if (basedir == null) {
+      basedir = new File(".").getCanonicalPath();
+    }
+
+    EasyMock.expect(config.getGatewaySecurityDir()).andReturn(basedir + "/target/test-classes");
+    EasyMock.expect(config.getSigningKeystoreName()).andReturn("server-keystore.jks");
+    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.getGatewayIdentityPassphrase()).andReturn("horton".toCharArray());
+
+    EasyMock.replay(principal, config, ms, as);
+
+    DefaultKeystoreService ks = new DefaultKeystoreService();
+    ks.setMasterService(ms);
+    ks.init(config, new HashMap<>());
+
+    DefaultTokenAuthorityService ta = new DefaultTokenAuthorityService();
+    ta.setAliasService(as);
+    ta.setKeystoreService(ks);
+    ta.init(config, new HashMap<>());
+
+    JWT token = ta.issueToken(principal, Collections.emptyList(), "RS256", -1,
+        customSigningKeyName, customSigningKeyAlias, customSigningKeyPassphrase.toCharArray());
+    assertEquals("KNOXSSO", token.getIssuer());
+    assertEquals("john.doe@example.com", token.getSubject());
+
+    RSAPublicKey customPublicKey = (RSAPublicKey)ks.getSigningKeystore(customSigningKeyName)
+                                                     .getCertificate(customSigningKeyAlias).getPublicKey();
+    assertFalse(ta.verifyToken(token));
+    assertTrue(ta.verifyToken(token, customPublicKey));
+  }
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-server/src/test/resources/keystores/testSigningKeyName.jks
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/resources/keystores/testSigningKeyName.jks b/gateway-server/src/test/resources/keystores/testSigningKeyName.jks
new file mode 100644
index 0000000..d5e984a
Binary files /dev/null and b/gateway-server/src/test/resources/keystores/testSigningKeyName.jks differ

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
index 8f8002d..ab3702b 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/knox/gateway/service/knoxsso/WebSSOResource.java
@@ -47,6 +47,8 @@ import javax.ws.rs.WebApplicationException;
 import org.apache.knox.gateway.audit.log4j.audit.Log4jAuditor;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.services.GatewayServices;
+import org.apache.knox.gateway.services.security.AliasService;
+import org.apache.knox.gateway.services.security.AliasServiceException;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
 import org.apache.knox.gateway.services.security.token.TokenServiceException;
 import org.apache.knox.gateway.services.security.token.impl.JWT;
@@ -56,6 +58,7 @@ import org.apache.knox.gateway.util.WhitelistUtils;
 
 import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
 import static javax.ws.rs.core.MediaType.APPLICATION_XML;
+import static org.apache.knox.gateway.services.GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE;
 
 @Path( WebSSOResource.RESOURCE_PATH )
 public class WebSSOResource {
@@ -68,6 +71,10 @@ public class WebSSOResource {
   private static final String SSO_COOKIE_TOKEN_SIG_ALG = "knoxsso.token.sigalg";
   private static final String SSO_COOKIE_TOKEN_WHITELIST_PARAM = "knoxsso.redirect.whitelist.regex";
 
+  private static final String SSO_SIGNINGKEY_KEYSTORE_NAME = "knoxsso.signingkey.keystore.name";
+  private static final String SSO_SIGNINGKEY_KEYSTORE_ALIAS = "knoxsso.signingkey.keystore.alias";
+  private static final String SSO_SIGNINGKEY_KEYSTORE_PASSPHRASE_ALIAS = "knoxsso.signingkey.keystore.passphrase.alias";
+
   /* parameters expected by knoxsso */
   private static final String SSO_EXPECTED_PARAM = "knoxsso.expected.params";
 
@@ -88,6 +95,7 @@ public class WebSSOResource {
   private boolean enableSession = false;
   private String signatureAlgorithm = "RS256";
   private List<String> ssoExpectedparams = new ArrayList<>();
+  private String clusterName = null;
 
   @Context
   HttpServletRequest request;
@@ -100,19 +108,34 @@ public class WebSSOResource {
 
   @PostConstruct
   public void init() {
+    clusterName = String.valueOf(context.getAttribute(GATEWAY_CLUSTER_ATTRIBUTE));
+
+    handleCookieSetup();
+
+    String enableSessionStr = context.getInitParameter(SSO_ENABLE_SESSION_PARAM);
+    this.enableSession = Boolean.parseBoolean(enableSessionStr);
+
+    String sigAlg = context.getInitParameter(SSO_COOKIE_TOKEN_SIG_ALG);
+    if (sigAlg != null) {
+      signatureAlgorithm = sigAlg;
+    }
+
+    final String expectedParams = context.getInitParameter(SSO_EXPECTED_PARAM);
+    if (expectedParams != null) {
+      ssoExpectedparams = Arrays.asList(expectedParams.split(","));
+    }
+  }
 
-    // configured cookieName
+  private void handleCookieSetup() {
     cookieName = context.getInitParameter(SSO_COOKIE_NAME);
     if (cookieName == null) {
       cookieName = DEFAULT_SSO_COOKIE_NAME;
     }
 
     String secure = context.getInitParameter(SSO_COOKIE_SECURE_ONLY_INIT_PARAM);
-    if (secure != null) {
-      secureOnly = ("false".equals(secure) ? false : true);
-      if (!secureOnly) {
-        log.cookieSecureOnly(secureOnly);
-      }
+    secureOnly = Boolean.parseBoolean(secure);
+    if (!secureOnly) {
+      log.cookieSecureOnly(secureOnly);
     }
 
     String age = context.getInitParameter(SSO_COOKIE_MAX_AGE_INIT_PARAM);
@@ -136,8 +159,8 @@ public class WebSSOResource {
     String audiences = context.getInitParameter(SSO_COOKIE_TOKEN_AUDIENCES_PARAM);
     if (audiences != null) {
       String[] auds = audiences.split(",");
-      for (int i = 0; i < auds.length; i++) {
-        targetAudiences.add(auds[i].trim());
+      for (String aud : auds) {
+        targetAudiences.add(aud.trim());
       }
     }
 
@@ -154,19 +177,6 @@ public class WebSSOResource {
         log.invalidTokenTTLEncountered(ttl);
       }
     }
-
-    String enableSession = context.getInitParameter(SSO_ENABLE_SESSION_PARAM);
-    this.enableSession = ("true".equals(enableSession));
-
-    String sigAlg = context.getInitParameter(SSO_COOKIE_TOKEN_SIG_ALG);
-    if (sigAlg != null) {
-      signatureAlgorithm = sigAlg;
-    }
-
-    final String expectedParams = context.getInitParameter(SSO_EXPECTED_PARAM);
-    if (expectedParams != null) {
-      ssoExpectedparams = Arrays.asList(expectedParams.split(","));
-    }
   }
 
   @GET
@@ -185,7 +195,7 @@ public class WebSSOResource {
     GatewayServices services =
                 (GatewayServices) request.getServletContext().getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
     boolean removeOriginalUrlCookie = true;
-    String original = getCookieValue((HttpServletRequest) request, ORIGINAL_URL_COOKIE_NAME);
+    String original = getCookieValue(request, ORIGINAL_URL_COOKIE_NAME);
     if (original == null) {
       // in the case where there are no SAML redirects done before here
       // we need to get it from the request parameters
@@ -218,17 +228,22 @@ public class WebSSOResource {
       }
     }
 
+    AliasService as = services.getService(GatewayServices.ALIAS_SERVICE);
     JWTokenAuthority ts = services.getService(GatewayServices.TOKEN_SERVICE);
     Principal p = request.getUserPrincipal();
 
     try {
-      JWT token = null;
-      if (targetAudiences.isEmpty()) {
-        token = ts.issueToken(p, signatureAlgorithm, getExpiry());
-      } else {
-        token = ts.issueToken(p, targetAudiences, signatureAlgorithm, getExpiry());
+      String signingKeystoreName = context.getInitParameter(SSO_SIGNINGKEY_KEYSTORE_NAME);
+      String signingKeystoreAlias = context.getInitParameter(SSO_SIGNINGKEY_KEYSTORE_ALIAS);
+      String signingKeystorePassphraseAlias = context.getInitParameter(SSO_SIGNINGKEY_KEYSTORE_PASSPHRASE_ALIAS);
+      char[] signingKeystorePassphrase = null;
+      if(signingKeystorePassphraseAlias != null) {
+        signingKeystorePassphrase = as.getPasswordFromAliasForCluster(clusterName, signingKeystorePassphraseAlias);
       }
 
+      JWT token = ts.issueToken(p, targetAudiences, signatureAlgorithm, getExpiry(),
+          signingKeystoreName,  signingKeystoreAlias, signingKeystorePassphrase);
+
       // Coverity CID 1327959
       if( token != null ) {
         addJWTHadoopCookie( original, token );
@@ -246,8 +261,7 @@ public class WebSSOResource {
       } catch (IOException e) {
         log.unableToCloseOutputStream(e.getMessage(), Arrays.toString(e.getStackTrace()));
       }
-    }
-    catch (TokenServiceException e) {
+    } catch (TokenServiceException| AliasServiceException e) {
       log.unableToIssueToken(e);
     }
     URI location = null;
@@ -272,7 +286,7 @@ public class WebSSOResource {
 
   private String getOriginalUrlFromQueryParams() {
     String original = request.getParameter(ORIGINAL_URL_REQUEST_PARAM);
-    StringBuffer buf = new StringBuffer(original);
+    StringBuilder buf = new StringBuilder(original);
 
     boolean first = true;
 
@@ -310,7 +324,7 @@ public class WebSSOResource {
   }
 
   private long getExpiry() {
-    long expiry = 0l;
+    long expiry;
     if (tokenTTL == -1) {
       expiry = -1;
     }

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
index d719404..e5f972d 100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/knox/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -20,7 +20,10 @@ package org.apache.knox.gateway.service.knoxsso;
 import org.apache.http.HttpStatus;
 import org.apache.knox.gateway.audit.log4j.audit.Log4jAuditor;
 import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.services.security.AliasService;
 import org.apache.knox.gateway.util.RegExUtils;
+
+import static org.apache.knox.gateway.services.GatewayServices.GATEWAY_CLUSTER_ATTRIBUTE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
@@ -31,7 +34,6 @@ import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
@@ -73,21 +75,21 @@ import com.nimbusds.jose.crypto.RSASSAVerifier;
  */
 public class WebSSOResourceTest {
 
-  protected static RSAPublicKey publicKey;
-  protected static RSAPrivateKey privateKey;
+  private static RSAPublicKey gatewayPublicKey;
+  private static RSAPrivateKey gatewayPrivateKey;
 
   @BeforeClass
-  public static void setup() throws Exception, NoSuchAlgorithmException {
+  public static void setup() throws Exception {
     KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
     kpg.initialize(1024);
-    KeyPair KPair = kpg.generateKeyPair();
+    KeyPair keyPair = kpg.generateKeyPair();
 
-    publicKey = (RSAPublicKey) KPair.getPublic();
-    privateKey = (RSAPrivateKey) KPair.getPrivate();
+    gatewayPublicKey = (RSAPublicKey) keyPair.getPublic();
+    gatewayPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
   }
 
   @Test
-  public void testWhitelistMatching() throws Exception {
+  public void testWhitelistMatching() {
     String whitelist = "^https?://.*example.com:8080/.*$;" +
         "^https?://.*example.com/.*$;" +
         "^https?://.*example2.com:\\d{0,9}/.*$;" +
@@ -144,7 +146,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -154,7 +156,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -196,7 +198,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -206,7 +208,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -254,7 +256,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -264,7 +266,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -313,7 +315,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -323,7 +325,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -366,7 +368,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -376,7 +378,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -422,7 +424,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -432,7 +434,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -480,7 +482,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -490,7 +492,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -537,7 +539,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
 
     Principal principal = EasyMock.createNiceMock(Principal.class);
@@ -546,8 +548,8 @@ public class WebSSOResourceTest {
 
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
-
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -601,7 +603,7 @@ public class WebSSOResourceTest {
     EasyMock.expect(request.getParameter("originalUrl")).andReturn(
         URLEncoder.encode("http://disallowedhost:9080/service", StandardCharsets.UTF_8.name()));
     EasyMock.expect(request.getAttribute("targetServiceRole")).andReturn("KNOXSSO").anyTimes();
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
     EasyMock.expect(request.getServerName()).andReturn("localhost").anyTimes();
 
@@ -612,7 +614,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -636,7 +638,6 @@ public class WebSSOResourceTest {
     }
   }
 
-
   @Test
   public void testTopologyDefinedWhitelist() throws Exception {
     final String testServiceRole = "TEST";
@@ -659,7 +660,7 @@ public class WebSSOResourceTest {
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
-    EasyMock.expect(request.getParameterMap()).andReturn(Collections.<String,String[]>emptyMap());
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
     EasyMock.expect(request.getAttribute("targetServiceRole")).andReturn(testServiceRole).anyTimes();
     EasyMock.expect(request.getServerName()).andReturn("localhost").anyTimes();
@@ -671,7 +672,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -694,7 +695,7 @@ public class WebSSOResourceTest {
   }
 
   @Test
-  public void testExpectedKnoxSSOParams() throws Exception {
+  public void testExpectedKnoxSSOParams() {
 
     final HashMap<String, String[]> paramMap = new HashMap<>();
     paramMap.put("knoxtoken", new String[]{"eyJhbGciOiJSUzI1NiJ9.eyJzdWIiO"
@@ -723,7 +724,7 @@ public class WebSSOResourceTest {
     GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
     EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services).anyTimes();
 
-    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    JWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
     EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority).anyTimes();
 
     HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
@@ -759,7 +760,7 @@ public class WebSSOResourceTest {
   }
 
   @Test
-  public void testRedactToken() throws Exception {
+  public void testRedactToken() {
 
     final String token = "eyJhbGciOiJSUzI1NiJ9."
         + "eyJzdWIiOiJhZG1pbjEiLCJpc3MiOiJLTk9YU1NPIiwiZXhwIjoxNTMwNzkwMjkxfQ."
@@ -787,7 +788,85 @@ public class WebSSOResourceTest {
         "&originalUrl=http://www.local.com:8443/?gateway=one&knoxtoken", Log4jAuditor.maskTokenFromURL(fragment2));
     assertEquals("/gateway/knoxsso/api/v1/websso?test=value"+
         "&originalUrl=http://www.local.com:8443/?gateway=one&knoxtoken", Log4jAuditor.maskTokenFromURL(fragment3));
+  }
+
+  @Test
+  public void testCustomSigningKey() throws Exception {
+
+    String topologyName = "testCustomSigningKeyTopology";
+    String customSigningKeyName = "testSigningKeyName";
+    String customSigningKeyAlias = "testSigningKeyAlias";
+    String customSigningKeyPassphraseAlias = "testSigningKeyPassphraseAlias";
+    String customSigningKeyPassphrase = "testSigningKeyPassphrase";
+
+    KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+    kpg.initialize(1024);
+    KeyPair keyPair = kpg.generateKeyPair();
+    RSAPublicKey customPublicKey = (RSAPublicKey) keyPair.getPublic();
+    RSAPrivateKey customPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
+
+    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.name")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.secure.only")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.max.age")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.cookie.domain.suffix")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.redirect.whitelist.regex")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.token.audiences")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.token.ttl")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.enable.session")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knoxsso.signingkey.keystore.name"))
+        .andReturn(customSigningKeyName);
+    EasyMock.expect(context.getInitParameter("knoxsso.signingkey.keystore.alias"))
+        .andReturn(customSigningKeyAlias);
+    EasyMock.expect(context.getInitParameter("knoxsso.signingkey.keystore.passphrase.alias"))
+        .andReturn(customSigningKeyPassphraseAlias);
+    EasyMock.expect(context.getAttribute(GATEWAY_CLUSTER_ATTRIBUTE)).andReturn(topologyName);
+
+    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+    EasyMock.expect(request.getParameter("originalUrl")).andReturn("http://localhost:9080/service");
+    EasyMock.expect(request.getParameterMap()).andReturn(Collections.emptyMap());
+    EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
+
+    Principal principal = EasyMock.createNiceMock(Principal.class);
+    EasyMock.expect(principal.getName()).andReturn("alice").anyTimes();
+    EasyMock.expect(request.getUserPrincipal()).andReturn(principal).anyTimes();
+
+    GatewayServices services = EasyMock.createNiceMock(GatewayServices.class);
+    EasyMock.expect(context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE)).andReturn(services);
+
+    TestJWTokenAuthority authority = new TestJWTokenAuthority(gatewayPublicKey, gatewayPrivateKey);
+    authority.addCustomSigningKey(customSigningKeyName, customSigningKeyAlias, customSigningKeyPassphrase.toCharArray(),
+        customPrivateKey);
+    EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+    AliasService aliasService = EasyMock.createNiceMock(AliasService.class);
+    EasyMock.expect(aliasService.getPasswordFromAliasForCluster(topologyName, customSigningKeyPassphraseAlias))
+        .andReturn(customSigningKeyPassphrase.toCharArray());
+    EasyMock.expect(services.getService(GatewayServices.ALIAS_SERVICE)).andReturn(aliasService);
+
+    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+    ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+    CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+    EasyMock.replay(principal, services, context, request, aliasService);
+
+    WebSSOResource webSSOResponse = new WebSSOResource();
+    webSSOResponse.request = request;
+    webSSOResponse.response = responseWrapper;
+    webSSOResponse.context = context;
+    webSSOResponse.init();
 
+    // Issue a token
+    webSSOResponse.doGet();
+
+    // Check the cookie
+    Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
+    assertNotNull(cookie);
+
+    JWT parsedToken = new JWTToken(cookie.getValue());
+    assertEquals("alice", parsedToken.getSubject());
+    assertFalse(authority.verifyToken(parsedToken, gatewayPublicKey));
+    assertTrue(authority.verifyToken(parsedToken, customPublicKey));
   }
 
   /**
@@ -798,11 +877,7 @@ public class WebSSOResourceTest {
     private ServletOutputStream outputStream;
     private Map<String, Cookie> cookies = new HashMap<>();
 
-    public CookieResponseWrapper(HttpServletResponse response) {
-        super(response);
-    }
-
-    public CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) {
+    CookieResponseWrapper(HttpServletResponse response, ServletOutputStream outputStream) {
         super(response);
         this.outputStream = outputStream;
     }
@@ -818,22 +893,31 @@ public class WebSSOResourceTest {
         cookies.put(cookie.getName(), cookie);
     }
 
-    public Cookie getCookie(String name) {
+    Cookie getCookie(String name) {
         return cookies.get(name);
     }
-
   }
 
   private static class TestJWTokenAuthority implements JWTokenAuthority {
-
     private RSAPublicKey publicKey;
     private RSAPrivateKey privateKey;
+    private Map<String, Map<String,Object>> customSigningKeys = new HashMap<>();
 
-    public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
+    TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
       this.publicKey = publicKey;
       this.privateKey = privateKey;
     }
 
+    void addCustomSigningKey(String signingKeystoreName, String signingKeystoreAlias,
+                             char[] signingKeystorePassphrase, RSAPrivateKey customPrivateKey) {
+
+      Map<String, Object> signingKey = new HashMap<>();
+      signingKey.put("alias", signingKeystoreAlias);
+      signingKey.put("passphrase", signingKeystorePassphrase);
+      signingKey.put("privateKey", customPrivateKey);
+      customSigningKeys.put(signingKeystoreName, signingKey);
+    }
+
     @Override
     public JWT issueToken(Subject subject, String algorithm)
       throws TokenServiceException {
@@ -854,7 +938,7 @@ public class WebSSOResourceTest {
     }
 
     @Override
-    public boolean verifyToken(JWT token) throws TokenServiceException {
+    public boolean verifyToken(JWT token) {
       JWSVerifier verifier = new RSASSAVerifier(publicKey);
       return token.verify(verifier);
     }
@@ -872,7 +956,14 @@ public class WebSSOResourceTest {
 
     @Override
     public JWT issueToken(Principal p, List<String> audiences, String algorithm,
-                               long expires) throws TokenServiceException {
+                          long expires) throws TokenServiceException {
+      return issueToken(p, audiences, algorithm, expires, null, null, null);
+    }
+
+    @Override
+    public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires,
+                          String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase)
+        throws TokenServiceException {
       String[] claimArray = new String[4];
       claimArray[0] = "KNOXSSO";
       claimArray[1] = p.getName();
@@ -884,24 +975,36 @@ public class WebSSOResourceTest {
       }
 
       JWT token = new JWTToken(algorithm, claimArray, audiences);
+      RSAPrivateKey privateKey = getPrivateKey(signingKeystoreName, signingKeystoreAlias, signingKeystorePassphrase);
       JWSSigner signer = new RSASSASigner(privateKey);
       token.sign(signer);
 
       return token;
     }
 
+    private RSAPrivateKey getPrivateKey(String signingKeystoreName, String signingKeystoreAlias,
+                                        char[] signingKeystorePassphrase) throws TokenServiceException {
+      if(signingKeystoreName != null) {
+        Map<String, Object> signingKey = customSigningKeys.get(signingKeystoreName);
+        if(signingKey == null || !signingKey.get("alias").equals(signingKeystoreAlias) ||
+               !Arrays.equals((char[])signingKey.get("passphrase"), signingKeystorePassphrase)) {
+          throw new TokenServiceException("Invalid alias or passphrase");
+        }
+        return (RSAPrivateKey)signingKey.get("privateKey");
+      }
+      return privateKey;
+    }
+
     @Override
     public JWT issueToken(Principal p, String algorithm, long expiry)
         throws TokenServiceException {
-      return issueToken(p, Collections.<String>emptyList(), algorithm, expiry);
+      return issueToken(p, Collections.emptyList(), algorithm, expiry);
     }
 
     @Override
-    public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
+    public boolean verifyToken(JWT token, RSAPublicKey publicKey) {
       JWSVerifier verifier = new RSASSAVerifier(publicKey);
       return token.verify(verifier);
     }
-
   }
-
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index decef70..18a645f 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -24,7 +24,6 @@ import com.nimbusds.jose.crypto.RSASSAVerifier;
 import org.apache.knox.gateway.security.PrimaryPrincipal;
 import org.apache.knox.gateway.services.GatewayServices;
 import org.apache.knox.gateway.services.security.token.JWTokenAuthority;
-import org.apache.knox.gateway.services.security.token.TokenServiceException;
 import org.apache.knox.gateway.services.security.token.impl.JWT;
 import org.apache.knox.gateway.services.security.token.impl.JWTToken;
 import org.easymock.EasyMock;
@@ -38,7 +37,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.Response;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPrivateKey;
@@ -60,11 +58,11 @@ import static org.junit.Assert.assertTrue;
  */
 public class TokenServiceResourceTest {
 
-  protected static RSAPublicKey publicKey;
-  protected static RSAPrivateKey privateKey;
+  private static RSAPublicKey publicKey;
+  private static RSAPrivateKey privateKey;
 
   @BeforeClass
-  public static void setup() throws Exception, NoSuchAlgorithmException {
+  public static void setup() throws Exception {
     KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
     kpg.initialize(1024);
     KeyPair KPair = kpg.generateKeyPair();
@@ -74,25 +72,25 @@ public class TokenServiceResourceTest {
   }
 
   @Test
-  public void testTokenService() throws Exception {
+  public void testTokenService() {
     Assert.assertTrue(true);
   }
 
   @Test
-  public void testClientData() throws Exception {
+  public void testClientData() {
     TokenResource tr = new TokenResource();
 
     Map<String,Object> clientDataMap = new HashMap<>();
     tr.addClientDataToMap("cookie.name=hadoop-jwt,test=value".split(","), clientDataMap);
-    Assert.assertTrue(clientDataMap.size() == 2);
+    Assert.assertEquals(2, clientDataMap.size());
 
     clientDataMap = new HashMap<>();
     tr.addClientDataToMap("cookie.name=hadoop-jwt".split(","), clientDataMap);
-    Assert.assertTrue(clientDataMap.size() == 1);
+    Assert.assertEquals(1, clientDataMap.size());
 
     clientDataMap = new HashMap<>();
     tr.addClientDataToMap("".split(","), clientDataMap);
-    Assert.assertTrue(clientDataMap.size() == 0);
+    Assert.assertEquals(0, clientDataMap.size());
   }
 
   @Test
@@ -631,39 +629,36 @@ public class TokenServiceResourceTest {
     private RSAPublicKey publicKey;
     private RSAPrivateKey privateKey;
 
-    public TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
+    TestJWTokenAuthority(RSAPublicKey publicKey, RSAPrivateKey privateKey) {
       this.publicKey = publicKey;
       this.privateKey = privateKey;
     }
 
     @Override
-    public JWT issueToken(Subject subject, String algorithm)
-      throws TokenServiceException {
+    public JWT issueToken(Subject subject, String algorithm) {
       Principal p = (Principal) subject.getPrincipals().toArray()[0];
       return issueToken(p, algorithm);
     }
 
     @Override
-    public JWT issueToken(Principal p, String algorithm)
-      throws TokenServiceException {
+    public JWT issueToken(Principal p, String algorithm) {
       return issueToken(p, null, algorithm);
     }
 
     @Override
-    public JWT issueToken(Principal p, String audience, String algorithm)
-      throws TokenServiceException {
+    public JWT issueToken(Principal p, String audience, String algorithm) {
       return issueToken(p, audience, algorithm, -1);
     }
 
     @Override
-    public boolean verifyToken(JWT token) throws TokenServiceException {
+    public boolean verifyToken(JWT token) {
       JWSVerifier verifier = new RSASSAVerifier(publicKey);
       return token.verify(verifier);
     }
 
     @Override
     public JWT issueToken(Principal p, String audience, String algorithm,
-                               long expires) throws TokenServiceException {
+                               long expires) {
       ArrayList<String> audiences = null;
       if (audience != null) {
         audiences = new ArrayList<>();
@@ -673,8 +668,13 @@ public class TokenServiceResourceTest {
     }
 
     @Override
-    public JWT issueToken(Principal p, List<String> audiences, String algorithm,
-                               long expires) throws TokenServiceException {
+    public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires,
+                          String signingkeyName, String signingkeyAlias, char[] signingkeyPassphrase) {
+      return issueToken(p, audiences, algorithm, expires);
+    }
+
+    @Override
+    public JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires) {
       String[] claimArray = new String[4];
       claimArray[0] = "KNOXSSO";
       claimArray[1] = p.getName();
@@ -693,18 +693,14 @@ public class TokenServiceResourceTest {
     }
 
     @Override
-    public JWT issueToken(Principal p, String algorithm, long expiry)
-        throws TokenServiceException {
-      return issueToken(p, Collections.<String>emptyList(), algorithm, expiry);
+    public JWT issueToken(Principal p, String algorithm, long expiry) {
+      return issueToken(p, Collections.emptyList(), algorithm, expiry);
     }
 
     @Override
-    public boolean verifyToken(JWT token, RSAPublicKey publicKey) throws TokenServiceException {
+    public boolean verifyToken(JWT token, RSAPublicKey publicKey) {
       JWSVerifier verifier = new RSASSAVerifier(publicKey);
       return token.verify(verifier);
     }
-
   }
-
-
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
index 30dfe31..0467565 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/KeystoreService.java
@@ -22,33 +22,37 @@ import java.security.KeyStore;
 
 public interface KeystoreService {
 
-  public void createKeystoreForGateway() throws KeystoreServiceException;
+  void createKeystoreForGateway() throws KeystoreServiceException;
 
-  public void addSelfSignedCertForGateway(String alias, char[] passphrase) throws KeystoreServiceException;
+  void addSelfSignedCertForGateway(String alias, char[] passphrase) throws KeystoreServiceException;
   
   void addSelfSignedCertForGateway(String alias, char[] passphrase, String hostname) throws KeystoreServiceException;
 
-  public KeyStore getKeystoreForGateway() throws KeystoreServiceException;
+  KeyStore getKeystoreForGateway() throws KeystoreServiceException;
 
-  public KeyStore getSigningKeystore() throws KeystoreServiceException;
+  KeyStore getSigningKeystore() throws KeystoreServiceException;
 
-  public Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException;
+  KeyStore getSigningKeystore(String keystoreName) throws KeystoreServiceException;
 
-  public Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException;
+  Key getKeyForGateway(String alias, char[] passphrase) throws KeystoreServiceException;
 
-  public void createCredentialStoreForCluster(String clusterName) throws KeystoreServiceException;
-  
-  public boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException;
+  Key getSigningKey(String alias, char[] passphrase) throws KeystoreServiceException;
 
-  public boolean isKeystoreForGatewayAvailable() throws KeystoreServiceException;
-  
-  public KeyStore getCredentialStoreForCluster(String clusterName) throws KeystoreServiceException;
+  Key getSigningKey(String keystoreName, String alias, char[] passphrase) throws KeystoreServiceException;
+
+  void createCredentialStoreForCluster(String clusterName) throws KeystoreServiceException;
+
+  boolean isCredentialStoreForClusterAvailable(String clusterName) throws KeystoreServiceException;
+
+  boolean isKeystoreForGatewayAvailable() throws KeystoreServiceException;
+
+  KeyStore getCredentialStoreForCluster(String clusterName) throws KeystoreServiceException;
 
-  public void addCredentialForCluster(String clusterName, String alias, String key) throws KeystoreServiceException;
+  void addCredentialForCluster(String clusterName, String alias, String key) throws KeystoreServiceException;
 
-  public void removeCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException;
+  void removeCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException;
 
-  public char[] getCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException;
+  char[] getCredentialForCluster(String clusterName, String alias) throws KeystoreServiceException;
 
-  public String getKeystorePath();
+  String getKeystorePath();
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/2135bc22/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
index 6a5949b..2f71c2b 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/services/security/token/JWTokenAuthority.java
@@ -46,6 +46,10 @@ public interface JWTokenAuthority {
   JWT issueToken(Principal p, String audience, String algorithm,
       long expires) throws TokenServiceException;
 
-  JWT issueToken(Principal p, List<String> audience, String algorithm,
+  JWT issueToken(Principal p, List<String> audiences, String algorithm,
       long expires) throws TokenServiceException;
+
+  JWT issueToken(Principal p, List<String> audiences, String algorithm, long expires,
+                 String signingKeystoreName, String signingKeystoreAlias, char[] signingKeystorePassphrase)
+      throws TokenServiceException;
 }
\ No newline at end of file