Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 54C461843B for ; Wed, 21 Oct 2015 15:13:02 +0000 (UTC) Received: (qmail 48144 invoked by uid 500); 21 Oct 2015 15:12:58 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 47964 invoked by uid 500); 21 Oct 2015 15:12:58 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 46785 invoked by uid 99); 21 Oct 2015 15:12:57 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 21 Oct 2015 15:12:57 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C24F0E099B; Wed, 21 Oct 2015 15:12:57 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: coheigea@apache.org To: commits@cxf.apache.org Date: Wed, 21 Oct 2015 15:13:07 -0000 Message-Id: <474fa8eaf0744855ab6b64f56549ca26@git.apache.org> In-Reply-To: <3afc81b2e1134bcfbd2fd1b450a61fee@git.apache.org> References: <3afc81b2e1134bcfbd2fd1b450a61fee@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [11/20] cxf git commit: Renaming jose module http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenReaderWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenReaderWriter.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenReaderWriter.java new file mode 100644 index 0000000..fba861e --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenReaderWriter.java @@ -0,0 +1,55 @@ +/** + * 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.jwt; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; + + + + +public class JwtTokenReaderWriter extends JsonMapObjectReaderWriter { + private static final Set DATE_PROPERTIES = + new HashSet(Arrays.asList(JwtConstants.CLAIM_EXPIRY, + JwtConstants.CLAIM_ISSUED_AT, + JwtConstants.CLAIM_NOT_BEFORE)); + + public String claimsToJson(JwtClaims claims) { + return toJson(claims); + } + + public JwtClaims fromJsonClaims(String claimsJson) { + JwtClaims claims = new JwtClaims(); + fromJson(claims, claimsJson); + return claims; + + } + + @Override + protected Object readPrimitiveValue(String name, String json, int from, int to) { + Object value = super.readPrimitiveValue(name, json, from, to); + if (DATE_PROPERTIES.contains(name)) { + value = Long.valueOf(value.toString()); + } + return value; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java new file mode 100644 index 0000000..3f0a27e --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java @@ -0,0 +1,107 @@ +/** + * 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.jwt; + +import java.util.Date; + +public final class JwtUtils { + private JwtUtils() { + + } + public static String claimsToJson(JwtClaims claims) { + return claimsToJson(claims, null); + } + public static String claimsToJson(JwtClaims claims, JwtTokenReaderWriter writer) { + if (writer == null) { + writer = new JwtTokenReaderWriter(); + } + return writer.claimsToJson(claims); + } + public static JwtClaims jsonToClaims(String json) { + return new JwtTokenReaderWriter().fromJsonClaims(json); + } + + public static void validateJwtExpiry(JwtClaims claims, int clockOffset, boolean claimRequired) { + Long expiryTime = claims.getExpiryTime(); + if (expiryTime == null) { + if (claimRequired) { + throw new JwtException("The token has expired"); + } + return; + } + Date rightNow = new Date(); + Date expiresDate = new Date(expiryTime * 1000L); + if (clockOffset != 0) { + expiresDate.setTime(expiresDate.getTime() + (long)clockOffset * 1000L); + } + if (expiresDate.before(rightNow)) { + throw new JwtException("The token has expired"); + } + } + + public static void validateJwtNotBefore(JwtClaims claims, int clockOffset, boolean claimRequired) { + Long notBeforeTime = claims.getNotBefore(); + if (notBeforeTime == null) { + if (claimRequired) { + throw new JwtException("The token cannot be accepted yet"); + } + return; + } + + Date validCreation = new Date(); + long currentTime = validCreation.getTime(); + if (clockOffset != 0) { + validCreation.setTime(currentTime + (long)clockOffset * 1000L); + } + Date notBeforeDate = new Date(notBeforeTime * 1000L); + + // Check to see if the not before time is in the future + if (notBeforeDate.after(validCreation)) { + throw new JwtException("The token cannot be accepted yet"); + } + } + + public static void validateJwtIssuedAt(JwtClaims claims, int timeToLive, int clockOffset, boolean claimRequired) { + Long issuedAtInSecs = claims.getIssuedAt(); + if (issuedAtInSecs == null) { + if (claimRequired) { + throw new JwtException("Invalid issuedAt"); + } + return; + } + + Date createdDate = new Date(issuedAtInSecs * 1000L); + if (clockOffset != 0) { + // Calculate the time that is allowed for the message to travel + createdDate.setTime(createdDate.getTime() - (long)clockOffset * 1000L); + } + + Date validCreation = new Date(); + if (timeToLive != 0) { + long currentTime = validCreation.getTime(); + currentTime -= (long)timeToLive * 1000L; + validCreation.setTime(currentTime); + } + + if (createdDate.after(validCreation)) { + throw new JwtException("Invalid issuedAt"); + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwkJoseCookBookTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwkJoseCookBookTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwkJoseCookBookTest.java new file mode 100644 index 0000000..b9cb8bf --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwkJoseCookBookTest.java @@ -0,0 +1,187 @@ +/** + * 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.cookbook; + +import java.io.InputStream; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; +import org.apache.cxf.rs.security.jose.jwk.JwkUtils; +import org.apache.cxf.rs.security.jose.jwk.KeyType; +import org.apache.cxf.rs.security.jose.jwk.PublicKeyUse; + +import org.junit.Assert; +import org.junit.Test; + +public class JwkJoseCookBookTest extends Assert { + + private static final String EC_X_COORDINATE_VALUE = "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9" + + "A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt"; + private static final String EC_Y_COORDINATE_VALUE = "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy" + + "SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1"; + private static final String EC_KID_VALUE = "bilbo.baggins@hobbiton.example"; + private static final String EC_CURVE_VALUE = "P-521"; + private static final String EC_PRIVATE_KEY_VALUE = "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb" + + "KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt"; + private static final String RSA_MODULUS_VALUE = "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT" + + "-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV" + + "wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-" + + "oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde" + + "3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC" + + "LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g" + + "HdrNP5zw"; + private static final String RSA_PUBLIC_EXP_VALUE = "AQAB"; + private static final String RSA_KID_VALUE = "bilbo.baggins@hobbiton.example"; + private static final String RSA_PRIVATE_EXP_VALUE = "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e" + + "iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld" + + "Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b" + + "MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU" + + "6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj" + + "d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc" + + "OpBrQzwQ"; + private static final String RSA_FIRST_PRIME_FACTOR_VALUE = "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR" + + "aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG" + + "peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8" + + "bUq0k"; + private static final String RSA_SECOND_PRIME_FACTOR_VALUE = "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT" + + "8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an" + + "V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0" + + "s7pFc"; + private static final String RSA_FIRST_PRIME_CRT_VALUE = "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q" + + "1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn" + + "-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX" + + "59ehik"; + private static final String RSA_SECOND_PRIME_CRT_VALUE = "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr" + + "AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK" + + "bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK" + + "T1cYF8"; + private static final String RSA_FIRST_CRT_COEFFICIENT_VALUE = + "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N" + + "ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh" + + "jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP" + + "z8aaI4"; + private static final String SIGN_SECRET_VALUE = "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg"; + private static final String SIGN_KID_VALUE = "018c0ae5-4d9b-471b-bfd6-eef314bc7037"; + private static final String ENCRYPTION_SECRET_VALUE = "AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8"; + private static final String ENCRYPTION_KID_VALUE = "1e571774-2e08-40da-8308-e8d68773842d"; + @Test + public void testPublicSetAsList() throws Exception { + JsonWebKeys jwks = readKeySet("cookbookPublicSet.txt"); + List keys = jwks.getKeys(); + assertEquals(2, keys.size()); + JsonWebKey ecKey = keys.get(0); + assertEquals(6, ecKey.asMap().size()); + validatePublicEcKey(ecKey); + JsonWebKey rsaKey = keys.get(1); + assertEquals(5, rsaKey.asMap().size()); + validatePublicRsaKey(rsaKey); + } + @Test + public void testPublicSetAsMap() throws Exception { + JsonWebKeys jwks = readKeySet("cookbookPublicSet.txt"); + Map> keysMap = jwks.getKeyTypeMap(); + assertEquals(2, keysMap.size()); + List rsaKeys = keysMap.get(KeyType.RSA); + assertEquals(1, rsaKeys.size()); + assertEquals(5, rsaKeys.get(0).asMap().size()); + validatePublicRsaKey(rsaKeys.get(0)); + List ecKeys = keysMap.get(KeyType.EC); + assertEquals(1, ecKeys.size()); + assertEquals(6, ecKeys.get(0).asMap().size()); + validatePublicEcKey(ecKeys.get(0)); + } + @Test + public void testPrivateSetAsList() throws Exception { + JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt"); + validatePrivateSet(jwks); + } + private void validatePrivateSet(JsonWebKeys jwks) throws Exception { + List keys = jwks.getKeys(); + assertEquals(2, keys.size()); + JsonWebKey ecKey = keys.get(0); + assertEquals(7, ecKey.asMap().size()); + validatePrivateEcKey(ecKey); + JsonWebKey rsaKey = keys.get(1); + assertEquals(11, rsaKey.asMap().size()); + validatePrivateRsaKey(rsaKey); + } + @Test + public void testSecretSetAsList() throws Exception { + JsonWebKeys jwks = readKeySet("cookbookSecretSet.txt"); + List keys = jwks.getKeys(); + assertEquals(2, keys.size()); + JsonWebKey signKey = keys.get(0); + assertEquals(5, signKey.asMap().size()); + validateSecretSignKey(signKey); + JsonWebKey encKey = keys.get(1); + assertEquals(5, encKey.asMap().size()); + validateSecretEncKey(encKey); + } + private void validateSecretSignKey(JsonWebKey key) { + assertEquals(SIGN_SECRET_VALUE, key.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + assertEquals(SIGN_KID_VALUE, key.getKeyId()); + assertEquals(KeyType.OCTET, key.getKeyType()); + assertEquals(AlgorithmUtils.HMAC_SHA_256_ALGO, key.getAlgorithm()); + } + private void validateSecretEncKey(JsonWebKey key) { + assertEquals(ENCRYPTION_SECRET_VALUE, key.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + assertEquals(ENCRYPTION_KID_VALUE, key.getKeyId()); + assertEquals(KeyType.OCTET, key.getKeyType()); + assertEquals(AlgorithmUtils.A256GCM_ALGO, key.getAlgorithm()); + } + private void validatePublicRsaKey(JsonWebKey key) { + assertEquals(RSA_MODULUS_VALUE, key.getProperty(JsonWebKey.RSA_MODULUS)); + assertEquals(RSA_PUBLIC_EXP_VALUE, key.getProperty(JsonWebKey.RSA_PUBLIC_EXP)); + assertEquals(RSA_KID_VALUE, key.getKeyId()); + assertEquals(KeyType.RSA, key.getKeyType()); + } + private void validatePrivateRsaKey(JsonWebKey key) { + validatePublicRsaKey(key); + assertEquals(RSA_PRIVATE_EXP_VALUE, key.getProperty(JsonWebKey.RSA_PRIVATE_EXP)); + assertEquals(RSA_FIRST_PRIME_FACTOR_VALUE, key.getProperty(JsonWebKey.RSA_FIRST_PRIME_FACTOR)); + assertEquals(RSA_SECOND_PRIME_FACTOR_VALUE, key.getProperty(JsonWebKey.RSA_SECOND_PRIME_FACTOR)); + assertEquals(RSA_FIRST_PRIME_CRT_VALUE, key.getProperty(JsonWebKey.RSA_FIRST_PRIME_CRT)); + assertEquals(RSA_SECOND_PRIME_CRT_VALUE, key.getProperty(JsonWebKey.RSA_SECOND_PRIME_CRT)); + assertEquals(RSA_FIRST_CRT_COEFFICIENT_VALUE, key.getProperty(JsonWebKey.RSA_FIRST_CRT_COEFFICIENT)); + } + private void validatePublicEcKey(JsonWebKey key) { + assertEquals(EC_X_COORDINATE_VALUE, key.getProperty(JsonWebKey.EC_X_COORDINATE)); + assertEquals(EC_Y_COORDINATE_VALUE, key.getProperty(JsonWebKey.EC_Y_COORDINATE)); + assertEquals(EC_KID_VALUE, key.getKeyId()); + assertEquals(KeyType.EC, key.getKeyType()); + assertEquals(EC_CURVE_VALUE, key.getProperty(JsonWebKey.EC_CURVE)); + assertEquals(PublicKeyUse.SIGN, key.getPublicKeyUse()); + } + private void validatePrivateEcKey(JsonWebKey key) { + validatePublicEcKey(key); + assertEquals(EC_PRIVATE_KEY_VALUE, key.getProperty(JsonWebKey.EC_PRIVATE_KEY)); + } + public JsonWebKeys readKeySet(String fileName) throws Exception { + InputStream is = JwkJoseCookBookTest.class.getResourceAsStream(fileName); + String s = IOUtils.readStringFromStream(is); + return JwkUtils.readJwkSet(s); + } + public JsonWebKey readKey(String key) throws Exception { + return JwkUtils.readJwkKey(key); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java new file mode 100644 index 0000000..3902cf7 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/JwsJoseCookBookTest.java @@ -0,0 +1,665 @@ +/** + * 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.cookbook; + +import java.io.InputStream; +import java.security.Security; +import java.util.List; + +import javax.crypto.Cipher; + +import org.apache.cxf.common.util.Base64UrlUtility; +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.json.basic.JsonMapObjectReaderWriter; +import org.apache.cxf.rs.security.jose.jwa.AlgorithmUtils; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys; +import org.apache.cxf.rs.security.jose.jwk.JwkUtils; +import org.apache.cxf.rs.security.jose.jws.EcDsaJwsSignatureProvider; +import org.apache.cxf.rs.security.jose.jws.JwsCompactConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsCompactProducer; +import org.apache.cxf.rs.security.jose.jws.JwsHeaders; +import org.apache.cxf.rs.security.jose.jws.JwsJsonConsumer; +import org.apache.cxf.rs.security.jose.jws.JwsJsonProducer; +import org.apache.cxf.rs.security.jose.jws.JwsUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class JwsJoseCookBookTest { + private static final String PAYLOAD = "It’s a dangerous business, Frodo, going out your door. " + + "You step onto the road, and if you don't keep your feet, " + + "there’s no knowing where you might be swept off to."; + private static final String ENCODED_PAYLOAD = "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH" + + "lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk" + + "b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm" + + "UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4"; + private static final String RSA_KID_VALUE = "bilbo.baggins@hobbiton.example"; + private static final String RSA_V1_5_SIGNATURE_PROTECTED_HEADER = + "eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + + "hhbXBsZSJ9"; + private static final String RSA_V1_5_SIGNATURE_PROTECTED_HEADER_JSON = ("{" + + "\"alg\": \"RS256\"," + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}").replaceAll(" ", ""); + private static final String RSA_V1_5_SIGNATURE_VALUE = + "MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK" + + "ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J" + + "IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w" + + "W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP" + + "xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f" + + "cIe8u9ipH84ogoree7vjbU5y18kDquDg"; + private static final String RSA_V1_5_JSON_GENERAL_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"signatures\": [" + + "{" + + "\"protected\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2" + + "dpbnNAaG9iYml0b24uZXhhbXBsZSJ9\"," + + "\"signature\": \"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHo" + + "xnW2e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII" + + "7PNDi84wnB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0Rnlt" + + "uYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPo" + + "cSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxU" + + "Ahb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJush" + + "Z41Axf_fcIe8u9ipH84ogoree7vjbU5y18kDquDg\"" + + "}" + + "]" + + "}").replaceAll(" ", ""); + private static final String RSA_V1_5_JSON_FLATTENED_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"protected\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbn" + + "NAaG9iYml0b24uZXhhbXBsZSJ9\"," + + "\"signature\": \"MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2" + + "e5CZ5NlKtainoFmKZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84w" + + "nB-BDkoBwA78185hX-Es4JIwmDLJK3lfWRa-XtL0RnltuYv746iYTh_q" + + "HRD68BNt1uSNCrUCTJDt5aAE6x8wW1Kt9eRo4QPocSadnHXFxnt8Is9U" + + "zpERV0ePPQdLuW3IS_de3xyIrDaLGdjluPxUAhb6L2aXic1U12podGU0" + + "KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_fcIe8u9ipH84ogore" + + "e7vjbU5y18kDquDg\"" + + "}").replaceAll(" ", ""); + private static final String RSA_PSS_SIGNATURE_PROTECTED_HEADER_JSON = ("{" + + "\"alg\": \"PS384\"," + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}").replaceAll(" ", ""); + private static final String RSA_PSS_SIGNATURE_PROTECTED_HEADER = + "eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + + "hhbXBsZSJ9"; + private static final String RSA_PSS_SIGNATURE_VALUE = + "cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I" + + "pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU" + + "vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX" + + "e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT" + + "0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a" + + "6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw"; + private static final String RSA_PSS_JSON_GENERAL_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"signatures\": [" + + "{" + + "\"protected\": \"eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2" + + "dpbnNAaG9iYml0b24uZXhhbXBsZSJ9\"," + + "\"signature\": \"cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy" + + "42miAh2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5" + + "dDDItllVo6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz2" + + "8zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vd" + + "z0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0q" + + "I0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uT" + + "OcbH510a6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw\"" + + "}" + + "]" + + "}").replace(" ", ""); + private static final String RSA_PSS_JSON_FLATTENED_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"protected\": \"eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbn" + + "NAaG9iYml0b24uZXhhbXBsZSJ9\"," + + "\"signature\": \"cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42mi" + + "Ah2qyBzk1xEsnk2IpN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllV" + + "o6_1OLPpcbUrhiUSMxbbXUvdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf" + + "8ZnyPEgVFn442ZdNqiVJRmBqrYRXe8P_ijQ7p8Vdz0TTrxUeT3lm8d9s" + + "hnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT0qI0n6uiP1aCN_2_jLAeQT" + + "lqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a6GYmJUAfmWjwZ6oD" + + "4ifKo8DYM-X72Eaw\"" + + "}").replace(" ", ""); + private static final String ECDSA_KID_VALUE = "bilbo.baggins@hobbiton.example"; + private static final String ECDSA_SIGNATURE_PROTECTED_HEADER_JSON = ("{" + + "\"alg\": \"ES512\"," + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}").replace(" ", ""); + private static final String ECSDA_SIGNATURE_PROTECTED_HEADER = + "eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX" + + "hhbXBsZSJ9"; + private static final String HMAC_KID_VALUE = "018c0ae5-4d9b-471b-bfd6-eef314bc7037"; + private static final String HMAC_SIGNATURE_PROTECTED_HEADER_JSON = ("{" + + "\"alg\": \"HS256\"," + + "\"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\"" + + "}").replaceAll(" ", ""); + private static final String HMAC_SIGNATURE_PROTECTED_HEADER = + "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" + + "VlZjMxNGJjNzAzNyJ9"; + private static final String HMAC_SIGNATURE_VALUE = "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0"; + private static final String HMAC_JSON_GENERAL_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"signatures\": [" + + "{" + + "\"protected\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LT" + + "RkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9\"," + + "\"signature\": \"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p" + + "0\"" + + "}" + + "]" + + "}").replaceAll(" ", ""); + private static final String HMAC_JSON_FLATTENED_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"protected\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOW" + + "ItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9\"," + + "\"signature\": \"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0\"" + + "}").replaceAll(" ", ""); + private static final String DETACHED_HMAC_JWS = + ("eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" + + "VlZjMxNGJjNzAzNyJ9" + + "." + + "." + + "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0").replaceAll(" ", ""); + private static final String HMAC_DETACHED_JSON_GENERAL_SERIALIZATION = ("{" + + "\"signatures\": [" + + "{" + + "\"protected\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LT" + + "RkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9\"," + + "\"signature\": \"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p" + + "0\"" + + "}" + + "]" + + "}").replaceAll(" ", ""); + private static final String HMAC_DETACHED_JSON_FLATTENED_SERIALIZATION = ("{" + + "\"protected\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOW" + + "ItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9\"," + + "\"signature\": \"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0\"" + + "}").replaceAll(" ", ""); + private static final String PROTECTING_SPECIFIC_HEADER_FIELDS_JSON_GENERAL_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"signatures\": [" + + "{" + + "\"protected\": \"eyJhbGciOiJIUzI1NiJ9\"," + + "\"header\": {" + + "\"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\"" + + "}," + + "\"signature\": \"bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr2" + + "0\"" + + "}" + + "]" + + "}").replace(" ", ""); + private static final String PROTECTING_SPECIFIC_HEADER_FIELDS_JSON_FLATTENED_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"protected\": \"eyJhbGciOiJIUzI1NiJ9\"," + + "\"header\": {" + + "\"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\"" + + "}," + + "\"signature\": \"bWUSVaxorn7bEF1djytBd0kHv70Ly5pvbomzMWSOr20\"" + + "}").replace(" ", ""); + private static final String PROTECTING_CONTENT_ONLY_JSON_GENERAL_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"signatures\": [" + + "{" + + "\"header\": {" + + "\"alg\": \"HS256\"," + + "\"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\"" + + "}," + + "\"signature\": \"xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZu" + + "k\"" + + "}" + + "]" + + "}").replace(" ", ""); + private static final String PROTECTING_CONTENT_ONLY_JSON_FLATTENED_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"header\": {" + + "\"alg\": \"HS256\"," + + "\"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\"" + + "}," + + "\"signature\": \"xuLifqLGiblpv9zBpuZczWhNj1gARaLV3UxvxhJxZuk\"" + + "}").replace(" ", ""); + private static final String FIRST_SIGNATURE_ENTRY_MULTIPLE_SIGNATURES = ("{" + + "\"protected\": \"eyJhbGciOiJSUzI1NiJ9\"," + + "\"header\": {" + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}," + + "\"signature\": \"MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5NvyG53u" + + "oimic1tcMdSg-qptrzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFcryNFiHkS" + + "w129EghGpwkpxaTn_THJTCglNbADko1MZBCdwzJxwqZc-1RlpO2HibUY" + + "yXSwO97BSe0_evZKdjvvKSgsIqjytKSeAMbhMBdMma622_BG5t4sdbuC" + + "HtFjp9iJmkio47AIwqkZV1aIZsv33uPUqBBCXbYoQJwt7mxPftHmNlGo" + + "OSMxR_3thmXTCm4US-xiNOyhbm8afKK64jU6_TPtQHiJeQJxz9G3Tx-0" + + "83B745_AfYOnlC9w\"" + + "}").replace(" ", ""); + private static final String SECOND_SIGNATURE_ENTRY_MULTIPLE_SIGNATURES = ("{" + + "\"header\": {" + + "\"alg\": \"ES512\"," + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}," + + "\"signature\": \"ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhcdCoF" + + "ZFFjfISu0Cdkn9Ybdlmi54ho0x924DUz8sK7ZXkhc7AFM8ObLfTvNCrq" + + "cI3Jkl2U5IX3utNhODH6v7xgy1Qahsn0fyb4zSAkje8bAWz4vIfj5pCM" + + "Yxxm4fgV3q7ZYhm5eD\"" + + "}").replace(" ", ""); + private static final String SECOND_SIGNATURE_UNPROTECTED_HEADER_MULTIPLE_SIGNATURES = ("{" + + "\"alg\": \"ES512\"," + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}").replace(" ", ""); + private static final String THIRD_SIGNATURE_ENTRY_MULTIPLE_SIGNATURES = ("{" + + "\"protected\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOW" + + "ItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9\"," + + "\"signature\": \"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0\"" + + "}").replace(" ", ""); + private static final String MULTIPLE_SIGNATURES_JSON_GENERAL_SERIALIZATION = ("{" + + "\"payload\": \"SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + + "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + + "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + + "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + + "ZiB0by4\"," + + "\"signatures\": [" + + "{" + + "\"protected\": \"eyJhbGciOiJSUzI1NiJ9\"," + + "\"header\": {" + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}," + + "\"signature\": \"MIsjqtVlOpa71KE-Mss8_Nq2YH4FGhiocsqrgi5Nvy" + + "G53uoimic1tcMdSg-qptrzZc7CG6Svw2Y13TDIqHzTUrL_lR2ZFc" + + "ryNFiHkSw129EghGpwkpxaTn_THJTCglNbADko1MZBCdwzJxwqZc" + + "-1RlpO2HibUYyXSwO97BSe0_evZKdjvvKSgsIqjytKSeAMbhMBdM" + + "ma622_BG5t4sdbuCHtFjp9iJmkio47AIwqkZV1aIZsv33uPUqBBC" + + "XbYoQJwt7mxPftHmNlGoOSMxR_3thmXTCm4US-xiNOyhbm8afKK6" + + "4jU6_TPtQHiJeQJxz9G3Tx-083B745_AfYOnlC9w\"" + + "}," + + "{" + + "\"header\": {" + + "\"alg\": \"ES512\"," + + "\"kid\": \"bilbo.baggins@hobbiton.example\"" + + "}," + + "\"signature\": \"ARcVLnaJJaUWG8fG-8t5BREVAuTY8n8YHjwDO1muhc" + + "dCoFZFFjfISu0Cdkn9Ybdlmi54ho0x924DUz8sK7ZXkhc7AFM8Ob" + + "LfTvNCrqcI3Jkl2U5IX3utNhODH6v7xgy1Qahsn0fyb4zSAkje8b" + + "AWz4vIfj5pCMYxxm4fgV3q7ZYhm5eD\"" + + "}," + + "{" + + "\"protected\": \"eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LT" + + "RkOWItNDcxYi1iZmQ2LWVlZjMxNGJjNzAzNyJ9\"," + + "\"signature\": \"s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p" + + "0\"" + + "}" + + "]" + + "}").replace(" ", "");; + @Test + public void testEncodedPayload() throws Exception { + assertEquals(Base64UrlUtility.encode(PAYLOAD), ENCODED_PAYLOAD); + } + @Test + public void testRSAv15Signature() throws Exception { + JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD); + compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.RS256); + compactProducer.getJwsHeaders().setKeyId(RSA_KID_VALUE); + JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); + assertEquals(reader.toJson(compactProducer.getJwsHeaders().asMap()), RSA_V1_5_SIGNATURE_PROTECTED_HEADER_JSON); + assertEquals(compactProducer.getUnsignedEncodedJws(), + RSA_V1_5_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD); + JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey rsaKey = keys.get(1); + compactProducer.signWith(rsaKey); + assertEquals(compactProducer.getSignedEncodedJws(), + RSA_V1_5_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD + "." + RSA_V1_5_SIGNATURE_VALUE); + JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws()); + JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt"); + List publicKeys = publicJwks.getKeys(); + JsonWebKey rsaPublicKey = publicKeys.get(1); + assertTrue(compactConsumer.verifySignatureWith(rsaPublicKey, + SignatureAlgorithm.RS256)); + + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders protectedHeader = new JwsHeaders(); + protectedHeader.setSignatureAlgorithm(SignatureAlgorithm.RS256); + protectedHeader.setKeyId(RSA_KID_VALUE); + jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, + SignatureAlgorithm.RS256), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), RSA_V1_5_JSON_GENERAL_SERIALIZATION); + JwsJsonConsumer jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, SignatureAlgorithm.RS256)); + + jsonProducer = new JwsJsonProducer(PAYLOAD, true); + jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, SignatureAlgorithm.RS256), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), RSA_V1_5_JSON_FLATTENED_SERIALIZATION); + jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, SignatureAlgorithm.RS256)); + } + @Test + public void testRSAPSSSignature() throws Exception { + try { + Cipher.getInstance(AlgorithmUtils.PS_SHA_384_JAVA); + } catch (Throwable t) { + Security.addProvider(new BouncyCastleProvider()); + } + + JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD); + compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.PS384); + compactProducer.getJwsHeaders().setKeyId(RSA_KID_VALUE); + JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); + assertEquals(reader.toJson(compactProducer.getJwsHeaders().asMap()), RSA_PSS_SIGNATURE_PROTECTED_HEADER_JSON); + assertEquals(compactProducer.getUnsignedEncodedJws(), + RSA_PSS_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD); + JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey rsaKey = keys.get(1); + compactProducer.signWith(rsaKey); + assertEquals(compactProducer.getSignedEncodedJws().length(), + (RSA_PSS_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD + "." + RSA_PSS_SIGNATURE_VALUE).length()); + JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws()); + JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt"); + List publicKeys = publicJwks.getKeys(); + JsonWebKey rsaPublicKey = publicKeys.get(1); + assertTrue(compactConsumer.verifySignatureWith(rsaPublicKey, SignatureAlgorithm.PS384)); + + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders protectedHeader = new JwsHeaders(); + protectedHeader.setSignatureAlgorithm(SignatureAlgorithm.PS384); + protectedHeader.setKeyId(RSA_KID_VALUE); + jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, SignatureAlgorithm.PS384), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument().length(), RSA_PSS_JSON_GENERAL_SERIALIZATION.length()); + JwsJsonConsumer jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, SignatureAlgorithm.PS384)); + + jsonProducer = new JwsJsonProducer(PAYLOAD, true); + jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, SignatureAlgorithm.PS384), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument().length(), RSA_PSS_JSON_FLATTENED_SERIALIZATION.length()); + jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, SignatureAlgorithm.PS384)); + + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + @Test + public void testECDSASignature() throws Exception { + + try { + Cipher.getInstance(AlgorithmUtils.ES_SHA_512_JAVA); + } catch (Throwable t) { + Security.addProvider(new BouncyCastleProvider()); + } + try { + JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD); + compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.ES512); + compactProducer.getJwsHeaders().setKeyId(ECDSA_KID_VALUE); + JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); + assertEquals(reader.toJson(compactProducer.getJwsHeaders().asMap()), + ECDSA_SIGNATURE_PROTECTED_HEADER_JSON); + assertEquals(compactProducer.getUnsignedEncodedJws(), + ECSDA_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD); + JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey ecKey = keys.get(0); + compactProducer.signWith(new EcDsaJwsSignatureProvider(JwkUtils.toECPrivateKey(ecKey), + SignatureAlgorithm.ES512)); + assertEquals(compactProducer.getUnsignedEncodedJws(), + ECSDA_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD); + assertEquals(132, Base64UrlUtility.decode(compactProducer.getEncodedSignature()).length); + + JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws()); + JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt"); + List publicKeys = publicJwks.getKeys(); + JsonWebKey ecPublicKey = publicKeys.get(0); + assertTrue(compactConsumer.verifySignatureWith(ecPublicKey, SignatureAlgorithm.ES512)); + } finally { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + } + @Test + public void testHMACSignature() throws Exception { + JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD); + compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.HS256); + compactProducer.getJwsHeaders().setKeyId(HMAC_KID_VALUE); + JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); + assertEquals(reader.toJson(compactProducer.getJwsHeaders().asMap()), HMAC_SIGNATURE_PROTECTED_HEADER_JSON); + assertEquals(compactProducer.getUnsignedEncodedJws(), + HMAC_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD); + JsonWebKeys jwks = readKeySet("cookbookSecretSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey key = keys.get(0); + compactProducer.signWith(key); + assertEquals(compactProducer.getSignedEncodedJws(), + HMAC_SIGNATURE_PROTECTED_HEADER + "." + ENCODED_PAYLOAD + "." + HMAC_SIGNATURE_VALUE); + JwsCompactConsumer compactConsumer = new JwsCompactConsumer(compactProducer.getSignedEncodedJws()); + assertTrue(compactConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders protectedHeader = new JwsHeaders(); + protectedHeader.setSignatureAlgorithm(SignatureAlgorithm.HS256); + protectedHeader.setKeyId(HMAC_KID_VALUE); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), HMAC_JSON_GENERAL_SERIALIZATION); + JwsJsonConsumer jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + + jsonProducer = new JwsJsonProducer(PAYLOAD, true); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), HMAC_JSON_FLATTENED_SERIALIZATION); + jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + } + @Test + public void testDetachedHMACSignature() throws Exception { + JwsCompactProducer compactProducer = new JwsCompactProducer(PAYLOAD, true); + compactProducer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.HS256); + compactProducer.getJwsHeaders().setKeyId(HMAC_KID_VALUE); + JsonMapObjectReaderWriter reader = new JsonMapObjectReaderWriter(); + assertEquals(reader.toJson(compactProducer.getJwsHeaders().asMap()), HMAC_SIGNATURE_PROTECTED_HEADER_JSON); + assertEquals(compactProducer.getUnsignedEncodedJws(), + HMAC_SIGNATURE_PROTECTED_HEADER + "."); + JsonWebKeys jwks = readKeySet("cookbookSecretSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey key = keys.get(0); + compactProducer.signWith(key); + assertEquals(compactProducer.getSignedEncodedJws(), DETACHED_HMAC_JWS); + JwsCompactConsumer compactConsumer = + new JwsCompactConsumer(compactProducer.getSignedEncodedJws(), ENCODED_PAYLOAD); + assertTrue(compactConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders protectedHeader = new JwsHeaders(); + protectedHeader.setSignatureAlgorithm(SignatureAlgorithm.HS256); + protectedHeader.setKeyId(HMAC_KID_VALUE); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(true), HMAC_DETACHED_JSON_GENERAL_SERIALIZATION); + JwsJsonConsumer jsonConsumer = + new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument(true), ENCODED_PAYLOAD); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + + jsonProducer = new JwsJsonProducer(PAYLOAD, true); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), protectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(true), HMAC_DETACHED_JSON_FLATTENED_SERIALIZATION); + jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument(true), ENCODED_PAYLOAD); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + } + @Test + public void testProtectingSpecificHeaderFieldsSignature() throws Exception { + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders protectedHeader = new JwsHeaders(); + protectedHeader.setSignatureAlgorithm(SignatureAlgorithm.HS256); + JwsHeaders unprotectedHeader = new JwsHeaders(); + unprotectedHeader.setKeyId(HMAC_KID_VALUE); + JsonWebKeys jwks = readKeySet("cookbookSecretSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey key = keys.get(0); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), + protectedHeader, unprotectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), + PROTECTING_SPECIFIC_HEADER_FIELDS_JSON_GENERAL_SERIALIZATION); + JwsJsonConsumer jsonConsumer = + new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + + jsonProducer = new JwsJsonProducer(PAYLOAD, true); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), + protectedHeader, unprotectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), + PROTECTING_SPECIFIC_HEADER_FIELDS_JSON_FLATTENED_SERIALIZATION); + jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + } + @Test + public void testProtectingContentOnlySignature() throws Exception { + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders unprotectedHeader = new JwsHeaders(); + unprotectedHeader.setSignatureAlgorithm(SignatureAlgorithm.HS256); + unprotectedHeader.setKeyId(HMAC_KID_VALUE); + JsonWebKeys jwks = readKeySet("cookbookSecretSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey key = keys.get(0); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), + null, unprotectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), + PROTECTING_CONTENT_ONLY_JSON_GENERAL_SERIALIZATION); + JwsJsonConsumer jsonConsumer = + new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + + jsonProducer = new JwsJsonProducer(PAYLOAD, true); + jsonProducer.signWith(JwsUtils.getSignatureProvider(key, SignatureAlgorithm.HS256), + null, unprotectedHeader); + assertEquals(jsonProducer.getJwsJsonSignedDocument(), + PROTECTING_CONTENT_ONLY_JSON_FLATTENED_SERIALIZATION); + jsonConsumer = new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + assertTrue(jsonConsumer.verifySignatureWith(key, SignatureAlgorithm.HS256)); + } + @Test + public void testMultipleSignatures() throws Exception { + try { + Cipher.getInstance(AlgorithmUtils.ES_SHA_512_JAVA); + } catch (Throwable t) { + Security.addProvider(new BouncyCastleProvider()); + } + try { + JwsJsonProducer jsonProducer = new JwsJsonProducer(PAYLOAD); + assertEquals(jsonProducer.getPlainPayload(), PAYLOAD); + assertEquals(jsonProducer.getUnsignedEncodedPayload(), ENCODED_PAYLOAD); + JwsHeaders firstSignerProtectedHeader = new JwsHeaders(); + firstSignerProtectedHeader.setSignatureAlgorithm(SignatureAlgorithm.RS256); + JwsHeaders firstSignerUnprotectedHeader = new JwsHeaders(); + firstSignerUnprotectedHeader.setKeyId(RSA_KID_VALUE); + JsonWebKeys jwks = readKeySet("cookbookPrivateSet.txt"); + List keys = jwks.getKeys(); + JsonWebKey rsaKey = keys.get(1); + jsonProducer.signWith(JwsUtils.getSignatureProvider(rsaKey, SignatureAlgorithm.RS256), + firstSignerProtectedHeader, firstSignerUnprotectedHeader); + assertEquals(jsonProducer.getSignatureEntries().get(0).toJson(), + FIRST_SIGNATURE_ENTRY_MULTIPLE_SIGNATURES); + + JwsHeaders secondSignerUnprotectedHeader = new JwsHeaders(); + secondSignerUnprotectedHeader.setSignatureAlgorithm(SignatureAlgorithm.ES512); + secondSignerUnprotectedHeader.setKeyId(ECDSA_KID_VALUE); + JsonWebKey ecKey = keys.get(0); + jsonProducer.signWith(JwsUtils.getSignatureProvider(ecKey, SignatureAlgorithm.ES512), + null, secondSignerUnprotectedHeader); + assertEquals(new JsonMapObjectReaderWriter().toJson( + jsonProducer.getSignatureEntries().get(1).getUnprotectedHeader()), + SECOND_SIGNATURE_UNPROTECTED_HEADER_MULTIPLE_SIGNATURES); + assertEquals(jsonProducer.getSignatureEntries().get(1).toJson().length(), + SECOND_SIGNATURE_ENTRY_MULTIPLE_SIGNATURES.length()); + + JwsHeaders thirdSignerProtectedHeader = new JwsHeaders(); + thirdSignerProtectedHeader.setSignatureAlgorithm(SignatureAlgorithm.HS256); + thirdSignerProtectedHeader.setKeyId(HMAC_KID_VALUE); + JsonWebKeys secretJwks = readKeySet("cookbookSecretSet.txt"); + List secretKeys = secretJwks.getKeys(); + JsonWebKey hmacKey = secretKeys.get(0); + jsonProducer.signWith(JwsUtils.getSignatureProvider(hmacKey, SignatureAlgorithm.HS256), + thirdSignerProtectedHeader); + assertEquals(jsonProducer.getSignatureEntries().get(2).toJson(), + THIRD_SIGNATURE_ENTRY_MULTIPLE_SIGNATURES); + assertEquals(jsonProducer.getJwsJsonSignedDocument().length(), + MULTIPLE_SIGNATURES_JSON_GENERAL_SERIALIZATION.length()); + JwsJsonConsumer jsonConsumer = + new JwsJsonConsumer(jsonProducer.getJwsJsonSignedDocument()); + JsonWebKeys publicJwks = readKeySet("cookbookPublicSet.txt"); + List publicKeys = publicJwks.getKeys(); + JsonWebKey rsaPublicKey = publicKeys.get(1); + JsonWebKey ecPublicKey = publicKeys.get(0); + assertTrue(jsonConsumer.verifySignatureWith(rsaPublicKey, SignatureAlgorithm.RS256)); + assertTrue(jsonConsumer.verifySignatureWith(ecPublicKey, SignatureAlgorithm.ES512)); + assertTrue(jsonConsumer.verifySignatureWith(hmacKey, SignatureAlgorithm.HS256)); + } finally { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + } + public JsonWebKeys readKeySet(String fileName) throws Exception { + InputStream is = JwsJoseCookBookTest.class.getResourceAsStream(fileName); + String s = IOUtils.readStringFromStream(is); + return JwkUtils.readJwkSet(s); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPrivateSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPrivateSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPrivateSet.txt new file mode 100644 index 0000000..03e2704 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPrivateSet.txt @@ -0,0 +1,24 @@ +{"keys": + [ + {"kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", + "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zbKipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" + }, + {"kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB", + "d": "bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78eiZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRldY7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-bMwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDjd18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOcOpBrQzwQ", + "p": "3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nRaO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmGpeNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8bUq0k", + "q": "uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7anV5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0s7pFc", + "dp": "B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn-RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX59ehik", + "dq": "CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pErAMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJKbi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdKT1cYF8", + "qi": "3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-NZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDhjJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpPz8aaI4" + } + ] + } http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPublicSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPublicSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPublicSet.txt new file mode 100644 index 0000000..15b4808 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookPublicSet.txt @@ -0,0 +1,16 @@ +{"keys": + [ + {"kty": "EC", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "crv": "P-521", + "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", + "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" + }, + {"kty": "RSA", + "kid": "bilbo.baggins@hobbiton.example", + "use": "sig", + "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5gHdrNP5zw", + "e": "AQAB"} + ] + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookSecretSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookSecretSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookSecretSet.txt new file mode 100644 index 0000000..2f4bc08 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/cookbook/cookbookSecretSet.txt @@ -0,0 +1,16 @@ +{"keys": + [ + {"kty": "oct", + "kid": "018c0ae5-4d9b-471b-bfd6-eef314bc7037", + "use": "sig", + "alg": "HS256", + "k": "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" + }, + {"kty": "oct", + "kid": "1e571774-2e08-40da-8308-e8d68773842d", + "use": "enc", + "alg": "A256GCM", + "k": "AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8" + } + ] + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java new file mode 100644 index 0000000..2cdda1a --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java @@ -0,0 +1,288 @@ +/** + * 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.Security; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +import org.apache.cxf.common.util.Base64UrlUtility; +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; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jws.JwsCompactReaderWriterTest; +import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class JweCompactReaderWriterTest extends Assert { + // A1 example + static final byte[] CONTENT_ENCRYPTION_KEY_A1 = { + (byte)177, (byte)161, (byte)244, (byte)128, 84, (byte)143, (byte)225, + 115, 63, (byte)180, 3, (byte)255, 107, (byte)154, (byte)212, (byte)246, + (byte)138, 7, 110, 91, 112, 46, 34, 105, 47, + (byte)130, (byte)203, 46, 122, (byte)234, 64, (byte)252}; + static final String RSA_MODULUS_ENCODED_A1 = "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"; + static final String RSA_PUBLIC_EXPONENT_ENCODED_A1 = "AQAB"; + static final String RSA_PRIVATE_EXPONENT_ENCODED_A1 = + "kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N" + + "WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9" + + "3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk" + + "qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl" + + "t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd" + + "VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ"; + + static final byte[] INIT_VECTOR_A1 = {(byte)227, (byte)197, 117, (byte)252, 2, (byte)219, + (byte)233, 68, (byte)180, (byte)225, 77, (byte)219}; + + // A3 example + static final byte[] CONTENT_ENCRYPTION_KEY_A3 = { + 4, (byte)211, 31, (byte)197, 84, (byte)157, (byte)252, (byte)254, 11, 100, + (byte)157, (byte)250, 63, (byte)170, 106, (byte)206, 107, 124, (byte)212, + 45, 111, 107, 9, (byte)219, (byte)200, (byte)177, 0, (byte)240, (byte)143, + (byte)156, 44, (byte)207}; + static final byte[] INIT_VECTOR_A3 = { + 3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101}; + static final String KEY_ENCRYPTION_KEY_A3 = "GawgguFyGrWKav7AX4VKUg"; + private static final String JWE_OUTPUT_A3 = + "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" + + ".6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ" + + ".AxY8DCtDaGlsbGljb3RoZQ" + + ".KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY" + + ".U0m_YmjN04DJvceFICbCVQ"; + + private static final Boolean SKIP_AES_GCM_TESTS = isJava6(); + + private static boolean isJava6() { + String version = System.getProperty("java.version"); + return 1.6D == Double.parseDouble(version.substring(0, 3)); + } + + @BeforeClass + public static void registerBouncyCastleIfNeeded() throws Exception { + + try { + if (!SKIP_AES_GCM_TESTS) { + Cipher.getInstance(AlgorithmUtils.AES_GCM_ALGO_JAVA); + } + Cipher.getInstance(AlgorithmUtils.AES_CBC_ALGO_JAVA); + } catch (Throwable t) { + Security.addProvider(new BouncyCastleProvider()); + } + + } + @AfterClass + public static void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + @Test + public void testEncryptDecryptAesWrapA128CBCHS256() throws Exception { + final String specPlainText = "Live long and prosper."; + + byte[] cekEncryptionKey = Base64UrlUtility.decode(KEY_ENCRYPTION_KEY_A3); + + AesWrapKeyEncryptionAlgorithm keyEncryption = + new AesWrapKeyEncryptionAlgorithm(cekEncryptionKey, KeyAlgorithm.A128KW); + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(ContentAlgorithm.A128CBC_HS256, + CONTENT_ENCRYPTION_KEY_A3, + INIT_VECTOR_A3, + keyEncryption); + String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + assertEquals(JWE_OUTPUT_A3, jweContent); + + AesWrapKeyDecryptionAlgorithm keyDecryption = new AesWrapKeyDecryptionAlgorithm(cekEncryptionKey); + JweDecryptionProvider decryption = new AesCbcHmacJweDecryption(keyDecryption); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + } + + @Test + public void testECDHESDirectKeyEncryption() throws Exception { + if (SKIP_AES_GCM_TESTS) { + return; + } + ECPrivateKey bobPrivateKey = + CryptoUtils.getECPrivateKey(JsonWebKey.EC_CURVE_P256, + "VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw"); + + final ECPublicKey bobPublicKey = + CryptoUtils.getECPublicKey(JsonWebKey.EC_CURVE_P256, + "weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ", + "e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck"); + JweEncryptionProvider jweOut = + new EcdhDirectKeyJweEncryption(bobPublicKey, + JsonWebKey.EC_CURVE_P256, + "Alice", + "Bob", + ContentAlgorithm.A128GCM); + + String jweOutput = jweOut.encrypt("Hello".getBytes(), null); + JweDecryptionProvider jweIn = + new EcdhDirectKeyJweDecryption(bobPrivateKey, ContentAlgorithm.A128GCM); + assertEquals("Hello", jweIn.decrypt(jweOutput).getContentText()); + } + @Test + public void testEncryptDecryptRSA15WrapA128CBCHS256() throws Exception { + final String specPlainText = "Live long and prosper."; + + RSAPublicKey publicKey = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED_A1, + RSA_PUBLIC_EXPONENT_ENCODED_A1); + + KeyEncryptionProvider keyEncryption = new RSAKeyEncryptionAlgorithm(publicKey, + KeyAlgorithm.RSA1_5); + + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(ContentAlgorithm.A128CBC_HS256, + CONTENT_ENCRYPTION_KEY_A3, + INIT_VECTOR_A3, + keyEncryption); + String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + + RSAPrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED_A1, + RSA_PRIVATE_EXPONENT_ENCODED_A1); + KeyDecryptionProvider keyDecryption = new RSAKeyDecryptionAlgorithm(privateKey, + KeyAlgorithm.RSA1_5); + JweDecryptionProvider decryption = new AesCbcHmacJweDecryption(keyDecryption); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + } + @Test + public void testEncryptDecryptAesGcmWrapA128CBCHS256() throws Exception { + // + // This test fails with the IBM JDK and on Java 6 + // + if ("IBM Corporation".equals(System.getProperty("java.vendor")) + || SKIP_AES_GCM_TESTS) { + return; + } + final String specPlainText = "Live long and prosper."; + + byte[] cekEncryptionKey = Base64UrlUtility.decode(KEY_ENCRYPTION_KEY_A3); + + AesGcmWrapKeyEncryptionAlgorithm keyEncryption = + new AesGcmWrapKeyEncryptionAlgorithm(cekEncryptionKey, KeyAlgorithm.A128GCMKW); + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(ContentAlgorithm.A128CBC_HS256, + CONTENT_ENCRYPTION_KEY_A3, + INIT_VECTOR_A3, + keyEncryption); + String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + + AesGcmWrapKeyDecryptionAlgorithm keyDecryption = new AesGcmWrapKeyDecryptionAlgorithm(cekEncryptionKey); + JweDecryptionProvider decryption = new AesCbcHmacJweDecryption(keyDecryption); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + } + + @Test + public void testEncryptDecryptSpecExample() throws Exception { + if (SKIP_AES_GCM_TESTS) { + return; + } + final String specPlainText = "The true sign of intelligence is not knowledge but imagination."; + String jweContent = encryptContent(specPlainText, true); + + decrypt(jweContent, specPlainText, true); + } + + @Test + public void testDirectKeyEncryptDecrypt() throws Exception { + if (SKIP_AES_GCM_TESTS) { + return; + } + final String specPlainText = "The true sign of intelligence is not knowledge but imagination."; + SecretKey key = createSecretKey(true); + String jweContent = encryptContentDirect(key, specPlainText); + + decryptDirect(key, jweContent, specPlainText); + } + + @Test + public void testEncryptDecryptJwsToken() throws Exception { + if (SKIP_AES_GCM_TESTS) { + return; + } + String jweContent = encryptContent(JwsCompactReaderWriterTest.ENCODED_TOKEN_SIGNED_BY_MAC, false); + decrypt(jweContent, JwsCompactReaderWriterTest.ENCODED_TOKEN_SIGNED_BY_MAC, false); + } + + private String encryptContent(String content, boolean createIfException) throws Exception { + RSAPublicKey publicKey = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED_A1, + RSA_PUBLIC_EXPONENT_ENCODED_A1); + SecretKey key = createSecretKey(createIfException); + String jwtKeyName = null; + if (key == null) { + // the encryptor will generate it + jwtKeyName = ContentAlgorithm.A128GCM.getJwaName(); + } else { + jwtKeyName = AlgorithmUtils.toJwaName(key.getAlgorithm(), key.getEncoded().length * 8); + } + KeyEncryptionProvider keyEncryptionAlgo = new RSAKeyEncryptionAlgorithm(publicKey, + KeyAlgorithm.RSA_OAEP); + ContentEncryptionProvider contentEncryptionAlgo = + new AesGcmContentEncryptionAlgorithm(key == null ? null : key.getEncoded(), INIT_VECTOR_A1, + ContentAlgorithm.getAlgorithm(jwtKeyName)); + JweEncryptionProvider encryptor = new JweEncryption(keyEncryptionAlgo, contentEncryptionAlgo); + return encryptor.encrypt(content.getBytes("UTF-8"), null); + } + private String encryptContentDirect(SecretKey key, String content) throws Exception { + JweEncryption encryptor = new JweEncryption(new DirectKeyEncryptionAlgorithm(), + new AesGcmContentEncryptionAlgorithm(key, INIT_VECTOR_A1, ContentAlgorithm.A128GCM)); + return encryptor.encrypt(content.getBytes("UTF-8"), null); + } + private void decrypt(String jweContent, String plainContent, boolean unwrap) throws Exception { + RSAPrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED_A1, + RSA_PRIVATE_EXPONENT_ENCODED_A1); + ContentAlgorithm algo = Cipher.getMaxAllowedKeyLength("AES") > 128 + ? ContentAlgorithm.A256GCM : ContentAlgorithm.A128GCM; + JweDecryptionProvider decryptor = new JweDecryption(new RSAKeyDecryptionAlgorithm(privateKey), + new AesGcmContentDecryptionAlgorithm(algo)); + String decryptedText = decryptor.decrypt(jweContent).getContentText(); + assertEquals(decryptedText, plainContent); + } + private void decryptDirect(SecretKey key, String jweContent, String plainContent) throws Exception { + JweDecryption decryptor = new JweDecryption(new DirectKeyDecryptionAlgorithm(key), + new AesGcmContentDecryptionAlgorithm(ContentAlgorithm.A128GCM)); + String decryptedText = decryptor.decrypt(jweContent).getContentText(); + assertEquals(decryptedText, plainContent); + } + private SecretKey createSecretKey(boolean createIfException) throws Exception { + SecretKey key = null; + if (Cipher.getMaxAllowedKeyLength("AES") > 128) { + key = CryptoUtils.createSecretKeySpec(CONTENT_ENCRYPTION_KEY_A1, "AES"); + } else if (createIfException) { + key = CryptoUtils.createSecretKeySpec(CryptoUtils.generateSecureRandomBytes(128 / 8), "AES"); + } + return key; + } +} + http://git-wip-us.apache.org/repos/asf/cxf/blob/532c52a7/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumerTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumerTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumerTest.java new file mode 100644 index 0000000..de7bc94 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonConsumerTest.java @@ -0,0 +1,184 @@ +/** + * 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.Security; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +import org.apache.cxf.common.util.Base64UrlUtility; +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; +import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class JweJsonConsumerTest extends Assert { + + static final String SINGLE_RECIPIENT_ALL_HEADERS_AAD_MODIFIED_OUTPUT = + "{" + + "\"protected\":\"eyJlbmMiOiJBMTI4R0NNIn0\"," + + "\"unprotected\":{\"jku\":\"https://server.example.com/keys.jwks\"}," + + "\"recipients\":" + + "[" + + "{" + + "\"header\":{\"alg\":\"A128KW\"}," + + "\"encrypted_key\":\"b3-M9_CRgT3wEBhhXlpb-BoY7vtA4W_N\"" + + "}" + + "]," + + "\"aad\":\"" + Base64UrlUtility.encode(JweJsonProducerTest.EXTRA_AAD_SOURCE + ".") + "\"," + + "\"iv\":\"48V1_ALb6US04U3b\"," + + "\"ciphertext\":\"KTuJBMk9QG59xPB-c_YLM5-J7VG40_eMPvyHDD7eB-WHj_34YiWgpBOydTBm4RW0zUCJZ09xqorhWJME-DcQ\"," + + "\"tag\":\"oVUQGS9608D-INq61-vOaA\"" + + "}"; + + private static final Boolean SKIP_AES_GCM_TESTS = isJava6(); + + private static boolean isJava6() { + String version = System.getProperty("java.version"); + return 1.6D == Double.parseDouble(version.substring(0, 3)); + } + + + @BeforeClass + public static void registerBouncyCastleIfNeeded() throws Exception { + try { + Cipher.getInstance(AlgorithmUtils.AES_GCM_ALGO_JAVA); + Cipher.getInstance(AlgorithmUtils.AES_CBC_ALGO_JAVA); + } catch (Throwable t) { + Security.addProvider(new BouncyCastleProvider()); + } + } + @AfterClass + public static void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + + @Test + public void testSingleRecipientGcm() throws Exception { + final String text = "The true sign of intelligence is not knowledge but imagination."; + doTestSingleRecipient(text, + JweJsonProducerTest.SINGLE_RECIPIENT_OUTPUT, + ContentAlgorithm.A128GCM, + JweJsonProducerTest.WRAPPER_BYTES1, + null); + } + @Test + public void testSingleRecipientFlatGcm() throws Exception { + final String text = "The true sign of intelligence is not knowledge but imagination."; + doTestSingleRecipient(text, + JweJsonProducerTest.SINGLE_RECIPIENT_FLAT_OUTPUT, + ContentAlgorithm.A128GCM, + JweJsonProducerTest.WRAPPER_BYTES1, + null); + } + @Test + public void testSingleRecipientDirectGcm() throws Exception { + final String text = "The true sign of intelligence is not knowledge but imagination."; + doTestSingleRecipient(text, + JweJsonProducerTest.SINGLE_RECIPIENT_DIRECT_OUTPUT, + ContentAlgorithm.A128GCM, + null, + JweJsonProducerTest.CEK_BYTES); + } + @Test + public void testSingleRecipientDirectA128CBCHS256() throws Exception { + String text = "Live long and prosper."; + doTestSingleRecipient(text, + JweJsonProducerTest.SINGLE_RECIPIENT_A128CBCHS256_DIRECT_OUTPUT, + ContentAlgorithm.A128CBC_HS256, + null, + JweCompactReaderWriterTest.CONTENT_ENCRYPTION_KEY_A3); + } + @Test + public void testSingleRecipientA128CBCHS256() throws Exception { + String text = "Live long and prosper."; + doTestSingleRecipient(text, + JweJsonProducerTest.SINGLE_RECIPIENT_A128CBCHS256_OUTPUT, + ContentAlgorithm.A128CBC_HS256, + Base64UrlUtility.decode(JweCompactReaderWriterTest.KEY_ENCRYPTION_KEY_A3), + null); + } + @Test + public void testSingleRecipientAllTypeOfHeadersAndAad() { + if (SKIP_AES_GCM_TESTS) { + return; + } + final String text = "The true sign of intelligence is not knowledge but imagination."; + + SecretKey wrapperKey = CryptoUtils.createSecretKeySpec(JweJsonProducerTest.WRAPPER_BYTES1, + "AES"); + JweDecryptionProvider jwe = JweUtils.createJweDecryptionProvider(wrapperKey, + KeyAlgorithm.A128KW, + ContentAlgorithm.A128GCM); + JweJsonConsumer consumer = new JweJsonConsumer(JweJsonProducerTest.SINGLE_RECIPIENT_ALL_HEADERS_AAD_OUTPUT); + JweDecryptionOutput out = consumer.decryptWith(jwe); + assertEquals(text, out.getContentText()); + assertEquals(JweJsonProducerTest.EXTRA_AAD_SOURCE, consumer.getAadText()); + } + @Test + public void testSingleRecipientAllTypeOfHeadersAndAadModified() { + if (SKIP_AES_GCM_TESTS) { + return; + } + SecretKey wrapperKey = CryptoUtils.createSecretKeySpec(JweJsonProducerTest.WRAPPER_BYTES1, + "AES"); + JweDecryptionProvider jwe = JweUtils.createJweDecryptionProvider(wrapperKey, + KeyAlgorithm.A128KW, + ContentAlgorithm.A128GCM); + JweJsonConsumer consumer = new JweJsonConsumer(SINGLE_RECIPIENT_ALL_HEADERS_AAD_MODIFIED_OUTPUT); + try { + consumer.decryptWith(jwe); + fail("AAD check has passed unexpectedly"); + } catch (SecurityException ex) { + // expected + } + + } + private void doTestSingleRecipient(String text, + String input, + ContentAlgorithm contentEncryptionAlgo, + final byte[] wrapperKeyBytes, + final byte[] cek) throws Exception { + if (AlgorithmUtils.A128GCM_ALGO.equals(contentEncryptionAlgo.getJwaName()) && SKIP_AES_GCM_TESTS) { + return; + } + JweDecryptionProvider jwe = null; + if (wrapperKeyBytes != null) { + SecretKey wrapperKey = CryptoUtils.createSecretKeySpec(wrapperKeyBytes, "AES"); + jwe = JweUtils.createJweDecryptionProvider(wrapperKey, + KeyAlgorithm.A128KW, + contentEncryptionAlgo); + } else { + SecretKey cekKey = CryptoUtils.createSecretKeySpec(cek, "AES"); + jwe = JweUtils.getDirectKeyJweDecryption(cekKey, contentEncryptionAlgo); + } + JweJsonConsumer consumer = new JweJsonConsumer(input); + JweDecryptionOutput out = consumer.decryptWith(jwe); + assertEquals(text, out.getContentText()); + } + +} +