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 2019/11/28 12:19:28 UTC
[cxf] branch master updated: [CXF-8162] JWE with multiple
recipients does not work for AES CBC Encryption (#604)
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 d3a968f [CXF-8162] JWE with multiple recipients does not work for AES CBC Encryption (#604)
d3a968f is described below
commit d3a968fb0ea586ac185483a169b1c5ab29dc5615
Author: frelibert <fr...@yahoo.com>
AuthorDate: Thu Nov 28 13:19:17 2019 +0100
[CXF-8162] JWE with multiple recipients does not work for AES CBC Encryption (#604)
---
.../jose/jwe/AesCbcContentEncryptionAlgorithm.java | 82 +++++++++++++
.../security/jose/jwe/AesCbcHmacJweEncryption.java | 96 ++++++---------
.../apache/cxf/rs/security/jose/jwe/JweUtils.java | 3 +
.../rs/security/jose/jwe/JweJsonProducerTest.java | 130 ++++++++++++++++++++-
4 files changed, 247 insertions(+), 64 deletions(-)
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcContentEncryptionAlgorithm.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcContentEncryptionAlgorithm.java
new file mode 100644
index 0000000..c87907a
--- /dev/null
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcContentEncryptionAlgorithm.java
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.rs.security.jose.jwe;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.spec.IvParameterSpec;
+
+import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils;
+import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm;
+
+public class AesCbcContentEncryptionAlgorithm extends AbstractContentEncryptionAlgorithm {
+
+ static final Map<String, String> AES_HMAC_MAP;
+ static final Map<String, Integer> AES_CEK_SIZE_MAP;
+
+ static {
+ AES_HMAC_MAP = new HashMap<>();
+ AES_HMAC_MAP.put(ContentAlgorithm.A128CBC_HS256.getJwaName(), AlgorithmUtils.HMAC_SHA_256_JAVA);
+ AES_HMAC_MAP.put(ContentAlgorithm.A192CBC_HS384.getJwaName(), AlgorithmUtils.HMAC_SHA_384_JAVA);
+ AES_HMAC_MAP.put(ContentAlgorithm.A256CBC_HS512.getJwaName(), AlgorithmUtils.HMAC_SHA_512_JAVA);
+
+ AES_CEK_SIZE_MAP = new HashMap<>();
+ AES_CEK_SIZE_MAP.put(ContentAlgorithm.A128CBC_HS256.getJwaName(), 32);
+ AES_CEK_SIZE_MAP.put(ContentAlgorithm.A192CBC_HS384.getJwaName(), 48);
+ AES_CEK_SIZE_MAP.put(ContentAlgorithm.A256CBC_HS512.getJwaName(), 64);
+ }
+
+ public AesCbcContentEncryptionAlgorithm(ContentAlgorithm algo, boolean generateCekOnce) {
+ super(validateCekAlgorithm(algo), generateCekOnce);
+ }
+
+ public AesCbcContentEncryptionAlgorithm(byte[] cek, byte[] iv, ContentAlgorithm algo) {
+ super(cek, iv, validateCekAlgorithm(algo));
+ }
+
+ @Override
+ public AlgorithmParameterSpec getAlgorithmParameterSpec(byte[] theIv) {
+ return new IvParameterSpec(theIv);
+ }
+
+ @Override
+ public byte[] getAdditionalAuthenticationData(String headersJson, byte[] aad) {
+ return null;
+ }
+
+ @Override
+ protected int getContentEncryptionKeySize(JweHeaders headers) {
+ return getFullCekKeySize(getAlgorithm().getJwaName()) * 8;
+ }
+
+ protected static int getFullCekKeySize(String algoJwt) {
+ return AES_CEK_SIZE_MAP.get(algoJwt);
+ }
+
+ protected static ContentAlgorithm validateCekAlgorithm(ContentAlgorithm cekAlgo) {
+ if (!AlgorithmUtils.isAesCbcHmac(cekAlgo.getJwaName())) {
+ LOG.warning("Invalid content encryption algorithm");
+ throw new JweException(JweException.Error.INVALID_CONTENT_ALGORITHM);
+ }
+ return cekAlgo;
+ }
+
+}
diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcHmacJweEncryption.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcHmacJweEncryption.java
index 12170c1..b995245 100644
--- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcHmacJweEncryption.java
+++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AesCbcHmacJweEncryption.java
@@ -19,66 +19,61 @@
package org.apache.cxf.rs.security.jose.jwe;
import java.nio.ByteBuffer;
-import java.security.spec.AlgorithmParameterSpec;
-import java.util.HashMap;
-import java.util.Map;
import javax.crypto.Mac;
-import javax.crypto.spec.IvParameterSpec;
-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.jwe.JweException.Error;
import org.apache.cxf.rt.security.crypto.HmacUtils;
public class AesCbcHmacJweEncryption extends JweEncryption {
- private static final Map<String, String> AES_HMAC_MAP;
- private static final Map<String, Integer> AES_CEK_SIZE_MAP;
- static {
- AES_HMAC_MAP = new HashMap<>();
- AES_HMAC_MAP.put(ContentAlgorithm.A128CBC_HS256.getJwaName(), AlgorithmUtils.HMAC_SHA_256_JAVA);
- AES_HMAC_MAP.put(ContentAlgorithm.A192CBC_HS384.getJwaName(), AlgorithmUtils.HMAC_SHA_384_JAVA);
- AES_HMAC_MAP.put(ContentAlgorithm.A256CBC_HS512.getJwaName(), AlgorithmUtils.HMAC_SHA_512_JAVA);
-
- AES_CEK_SIZE_MAP = new HashMap<>();
- AES_CEK_SIZE_MAP.put(ContentAlgorithm.A128CBC_HS256.getJwaName(), 32);
- AES_CEK_SIZE_MAP.put(ContentAlgorithm.A192CBC_HS384.getJwaName(), 48);
- AES_CEK_SIZE_MAP.put(ContentAlgorithm.A256CBC_HS512.getJwaName(), 64);
- }
+
public AesCbcHmacJweEncryption(ContentAlgorithm cekAlgoJwt,
KeyEncryptionProvider keyEncryptionAlgorithm) {
this(cekAlgoJwt, keyEncryptionAlgorithm, false);
}
+
public AesCbcHmacJweEncryption(ContentAlgorithm cekAlgoJwt,
KeyEncryptionProvider keyEncryptionAlgorithm,
boolean generateCekOnce) {
- super(keyEncryptionAlgorithm,
- new AesCbcContentEncryptionAlgorithm(validateCekAlgorithm(cekAlgoJwt),
- generateCekOnce));
+ super(keyEncryptionAlgorithm, new AesCbcContentEncryptionAlgorithm(cekAlgoJwt, generateCekOnce));
}
+
public AesCbcHmacJweEncryption(ContentAlgorithm cekAlgoJwt, byte[] cek,
byte[] iv, KeyEncryptionProvider keyEncryptionAlgorithm) {
- super(keyEncryptionAlgorithm,
- new AesCbcContentEncryptionAlgorithm(cek, iv,
- validateCekAlgorithm(cekAlgoJwt)));
-
+ super(keyEncryptionAlgorithm, new AesCbcContentEncryptionAlgorithm(cek, iv, cekAlgoJwt));
}
+
+ public AesCbcHmacJweEncryption(KeyEncryptionProvider keyEncryptionAlgorithm,
+ AesCbcContentEncryptionAlgorithm contentEncryptionAlgorithm) {
+ super(keyEncryptionAlgorithm, contentEncryptionAlgorithm);
+ }
+
@Override
protected byte[] getActualCek(byte[] theCek, String algoJwt) {
return doGetActualCek(theCek, algoJwt);
}
+
protected static byte[] doGetActualCek(byte[] theCek, String algoJwt) {
- int size = getFullCekKeySize(algoJwt) / 2;
- byte[] actualCek = new byte[size];
- System.arraycopy(theCek, size, actualCek, 0, size);
- return actualCek;
+ // K
+ int inputKeySize = AesCbcContentEncryptionAlgorithm.getFullCekKeySize(algoJwt);
+ if (theCek.length != inputKeySize) {
+ LOG.warning("Length input key [" + theCek.length + "] invalid for algorithm " + algoJwt
+ + " [" + inputKeySize + "]");
+ throw new JweException(Error.INVALID_CONTENT_KEY);
+ }
+ // MAC_KEY, ENC_KEY
+ int secondaryKeySize = inputKeySize / 2;
+ // Extract secondary key ENC_KEY from the input key K
+ byte[] encKey = new byte[secondaryKeySize];
+ System.arraycopy(theCek, secondaryKeySize, encKey, 0, secondaryKeySize);
+ return encKey;
}
- protected static int getFullCekKeySize(String algoJwt) {
- return AES_CEK_SIZE_MAP.get(algoJwt);
- }
protected byte[] getActualCipher(byte[] cipher) {
return cipher;
}
+
protected byte[] getAuthenticationTag(JweEncryptionInternal state, byte[] cipher) {
final MacState macState = getInitializedMacState(state);
macState.mac.update(cipher);
@@ -94,21 +89,23 @@ public class AesCbcHmacJweEncryption extends JweEncryption {
System.arraycopy(sig, 0, authTag, 0, authTagLen);
return authTag;
}
+
private MacState getInitializedMacState(final JweEncryptionInternal state) {
return getInitializedMacState(state.secretKey, state.theIv, state.aad,
state.theHeaders, state.protectedHeadersJson);
}
+
protected static MacState getInitializedMacState(byte[] secretKey,
byte[] theIv,
byte[] extraAad,
JweHeaders theHeaders,
String protectedHeadersJson) {
String algoJwt = theHeaders.getContentEncryptionAlgorithm().getJwaName();
- int size = getFullCekKeySize(algoJwt) / 2;
+ int size = AesCbcContentEncryptionAlgorithm.getFullCekKeySize(algoJwt) / 2;
byte[] macKey = new byte[size];
System.arraycopy(secretKey, 0, macKey, 0, size);
- String hmacAlgoJava = AES_HMAC_MAP.get(algoJwt);
+ String hmacAlgoJava = AesCbcContentEncryptionAlgorithm.AES_HMAC_MAP.get(algoJwt);
Mac mac = HmacUtils.getInitializedMac(macKey, hmacAlgoJava, null);
byte[] aad = JweUtils.getAdditionalAuthenticationData(protectedHeadersJson, extraAad);
@@ -140,42 +137,15 @@ public class AesCbcHmacJweEncryption extends JweEncryption {
}
};
}
+
@Override
protected byte[] getEncryptedContentEncryptionKey(JweHeaders headers, byte[] theCek) {
return getKeyEncryptionAlgo().getEncryptedContentEncryptionKey(headers, theCek);
}
- private static class AesCbcContentEncryptionAlgorithm extends AbstractContentEncryptionAlgorithm {
- AesCbcContentEncryptionAlgorithm(ContentAlgorithm algo, boolean generateCekOnce) {
- super(algo, generateCekOnce);
- }
- AesCbcContentEncryptionAlgorithm(byte[] cek, byte[] iv, ContentAlgorithm algo) {
- super(cek, iv, algo);
- }
- @Override
- public AlgorithmParameterSpec getAlgorithmParameterSpec(byte[] theIv) {
- return new IvParameterSpec(theIv);
- }
- @Override
- public byte[] getAdditionalAuthenticationData(String headersJson, byte[] aad) {
- return null;
- }
- @Override
- protected int getContentEncryptionKeySize(JweHeaders headers) {
- return getFullCekKeySize(getAlgorithm().getJwaName()) * 8;
- }
- }
-
protected static class MacState {
protected Mac mac;
private byte[] al;
}
-
- private static ContentAlgorithm validateCekAlgorithm(ContentAlgorithm cekAlgo) {
- if (!AlgorithmUtils.isAesCbcHmac(cekAlgo.getJwaName())) {
- LOG.warning("Invalid content encryption algorithm");
- throw new JweException(JweException.Error.INVALID_CONTENT_ALGORITHM);
- }
- return cekAlgo;
- }
+
}
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 b2b91e0..32696b0 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
@@ -267,6 +267,9 @@ public final class JweUtils {
if (AlgorithmUtils.isAesGcm(algorithm.getJwaName())) {
return new AesGcmContentEncryptionAlgorithm(key, null, algorithm);
}
+ if (AlgorithmUtils.isAesCbcHmac(algorithm.getJwaName())) {
+ return new AesCbcContentEncryptionAlgorithm(key, null, algorithm);
+ }
return null;
}
public static ContentEncryptionProvider getContentEncryptionProvider(ContentAlgorithm algorithm) {
diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java
index 6d438e2..c1a2f35 100644
--- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java
+++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java
@@ -19,6 +19,7 @@
package org.apache.cxf.rs.security.jose.jwe;
import java.security.Security;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -32,8 +33,10 @@ import org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm;
import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm;
import org.apache.cxf.rt.security.crypto.CryptoUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -43,6 +46,7 @@ public class JweJsonProducerTest {
static final byte[] WRAPPER_BYTES1 = {91, 96, 105, 38, 99, 108, 110, 8, -93, 50, -15, 62, 0, -115, 73, -39};
static final byte[] WRAPPER_BYTES2 = {-39, 96, 105, 38, 99, 108, 110, 8, -93, 50, -15, 62, 0, -115, 73, 91};
static final byte[] CEK_BYTES = {-43, 123, 77, 115, 40, 49, -4, -9, -48, -74, 62, 59, 60, 102, -22, -100};
+ static final String CEK_32_HEX = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
static final String SINGLE_RECIPIENT_OUTPUT =
"{"
+ "\"protected\":\"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0\","
@@ -148,6 +152,30 @@ public class JweJsonProducerTest {
+ "\"ciphertext\":\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\","
+ "\"tag\":\"Mz-VPPyU4RlcuYv1IwIvzw\""
+ "}";
+ static final String MULTIPLE_RECIPIENTS_A128CBCHS256_JSON_OUTPUT =
+ "{"
+ + "\"protected\":\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\","
+ + "\"unprotected\":"
+ + "{"
+ + "\"jku\":\"https://server.example.com/keys.jwks\","
+ + "\"alg\":\"A128KW\""
+ + "},"
+ + "\"recipients\":["
+ + "{"
+ + "\"header\":{\"kid\":\"key1\"},"
+ + "\"encrypted_key\":\"NrhRVDNccP-gul5SB393C8DlbEtgGdvSgYgH5QRUXJYt-r8_wzef1g\""
+ + "},{"
+ + "\"header\":{\"kid\":\"key2\"},"
+ + "\"encrypted_key\":\"6a_nnEYO45qB_Vp6N2QbFQ7Cv1uecbiE\""
+ + "}],"
+ + "\"aad\":\"WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5ke"
+ + "WJ1Y2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0Iiwi"
+ + "VEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d\","
+ + "\"iv\":\"AxY8DCtDaGlsbGljb3RoZQ\","
+ + "\"ciphertext\":\"pwitNt2DsK1zM72z5CxGClCr8ANYuIYZgCnohazsPyZhvR8atJnnlkR3fdSpyXYJcNx2LP-gcm3oNWiaAk0H2A\","
+ + "\"tag\":\"nNSN9kYhubsQ9QELBmZIhA\""
+ + "}";
+
@BeforeClass
public static void registerBouncyCastleIfNeeded() throws Exception {
try {
@@ -270,7 +298,7 @@ public class JweJsonProducerTest {
assertEquals(SINGLE_RECIPIENT_ALL_HEADERS_AAD_OUTPUT, jweJson);
}
@Test
- public void testMultipleRecipients() {
+ public void testMultipleRecipientsA128GCM() {
final String text = "The true sign of intelligence is not knowledge but imagination.";
SecretKey wrapperKey1 = CryptoUtils.createSecretKeySpec(WRAPPER_BYTES1, "AES");
SecretKey wrapperKey2 = CryptoUtils.createSecretKeySpec(WRAPPER_BYTES2, "AES");
@@ -308,4 +336,104 @@ public class JweJsonProducerTest {
String jweJson = p.encryptWith(jweProviders, perRecipientHeades);
assertEquals(MULTIPLE_RECIPIENTS_OUTPUT, jweJson);
}
+
+ @Test
+ public void testMultipleRecipientsA128CBCHS256GivenCek() throws Exception {
+ final String text = "The true sign of intelligence is not knowledge but imagination.";
+
+ KeyAlgorithm keyAlgo = KeyAlgorithm.A128KW;
+ ContentAlgorithm contentAlgo = ContentAlgorithm.A128CBC_HS256;
+
+ SecretKey wrapperKey1 = CryptoUtils.createSecretKeySpec(WRAPPER_BYTES1, "AES");
+ SecretKey wrapperKey2 = CryptoUtils.createSecretKeySpec(WRAPPER_BYTES2, "AES");
+
+ JweHeaders protectedHeaders = new JweHeaders(contentAlgo);
+ JweHeaders sharedUnprotectedHeaders = new JweHeaders();
+ sharedUnprotectedHeaders.setJsonWebKeysUrl("https://server.example.com/keys.jwks");
+
+ sharedUnprotectedHeaders.setKeyEncryptionAlgorithm(keyAlgo);
+
+ List<JweEncryptionProvider> jweProviders = new LinkedList<>();
+
+ KeyEncryptionProvider keyEncryption1 =
+ JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey1, keyAlgo);
+
+ JweEncryptionProvider jwe1 = new AesCbcHmacJweEncryption(contentAlgo, Hex.decode(CEK_32_HEX),
+ JweCompactReaderWriterTest.INIT_VECTOR_A3, keyEncryption1);
+ KeyEncryptionProvider keyEncryption2 =
+ JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey2, keyAlgo);
+ JweEncryptionProvider jwe2 = new AesCbcHmacJweEncryption(contentAlgo, CEK_BYTES,
+ JweCompactReaderWriterTest.INIT_VECTOR_A3, keyEncryption2);
+ jweProviders.add(jwe1);
+ jweProviders.add(jwe2);
+
+ List<JweHeaders> perRecipientHeades = new LinkedList<>();
+ perRecipientHeades.add(new JweHeaders("key1"));
+ perRecipientHeades.add(new JweHeaders("key2"));
+
+ JweJsonProducer p = new JweJsonProducer(protectedHeaders,
+ sharedUnprotectedHeaders,
+ StringUtils.toBytesUTF8(text),
+ StringUtils.toBytesUTF8(EXTRA_AAD_SOURCE),
+ false);
+
+ String jweJson = p.encryptWith(jweProviders, perRecipientHeades);
+ assertEquals(MULTIPLE_RECIPIENTS_A128CBCHS256_JSON_OUTPUT, jweJson);
+ }
+
+ @Test
+ public void testMultipleRecipientsA128CBCHS256() {
+ final String text = "The true sign of intelligence is not knowledge but imagination.";
+
+ KeyAlgorithm keyAlgo = KeyAlgorithm.A128KW;
+ ContentAlgorithm contentAlgo = ContentAlgorithm.A128CBC_HS256;
+
+ SecretKey wrapperKey1 = CryptoUtils.createSecretKeySpec(WRAPPER_BYTES1, "AES");
+ SecretKey wrapperKey2 = CryptoUtils.createSecretKeySpec(WRAPPER_BYTES2, "AES");
+
+ JweHeaders protectedHeaders = new JweHeaders(contentAlgo);
+ JweHeaders sharedUnprotectedHeaders = new JweHeaders();
+ sharedUnprotectedHeaders.setJsonWebKeysUrl("https://server.example.com/keys.jwks");
+
+ sharedUnprotectedHeaders.setKeyEncryptionAlgorithm(keyAlgo);
+
+ List<JweEncryptionProvider> jweProviders = new LinkedList<>();
+
+ AesCbcContentEncryptionAlgorithm contentEncryption = new AesCbcContentEncryptionAlgorithm(contentAlgo, true);
+
+ KeyEncryptionProvider keyEncryption1 = JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey1, keyAlgo);
+ JweEncryptionProvider jwe1 = new AesCbcHmacJweEncryption(keyEncryption1, contentEncryption);
+ KeyEncryptionProvider keyEncryption2 = JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey2, keyAlgo);
+ JweEncryptionProvider jwe2 = new AesCbcHmacJweEncryption(keyEncryption2, contentEncryption);
+
+ jweProviders.add(jwe1);
+ jweProviders.add(jwe2);
+
+ List<JweHeaders> perRecipientHeades = new LinkedList<>();
+ perRecipientHeades.add(new JweHeaders("key1"));
+ perRecipientHeades.add(new JweHeaders("key2"));
+
+ JweJsonProducer p = new JweJsonProducer(protectedHeaders,
+ sharedUnprotectedHeaders,
+ StringUtils.toBytesUTF8(text),
+ StringUtils.toBytesUTF8(EXTRA_AAD_SOURCE),
+ false);
+
+ String jweJson = p.encryptWith(jweProviders, perRecipientHeades);
+
+ JweJsonConsumer consumer = new JweJsonConsumer(jweJson);
+ Assert.assertEquals(keyAlgo, consumer.getSharedUnprotectedHeader().getKeyEncryptionAlgorithm());
+ Assert.assertEquals(contentAlgo, consumer.getProtectedHeader().getContentEncryptionAlgorithm());
+
+ // Recipient 1
+ JweDecryptionProvider jwd1 = JweUtils.createJweDecryptionProvider(wrapperKey1, keyAlgo, contentAlgo);
+ JweDecryptionOutput out1 = consumer.decryptWith(jwd1, Collections.singletonMap("kid", "key1"));
+ assertEquals(text, out1.getContentText());
+ // Recipient 2
+ JweDecryptionProvider jwd2 = JweUtils.createJweDecryptionProvider(wrapperKey2, keyAlgo, contentAlgo);
+
+ JweDecryptionOutput out2 = consumer.decryptWith(jwd2, Collections.singletonMap("kid", "key2"));
+ assertEquals(text, out2.getContentText());
+ }
+
}