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/06 19:04:21 UTC

[2/2] cxf git commit: Adding the ability to sign JWT tokens in the STS plus some NPE fixes

Adding the ability to sign JWT tokens in the STS plus some NPE fixes


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

Branch: refs/heads/master
Commit: 6113febb17a728296d08667ce207aa5ee40d7c4a
Parents: bb4ddb5
Author: Colm O hEigeartaigh <co...@apache.org>
Authored: Fri Nov 6 17:21:29 2015 +0000
Committer: Colm O hEigeartaigh <co...@apache.org>
Committed: Fri Nov 6 18:02:32 2015 +0000

----------------------------------------------------------------------
 .../org/apache/cxf/message/MessageUtils.java    |   8 +-
 .../rs/security/jose/common/JoseConstants.java  |   5 +
 .../jose/common/KeyManagementUtils.java         |  37 +++---
 .../cxf/rs/security/jose/jws/JwsUtils.java      |   2 +-
 .../token/provider/jwt/JWTTokenProvider.java    | 115 ++++++++++++++++---
 .../token/provider/JWTTokenProviderTest.java    |  40 +++++++
 6 files changed, 168 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/6113febb/core/src/main/java/org/apache/cxf/message/MessageUtils.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/cxf/message/MessageUtils.java b/core/src/main/java/org/apache/cxf/message/MessageUtils.java
index d8bbf0c..d964070 100644
--- a/core/src/main/java/org/apache/cxf/message/MessageUtils.java
+++ b/core/src/main/java/org/apache/cxf/message/MessageUtils.java
@@ -124,9 +124,11 @@ public final class MessageUtils {
     }
     
     public static boolean getContextualBoolean(Message m, String key, boolean defaultValue) {
-        Object o = m.getContextualProperty(key);
-        if (o != null) {
-            return PropertyUtils.isTrue(o);
+        if (m != null) {
+            Object o = m.getContextualProperty(key);
+            if (o != null) {
+                return PropertyUtils.isTrue(o);
+            }
         }
         return defaultValue;
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/6113febb/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java
index cc990b5..cf9f90a 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/JoseConstants.java
@@ -95,6 +95,11 @@ public final class JoseConstants {
     public static final String RSSEC_KEY_STORE_FILE = "rs.security.keystore.file";
     
     /**
+     * The KeyStore Object.
+     */
+    public static final String RSSEC_KEY_STORE= "rs.security.keystore";
+    
+    /**
      * A reference to a PrivateKeyPasswordProvider instance used to retrieve passwords to access keys.
      */
     public static final String RSSEC_KEY_PSWD_PROVIDER = "rs.security.key.password.provider";

http://git-wip-us.apache.org/repos/asf/cxf/blob/6113febb/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java
index a53e7a8..2ca6e80 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/common/KeyManagementUtils.java
@@ -139,8 +139,6 @@ public final class KeyManagementUtils {
     private static PrivateKey loadPrivateKey(KeyStore keyStore, 
                                             Message m,
                                             Properties props, 
-                                            Bus bus, 
-                                            PrivateKeyPasswordProvider provider,
                                             KeyOperation keyOper,
                                             String alias) {
         
@@ -149,8 +147,11 @@ public final class KeyManagementUtils {
         if (theAlias != null) {
             props.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, theAlias);
         }
-        char[] keyPswdChars = provider != null ? provider.getPassword(props) 
-            : keyPswd != null ? keyPswd.toCharArray() : null;    
+        char[] keyPswdChars = keyPswd != null ? keyPswd.toCharArray() : null;
+        if (keyPswdChars == null) {
+            PrivateKeyPasswordProvider provider = loadPasswordProvider(m, props, keyOper);
+            keyPswdChars = provider != null ? provider.getPassword(props) : null;
+        }
         return CryptoUtils.loadPrivateKey(keyStore, keyPswdChars, theAlias);
     }
     
@@ -174,7 +175,7 @@ public final class KeyManagementUtils {
                                   KeyOperation keyOper) {
         String kid = null;
         String altPropertyName = null;
-        if (keyOper != null) {
+        if (keyOper != null && m != null) {
             if (keyOper == KeyOperation.ENCRYPT || keyOper == KeyOperation.DECRYPT) {
                 altPropertyName = preferredPropertyName + ".jwe";
             } else if (keyOper == KeyOperation.SIGN || keyOper == KeyOperation.VERIFY) {
@@ -216,21 +217,25 @@ public final class KeyManagementUtils {
         KeyStore keyStore = loadPersistKeyStore(m, props);
         return loadPrivateKey(keyStore, m, props, keyOper, null);
     }
-    private static PrivateKey loadPrivateKey(KeyStore keyStore, Message m, Properties props, KeyOperation keyOper, 
-                                                String alias) {
-        Bus bus = m.getExchange().getBus();
-        PrivateKeyPasswordProvider cb = loadPasswordProvider(m, props, keyOper);
-        return loadPrivateKey(keyStore, m, props, bus, cb, keyOper, alias);
-    }
     public static KeyStore loadPersistKeyStore(Message m, Properties props) {
-        if (!props.containsKey(JoseConstants.RSSEC_KEY_STORE_FILE)) {
-            LOG.warning("No keystore file has been configured");
-            throw new JoseException("No keystore file has been configured");
+        KeyStore keyStore = null;
+        if (props.containsKey(JoseConstants.RSSEC_KEY_STORE)) {
+            keyStore = (KeyStore)props.get(JoseConstants.RSSEC_KEY_STORE);
+        }
+        
+        if (keyStore == null) {
+            if (!props.containsKey(JoseConstants.RSSEC_KEY_STORE_FILE)) {
+                LOG.warning("No keystore file has been configured");
+                throw new JoseException("No keystore file has been configured");
+            }
+            keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE));
         }
-        KeyStore keyStore = (KeyStore)m.getExchange().get(props.get(JoseConstants.RSSEC_KEY_STORE_FILE));
+        
         if (keyStore == null) {
             keyStore = loadKeyStore(props, m.getExchange().getBus());
-            m.getExchange().put((String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE), keyStore);
+            if (m != null) {
+                m.getExchange().put((String)props.get(JoseConstants.RSSEC_KEY_STORE_FILE), keyStore);
+            }
         }
         return keyStore;
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/6113febb/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java
index b9f0001..0bce50e 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsUtils.java
@@ -261,7 +261,7 @@ public final class JwsUtils {
         //TODO: validate JWS specific constraints
         return JoseUtils.validateCriticalHeaders(headers);
     }
-    private static JwsSignatureProvider loadSignatureProvider(Message m, 
+    public static JwsSignatureProvider loadSignatureProvider(Message m, 
                                                               Properties props,
                                                               JoseHeaders headers,
                                                               boolean ignoreNullProvider) {

http://git-wip-us.apache.org/repos/asf/cxf/blob/6113febb/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 b458281..573788b 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
@@ -19,6 +19,7 @@
 
 package org.apache.cxf.sts.token.provider.jwt;
 
+import java.security.KeyStore;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -26,17 +27,29 @@ import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import javax.security.auth.callback.CallbackHandler;
+
 import org.apache.cxf.common.logging.LogUtils;
+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.SignatureAlgorithm;
 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;
 import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
 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.request.TokenRequirements;
 import org.apache.cxf.sts.token.provider.TokenProvider;
 import org.apache.cxf.sts.token.provider.TokenProviderParameters;
 import org.apache.cxf.sts.token.provider.TokenProviderResponse;
 import org.apache.cxf.sts.token.realm.SAMLRealm;
 import org.apache.cxf.ws.security.sts.provider.STSException;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.Merlin;
+import org.apache.wss4j.common.ext.WSPasswordCallback;
 
 /**
  * A TokenProvider implementation that provides a JWT Token.
@@ -85,13 +98,6 @@ public class JWTTokenProvider implements TokenProvider {
         
         JwtClaims claims = jwtClaimsProvider.getJwtClaims(jwtClaimsProviderParameters);
         
-        /*
-        if (signToken) {
-            STSPropertiesMBean stsProperties = tokenParameters.getStsProperties();
-            signToken(assertion, samlRealm, stsProperties, tokenParameters.getKeyRequirements());
-        }
-        */
-        
         try {
             /*
             Document doc = DOMUtils.createDocument();
@@ -120,12 +126,8 @@ public class JWTTokenProvider implements TokenProvider {
             
             JwtToken token = new JwtToken(claims);
             
-            Properties signingProperties = new Properties();
-            signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, "none");
-            
-            JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token);
-            jws.setSignatureProperties(signingProperties);
-            String tokenData = jws.getSignedEncodedJws();
+            String tokenData = signToken(token, null, tokenParameters.getStsProperties(), 
+                      tokenParameters.getTokenRequirements());
             
             TokenProviderResponse response = new TokenProviderResponse();
             response.setToken(tokenData);
@@ -139,12 +141,6 @@ public class JWTTokenProvider implements TokenProvider {
                 response.setExpires(new Date(claims.getExpiryTime() * 1000L));
             }
             
-            /*response.setEntropy(entropyBytes);
-            if (keySize > 0) {
-                response.setKeySize(keySize);
-            }
-            response.setComputedKey(computedKey);
-            */
             LOG.fine("JWT Token successfully created");
             return response;
         } catch (Exception e) {
@@ -192,4 +188,85 @@ public class JWTTokenProvider implements TokenProvider {
         this.jwtClaimsProvider = jwtClaimsProvider;
     }
     
+    private String signToken(
+        JwtToken token, 
+        SAMLRealm samlRealm,
+        STSPropertiesMBean stsProperties,
+        TokenRequirements tokenRequirements
+    ) throws Exception {
+        
+        Properties signingProperties = new Properties();
+        
+        if (signToken) {
+            // Initialise signature objects with defaults of STSPropertiesMBean
+            Crypto signatureCrypto = stsProperties.getSignatureCrypto();
+            CallbackHandler callbackHandler = stsProperties.getCallbackHandler();
+            SignatureProperties signatureProperties = stsProperties.getSignatureProperties();
+            String alias = stsProperties.getSignatureUsername();
+
+            if (samlRealm != null) {
+                // If SignatureCrypto configured in realm then
+                // callbackhandler and alias of STSPropertiesMBean is ignored
+                if (samlRealm.getSignatureCrypto() != null) {
+                    LOG.fine("SAMLRealm signature keystore used");
+                    signatureCrypto = samlRealm.getSignatureCrypto();
+                    callbackHandler = samlRealm.getCallbackHandler();
+                    alias = samlRealm.getSignatureAlias();
+                }
+                // SignatureProperties can be defined independently of SignatureCrypto
+                if (samlRealm.getSignatureProperties() != null) {
+                    signatureProperties = samlRealm.getSignatureProperties();
+                }
+            }
+
+            // Get the signature algorithm to use - for now we don't allow the client to ask
+            // for a particular signature algorithm, as with SAML
+            String signatureAlgorithm = signatureProperties.getSignatureAlgorithm();
+            try {
+                SignatureAlgorithm.getAlgorithm(signatureAlgorithm);
+            } catch (IllegalArgumentException ex) {
+                signatureAlgorithm = SignatureAlgorithm.RS256.name();
+            }
+
+            // If alias not defined, get the default of the SignatureCrypto
+            if ((alias == null || "".equals(alias)) && (signatureCrypto != null)) {
+                alias = signatureCrypto.getDefaultX509Identifier();
+                if (LOG.isLoggable(Level.FINE)) {
+                    LOG.fine("Signature alias is null so using default alias: " + alias);
+                }
+            }
+            // Get the password
+            WSPasswordCallback[] cb = {new WSPasswordCallback(alias, WSPasswordCallback.SIGNATURE)};
+            callbackHandler.handle(cb);
+            String password = cb[0].getPassword();
+
+            signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, signatureAlgorithm);
+            signingProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, alias);
+            signingProperties.put(JoseConstants.RSSEC_KEY_PSWD, password);
+            
+            if (!(signatureCrypto instanceof Merlin)) {
+                throw new STSException("Can't get the keystore", STSException.REQUEST_FAILED);
+            }
+            KeyStore keystore = ((Merlin)signatureCrypto).getKeyStore();
+            signingProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore);
+            
+            JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token);
+            jws.setSignatureProperties(signingProperties);
+            
+            Message m = PhaseInterceptorChain.getCurrentMessage();
+            JwsSignatureProvider sigProvider = 
+                JwsUtils.loadSignatureProvider(m, signingProperties, token.getJwsHeaders(), false);
+            token.getJwsHeaders().setSignatureAlgorithm(sigProvider.getAlgorithm());
+            
+            return jws.signWith(sigProvider);
+        } else {
+            signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, "none");
+            
+            JwsJwtCompactProducer jws = new JwsJwtCompactProducer(token);
+            jws.setSignatureProperties(signingProperties);
+            return jws.getSignedEncodedJws();
+        }
+        
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/6113febb/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 19d41f2..aed28ef 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,11 +18,13 @@
  */
 package org.apache.cxf.sts.token.provider;
 
+import java.security.cert.X509Certificate;
 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.jwa.SignatureAlgorithm;
 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;
@@ -36,6 +38,7 @@ import org.apache.cxf.sts.token.provider.jwt.JWTTokenProvider;
 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.ext.WSSecurityException;
 import org.apache.wss4j.common.principal.CustomTokenPrincipal;
 import org.junit.Assert;
@@ -50,6 +53,7 @@ public class JWTTokenProviderTest extends org.junit.Assert {
     @org.junit.Test
     public void testCreateUnsignedJWT() throws Exception {
         TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(false);
         
         TokenProviderParameters providerParameters = createProviderParameters();
         
@@ -73,6 +77,42 @@ public class JWTTokenProviderTest extends org.junit.Assert {
                             jwt.getClaim(JwtConstants.CLAIM_EXPIRY));
     }
     
+    @org.junit.Test
+    public void testCreateSignedJWT() throws Exception {
+        TokenProvider jwtTokenProvider = new JWTTokenProvider();
+        ((JWTTokenProvider)jwtTokenProvider).setSignToken(true);
+        
+        TokenProviderParameters providerParameters = createProviderParameters();
+        
+        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 == 3);
+        
+        // Validate the token
+        JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token);
+        JwtToken jwt = jwtConsumer.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));
+        
+        // Verify Signature
+        Crypto crypto = providerParameters.getStsProperties().getSignatureCrypto();
+        CryptoType cryptoType = new CryptoType(CryptoType.TYPE.ALIAS);
+        cryptoType.setAlias(providerParameters.getStsProperties().getSignatureUsername());
+        X509Certificate[] certs = crypto.getX509Certificates(cryptoType);
+        assertNotNull(certs);
+        
+        assertTrue(jwtConsumer.verifySignatureWith(certs[0], SignatureAlgorithm.RS256));
+    }
+    
     private TokenProviderParameters createProviderParameters() throws WSSecurityException {
         TokenProviderParameters parameters = new TokenProviderParameters();