You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by co...@apache.org on 2015/11/11 18:18:11 UTC

[3/3] cxf git commit: Add the ability to encrypt JWT tokens from the STS

Add the ability to encrypt JWT tokens from the STS


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/4f92f75a
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/4f92f75a
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/4f92f75a

Branch: refs/heads/master
Commit: 4f92f75aa505a6223932f82812ae582296446ec9
Parents: fd34eab
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Wed Nov 11 17:17:46 2015 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Wed Nov 11 17:17:46 2015 +0000

----------------------------------------------------------------------
 .../token/provider/jwt/JWTTokenProvider.java    |  77 +++++++++++-
 .../token/provider/JWTTokenProviderTest.java    | 125 ++++++++++++++++++-
 2 files changed, 195 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/4f92f75a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java
index 5afffda..54a4c4e 100644
--- a/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java
+++ b/services/sts/sts-core/src/main/java/org/apache/cxf/sts/token/provider/jwt/JWTTokenProvider.java
@@ -31,10 +31,16 @@ import java.util.logging.Logger;
 import javax.security.auth.callback.CallbackHandler;
 
 import org.apache.cxf.common.logging.LogUtils;
+import org.apache.cxf.common.util.StringUtils;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.phase.PhaseInterceptorChain;
 import org.apache.cxf.rs.security.jose.common.JoseConstants;
+import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm;
+import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm;
 import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider;
+import org.apache.cxf.rs.security.jose.jwe.JweHeaders;
+import org.apache.cxf.rs.security.jose.jwe.JweUtils;
 import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
 import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
 import org.apache.cxf.rs.security.jose.jws.JwsUtils;
@@ -43,7 +49,9 @@ import org.apache.cxf.rs.security.jose.jwt.JwtToken;
 import org.apache.cxf.sts.STSPropertiesMBean;
 import org.apache.cxf.sts.SignatureProperties;
 import org.apache.cxf.sts.cache.CacheUtils;
+import org.apache.cxf.sts.request.KeyRequirements;
 import org.apache.cxf.sts.request.TokenRequirements;
+import org.apache.cxf.sts.service.EncryptionProperties;
 import org.apache.cxf.sts.token.provider.TokenProvider;
 import org.apache.cxf.sts.token.provider.TokenProviderParameters;
 import org.apache.cxf.sts.token.provider.TokenProviderResponse;
@@ -113,8 +121,13 @@ public class JWTTokenProvider implements TokenProvider {
         try {
             JwtToken token = new JwtToken(claims);
             
-            String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties(), 
-                      tokenParameters.getTokenRequirements());
+            String tokenData = signToken(token, jwtRealm, tokenParameters.getStsProperties());
+            if (tokenParameters.isEncryptToken()) {
+                tokenData = encryptToken(tokenData, token.getJweHeaders(), 
+                                         tokenParameters.getStsProperties(),
+                                         tokenParameters.getEncryptionProperties(),
+                                         tokenParameters.getKeyRequirements());
+            }
             
             TokenProviderResponse response = new TokenProviderResponse();
             response.setToken(tokenData);
@@ -194,8 +207,7 @@ public class JWTTokenProvider implements TokenProvider {
     private String signToken(
         JwtToken token, 
         RealmProperties jwtRealm,
-        STSPropertiesMBean stsProperties,
-        TokenRequirements tokenRequirements
+        STSPropertiesMBean stsProperties
     ) throws Exception {
         
         Properties signingProperties = new Properties();
@@ -277,5 +289,62 @@ public class JWTTokenProvider implements TokenProvider {
         }
         
     }
+    
+    private String encryptToken(
+        String token,
+        JweHeaders jweHeaders,
+        STSPropertiesMBean stsProperties,
+        EncryptionProperties encryptionProperties,
+        KeyRequirements keyRequirements
+    ) throws Exception {
+
+        Properties encProperties = new Properties();
+        
+        String name = encryptionProperties.getEncryptionName();
+        if (name == null) {
+            name = stsProperties.getEncryptionUsername();
+        }
+        if (name == null) {
+            LOG.fine("No encryption alias is configured");
+            return token;
+        }
+        encProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, name);
+        
+        // Get the encryption algorithm to use - for now we don't allow the client to ask
+        // for a particular encryption algorithm, as with SAML
+        String encryptionAlgorithm = encryptionProperties.getEncryptionAlgorithm();
+        try {
+            ContentAlgorithm.getAlgorithm(encryptionAlgorithm);
+        } catch (IllegalArgumentException ex) {
+            encryptionAlgorithm = ContentAlgorithm.A128GCM.name();
+        }
+        encProperties.put(JoseConstants.RSSEC_ENCRYPTION_CONTENT_ALGORITHM, encryptionAlgorithm);
+        
+        // Get the key-wrap algorithm to use - for now we don't allow the client to ask
+        // for a particular encryption algorithm, as with SAML
+        String keyWrapAlgorithm = encryptionProperties.getKeyWrapAlgorithm();
+        try {
+            KeyAlgorithm.getAlgorithm(keyWrapAlgorithm);
+        } catch (IllegalArgumentException ex) {
+            keyWrapAlgorithm = KeyAlgorithm.RSA_OAEP.name();
+        }
+        encProperties.put(JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM, keyWrapAlgorithm);
+
+        // Initialise encryption objects with defaults of STSPropertiesMBean
+        Crypto encryptionCrypto = stsProperties.getEncryptionCrypto();
+
+        if (!(encryptionCrypto instanceof Merlin)) {
+            throw new STSException("Can't get the keystore", STSException.REQUEST_FAILED);
+        }
+        KeyStore keystore = ((Merlin)encryptionCrypto).getKeyStore();
+        encProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore);
+
+        JweEncryptionProvider encProvider =
+            JweUtils.loadEncryptionProvider(encProperties, jweHeaders, false);
+        // token.getJwsHeaders().setSignatureAlgorithm(sigProvider.getAlgorithm());
+
+        return encProvider.encrypt(StringUtils.toBytesUTF8(token), null);
+
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/4f92f75a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java
----------------------------------------------------------------------
diff --git a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java
index 6273e0e..2af75c2 100644
--- a/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java
+++ b/services/sts/sts-core/src/test/java/org/apache/cxf/sts/token/provider/JWTTokenProviderTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.cxf.sts.token.provider;
 
+import java.security.KeyStore;
 import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Properties;
@@ -25,13 +26,19 @@ import java.util.Properties;
 import org.apache.cxf.jaxws.context.WebServiceContextImpl;
 import org.apache.cxf.jaxws.context.WrappedMessageContext;
 import org.apache.cxf.message.MessageImpl;
+import org.apache.cxf.rs.security.jose.common.JoseConstants;
 import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput;
+import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider;
+import org.apache.cxf.rs.security.jose.jwe.JweJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwe.JweUtils;
 import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
 import org.apache.cxf.rs.security.jose.jwt.JwtConstants;
 import org.apache.cxf.rs.security.jose.jwt.JwtToken;
 import org.apache.cxf.sts.StaticSTSProperties;
 import org.apache.cxf.sts.cache.DefaultInMemoryTokenStore;
 import org.apache.cxf.sts.common.PasswordCallbackHandler;
+import org.apache.cxf.sts.common.TestUtils;
 import org.apache.cxf.sts.request.KeyRequirements;
 import org.apache.cxf.sts.request.TokenRequirements;
 import org.apache.cxf.sts.service.EncryptionProperties;
@@ -41,6 +48,7 @@ import org.apache.cxf.ws.security.tokenstore.TokenStore;
 import org.apache.wss4j.common.crypto.Crypto;
 import org.apache.wss4j.common.crypto.CryptoFactory;
 import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.crypto.Merlin;
 import org.apache.wss4j.common.ext.WSSecurityException;
 import org.apache.wss4j.common.principal.CustomTokenPrincipal;
 import org.junit.Assert;
@@ -49,9 +57,13 @@ import org.junit.Assert;
  * Some unit tests for creating JWTTokens.
  */
 public class JWTTokenProviderTest extends org.junit.Assert {
-    
+    private static boolean unrestrictedPoliciesInstalled;
     private static TokenStore tokenStore = new DefaultInMemoryTokenStore();
     
+    static {
+        unrestrictedPoliciesInstalled = TestUtils.checkUnrestrictedPoliciesInstalled();
+    };
+    
     @org.junit.Test
     public void testCreateUnsignedJWT() throws Exception {
         TokenProvider jwtTokenProvider = new JWTTokenProvider();
@@ -147,6 +159,97 @@ public class JWTTokenProviderTest extends org.junit.Assert {
         Assert.assertNotNull(secToken);
     }
     
+    @org.junit.Test
+    public void testCreateUnsignedEncryptedJWT() throws Exception {
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(false);
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        providerParameters.setEncryptToken(true);
+        
+        assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE));
+        TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters);
+        assertTrue(providerResponse != null);
+        assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null);
+
+        String token = (String)providerResponse.getToken();
+        assertNotNull(token);
+        assertTrue(token.split("\\.").length == 5);
+        
+        if (unrestrictedPoliciesInstalled) {
+            // Validate the token
+            JweJwtCompactConsumer jwtConsumer = new JweJwtCompactConsumer(token);
+            Properties decProperties = new Properties();
+            Crypto decryptionCrypto = CryptoFactory.getInstance(getDecryptionProperties());
+            KeyStore keystore = ((Merlin)decryptionCrypto).getKeyStore();
+            decProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore);
+            decProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, "myservicekey");
+            decProperties.put(JoseConstants.RSSEC_KEY_PSWD, "skpass");
+            
+            JweDecryptionProvider decProvider =
+                JweUtils.loadDecryptionProvider(decProperties, jwtConsumer.getHeaders(), false);
+            
+            JweDecryptionOutput decOutput = decProvider.decrypt(token);
+            String decToken = decOutput.getContentText();
+            
+            JwsJwtCompactConsumer jwtJwsConsumer = new JwsJwtCompactConsumer(decToken);
+            JwtToken jwt = jwtJwsConsumer.getJwtToken();
+            
+            Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT));
+            Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID));
+            Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, 
+                                jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT));
+            Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, 
+                                jwt.getClaim(JwtConstants.CLAIM_EXPIRY));
+        }
+                            
+    }
+    
+    @org.junit.Test
+    public void testCreateSignedEncryptedJWT() throws Exception {
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        providerParameters.setEncryptToken(true);
+        
+        assertTrue(jwtTokenProvider.canHandleToken(JWTTokenProvider.JWT_TOKEN_TYPE));
+        TokenProviderResponse providerResponse = jwtTokenProvider.createToken(providerParameters);
+        assertTrue(providerResponse != null);
+        assertTrue(providerResponse.getToken() != null && providerResponse.getTokenId() != null);
+
+        String token = (String)providerResponse.getToken();
+        assertNotNull(token);
+        assertTrue(token.split("\\.").length == 5);
+        
+        if (unrestrictedPoliciesInstalled) {
+            // Validate the token
+            JweJwtCompactConsumer jwtConsumer = new JweJwtCompactConsumer(token);
+            Properties decProperties = new Properties();
+            Crypto decryptionCrypto = CryptoFactory.getInstance(getDecryptionProperties());
+            KeyStore keystore = ((Merlin)decryptionCrypto).getKeyStore();
+            decProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore);
+            decProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, "myservicekey");
+            decProperties.put(JoseConstants.RSSEC_KEY_PSWD, "skpass");
+            
+            JweDecryptionProvider decProvider =
+                JweUtils.loadDecryptionProvider(decProperties, jwtConsumer.getHeaders(), false);
+            
+            JweDecryptionOutput decOutput = decProvider.decrypt(token);
+            String decToken = decOutput.getContentText();
+            
+            JwsJwtCompactConsumer jwtJwsConsumer = new JwsJwtCompactConsumer(decToken);
+            JwtToken jwt = jwtJwsConsumer.getJwtToken();
+            
+            Assert.assertEquals("alice", jwt.getClaim(JwtConstants.CLAIM_SUBJECT));
+            Assert.assertEquals(providerResponse.getTokenId(), jwt.getClaim(JwtConstants.CLAIM_JWT_ID));
+            Assert.assertEquals(providerResponse.getCreated().getTime() / 1000L, 
+                                jwt.getClaim(JwtConstants.CLAIM_ISSUED_AT));
+            Assert.assertEquals(providerResponse.getExpires().getTime() / 1000L, 
+                                jwt.getClaim(JwtConstants.CLAIM_EXPIRY));
+        }
+                            
+    }
+    
     private TokenProviderParameters createProviderParameters() throws WSSecurityException {
         TokenProviderParameters parameters = new TokenProviderParameters();
         
@@ -178,6 +281,9 @@ public class JWTTokenProviderTest extends org.junit.Assert {
         parameters.setStsProperties(stsProperties);
         
         parameters.setEncryptionProperties(new EncryptionProperties());
+        stsProperties.setEncryptionCrypto(crypto);
+        stsProperties.setEncryptionUsername("myservicekey");
+        stsProperties.setCallbackHandler(new PasswordCallbackHandler());
         
         return parameters;
     }
@@ -188,11 +294,24 @@ public class JWTTokenProviderTest extends org.junit.Assert {
             "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin"
         );
         properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "stsspass");
-        properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks");
+        if (unrestrictedPoliciesInstalled) {
+            properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "stsstore.jks");
+        } else {
+            properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "restricted/stsstore.jks");
+        }
         
         return properties;
     }
     
-  
+    private Properties getDecryptionProperties() {
+        Properties properties = new Properties();
+        properties.put(
+            "org.apache.wss4j.crypto.provider", "org.apache.wss4j.common.crypto.Merlin"
+        );
+        properties.put("org.apache.wss4j.crypto.merlin.keystore.password", "sspass");
+        properties.put("org.apache.wss4j.crypto.merlin.keystore.file", "servicestore.jks");
+        
+        return properties;
+    }
     
 }