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();