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 2017/11/17 17:01:59 UTC
[cxf] branch master updated: CXF-7568 - Support JWE PBES encryption
This is an automated email from the ASF dual-hosted git repository.
coheigea pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/master by this push:
new eeae043 CXF-7568 - Support JWE PBES encryption
eeae043 is described below
commit eeae04330ebd610be9cb46891e9f05851afad2a4
Author: Colm O hEigeartaigh <co...@apache.org>
AuthorDate: Fri Nov 17 17:01:19 2017 +0000
CXF-7568 - Support JWE PBES encryption
---
.../cxf/rs/security/jose/common/JoseConstants.java | 9 +-
.../security/jose/common/KeyManagementUtils.java | 4 +-
.../apache/cxf/rs/security/jose/jwe/JweUtils.java | 99 ++++++++++++++--------
.../security/jose/jwejws/JweJwsAlgorithmTest.java | 61 +++++++++++++
.../security/jose/jwejws/algorithms-server.xml | 19 +++++
5 files changed, 155 insertions(+), 37 deletions(-)
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 9fb4bdf..a6897a9 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
@@ -173,7 +173,7 @@ public final class JoseConstants {
* Include the X.509 certificate SHA-1 digest for signature in the "x5t" header.
*/
public static final String RSSEC_SIGNATURE_INCLUDE_CERT_SHA1 = "rs.security.signature.include.cert.sha1";
-
+
/**
* Include the X.509 certificate SHA-256 digest for signature in the "x5t#S256" header.
*/
@@ -241,12 +241,17 @@ public final class JoseConstants {
* Include the X.509 certificate SHA-1 digest for encryption in the "x5t" header.
*/
public static final String RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1 = "rs.security.encryption.include.cert.sha1";
-
+
/**
* Include the X.509 certificate SHA-256 digest for encryption in the "x5t#S256" header.
*/
public static final String RSSEC_ENCRYPTION_INCLUDE_CERT_SHA256 = "rs.security.encryption.include.cert.sha256";
+ /**
+ * The value to be used for the "p2c" (PBES2 count) Header Parameter. The default is 4096.
+ */
+ public static final String RSSEC_ENCRYPTION_PBES2_COUNT = "rs.security.encryption.pbes2.count";
+
//
// JWT specific configuration
//
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 3063971..5a7942b 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
@@ -439,7 +439,7 @@ public final class KeyManagementUtils {
if (props == null) {
if (required) {
LOG.warning("Properties resource is not identified");
- throw new JoseException();
+ throw new JoseException("Properties resource is not identified");
}
props = new Properties();
}
@@ -509,7 +509,7 @@ public final class KeyManagementUtils {
return null;
}
-
+
public static void setSha1DigestHeader(JoseHeaders headers, Message m, Properties props) {
String digest = loadDigestAndEncodeX509Certificate(m, props, MessageDigestUtils.ALGO_SHA_1);
if (digest != null) {
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java
index d45ada7..8447a59 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweUtils.java
@@ -44,11 +44,13 @@ import javax.crypto.SecretKey;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.message.Message;
+import org.apache.cxf.message.MessageUtils;
import org.apache.cxf.phase.PhaseInterceptorChain;
import org.apache.cxf.rs.security.jose.common.JoseConstants;
import org.apache.cxf.rs.security.jose.common.JoseHeaders;
import org.apache.cxf.rs.security.jose.common.JoseUtils;
import org.apache.cxf.rs.security.jose.common.KeyManagementUtils;
+import org.apache.cxf.rs.security.jose.common.PrivateKeyPasswordProvider;
import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils;
import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm;
import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm;
@@ -270,7 +272,7 @@ public final class JweUtils {
public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm) {
return getContentEncryptionProvider(algorithm, false);
}
- public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm,
+ public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm,
boolean generateCekOnce) {
if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) {
return new AesGcmContentEncryptionAlgorithm(algorithm, generateCekOnce);
@@ -367,9 +369,9 @@ public final class JweUtils {
Message m = PhaseInterceptorChain.getCurrentMessage();
return loadEncryptionProvider(props, m, headers);
}
-
+
public static JweEncryptionProvider loadEncryptionProvider(Properties props, Message m, JweHeaders headers) {
-
+
KeyEncryptionProvider keyEncryptionProvider = loadKeyEncryptionProvider(props, m, headers);
ContentAlgorithm contentAlgo = getContentEncryptionAlgorithm(m, props, null, ContentAlgorithm.A128GCM);
if (m != null) {
@@ -383,7 +385,7 @@ public final class JweUtils {
jwk.getAlgorithm() != null ? ContentAlgorithm.getAlgorithm(jwk.getAlgorithm()) : null,
contentAlgo);
ctEncryptionProvider = getContentEncryptionProvider(jwk, contentAlgo);
- }
+ }
}
String compression = props.getProperty(JoseConstants.RSSEC_ENCRYPTION_ZIP_ALGORITHM);
return createJweEncryptionProvider(keyEncryptionProvider,
@@ -392,21 +394,31 @@ public final class JweUtils {
compression,
headers);
}
-
+
public static KeyEncryptionProvider loadKeyEncryptionProvider(Properties props, Message m, JweHeaders headers) {
-
+
KeyEncryptionProvider keyEncryptionProvider = null;
KeyAlgorithm keyAlgo = getKeyEncryptionAlgorithm(m, props, null, null);
+
if (KeyAlgorithm.DIRECT == keyAlgo) {
- keyEncryptionProvider = new DirectKeyEncryptionAlgorithm();
+ keyEncryptionProvider = new DirectKeyEncryptionAlgorithm();
+ } else if (keyAlgo != null && AlgorithmUtils.PBES_HS_SET.contains(keyAlgo.getJwaName())) {
+ PrivateKeyPasswordProvider provider =
+ KeyManagementUtils.loadPasswordProvider(m, props, KeyOperation.ENCRYPT);
+ char[] password = provider != null ? provider.getPassword(props) : null;
+ if (password == null) {
+ throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE);
+ }
+ int pbes2Count = MessageUtils.getContextualInteger(m, JoseConstants.RSSEC_ENCRYPTION_PBES2_COUNT, 4096);
+ return new PbesHmacAesWrapKeyEncryptionAlgorithm(new String(password), pbes2Count, keyAlgo, false);
} else {
- boolean includeCert =
+ boolean includeCert =
JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT);
- boolean includeCertSha1 =
+ boolean includeCertSha1 =
JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA1);
- boolean includeCertSha256 =
+ boolean includeCertSha256 =
JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_CERT_SHA256);
- boolean includeKeyId =
+ boolean includeKeyId =
JoseUtils.checkBooleanProperty(headers, props, m, JoseConstants.RSSEC_ENCRYPTION_INCLUDE_KEY_ID);
if (JoseConstants.HEADER_JSON_WEB_KEY.equals(props.get(JoseConstants.RSSEC_KEY_STORE_TYPE))) {
@@ -416,9 +428,9 @@ public final class JweUtils {
KeyAlgorithm.getAlgorithm(jwk.getAlgorithm()),
getDefaultKeyAlgorithm(jwk));
keyEncryptionProvider = getKeyEncryptionProvider(jwk, keyAlgo);
-
- boolean includePublicKey =
- JoseUtils.checkBooleanProperty(headers, props, m,
+
+ boolean includePublicKey =
+ JoseUtils.checkBooleanProperty(headers, props, m,
JoseConstants.RSSEC_ENCRYPTION_INCLUDE_PUBLIC_KEY);
if (includeCert) {
JwkUtils.includeCertChain(jwk, headers, keyAlgo.getJwaName());
@@ -458,10 +470,10 @@ public final class JweUtils {
}
headers.setKeyEncryptionAlgorithm(keyEncryptionProvider.getAlgorithm());
return keyEncryptionProvider;
-
+
}
-
-
+
+
public static JweDecryptionProvider loadDecryptionProvider(boolean required) {
return loadDecryptionProvider(null, required);
}
@@ -494,7 +506,7 @@ public final class JweUtils {
keyAlgo = getDefaultPrivateKeyAlgorithm(privateKey);
}
contentAlgo = inHeaders.getContentEncryptionAlgorithm();
-
+
keyDecryptionProvider = getPrivateKeyDecryptionProvider(privateKey, keyAlgo);
} else if (inHeaders != null && inHeaders.getHeader(JoseConstants.HEADER_X509_THUMBPRINT) != null) {
X509Certificate foundCert =
@@ -543,6 +555,14 @@ public final class JweUtils {
getDefaultKeyAlgorithm(jwk));
keyDecryptionProvider = getKeyDecryptionProvider(jwk, keyAlgo);
}
+ } else if (keyAlgo != null && AlgorithmUtils.PBES_HS_SET.contains(keyAlgo.getJwaName())) {
+ PrivateKeyPasswordProvider provider =
+ KeyManagementUtils.loadPasswordProvider(m, props, KeyOperation.DECRYPT);
+ char[] password = provider != null ? provider.getPassword(props) : null;
+ if (password == null) {
+ throw new JweException(JweException.Error.KEY_DECRYPTION_FAILURE);
+ }
+ keyDecryptionProvider = new PbesHmacAesWrapKeyDecryptionAlgorithm(new String(password));
} else {
PrivateKey privateKey = KeyManagementUtils.loadPrivateKey(m, props, KeyOperation.DECRYPT);
if (keyAlgo == null) {
@@ -554,7 +574,7 @@ public final class JweUtils {
return createJweDecryptionProvider(keyDecryptionProvider, ctDecryptionKey,
contentAlgo);
}
-
+
public static JweEncryptionProvider createJweEncryptionProvider(PublicKey key,
KeyAlgorithm keyAlgo,
ContentAlgorithm contentEncryptionAlgo) {
@@ -611,7 +631,7 @@ public final class JweUtils {
contentEncryptionAlgo.getJwaName(), compression, null);
return createJweEncryptionProvider(keyEncryptionProvider, headers);
}
-
+
public static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider keyEncryptionProvider,
JweHeaders headers) {
return createJweEncryptionProvider(keyEncryptionProvider, headers, false);
@@ -662,7 +682,7 @@ public final class JweUtils {
JwkUtils.toECPublicKey(peerPublicKey),
partyUInfo, partyVInfo, algoName, algoKeyBitLen);
}
-
+
public static byte[] getECDHKey(ECPrivateKey privateKey,
ECPublicKey peerPublicKey,
byte[] partyUInfo,
@@ -670,17 +690,17 @@ public final class JweUtils {
String algoName,
int algoKeyBitLen) {
// Validate the peerPublicKey first
-
- // Credits:
+
+ // Credits:
// https://neilmadden.wordpress.com/2017/05/17/so-how-do-you-validate-nist-ecdh-public-keys/
- // https://blogs.adobe.com/security/2017/03/critical-vulnerability-uncovered-in-json-encryption.html
-
- // Step 1: Verify public key is not point at infinity.
+ // https://blogs.adobe.com/security/2017/03/critical-vulnerability-uncovered-in-json-encryption.html
+
+ // Step 1: Verify public key is not point at infinity.
if (ECPoint.POINT_INFINITY.equals(peerPublicKey.getW())) {
throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE);
}
EllipticCurve curve = peerPublicKey.getParams().getCurve();
-
+
final BigInteger x = peerPublicKey.getW().getAffineX();
final BigInteger y = peerPublicKey.getW().getAffineY();
final BigInteger p = ((ECFieldFp) curve.getField()).getP();
@@ -699,7 +719,7 @@ public final class JweUtils {
if (!ySquared.equals(xCubedPlusAXPlusB)) {
throw new JweException(JweException.Error.KEY_ENCRYPTION_FAILURE);
}
-
+
// Step 4: Verify that nQ = 0, where n is the order of the curve and Q is the public key.
// As per http://www.secg.org/sec1-v2.pdf section 3.2.2:
// "In Step 4, it may not be necessary to compute the point nQ. For example, if h = 1, then nQ = O is implied
@@ -710,7 +730,7 @@ public final class JweUtils {
}
// Finally calculate the derived key
-
+
byte[] keyZ = generateKeyZ(privateKey, peerPublicKey);
return calculateDerivedKey(keyZ, algoName, partyUInfo, partyVInfo, algoKeyBitLen);
}
@@ -800,7 +820,7 @@ public final class JweUtils {
}
return headers;
}
-
+
private static JweEncryptionProvider createJweEncryptionProvider(KeyEncryptionProvider keyEncryptionProvider,
ContentEncryptionProvider ctEncryptionProvider,
ContentAlgorithm contentEncryptionAlgo,
@@ -840,7 +860,7 @@ public final class JweUtils {
public static KeyAlgorithm getKeyEncryptionAlgorithm(Properties props, KeyAlgorithm defaultAlgo) {
return getKeyEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, defaultAlgo);
}
- public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, KeyAlgorithm defaultAlgo) {
+ public static KeyAlgorithm getKeyEncryptionAlgorithm(Message m, Properties props, KeyAlgorithm defaultAlgo) {
String algo = KeyManagementUtils.getKeyAlgorithm(m,
props,
JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM,
@@ -867,7 +887,7 @@ public final class JweUtils {
return algo;
}
public static ContentAlgorithm getContentEncryptionAlgorithm(Properties props) {
- return getContentEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, null);
+ return getContentEncryptionAlgorithm(PhaseInterceptorChain.getCurrentMessage(), props, null);
}
public static ContentAlgorithm getContentEncryptionAlgorithm(Properties props,
ContentAlgorithm defaultAlgo) {
@@ -901,6 +921,12 @@ public final class JweUtils {
}
public static Properties loadEncryptionInProperties(boolean required) {
Message m = PhaseInterceptorChain.getCurrentMessage();
+ String keyEncryptionAlgorithm =
+ (String)m.getContextualProperty(JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM);
+ if (keyEncryptionAlgorithm != null && AlgorithmUtils.PBES_HS_SET.contains(keyEncryptionAlgorithm)) {
+ // We don't need to load the keystore properties for the PBES case
+ required = false;
+ }
return KeyManagementUtils.loadStoreProperties(m, required,
JoseConstants.RSSEC_ENCRYPTION_IN_PROPS,
JoseConstants.RSSEC_ENCRYPTION_PROPS);
@@ -908,6 +934,13 @@ public final class JweUtils {
}
public static Properties loadEncryptionOutProperties(boolean required) {
Message m = PhaseInterceptorChain.getCurrentMessage();
+ String keyEncryptionAlgorithm =
+ (String)m.getContextualProperty(JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM);
+ if (keyEncryptionAlgorithm != null && AlgorithmUtils.PBES_HS_SET.contains(keyEncryptionAlgorithm)) {
+ // We don't need to load the keystore properties for the PBES case
+ required = false;
+ }
+
return KeyManagementUtils.loadStoreProperties(m, required,
JoseConstants.RSSEC_ENCRYPTION_OUT_PROPS,
JoseConstants.RSSEC_ENCRYPTION_PROPS);
@@ -936,7 +969,7 @@ public final class JweUtils {
JsonWebKey jwk = JwkUtils.fromPublicKey(key, props, JoseConstants.RSSEC_ENCRYPTION_KEY_ALGORITHM);
return new JsonWebKeys(jwk);
}
-
+
public static Properties loadJweProperties(Message m, String propLoc) {
try {
return JoseUtils.loadProperties(propLoc, m.getExchange().getBus());
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
index 2ca1dcb..d96b1db 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jose/jwejws/JweJwsAlgorithmTest.java
@@ -313,9 +313,70 @@ public class JweJwsAlgorithmTest extends AbstractBusClientServerTestBase {
assertNotEquals(response.getStatus(), 200);
}
+ @org.junit.Test
+ public void testEncryptionPBES() throws Exception {
+
+ URL busFile = JweJwsAlgorithmTest.class.getResource("client.xml");
+
+ List<Object> providers = new ArrayList<>();
+ providers.add(new JacksonJsonProvider());
+ providers.add(new JweWriterInterceptor());
+
+ String address = "http://localhost:" + PORT + "/jwepbes/bookstore/books";
+ WebClient client =
+ WebClient.create(address, providers, busFile.toString());
+ client.type("application/json").accept("application/json");
+
+ Map<String, Object> properties = new HashMap<>();
+ properties.put("rs.security.encryption.content.algorithm", "A128GCM");
+ properties.put("rs.security.encryption.key.algorithm", "PBES2-HS256+A128KW");
+ String password = "123456789123456789";
+ properties.put("rs.security.key.password.provider", new PrivateKeyPasswordProviderImpl(password));
+ WebClient.getConfig(client).getRequestContext().putAll(properties);
+
+ Response response = client.post(new Book("book", 123L));
+ assertEquals(response.getStatus(), 200);
+
+ Book returnedBook = response.readEntity(Book.class);
+ assertEquals(returnedBook.getName(), "book");
+ assertEquals(returnedBook.getId(), 123L);
+ }
+
+ @org.junit.Test
+ public void testEncryptionPBESDifferentCount() throws Exception {
+
+ URL busFile = JweJwsAlgorithmTest.class.getResource("client.xml");
+
+ List<Object> providers = new ArrayList<>();
+ providers.add(new JacksonJsonProvider());
+ providers.add(new JweWriterInterceptor());
+
+ String address = "http://localhost:" + PORT + "/jwepbes/bookstore/books";
+ WebClient client =
+ WebClient.create(address, providers, busFile.toString());
+ client.type("application/json").accept("application/json");
+
+ Map<String, Object> properties = new HashMap<>();
+ String password = "123456789123456789";
+ properties.put("rs.security.encryption.content.algorithm", "A128GCM");
+ properties.put("rs.security.encryption.key.algorithm", "PBES2-HS256+A128KW");
+ properties.put("rs.security.key.password.provider", new PrivateKeyPasswordProviderImpl(password));
+ properties.put("rs.security.encryption.pbes2.count", "1000");
+ WebClient.getConfig(client).getRequestContext().putAll(properties);
+
+ Response response = client.post(new Book("book", 123L));
+ assertEquals(response.getStatus(), 200);
+
+ Book returnedBook = response.readEntity(Book.class);
+ assertEquals(returnedBook.getName(), "book");
+ assertEquals(returnedBook.getId(), 123L);
+ }
+
+
//
// Signature tests
//
+
@org.junit.Test
public void testSignatureProperties() throws Exception {
diff --git a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
index c1cfebc..7d33348 100644
--- a/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
+++ b/systests/rs-security/src/test/resources/org/apache/cxf/systest/jaxrs/security/jose/jwejws/algorithms-server.xml
@@ -61,6 +61,25 @@ under the License.
</jaxrs:properties>
</jaxrs:server>
+ <bean id="pbesKeyProvider" class="org.apache.cxf.systest.jaxrs.security.jose.jwejws.PrivateKeyPasswordProviderImpl">
+ <constructor-arg>
+ <value>123456789123456789</value>
+ </constructor-arg>
+ </bean>
+
+ <jaxrs:server address="http://localhost:${testutil.ports.jaxrs-jwejws-algorithms}/jwepbes">
+ <jaxrs:serviceBeans>
+ <ref bean="serviceBean"/>
+ </jaxrs:serviceBeans>
+ <jaxrs:providers>
+ <ref bean="jweInFilter"/>
+ </jaxrs:providers>
+ <jaxrs:properties>
+ <entry key="rs.security.encryption.key.algorithm" value="PBES2-HS256+A128KW"/>
+ <entry key="rs.security.key.password.provider" value-ref="pbesKeyProvider"/>
+ </jaxrs:properties>
+ </jaxrs:server>
+
<bean id="jwsInFilter" class="org.apache.cxf.rs.security.jose.jaxrs.JwsContainerRequestFilter"/>
<jaxrs:server address="http://localhost:${testutil.ports.jaxrs-jwejws-algorithms}/jws">
--
To stop receiving notification emails like this one, please contact
['"commits@cxf.apache.org" <co...@cxf.apache.org>'].