You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by mo...@apache.org on 2017/10/25 19:20:38 UTC

[02/17] knox git commit: KNOX-1067 - Support different signature algorithms for JWTs

KNOX-1067 - Support different signature algorithms for JWTs


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

Branch: refs/heads/KNOX-998-Package_Restructuring
Commit: 9c7aa7e1c7471f71c783681b68beea8e6f3fc2dc
Parents: 6acfa43
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Mon Oct 16 12:26:28 2017 +0100
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Mon Oct 16 15:41:08 2017 +0100

----------------------------------------------------------------------
 .../jwt/filter/AbstractJWTFilter.java           |  45 ++++++--
 .../jwt/filter/JWTFederationFilter.java         |   5 +-
 .../jwt/filter/SSOCookieFederationFilter.java   |   5 +-
 .../federation/AbstractJWTFilterTest.java       | 102 ++++++++++++++++---
 .../federation/SSOCookieProviderTest.java       |   5 +-
 .../impl/DefaultTokenAuthorityService.java      |  22 +++-
 .../impl/DefaultTokenAuthorityServiceTest.java  |  93 +++++++++++++++++
 .../gateway/service/knoxsso/WebSSOResource.java |  11 +-
 .../service/knoxsso/WebSSOResourceTest.java     |  69 +++++++++++--
 .../service/knoxtoken/TokenResource.java        |  11 +-
 .../knoxtoken/TokenServiceResourceTest.java     |  76 +++++++++++---
 .../services/security/token/impl/JWTToken.java  |   3 -
 .../security/token/impl/JWTTokenTest.java       |  45 +++++---
 13 files changed, 412 insertions(+), 80 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index 7f8e733..deb3d5b 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@ -22,6 +22,7 @@ import java.security.Principal;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashSet;
@@ -54,7 +55,9 @@ import org.apache.hadoop.gateway.security.PrimaryPrincipal;
 import org.apache.hadoop.gateway.services.GatewayServices;
 import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
 import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
-import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
+
+import com.nimbusds.jose.JWSHeader;
 
 /**
  *
@@ -67,6 +70,13 @@ public abstract class AbstractJWTFilter implements Filter {
   public static final String JWT_EXPECTED_ISSUER = "jwt.expected.issuer";
   public static final String JWT_DEFAULT_ISSUER = "KNOXSSO";
 
+  /**
+   * If specified, this configuration property refers to the signature algorithm which a received
+   * token must match. Otherwise, the default value "RS256" is used
+   */
+  public static final String JWT_EXPECTED_SIGALG = "jwt.expected.sigalg";
+  public static final String JWT_DEFAULT_SIGALG = "RS256";
+
   static JWTMessages log = MessagesFactory.get( JWTMessages.class );
   private static AuditService auditService = AuditServiceFactory.getAuditService();
   private static Auditor auditor = auditService.getAuditor(
@@ -77,6 +87,7 @@ public abstract class AbstractJWTFilter implements Filter {
   protected JWTokenAuthority authority;
   protected RSAPublicKey publicKey = null;
   private String expectedIssuer;
+  private String expectedSigAlg;
 
   public abstract void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
       throws IOException, ServletException;
@@ -99,11 +110,16 @@ public abstract class AbstractJWTFilter implements Filter {
     }
   }
 
-  protected void configureExpectedIssuer(FilterConfig filterConfig) {
-    expectedIssuer = filterConfig.getInitParameter(JWT_EXPECTED_ISSUER);;
+  protected void configureExpectedParameters(FilterConfig filterConfig) {
+    expectedIssuer = filterConfig.getInitParameter(JWT_EXPECTED_ISSUER);
     if (expectedIssuer == null) {
       expectedIssuer = JWT_DEFAULT_ISSUER;
     }
+
+    expectedSigAlg = filterConfig.getInitParameter(JWT_EXPECTED_SIGALG);
+    if (expectedSigAlg == null) {
+      expectedSigAlg = JWT_DEFAULT_SIGALG;
+    }
   }
 
   /**
@@ -111,7 +127,7 @@ public abstract class AbstractJWTFilter implements Filter {
    * @return
    */
   protected List<String> parseExpectedAudiences(String expectedAudiences) {
-    ArrayList<String> audList = null;
+    List<String> audList = null;
     // setup the list of valid audiences for token validation
     if (expectedAudiences != null) {
       // parse into the list
@@ -124,7 +140,7 @@ public abstract class AbstractJWTFilter implements Filter {
     return audList;
   }
 
-  protected boolean tokenIsStillValid(JWTToken jwtToken) {
+  protected boolean tokenIsStillValid(JWT jwtToken) {
     // if there is no expiration date then the lifecycle is tied entirely to
     // the cookie validity - otherwise ensure that the current time is before
     // the designated expiration time
@@ -141,7 +157,7 @@ public abstract class AbstractJWTFilter implements Filter {
    *          the JWT token where the allowed audiences will be found
    * @return true if an expected audience is present, otherwise false
    */
-  protected boolean validateAudiences(JWTToken jwtToken) {
+  protected boolean validateAudiences(JWT jwtToken) {
     boolean valid = false;
 
     String[] tokenAudienceList = jwtToken.getAudienceClaims();
@@ -202,7 +218,7 @@ public abstract class AbstractJWTFilter implements Filter {
     }
   }
 
-  protected Subject createSubjectFromToken(JWTToken token) {
+  protected Subject createSubjectFromToken(JWT token) {
     final String principal = token.getSubject();
 
     @SuppressWarnings("rawtypes")
@@ -223,7 +239,7 @@ public abstract class AbstractJWTFilter implements Filter {
   }
 
   protected boolean validateToken(HttpServletRequest request, HttpServletResponse response,
-      FilterChain chain, JWTToken token)
+      FilterChain chain, JWT token)
       throws IOException, ServletException {
     boolean verified = false;
     try {
@@ -237,6 +253,19 @@ public abstract class AbstractJWTFilter implements Filter {
       log.unableToVerifyToken(e);
     }
 
+    // Check received signature algorithm
+    if (verified) {
+      try {
+        String receivedSigAlg = JWSHeader.parse(token.getHeader()).getAlgorithm().getName();
+        if (!receivedSigAlg.equals(expectedSigAlg)) {
+          verified = false;
+        }
+      } catch (ParseException e) {
+        log.unableToVerifyToken(e);
+        verified = false;
+      }
+    }
+
     if (verified) {
       // confirm that issue matches intended target
       if (expectedIssuer.equals(token.getIssuer())) {

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
index 401e449..dcc52c0 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/JWTFederationFilter.java
@@ -17,6 +17,7 @@
  */
 package org.apache.hadoop.gateway.provider.federation.jwt.filter;
 
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
 import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
 import org.apache.hadoop.gateway.util.CertificateUtils;
 
@@ -63,7 +64,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
       publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM);
     }
 
-    configureExpectedIssuer(filterConfig);
+    configureExpectedParameters(filterConfig);
   }
 
   public void destroy() {
@@ -84,7 +85,7 @@ public class JWTFederationFilter extends AbstractJWTFilter {
 
     if (wireToken != null) {
       try {
-        JWTToken token = new JWTToken(wireToken);
+        JWT token = new JWTToken(wireToken);
         if (validateToken((HttpServletRequest)request, (HttpServletResponse)response, chain, token)) {
           Subject subject = createSubjectFromToken(token);
           continueWithEstablishedSecurityContext(subject, (HttpServletRequest)request, (HttpServletResponse)response, chain);

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
index cf14863..7e1c64a 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/hadoop/gateway/provider/federation/jwt/filter/SSOCookieFederationFilter.java
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 import org.apache.hadoop.gateway.provider.federation.jwt.JWTMessages;
 import org.apache.hadoop.gateway.security.PrimaryPrincipal;
+import org.apache.hadoop.gateway.services.security.token.impl.JWT;
 import org.apache.hadoop.gateway.services.security.token.impl.JWTToken;
 import org.apache.hadoop.gateway.util.CertificateUtils;
 
@@ -78,7 +79,7 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
       publicKey = CertificateUtils.parseRSAPublicKey(verificationPEM);
     }
 
-    configureExpectedIssuer(filterConfig);
+    configureExpectedParameters(filterConfig);
   }
 
   public void destroy() {
@@ -105,7 +106,7 @@ public class SSOCookieFederationFilter extends AbstractJWTFilter {
     }
     else {
       try {
-        JWTToken token = new JWTToken(wireToken);
+        JWT token = new JWTToken(wireToken);
         if (validateToken((HttpServletRequest)request, (HttpServletResponse)response, chain, token)) {
           Subject subject = createSubjectFromToken(token);
           continueWithEstablishedSecurityContext(subject, (HttpServletRequest)request, (HttpServletResponse)response, chain);

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
index bd34c04..b261081 100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/AbstractJWTFilterTest.java
@@ -116,7 +116,8 @@ public abstract class AbstractJWTFilterTest  {
       Properties props = getProperties();
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 5000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -147,7 +148,8 @@ public abstract class AbstractJWTFilterTest  {
       props.put(getAudienceProperty(), "bar");
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 5000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -180,7 +182,8 @@ public abstract class AbstractJWTFilterTest  {
 
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 5000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -209,7 +212,8 @@ public abstract class AbstractJWTFilterTest  {
       props.put(getAudienceProperty(), " foo, bar ");
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 5000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -245,7 +249,8 @@ public abstract class AbstractJWTFilterTest  {
       props.put(getVerificationPemProperty(), pem);
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 50000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -275,7 +280,8 @@ public abstract class AbstractJWTFilterTest  {
       Properties props = getProperties();
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() - 1000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() - 1000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -303,7 +309,7 @@ public abstract class AbstractJWTFilterTest  {
       Properties props = getProperties();
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", null, privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", null, privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -333,7 +339,8 @@ public abstract class AbstractJWTFilterTest  {
       Properties props = getProperties();
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                             new Date(new Date().getTime() + 5000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setGarbledTokenOnRequest(request, jwt);
@@ -367,8 +374,8 @@ public abstract class AbstractJWTFilterTest  {
       Properties props = getProperties();
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("bob", new Date(new Date().getTime() + 5000),
-                             (RSAPrivateKey)kp.getPrivate(), props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "bob",
+                             new Date(new Date().getTime() + 5000), (RSAPrivateKey)kp.getPrivate());
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -409,7 +416,8 @@ public abstract class AbstractJWTFilterTest  {
       props.put(getVerificationPemProperty(), failingPem);
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 50000), privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 50000), privateKey);
 
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
       setTokenOnRequest(request, jwt);
@@ -489,6 +497,67 @@ public abstract class AbstractJWTFilterTest  {
     }
   }
 
+  @Test
+  public void testRS512SignatureAlgorithm() throws Exception {
+    try {
+      Properties props = getProperties();
+      props.put(AbstractJWTFilter.JWT_EXPECTED_SIGALG, "RS512");
+      handler.init(new TestFilterConfig(props));
+
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", new Date(new Date().getTime() + 5000),
+                             privateKey, JWSAlgorithm.RS512.getName());
+
+      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+      setTokenOnRequest(request, jwt);
+
+      EasyMock.expect(request.getRequestURL()).andReturn(
+          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getQueryString()).andReturn(null);
+      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
+          SERVICE_URL);
+      EasyMock.replay(request);
+
+      TestFilterChain chain = new TestFilterChain();
+      handler.doFilter(request, response, chain);
+      Assert.assertTrue("doFilterCalled should not be false.", chain.doFilterCalled );
+      Set<PrimaryPrincipal> principals = chain.subject.getPrincipals(PrimaryPrincipal.class);
+      Assert.assertTrue("No PrimaryPrincipal", !principals.isEmpty());
+      Assert.assertEquals("Not the expected principal", "alice", ((Principal)principals.toArray()[0]).getName());
+    } catch (ServletException se) {
+      fail("Should NOT have thrown a ServletException.");
+    }
+  }
+
+  @Test
+  public void testInvalidSignatureAlgorithm() throws Exception {
+    try {
+      Properties props = getProperties();
+      handler.init(new TestFilterConfig(props));
+
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice", new Date(new Date().getTime() + 5000),
+                             privateKey, JWSAlgorithm.RS384.getName());
+
+      HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+      setTokenOnRequest(request, jwt);
+
+      EasyMock.expect(request.getRequestURL()).andReturn(
+          new StringBuffer(SERVICE_URL)).anyTimes();
+      EasyMock.expect(request.getQueryString()).andReturn(null);
+      HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+      EasyMock.expect(response.encodeRedirectURL(SERVICE_URL)).andReturn(
+          SERVICE_URL);
+      EasyMock.replay(request);
+
+      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);
+    } catch (ServletException se) {
+      fail("Should NOT have thrown a ServletException.");
+    }
+  }
+
   protected Properties getProperties() {
     Properties props = new Properties();
     props.setProperty(
@@ -497,12 +566,13 @@ public abstract class AbstractJWTFilterTest  {
     return props;
   }
 
-  protected SignedJWT getJWT(String sub, Date expires, RSAPrivateKey privateKey,
-      Properties props) throws Exception {
-    return getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, sub, expires, privateKey);
+  protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey)
+      throws Exception {
+    return getJWT(issuer, sub, expires, privateKey, JWSAlgorithm.RS256.getName());
   }
 
-  protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey)
+  protected SignedJWT getJWT(String issuer, String sub, Date expires, RSAPrivateKey privateKey,
+                             String signatureAlgorithm)
       throws Exception {
     List<String> aud = new ArrayList<String>();
     aud.add("bar");
@@ -515,7 +585,7 @@ public abstract class AbstractJWTFilterTest  {
     .claim("scope", "openid")
     .build();
 
-    JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
+    JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.parse(signatureAlgorithm)).build();
 
     SignedJWT signedJWT = new SignedJWT(header, claims);
     JWSSigner signer = new RSASSASigner(privateKey);

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java
----------------------------------------------------------------------
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java
index 768755b..38e7381 100644
--- a/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/hadoop/gateway/provider/federation/SSOCookieProviderTest.java
@@ -30,6 +30,7 @@ import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.hadoop.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
 import org.apache.hadoop.gateway.provider.federation.jwt.filter.SSOCookieFederationFilter;
 import org.apache.hadoop.gateway.security.PrimaryPrincipal;
 import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
@@ -70,8 +71,8 @@ public class SSOCookieProviderTest extends AbstractJWTFilterTest {
       props.put("sso.cookie.name", "jowt");
       handler.init(new TestFilterConfig(props));
 
-      SignedJWT jwt = getJWT("alice", new Date(new Date().getTime() + 5000),
-          privateKey, props);
+      SignedJWT jwt = getJWT(AbstractJWTFilter.JWT_DEFAULT_ISSUER, "alice",
+                             new Date(new Date().getTime() + 5000), privateKey);
 
       Cookie cookie = new Cookie("jowt", jwt.serialize());
       HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
index 33b86bd..0c33cdf 100644
--- a/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
+++ b/gateway-server/src/main/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityService.java
@@ -23,8 +23,10 @@ import java.security.PublicKey;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
 import java.util.Map;
+import java.util.Set;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.HashSet;
 
 import javax.security.auth.Subject;
 
@@ -48,10 +50,22 @@ import com.nimbusds.jose.crypto.RSASSAVerifier;
 public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
 
   private static final String SIGNING_KEY_PASSPHRASE = "signing.key.passphrase";
+  private static final Set<String> SUPPORTED_SIG_ALGS = new HashSet<>();
   private AliasService as = null;
   private KeystoreService ks = null;
   String signingKeyAlias = null;
 
+  static {
+      // Only standard RSA signature algorithms are accepted
+      // https://tools.ietf.org/html/rfc7518
+      SUPPORTED_SIG_ALGS.add("RS256");
+      SUPPORTED_SIG_ALGS.add("RS384");
+      SUPPORTED_SIG_ALGS.add("RS512");
+      SUPPORTED_SIG_ALGS.add("PS256");
+      SUPPORTED_SIG_ALGS.add("PS384");
+      SUPPORTED_SIG_ALGS.add("PS512");
+  }
+
   public void setKeystoreService(KeystoreService ks) {
     this.ks = ks;
   }
@@ -96,7 +110,7 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
   @Override
   public JWT issueToken(Principal p, String audience, String algorithm, long expires)
       throws TokenServiceException {
-    ArrayList<String> audiences = null;
+    List<String> audiences = null;
     if (audience != null) {
       audiences = new ArrayList<String>();
       audiences.add(audience);
@@ -118,9 +132,9 @@ public class DefaultTokenAuthorityService implements JWTokenAuthority, Service {
       claimArray[3] = String.valueOf(expires);
     }
 
-    JWTToken token = null;
-    if ("RS256".equals(algorithm)) {
-      token = new JWTToken("RS256", claimArray, audiences);
+    JWT token = null;
+    if (SUPPORTED_SIG_ALGS.contains(algorithm)) {
+      token = new JWTToken(algorithm, claimArray, audiences);
       RSAPrivateKey key;
       char[] passphrase = null;
       try {

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
----------------------------------------------------------------------
diff --git a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
index 7cc9971..48616c0 100644
--- a/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
+++ b/gateway-server/src/test/java/org/apache/hadoop/gateway/services/token/impl/DefaultTokenAuthorityServiceTest.java
@@ -27,6 +27,7 @@ import org.apache.hadoop.gateway.services.security.KeystoreService;
 import org.apache.hadoop.gateway.services.security.MasterService;
 import org.apache.hadoop.gateway.services.security.impl.DefaultKeystoreService;
 import org.apache.hadoop.gateway.services.security.token.JWTokenAuthority;
+import org.apache.hadoop.gateway.services.security.token.TokenServiceException;
 import org.apache.hadoop.gateway.services.security.token.impl.JWT;
 import org.easymock.EasyMock;
 import org.junit.Test;
@@ -74,6 +75,8 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
     JWT token = ta.issueToken(principal, "RS256");
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
+
+    assertTrue(ta.verifyToken(token));
   }
 
   @Test
@@ -115,6 +118,8 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
     assertEquals("https://login.example.com", token.getAudience());
+
+    assertTrue(ta.verifyToken(token));
   }
 
   @Test
@@ -155,6 +160,94 @@ public class DefaultTokenAuthorityServiceTest extends org.junit.Assert {
     JWT token = ta.issueToken(principal, null, "RS256");
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
+
+    assertTrue(ta.verifyToken(token));
+  }
+
+  @Test
+  public void testTokenCreationSignatureAlgorithm() throws Exception {
+
+    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);
+
+    KeystoreService ks = new DefaultKeystoreService();
+    ((DefaultKeystoreService)ks).setMasterService(ms);
+
+    ((DefaultKeystoreService)ks).init(config, new HashMap<String, String>());
+
+    JWTokenAuthority ta = new DefaultTokenAuthorityService();
+    ((DefaultTokenAuthorityService)ta).setAliasService(as);
+    ((DefaultTokenAuthorityService)ta).setKeystoreService(ks);
+
+    ((DefaultTokenAuthorityService)ta).init(config, new HashMap<String, String>());
+
+    JWT token = ta.issueToken(principal, "RS512");
+    assertEquals("KNOXSSO", token.getIssuer());
+    assertEquals("john.doe@example.com", token.getSubject());
+    assertTrue(token.getHeader().contains("RS512"));
+
+    assertTrue(ta.verifyToken(token));
+  }
+
+  @Test
+  public void testTokenCreationBadSignatureAlgorithm() throws Exception {
+
+    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);
+
+    KeystoreService ks = new DefaultKeystoreService();
+    ((DefaultKeystoreService)ks).setMasterService(ms);
+
+    ((DefaultKeystoreService)ks).init(config, new HashMap<String, String>());
+
+    JWTokenAuthority ta = new DefaultTokenAuthorityService();
+    ((DefaultTokenAuthorityService)ta).setAliasService(as);
+    ((DefaultTokenAuthorityService)ta).setKeystoreService(ks);
+
+    ((DefaultTokenAuthorityService)ta).init(config, new HashMap<String, String>());
+
+    try {
+      ta.issueToken(principal, "none");
+      fail("Failure expected on a bad signature algorithm");
+    } catch (TokenServiceException ex) {
+        // expected
+    }
   }
 
 }

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
index 70228d3..36aa075 100644
--- a/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
+++ b/gateway-service-knoxsso/src/main/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResource.java
@@ -60,6 +60,7 @@ public class WebSSOResource {
   private static final String SSO_COOKIE_DOMAIN_SUFFIX_PARAM = "knoxsso.cookie.domain.suffix";
   private static final String SSO_COOKIE_TOKEN_TTL_PARAM = "knoxsso.token.ttl";
   private static final String SSO_COOKIE_TOKEN_AUDIENCES_PARAM = "knoxsso.token.audiences";
+  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_ENABLE_SESSION_PARAM = "knoxsso.enable.session";
   private static final String ORIGINAL_URL_REQUEST_PARAM = "originalUrl";
@@ -77,6 +78,7 @@ public class WebSSOResource {
   private String domainSuffix = null;
   private List<String> targetAudiences = new ArrayList<>();
   private boolean enableSession = false;
+  private String signatureAlgorithm = "RS256";
 
   @Context
   HttpServletRequest request;
@@ -143,6 +145,11 @@ public class WebSSOResource {
 
     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;
+    }
   }
 
   @GET
@@ -185,9 +192,9 @@ public class WebSSOResource {
     try {
       JWT token = null;
       if (targetAudiences.isEmpty()) {
-        token = ts.issueToken(p, "RS256", getExpiry());
+        token = ts.issueToken(p, signatureAlgorithm, getExpiry());
       } else {
-        token = ts.issueToken(p, targetAudiences, "RS256", getExpiry());
+        token = ts.issueToken(p, targetAudiences, signatureAlgorithm, getExpiry());
       }
 
       // Coverity CID 1327959

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
index 568f0fe..516f9ae 100644
--- a/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
+++ b/gateway-service-knoxsso/src/test/java/org/apache/hadoop/gateway/service/knoxsso/WebSSOResourceTest.java
@@ -166,7 +166,7 @@ public class WebSSOResourceTest {
     Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
     assertNotNull(cookie);
 
-    JWTToken parsedToken = new JWTToken(cookie.getValue());
+    JWT parsedToken = new JWTToken(cookie.getValue());
     assertEquals("alice", parsedToken.getSubject());
     assertTrue(authority.verifyToken(parsedToken));
   }
@@ -218,7 +218,7 @@ public class WebSSOResourceTest {
     Cookie cookie = responseWrapper.getCookie("hadoop-jwt");
     assertNotNull(cookie);
 
-    JWTToken parsedToken = new JWTToken(cookie.getValue());
+    JWT parsedToken = new JWTToken(cookie.getValue());
     assertEquals("alice", parsedToken.getSubject());
     assertTrue(authority.verifyToken(parsedToken));
 
@@ -287,6 +287,60 @@ public class WebSSOResourceTest {
     assertTrue(audiences.contains("recipient2"));
   }
 
+  @Test
+  public void testSignatureAlgorithm() throws Exception {
+
+    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.token.sigalg")).andReturn("RS512");
+
+    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.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);
+
+    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+    ServletOutputStream outputStream = EasyMock.createNiceMock(ServletOutputStream.class);
+    CookieResponseWrapper responseWrapper = new CookieResponseWrapper(response, outputStream);
+
+    EasyMock.replay(principal, services, context, request);
+
+    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());
+    assertTrue(authority.verifyToken(parsedToken));
+    assertTrue(parsedToken.getHeader().contains("RS512"));
+  }
+
   /**
    * A wrapper for HttpServletResponseWrapper to store the cookies
    */
@@ -380,14 +434,9 @@ public class WebSSOResourceTest {
         claimArray[3] = String.valueOf(expires);
       }
 
-      JWTToken token = null;
-      if ("RS256".equals(algorithm)) {
-        token = new JWTToken("RS256", claimArray, audiences);
-        JWSSigner signer = new RSASSASigner(privateKey);
-        token.sign(signer);
-      } else {
-        throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
-      }
+      JWT token = new JWTToken(algorithm, claimArray, audiences);
+      JWSSigner signer = new RSASSASigner(privateKey);
+      token.sign(signer);
 
       return token;
     }

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
index df8288a..afa6a3a 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/hadoop/gateway/service/knoxtoken/TokenResource.java
@@ -57,6 +57,7 @@ public class TokenResource {
   private static final String TOKEN_CLIENT_DATA = "knox.token.client.data";
   private static final String TOKEN_CLIENT_CERT_REQUIRED = "knox.token.client.cert.required";
   private static final String TOKEN_ALLOWED_PRINCIPALS = "knox.token.allowed.principals";
+  private static final String TOKEN_SIG_ALG = "knox.token.sigalg";
   static final String RESOURCE_PATH = "knoxtoken/api/v1/token";
   private static TokenServiceMessages log = MessagesFactory.get( TokenServiceMessages.class );
   private long tokenTTL = 30000l;
@@ -65,6 +66,7 @@ public class TokenResource {
   private Map<String,Object> tokenClientDataMap = null;
   private ArrayList<String> allowedDNs = new ArrayList<>();
   private boolean clientCertRequired = false;
+  private String signatureAlgorithm = "RS256";
 
   @Context
   HttpServletRequest request;
@@ -115,6 +117,11 @@ public class TokenResource {
       String[] tokenClientData = clientData.split(",");
       addClientDataToMap(tokenClientData, tokenClientDataMap);
     }
+
+    String sigAlg = context.getInitParameter(TOKEN_SIG_ALG);
+    if (sigAlg != null) {
+      signatureAlgorithm = sigAlg;
+    }
   }
 
   @GET
@@ -159,9 +166,9 @@ public class TokenResource {
     try {
       JWT token = null;
       if (targetAudiences.isEmpty()) {
-        token = ts.issueToken(p, "RS256", expires);
+        token = ts.issueToken(p, signatureAlgorithm, expires);
       } else {
-        token = ts.issueToken(p, targetAudiences, "RS256", expires);
+        token = ts.issueToken(p, targetAudiences, signatureAlgorithm, expires);
       }
 
       if (token != null) {

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
----------------------------------------------------------------------
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 0046bd9..80f359d 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/hadoop/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -101,11 +101,8 @@ public class TokenServiceResourceTest {
 
   @Test
   public void testGetToken() throws Exception {
-    TokenResource tr = new TokenResource();
 
     ServletContext context = EasyMock.createNiceMock(ServletContext.class);
-    //tr.context = context;
-    // tr.init();
 
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
@@ -126,6 +123,7 @@ public class TokenServiceResourceTest {
 
     EasyMock.replay(principal, services, context, request, response);
 
+    TokenResource tr = new TokenResource();
     tr.request = request;
     tr.response = response;
 
@@ -142,7 +140,7 @@ public class TokenServiceResourceTest {
     assertNotNull(expiry);
 
     // Verify the token
-    JWTToken parsedToken = new JWTToken(accessToken);
+    JWT parsedToken = new JWTToken(accessToken);
     assertEquals("alice", parsedToken.getSubject());
     assertTrue(authority.verifyToken(parsedToken));
   }
@@ -194,7 +192,7 @@ public class TokenServiceResourceTest {
     assertNotNull(expiry);
 
     // Verify the token
-    JWTToken parsedToken = new JWTToken(accessToken);
+    JWT parsedToken = new JWTToken(accessToken);
     assertEquals("alice", parsedToken.getSubject());
     assertTrue(authority.verifyToken(parsedToken));
 
@@ -252,7 +250,7 @@ public class TokenServiceResourceTest {
     assertNotNull(expiry);
 
     // Verify the token
-    JWTToken parsedToken = new JWTToken(accessToken);
+    JWT parsedToken = new JWTToken(accessToken);
     assertEquals("alice", parsedToken.getSubject());
     assertTrue(authority.verifyToken(parsedToken));
 
@@ -315,7 +313,7 @@ public class TokenServiceResourceTest {
     assertNotNull(expiry);
 
     // Verify the token
-    JWTToken parsedToken = new JWTToken(accessToken);
+    JWT parsedToken = new JWTToken(accessToken);
     assertEquals("alice", parsedToken.getSubject());
     assertTrue(authority.verifyToken(parsedToken));
   }
@@ -405,6 +403,59 @@ public class TokenServiceResourceTest {
     assertEquals(403, retResponse.getStatus());
   }
 
+  @Test
+  public void testSignatureAlgorithm() throws Exception {
+    ServletContext context = EasyMock.createNiceMock(ServletContext.class);
+    EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
+    EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
+    EasyMock.expect(context.getInitParameter("knox.token.sigalg")).andReturn("RS512");
+
+    HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
+    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);
+
+    JWTokenAuthority authority = new TestJWTokenAuthority(publicKey, privateKey);
+    EasyMock.expect(services.getService(GatewayServices.TOKEN_SERVICE)).andReturn(authority);
+
+    StringWriter writer = new StringWriter();
+    PrintWriter printWriter = new PrintWriter(writer);
+    HttpServletResponse response = EasyMock.createNiceMock(HttpServletResponse.class);
+    EasyMock.expect(response.getWriter()).andReturn(printWriter);
+
+    EasyMock.replay(principal, services, context, request, response);
+
+    TokenResource tr = new TokenResource();
+    tr.request = request;
+    tr.response = response;
+    tr.context = context;
+    tr.init();
+
+    // Issue a token
+    Response retResponse = tr.doGet();
+
+    assertEquals(200, retResponse.getStatus());
+
+    // Parse the response
+    String retString = writer.toString();
+    String accessToken = getTagValue(retString, "access_token");
+    assertNotNull(accessToken);
+    String expiry = getTagValue(retString, "expires_in");
+    assertNotNull(expiry);
+
+    // Verify the token
+    JWT parsedToken = new JWTToken(accessToken);
+    assertEquals("alice", parsedToken.getSubject());
+    assertTrue(authority.verifyToken(parsedToken));
+    assertTrue(parsedToken.getHeader().contains("RS512"));
+  }
+
   private String getTagValue(String token, String tagName) {
     String searchString = tagName + "\":";
     String value = token.substring(token.indexOf(searchString) + searchString.length());
@@ -479,14 +530,9 @@ public class TokenServiceResourceTest {
         claimArray[3] = String.valueOf(expires);
       }
 
-      JWTToken token = null;
-      if ("RS256".equals(algorithm)) {
-        token = new JWTToken("RS256", claimArray, audiences);
-        JWSSigner signer = new RSASSASigner(privateKey);
-        token.sign(signer);
-      } else {
-        throw new TokenServiceException("Cannot issue token - Unsupported algorithm");
-      }
+      JWT token = new JWTToken(algorithm, claimArray, audiences);
+      JWSSigner signer = new RSASSASigner(privateKey);
+      token.sign(signer);
 
       return token;
     }

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
index 567c156..be2a331 100644
--- a/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
+++ b/gateway-spi/src/main/java/org/apache/hadoop/gateway/services/security/token/impl/JWTToken.java
@@ -17,14 +17,11 @@
    */
 package org.apache.hadoop.gateway.services.security.token.impl;
 
-import java.io.UnsupportedEncodingException;
 import java.text.ParseException;
 import java.util.Date;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
 
 import com.nimbusds.jose.JOSEException;

http://git-wip-us.apache.org/repos/asf/knox/blob/9c7aa7e1/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
----------------------------------------------------------------------
diff --git a/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java b/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
index 6372f0c..d971eca 100644
--- a/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
+++ b/gateway-spi/src/test/java/org/apache/hadoop/gateway/services/security/token/impl/JWTTokenTest.java
@@ -22,9 +22,12 @@ import java.security.KeyPairGenerator;
 import java.security.NoSuchAlgorithmException;
 import java.security.interfaces.RSAPrivateKey;
 import java.security.interfaces.RSAPublicKey;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
 
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import com.nimbusds.jose.JWSAlgorithm;
@@ -37,10 +40,11 @@ public class JWTTokenTest extends org.junit.Assert {
   private static final String JWT_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE0MTY5MjkxMDksImp0aSI6ImFhN2Y4ZDBhOTVjIiwic2NvcGVzIjpbInJlcG8iLCJwdWJsaWNfcmVwbyJdfQ.XCEwpBGvOLma4TCoh36FU7XhUbcskygS81HE1uHLf0E";
   private static final String HEADER = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
 
-  private RSAPublicKey publicKey;
-  private RSAPrivateKey privateKey;
+  private static RSAPublicKey publicKey;
+  private static RSAPrivateKey privateKey;
 
-  public JWTTokenTest() throws Exception, NoSuchAlgorithmException {
+  @BeforeClass
+  public static void setup() throws Exception, NoSuchAlgorithmException {
     KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
     kpg.initialize(2048);
 
@@ -64,7 +68,7 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = "https://login.example.com";
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    JWTToken token = new JWTToken("RS256", claims);
+    JWT token = new JWTToken("RS256", claims);
 
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
@@ -78,10 +82,10 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = null;
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    ArrayList<String> audiences = new ArrayList<String>();
+    List<String> audiences = new ArrayList<String>();
     audiences.add("https://login.example.com");
 
-    JWTToken token = new JWTToken("RS256", claims, audiences);
+    JWT token = new JWTToken("RS256", claims, audiences);
 
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
@@ -96,11 +100,11 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = null;
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    ArrayList<String> audiences = new ArrayList<String>();
+    List<String> audiences = new ArrayList<String>();
     audiences.add("https://login.example.com");
     audiences.add("KNOXSSO");
 
-    JWTToken token = new JWTToken("RS256", claims, audiences);
+    JWT token = new JWTToken("RS256", claims, audiences);
 
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
@@ -134,9 +138,9 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = null;
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    ArrayList<String> audiences = null;
+    List<String> audiences = null;
 
-    JWTToken token = new JWTToken("RS256", claims, audiences);
+    JWT token = new JWTToken("RS256", claims, audiences);
 
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
@@ -166,8 +170,7 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = "https://login.example.com";
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    JWTToken token = new JWTToken("RS256", claims);
-
+    JWT token = new JWTToken("RS256", claims);
 
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
@@ -190,7 +193,7 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = "https://login.example.com";
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    JWTToken token = new JWTToken(JWSAlgorithm.RS512.getName(), claims);
+    JWT token = new JWTToken(JWSAlgorithm.RS512.getName(), claims);
 
     assertEquals("KNOXSSO", token.getIssuer());
     assertEquals("john.doe@example.com", token.getSubject());
@@ -214,10 +217,24 @@ public class JWTTokenTest extends org.junit.Assert {
     claims[1] = "john.doe@example.com";
     claims[2] = "https://login.example.com";
     claims[3] = Long.toString( ( System.currentTimeMillis()/1000 ) + 300);
-    JWTToken token = new JWTToken("RS256", claims);
+    JWT token = new JWTToken("RS256", claims);
 
     assertNotNull(token.getExpires());
     assertNotNull(token.getExpiresDate());
     assertEquals(token.getExpiresDate(), new Date(Long.valueOf(token.getExpires())));
   }
+
+  @Test
+  public void testUnsignedToken() throws Exception {
+      String unsignedToken = "eyJhbGciOiJub25lIn0.eyJzdWIiOiJhbGljZSIsImp0aSI6ImY2YmNj"
+          + "MDVjLWI4MTktNGM0Mi1iMGMyLWJlYmY1MDE4YWFiZiJ9.";
+
+      try {
+          new JWTToken(unsignedToken);
+          fail("Failure expected on an unsigned token");
+      } catch (ParseException ex) {
+          // expected
+      }
+  }
+
 }