This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch 3.3.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf.git
commit 2fad69a02a19773bdb6ca1261bc7bc3f1864e685 Author: frelibert <[email protected]> AuthorDate: Thu Nov 28 13:19:17 2019 +0100 [CXF-8162] JWE with multiple recipients does not work for AES CBC Encryption (#604) (cherry picked from commit d3a968fb0ea586ac185483a169b1c5ab29dc5615) --- .../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()); + } + }
