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/JweJsonProducerTest.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..c3ac760 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducerTest.java @@ -0,0 +1,328 @@ +/** + * 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.util.LinkedList; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; + +import org.apache.cxf.common.util.Base64UrlUtility; +import org.apache.cxf.common.util.StringUtils; +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 JweJsonProducerTest extends Assert { + 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 SINGLE_RECIPIENT_OUTPUT = + "{" + + "\"protected\":\"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0\"," + + "\"recipients\":" + + "[" + + "{\"encrypted_key\":\"b3-M9_CRgT3wEBhhXlpb-BoY7vtA4W_N\"}" + + "]," + + "\"iv\":\"48V1_ALb6US04U3b\"," + + "\"ciphertext\":\"KTuJBMk9QG59xPB-c_YLM5-J7VG40_eMPvyHDD7eB-WHj_34YiWgpBOydTBm4RW0zUCJZ09xqorhWJME-DcQ\"," + + "\"tag\":\"GxWlwvTPmHi4ZnQgafiHew\"" + + "}"; + static final String SINGLE_RECIPIENT_FLAT_OUTPUT = + "{" + + "\"protected\":\"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0\"," + + "\"encrypted_key\":\"b3-M9_CRgT3wEBhhXlpb-BoY7vtA4W_N\"," + + "\"iv\":\"48V1_ALb6US04U3b\"," + + "\"ciphertext\":\"KTuJBMk9QG59xPB-c_YLM5-J7VG40_eMPvyHDD7eB-WHj_34YiWgpBOydTBm4RW0zUCJZ09xqorhWJME-DcQ\"," + + "\"tag\":\"GxWlwvTPmHi4ZnQgafiHew\"" + + "}"; + static final String SINGLE_RECIPIENT_DIRECT_OUTPUT = + "{" + + "\"protected\":\"eyJlbmMiOiJBMTI4R0NNIn0\"," + + "\"recipients\":" + + "[" + + "{}" + + "]," + + "\"iv\":\"48V1_ALb6US04U3b\"," + + "\"ciphertext\":\"KTuJBMk9QG59xPB-c_YLM5-J7VG40_eMPvyHDD7eB-WHj_34YiWgpBOydTBm4RW0zUCJZ09xqorhWJME-DcQ\"," + + "\"tag\":\"Te59ApbK8wNBDY_1_dgYSw\"" + + "}"; + static final String SINGLE_RECIPIENT_DIRECT_FLAT_OUTPUT = + "{" + + "\"protected\":\"eyJlbmMiOiJBMTI4R0NNIn0\"," + + "\"iv\":\"48V1_ALb6US04U3b\"," + + "\"ciphertext\":\"KTuJBMk9QG59xPB-c_YLM5-J7VG40_eMPvyHDD7eB-WHj_34YiWgpBOydTBm4RW0zUCJZ09xqorhWJME-DcQ\"," + + "\"tag\":\"Te59ApbK8wNBDY_1_dgYSw\"" + + "}"; + static final String SINGLE_RECIPIENT_ALL_HEADERS_AAD_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\"" + + "}"; + static final String MULTIPLE_RECIPIENTS_OUTPUT = + "{" + + "\"protected\":\"eyJlbmMiOiJBMTI4R0NNIn0\"," + + "\"unprotected\":{\"jku\":\"https://server.example.com/keys.jwks\",\"alg\":\"A128KW\"}," + + "\"recipients\":" + + "[" + + "{" + + "\"encrypted_key\":\"b3-M9_CRgT3wEBhhXlpb-BoY7vtA4W_N\"" + + "}," + + "{" + + "\"encrypted_key\":\"6a_nnEYO45qB_Vp6N2QbFQ7Cv1uecbiE\"" + + "}" + + "]," + + "\"aad\":\"WyJ2Y2FyZCIsW1sidmVyc2lvbiIse30sInRleHQiLCI0LjAiXSxbImZuIix7fSwidGV4dCIsIk1lcmlhZG9jIEJyYW5keWJ1Y" + + "2siXSxbIm4iLHt9LCJ0ZXh0IixbIkJyYW5keWJ1Y2siLCJNZXJpYWRvYyIsIk1yLiIsIiJdXSxbImJkYXkiLHt9LCJ0ZXh0" + + "IiwiVEEgMjk4MiJdLFsiZ2VuZGVyIix7fSwidGV4dCIsIk0iXV1d\"," + + "\"iv\":\"48V1_ALb6US04U3b\"," + + "\"ciphertext\":\"KTuJBMk9QG59xPB-c_YLM5-J7VG40_eMPvyHDD7eB-WHj_34YiWgpBOydTBm4RW0zUCJZ09xqorhWJME-DcQ\"," + + "\"tag\":\"oVUQGS9608D-INq61-vOaA\"" + + "}"; + static final String EXTRA_AAD_SOURCE = + "[\"vcard\",[" + + "[\"version\",{},\"text\",\"4.0\"]," + + "[\"fn\",{},\"text\",\"Meriadoc Brandybuck\"]," + + "[\"n\",{},\"text\",[\"Brandybuck\",\"Meriadoc\",\"Mr.\",\"\"]]," + + "[\"bday\",{},\"text\",\"TA 2982\"]," + + "[\"gender\",{},\"text\",\"M\"]" + + "]]"; + static final String SINGLE_RECIPIENT_A128CBCHS256_OUTPUT = + "{" + + "\"protected\":\"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," + + "\"recipients\":" + + "[" + + "{\"encrypted_key\":\"6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ\"}" + + "]," + + "\"iv\":\"AxY8DCtDaGlsbGljb3RoZQ\"," + + "\"ciphertext\":\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," + + "\"tag\":\"U0m_YmjN04DJvceFICbCVQ\"" + + "}"; + static final String SINGLE_RECIPIENT_A128CBCHS256_DIRECT_OUTPUT = + "{" + + "\"protected\":\"eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\"," + + "\"recipients\":" + + "[" + + "{}" + + "]," + + "\"iv\":\"AxY8DCtDaGlsbGljb3RoZQ\"," + + "\"ciphertext\":\"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY\"," + + "\"tag\":\"Mz-VPPyU4RlcuYv1IwIvzw\"" + + "}"; + + 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, SINGLE_RECIPIENT_OUTPUT, ContentAlgorithm.A128GCM, + WRAPPER_BYTES1, JweCompactReaderWriterTest.INIT_VECTOR_A1, + CEK_BYTES, false); + } + @Test + public void testSingleRecipientDirectGcm() throws Exception { + final String text = "The true sign of intelligence is not knowledge but imagination."; + doTestSingleRecipient(text, SINGLE_RECIPIENT_DIRECT_OUTPUT, ContentAlgorithm.A128GCM, + null, JweCompactReaderWriterTest.INIT_VECTOR_A1, + CEK_BYTES, false); + } + @Test + public void testSingleRecipientDirectFlatGcm() throws Exception { + final String text = "The true sign of intelligence is not knowledge but imagination."; + doTestSingleRecipient(text, SINGLE_RECIPIENT_DIRECT_FLAT_OUTPUT, ContentAlgorithm.A128GCM, + null, JweCompactReaderWriterTest.INIT_VECTOR_A1, + CEK_BYTES, true); + } + @Test + public void testSingleRecipientFlatGcm() throws Exception { + final String text = "The true sign of intelligence is not knowledge but imagination."; + doTestSingleRecipient(text, SINGLE_RECIPIENT_FLAT_OUTPUT, ContentAlgorithm.A128GCM, + WRAPPER_BYTES1, JweCompactReaderWriterTest.INIT_VECTOR_A1, + CEK_BYTES, true); + } + @Test + public void testSingleRecipientA128CBCHS256() throws Exception { + String text = "Live long and prosper."; + doTestSingleRecipient(text, SINGLE_RECIPIENT_A128CBCHS256_OUTPUT, ContentAlgorithm.A128CBC_HS256, + Base64UrlUtility.decode(JweCompactReaderWriterTest.KEY_ENCRYPTION_KEY_A3), + JweCompactReaderWriterTest.INIT_VECTOR_A3, + JweCompactReaderWriterTest.CONTENT_ENCRYPTION_KEY_A3, + false); + } + @Test + public void testSingleRecipientDirectA128CBCHS256() throws Exception { + String text = "Live long and prosper."; + doTestSingleRecipient(text, SINGLE_RECIPIENT_A128CBCHS256_DIRECT_OUTPUT, ContentAlgorithm.A128CBC_HS256, + null, + JweCompactReaderWriterTest.INIT_VECTOR_A3, + JweCompactReaderWriterTest.CONTENT_ENCRYPTION_KEY_A3, + false); + } + + private String doTestSingleRecipient(String text, + String expectedOutput, + ContentAlgorithm contentEncryptionAlgo, + final byte[] wrapperKeyBytes, + final byte[] iv, + final byte[] cek, + boolean canBeFlat) throws Exception { + if (contentEncryptionAlgo.equals(ContentAlgorithm.A128GCM) && SKIP_AES_GCM_TESTS) { + return null; + } + JweHeaders headers = new JweHeaders(KeyAlgorithm.A128KW, + contentEncryptionAlgo); + JweEncryptionProvider jwe = null; + if (wrapperKeyBytes == null) { + headers.asMap().remove("alg"); + SecretKey cekKey = CryptoUtils.createSecretKeySpec(cek, "AES"); + jwe = JweUtils.getDirectKeyJweEncryption(cekKey, contentEncryptionAlgo); + } else { + SecretKey wrapperKey = CryptoUtils.createSecretKeySpec(wrapperKeyBytes, "AES"); + jwe = JweUtils.createJweEncryptionProvider(wrapperKey, headers); + } + JweJsonProducer p = new JweJsonProducer(headers, StringUtils.toBytesUTF8(text), canBeFlat) { + protected JweEncryptionInput createEncryptionInput(JweHeaders jsonHeaders) { + JweEncryptionInput input = super.createEncryptionInput(jsonHeaders); + input.setCek(cek); + input.setIv(iv); + return input; + } + }; + String jweJson = p.encryptWith(jwe); + assertEquals(expectedOutput, jweJson); + return jweJson; + } + @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(WRAPPER_BYTES1, "AES"); + + JweHeaders protectedHeaders = new JweHeaders(ContentAlgorithm.A128GCM); + JweHeaders sharedUnprotectedHeaders = new JweHeaders(); + sharedUnprotectedHeaders.setJsonWebKeysUrl("https://server.example.com/keys.jwks"); + + JweEncryptionProvider jwe = JweUtils.createJweEncryptionProvider(wrapperKey, + KeyAlgorithm.A128KW, + ContentAlgorithm.A128GCM, + null); + JweJsonProducer p = new JweJsonProducer(protectedHeaders, + sharedUnprotectedHeaders, + StringUtils.toBytesUTF8(text), + StringUtils.toBytesUTF8(EXTRA_AAD_SOURCE), + false) { + protected JweEncryptionInput createEncryptionInput(JweHeaders jsonHeaders) { + JweEncryptionInput input = super.createEncryptionInput(jsonHeaders); + input.setCek(CEK_BYTES); + input.setIv(JweCompactReaderWriterTest.INIT_VECTOR_A1); + return input; + } + }; + JweHeaders recepientUnprotectedHeaders = new JweHeaders(); + recepientUnprotectedHeaders.setKeyEncryptionAlgorithm(KeyAlgorithm.A128KW); + String jweJson = p.encryptWith(jwe, recepientUnprotectedHeaders); + assertEquals(SINGLE_RECIPIENT_ALL_HEADERS_AAD_OUTPUT, jweJson); + } + @Test + public void testMultipleRecipients() { + if (SKIP_AES_GCM_TESTS) { + return; + } + 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"); + + JweHeaders protectedHeaders = new JweHeaders(ContentAlgorithm.A128GCM); + JweHeaders sharedUnprotectedHeaders = new JweHeaders(); + sharedUnprotectedHeaders.setJsonWebKeysUrl("https://server.example.com/keys.jwks"); + sharedUnprotectedHeaders.setKeyEncryptionAlgorithm(KeyAlgorithm.A128KW); + + List<JweEncryptionProvider> jweList = new LinkedList<JweEncryptionProvider>(); + + KeyEncryptionProvider keyEncryption1 = + JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey1, KeyAlgorithm.A128KW); + ContentEncryptionProvider contentEncryption = + JweUtils.getContentEncryptionAlgorithm(AlgorithmUtils.A128GCM_ALGO); + JweEncryptionProvider jwe1 = new JweEncryption(keyEncryption1, contentEncryption); + KeyEncryptionProvider keyEncryption2 = + JweUtils.getSecretKeyEncryptionAlgorithm(wrapperKey2, KeyAlgorithm.A128KW); + JweEncryptionProvider jwe2 = new JweEncryption(keyEncryption2, contentEncryption); + jweList.add(jwe1); + jweList.add(jwe2); + + JweJsonProducer p = new JweJsonProducer(protectedHeaders, + sharedUnprotectedHeaders, + StringUtils.toBytesUTF8(text), + StringUtils.toBytesUTF8(EXTRA_AAD_SOURCE), + false) { + protected JweEncryptionInput createEncryptionInput(JweHeaders jsonHeaders) { + JweEncryptionInput input = super.createEncryptionInput(jsonHeaders); + input.setCek(CEK_BYTES); + input.setIv(JweCompactReaderWriterTest.INIT_VECTOR_A1); + return input; + } + }; + + String jweJson = p.encryptWith(jweList); + assertEquals(MULTIPLE_RECIPIENTS_OUTPUT, jweJson); + } +} +
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/JwePbeHmacAesWrapTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java new file mode 100644 index 0000000..b8aad88 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java @@ -0,0 +1,86 @@ +/** + * 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 org.apache.cxf.rs.security.jose.jwa.ContentAlgorithm; +import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class JwePbeHmacAesWrapTest extends Assert { + 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)); + } + @Before + public void registerBouncyCastleIfNeeded() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + } + @After + public void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + @Test + public void testEncryptDecryptPbesHmacAesWrapA128CBCHS256() throws Exception { + final String specPlainText = "Live long and prosper."; + final String password = "Thus from my lips, by yours, my sin is purged."; + KeyEncryptionProvider keyEncryption = + new PbesHmacAesWrapKeyEncryptionAlgorithm(password, KeyAlgorithm.PBES2_HS256_A128KW); + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(ContentAlgorithm.A128CBC_HS256, + keyEncryption); + String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + + PbesHmacAesWrapKeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); + JweDecryptionProvider decryption = new AesCbcHmacJweDecryption(keyDecryption); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + + } + @Test + public void testEncryptDecryptPbesHmacAesWrapAesGcm() throws Exception { + if (SKIP_AES_GCM_TESTS) { + return; + } + final String specPlainText = "Live long and prosper."; + JweHeaders headers = new JweHeaders(); + headers.setKeyEncryptionAlgorithm(KeyAlgorithm.PBES2_HS256_A128KW); + headers.setContentEncryptionAlgorithm(ContentAlgorithm.A128GCM); + final String password = "Thus from my lips, by yours, my sin is purged."; + KeyEncryptionProvider keyEncryption = + new PbesHmacAesWrapKeyEncryptionAlgorithm(password, KeyAlgorithm.PBES2_HS256_A128KW); + JweEncryptionProvider encryption = new JweEncryption(keyEncryption, + new AesGcmContentEncryptionAlgorithm(ContentAlgorithm.A128GCM)); + String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + PbesHmacAesWrapKeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); + JweDecryptionProvider decryption = new JweDecryption(keyDecryption, + new AesGcmContentDecryptionAlgorithm(ContentAlgorithm.A128GCM)); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + + } +} + 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/jwk/JsonWebKeyTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java new file mode 100644 index 0000000..e647d51 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java @@ -0,0 +1,231 @@ +/** + * 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.jwk; + +import java.io.InputStream; +import java.security.Security; +import java.security.cert.X509Certificate; +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.jwa.ContentAlgorithm; +import org.apache.cxf.rs.security.jose.jwa.KeyAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.JweCompactConsumer; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import org.junit.Assert; +import org.junit.Test; + +public class JsonWebKeyTest extends Assert { + private static final String RSA_MODULUS_VALUE = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" + + "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf" + + "0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt" + + "-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw"; + private static final String RSA_PUBLIC_EXP_VALUE = "AQAB"; + private static final String RSA_PRIVATE_EXP_VALUE = "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7d" + + "x5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ4" + + "6pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66" + + "jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q"; + private static final String RSA_FIRST_PRIME_FACTOR_VALUE = "83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQ" + + "BQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9" + + "RzzOGVQzXvNEvn7O0nVbfs"; + private static final String RSA_SECOND_PRIME_FACTOR_VALUE = "3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3" + + "vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfA" + + "ITAG9LUnADun4vIcb6yelxk"; + private static final String RSA_FIRST_PRIME_CRT_VALUE = "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0o" + + "imYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUm" + + "s6rY3Ob8YeiKkTiBj0"; + private static final String RSA_SECOND_PRIME_CRT_VALUE = "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6hu" + + "UUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvW" + + "rX-L18txXw494Q_cgk"; + private static final String RSA_FIRST_CRT_COEFFICIENT_VALUE = "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfm" + + "t0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKF" + + "YItdldUKGzO6Ia6zTKhAVRU"; + private static final String RSA_KID_VALUE = "2011-04-29"; + private static final String EC_CURVE_VALUE = JsonWebKey.EC_CURVE_P256; + private static final String EC_X_COORDINATE_VALUE = "MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4"; + private static final String EC_Y_COORDINATE_VALUE = "4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM"; + private static final String EC_PRIVATE_KEY_VALUE = "870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"; + private static final String EC_KID_VALUE = "1"; + private static final String AES_SECRET_VALUE = "GawgguFyGrWKav7AX4VKUg"; + private static final String AES_KID_VALUE = "AesWrapKey"; + private static final String HMAC_SECRET_VALUE = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3" + + "Yj0iPS4hcgUuTwjAzZr1Z9CAow"; + private static final String HMAC_KID_VALUE = "HMACKey"; + + @Test + public void testPublicSetAsList() throws Exception { + JsonWebKeys jwks = readKeySet("jwkPublicSet.txt"); + List<JsonWebKey> keys = jwks.getKeys(); + assertEquals(3, 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); + JsonWebKey rsaKeyCert = keys.get(2); + assertEquals(3, rsaKeyCert.asMap().size()); + assertEquals(3, rsaKeyCert.getX509Chain().size()); + List<X509Certificate> certs = JwkUtils.toX509CertificateChain(rsaKeyCert); + assertEquals(3, certs.size()); + } + + @Test + public void testPublicSetAsMap() throws Exception { + JsonWebKeys jwks = readKeySet("jwkPublicSet.txt"); + Map<String, JsonWebKey> keysMap = jwks.getKeyIdMap(); + assertEquals(3, keysMap.size()); + + JsonWebKey rsaKey = keysMap.get(RSA_KID_VALUE); + assertEquals(5, rsaKey.asMap().size()); + validatePublicRsaKey(rsaKey); + JsonWebKey ecKey = keysMap.get(EC_KID_VALUE); + assertEquals(6, ecKey.asMap().size()); + validatePublicEcKey(ecKey); + } + + @Test + public void testPrivateSetAsList() throws Exception { + JsonWebKeys jwks = readKeySet("jwkPrivateSet.txt"); + validatePrivateSet(jwks); + } + private void validatePrivateSet(JsonWebKeys jwks) throws Exception { + List<JsonWebKey> 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 testEncryptDecryptPrivateSet() throws Exception { + final String password = "Thus from my lips, by yours, my sin is purged."; + Security.addProvider(new BouncyCastleProvider()); + try { + JsonWebKeys jwks = readKeySet("jwkPrivateSet.txt"); + validatePrivateSet(jwks); + String encryptedKeySet = JwkUtils.encryptJwkSet(jwks, password.toCharArray()); + JweCompactConsumer c = new JweCompactConsumer(encryptedKeySet); + assertEquals("jwk-set+json", c.getJweHeaders().getContentType()); + assertEquals(KeyAlgorithm.PBES2_HS256_A128KW, c.getJweHeaders().getKeyEncryptionAlgorithm()); + assertEquals(ContentAlgorithm.A128CBC_HS256, c.getJweHeaders().getContentEncryptionAlgorithm()); + assertNotNull(c.getJweHeaders().getHeader("p2s")); + assertNotNull(c.getJweHeaders().getHeader("p2c")); + jwks = JwkUtils.decryptJwkSet(encryptedKeySet, password.toCharArray()); + validatePrivateSet(jwks); + } finally { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + } + @Test + public void testEncryptDecryptPrivateKey() throws Exception { + final String password = "Thus from my lips, by yours, my sin is purged."; + final String key = "{\"kty\":\"oct\"," + + "\"alg\":\"A128KW\"," + + "\"k\":\"GawgguFyGrWKav7AX4VKUg\"," + + "\"kid\":\"AesWrapKey\"}"; + Security.addProvider(new BouncyCastleProvider()); + try { + JsonWebKey jwk = readKey(key); + validateSecretAesKey(jwk); + String encryptedKey = JwkUtils.encryptJwkKey(jwk, password.toCharArray()); + JweCompactConsumer c = new JweCompactConsumer(encryptedKey); + assertEquals("jwk+json", c.getJweHeaders().getContentType()); + assertEquals(KeyAlgorithm.PBES2_HS256_A128KW, c.getJweHeaders().getKeyEncryptionAlgorithm()); + assertEquals(ContentAlgorithm.A128CBC_HS256, c.getJweHeaders().getContentEncryptionAlgorithm()); + assertNotNull(c.getJweHeaders().getHeader("p2s")); + assertNotNull(c.getJweHeaders().getHeader("p2c")); + jwk = JwkUtils.decryptJwkKey(encryptedKey, password.toCharArray()); + validateSecretAesKey(jwk); + } finally { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + } + + @Test + public void testSecretSetAsList() throws Exception { + JsonWebKeys jwks = readKeySet("jwkSecretSet.txt"); + List<JsonWebKey> keys = jwks.getKeys(); + assertEquals(2, keys.size()); + JsonWebKey aesKey = keys.get(0); + assertEquals(4, aesKey.asMap().size()); + validateSecretAesKey(aesKey); + JsonWebKey hmacKey = keys.get(1); + assertEquals(4, hmacKey.asMap().size()); + validateSecretHmacKey(hmacKey); + } + + private void validateSecretAesKey(JsonWebKey key) { + assertEquals(AES_SECRET_VALUE, key.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + assertEquals(AES_KID_VALUE, key.getKeyId()); + assertEquals(KeyType.OCTET, key.getKeyType()); + assertEquals(AlgorithmUtils.A128KW_ALGO, key.getAlgorithm()); + } + private void validateSecretHmacKey(JsonWebKey key) { + assertEquals(HMAC_SECRET_VALUE, key.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + assertEquals(HMAC_KID_VALUE, key.getKeyId()); + assertEquals(KeyType.OCTET, key.getKeyType()); + assertEquals(AlgorithmUtils.HMAC_SHA_256_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()); + assertEquals(AlgorithmUtils.RS_SHA_256_ALGO, key.getAlgorithm()); + } + 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.ENCRYPT, 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 = JsonWebKeyTest.class.getResourceAsStream(fileName); + String s = IOUtils.readStringFromStream(is); + return JwkUtils.readJwkSet(s); + } + public JsonWebKey readKey(String key) throws Exception { + return JwkUtils.readJwkKey(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/jwk/JwkUtilsTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JwkUtilsTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JwkUtilsTest.java new file mode 100644 index 0000000..0e5d584 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JwkUtilsTest.java @@ -0,0 +1,94 @@ +/** + * 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.jwk; + +import org.junit.Assert; +import org.junit.Test; + +public class JwkUtilsTest extends Assert { + private static final String RSA_KEY = "{" + + "\"kty\": \"RSA\"," + + "\"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" + + "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" + + "4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" + + "W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" + + "1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" + + "aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\"," + + "\"e\": \"AQAB\"," + + "\"alg\": \"RS256\"," + + "\"kid\": \"2011-04-29\"" + + "}"; + private static final String EC_256_KEY = "{" + + "\"kty\": \"EC\"," + + "\"x\": \"CEuRLUISufhcjrj-32N0Bvl3KPMiHH9iSw4ohN9jxrA\"," + + "\"y\": \"EldWz_iXSK3l_S7n4w_t3baxos7o9yqX0IjzG959vHc\"," + + "\"crv\": \"P-256\"" + + "}"; + private static final String EC_384_KEY = "{" + + "\"kty\": \"EC\"," + + "\"x\": \"2jCG5DmKUql9YPn7F2C-0ljWEbj8O8-vn5Ih1k7Wzb-y3NpBLiG1BiRa392b1kcQ\"," + + "\"y\": \"7Ragi9rT-5tSzaMbJlH_EIJl6rNFfj4V4RyFM5U2z4j1hesX5JXa8dWOsE-5wPIl\"," + + "\"crv\": \"P-384\"" + + "}"; + private static final String EC_521_KEY = "{" + + "\"kty\": \"EC\"," + + "\"x\": \"Aeq3uMrb3iCQEt0PzSeZMmrmYhsKP5DM1oMP6LQzTFQY9-F3Ab45xiK4AJxltXEI-87g3gRwId88hTyHgq180JDt\"," + + "\"y\": \"ARA0lIlrZMEzaXyXE4hjEkc50y_JON3qL7HSae9VuWpOv_2kit8p3pyJBiRb468_U5ztLT7FvDvtimyS42trhDTu\"," + + "\"crv\": \"P-521\"" + + "}"; + private static final String OCTET_KEY_1 = "{" + + "\"kty\": \"oct\"," + + "\"k\": \"ZW8Eg8TiwoT2YamLJfC2leYpLgLmUAh_PcMHqRzBnMg\"" + + "}"; + private static final String OCTET_KEY_2 = "{" + + "\"kty\": \"oct\"," + + "\"k\": \"NGbwp1rC4n85A1SaNxoHow\"" + + "}"; + @Test + public void testRsaKeyThumbprint() throws Exception { + String thumbprint = JwkUtils.getThumbprint(RSA_KEY); + assertEquals("NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs", thumbprint); + } + @Test + public void testOctetKey1Thumbprint() throws Exception { + String thumbprint = JwkUtils.getThumbprint(OCTET_KEY_1); + assertEquals("7WWD36NF4WCpPaYtK47mM4o0a5CCeOt01JXSuMayv5g", thumbprint); + } + @Test + public void testOctetKey2Thumbprint() throws Exception { + String thumbprint = JwkUtils.getThumbprint(OCTET_KEY_2); + assertEquals("5_qb56G0OJDw-lb5mkDaWS4MwuY0fatkn9LkNqUHqMk", thumbprint); + } + @Test + public void testEc256KeyThumbprint() throws Exception { + String thumbprint = JwkUtils.getThumbprint(EC_256_KEY); + assertEquals("j4UYwo9wrtllSHaoLDJNh7MhVCL8t0t8cGPPzChpYDs", thumbprint); + } + @Test + public void testEc384KeyThumbprint() throws Exception { + String thumbprint = JwkUtils.getThumbprint(EC_384_KEY); + assertEquals("vZtaWIw-zw95JNzzURg1YB7mWNLlm44YZDZzhrPNetM", thumbprint); + } + @Test + public void testEc521KeyThumbprint() throws Exception { + String thumbprint = JwkUtils.getThumbprint(EC_521_KEY); + assertEquals("rz4Ohmpxg-UOWIWqWKHlOe0bHSjNUFlHW5vwG_M7qYg", thumbprint); + } + +} 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/jwk/jwkPrivateSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt new file mode 100644 index 0000000..cb30c04 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt @@ -0,0 +1,23 @@ +{"keys": + [ + {"kty":"EC", + "crv":"P-256", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", + "use":"enc", + "kid":"1"}, + + {"kty":"RSA", + "n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e":"AQAB", + "d":"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", + "p":"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", + "q":"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", + "dp":"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq":"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "qi":"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "alg":"RS256", + "kid":"2011-04-29"} + ] + } 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/jwk/jwkPublicSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt new file mode 100644 index 0000000..2fd93f6 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt @@ -0,0 +1,27 @@ +{"keys": + [ + {"kty":"EC", + "crv":"P-256", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "use":"enc", + "kid":"1"}, + + {"kty":"RSA", + "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", + "e":"AQAB", + "alg":"RS256", + "kid":"2011-04-29"}, + + { + "kty":"RSA", + "kid":"RSACerts", + "x5c": [ + "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 tdEy8W9ViH0Pd" + ] + } + + ] + } \ 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/jwk/jwkSecretSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt new file mode 100644 index 0000000..6520c75 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt @@ -0,0 +1,13 @@ +{"keys": + [ + {"kty":"oct", + "alg":"A128KW", + "k":"GawgguFyGrWKav7AX4VKUg", + "kid":"AesWrapKey"}, + + {"kty":"oct", + "alg":"HS256", + "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", + "kid":"HMACKey"} + ] + } \ 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/jws/JwsCompactHeaderTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java new file mode 100644 index 0000000..39c6d3c --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java @@ -0,0 +1,203 @@ +/** + * 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.jws; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + +import org.junit.Assert; +import org.junit.Test; + +public class JwsCompactHeaderTest extends Assert { + + /** + * JWS string, which lacks the "alg" header field. + * + * => Must be rejected by verification operation, since the spec declares + * that the "alg" header field must be present in the compact serialization. + */ + public static final String MISSING_ALG_HEADER_FIELD_IN_JWS = + "eyAiZ2xhIiA6ICJDQU1IIiB9.eyAibXNnIjogIllvdSBjYW4ndCB0b3VjaCB0aGlzISIgfQ" + + ".Sqd_AuwlPPqv4L1EV4zPuR-HfFJpe9kOfvc597RlcoE"; + + /** + * JWS string, which contains two "alg" header fields. Bogus "alg" header + * field first. + * + * => Must be rejected by verification operation, since the spec declares + * that the "alg" header field must be present once in the compact + * serialization. + */ + public static final String TWO_ALG_HEADER_FIELDS_IN_JWS_BOGUS_FIRST = + "eyAiYWxnIjogIkJvZ3VzIiwgImFsZyI6ICJIUzI1NiIgfQ.eyAibXNnIjogIllvdSBjYW4ndCB0b3VjaCB0aGlzISIgfQ" + + ".FIgpDi1Wp9iIxxXfBw8Zce2kiZ8gmqAaVYPduRFR8kU"; + + /** + * JWS string, which contains two "alg" header fields. Bogus "alg" header + * field last. + * + * => Must be rejected by verification operation, since the spec declares + * that the "alg" header field must be present once in the compact + * serialization. + */ + public static final String TWO_ALG_HEADER_FIELDS_IN_JWS_BOGUS_LAST = + "eyAiYWxnIjogIkhTMjU2IiwgImFsZyI6ICJCb2d1cyIgfQ.eyAibXNnIjogIllvdSBjYW4ndCB0b3VjaCB0aGlzISIgfQ" + + ".Ftwla-nAg0Nty8ILVhjlIETOy2Tw1JsD3bBq55AS0PU"; + + /** + * JWS string, which contains an invalid "alg" header field value. + * + * (1): Algorithm not supported/known + * + * => Must be rejected by verification operation, since the spec declares + * that the signature is not valid if the "alg" value does not represent a + * supported algorithm. "alg" values should either be registered in the IANA + * JSON Web Signature and Encryption Algorithms registry defined in JWA or + * be a value that contains a Collision-Resistant Name. + */ + public static final String INVALID_ALG_HEADER_VALUE_IN_JWS_1 = "tba"; + + /** + * JWS string, which contains an invalid "alg" header field value. + * + * (2): Wrong value encoding + * + * => Must be rejected by verification operation, since the spec declares + * that the "alg" value is a case-sensitive string containing a StringOrURI + * value. + */ + public static final String INVALID_ALG_HEADER_VALUE_IN_JWS_2 = "tba"; + + /** + * JWS string, which contains a "alg" header field value of "none". The + * signature has been generated with "HS256" and the signed JWS has been + * altered afterwards to the value "none". + * + * => Must be rejected by verification operation, since the "none" algorithm + * is considered harmful. + */ + public static final String ALG_HEADER_VALUE_HS256_IN_JWS = + "eyAiYWxnIjogIkhTMjU2IiB9" + + ".eyAibXNnIjogIllvdSBjYW4ndCB0b3VjaCB0aGlzISIgfQ" + + ".as_gclokwAmukh3zVF1X5sUCCfSc8TbjDdhdvk6C5c8"; + public static final String ALG_HEADER_VALUE_NONE_IN_JWS = + "eyAiYWxnIjogIm5vbmUiIH0" + + ".eyAibXNnIjogIllvdSBjYW4ndCB0b3VjaCB0aGlzISIgfQ" + + ".as_gclokwAmukh3zVF1X5sUCCfSc8TbjDdhdvk6C5c8"; + + + /** + * Support material (keys, etc.) + */ + private static final String ENCODED_MAC_KEY = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75" + + "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"; + + // JWS string, which contains crit header field + // JWS string, which contains more than three parts + // JWS string, which contains less than three parts + // JWS string, which contains null bytes padding + + @Test + public void verifyJwsWithMissingAlgHeaderField() throws Exception { + JwsCompactConsumer jwsConsumer = new JwsCompactConsumer(MISSING_ALG_HEADER_FIELD_IN_JWS); + + assertFalse(jwsConsumer.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256))); + } + + @Test + public void verifyJwsWithTwoAlgHeaderFieldsBogusFieldFirst() throws Exception { + JwsCompactConsumer jwsConsumer = new JwsCompactConsumer(TWO_ALG_HEADER_FIELDS_IN_JWS_BOGUS_FIRST); + + boolean result = jwsConsumer.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256)); + assertFalse(result); + } + + @Test + public void verifyJwsWithTwoAlgHeaderFieldsBogusFieldLast() throws Exception { + JwsCompactConsumer jwsConsumer = new JwsCompactConsumer(TWO_ALG_HEADER_FIELDS_IN_JWS_BOGUS_LAST); + + assertFalse(jwsConsumer.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256))); + } + + @Test + public void verifyJwsWithAlgHeaderValueNone() throws Exception { + JwsCompactConsumer jwsConsumerOriginal = new JwsCompactConsumer(ALG_HEADER_VALUE_HS256_IN_JWS); + + JwsCompactConsumer jwsConsumerAltered = new JwsCompactConsumer(ALG_HEADER_VALUE_NONE_IN_JWS); + + assertTrue(jwsConsumerOriginal.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256))); + + assertFalse(jwsConsumerAltered.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256))); + } + + @Test + public void testCriticalHeader() { + String payload = "this is a JWS with critical header"; + String criticalParameter = "criticalParameter"; + String criticalParameter1 = "criticalParameter1"; + String criticalParameter2 = "criticalParameter2"; + String criticalParameter3 = "criticalParameter3"; + String criticalValue = "criticalValue"; + String criticalValue1 = "criticalValue1"; + String criticalValue2 = "criticalValue2"; + String criticalValue3 = "criticalValue3"; + JwsCompactProducer producer = new JwsCompactProducer(payload); + producer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.HS512); + List<String> criticalHeader = new ArrayList<String>(); + criticalHeader.add(criticalParameter1); + producer.getJwsHeaders().setCritical(criticalHeader); + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + String signedJws = producer.getSignedEncodedJws(); + JwsCompactConsumer consumer = new JwsCompactConsumer(signedJws); + assertFalse(consumer.validateCriticalHeaders()); + + criticalHeader.add(criticalParameter2); + criticalHeader.add(criticalParameter3); + producer = new JwsCompactProducer(payload); + producer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.HS512); + producer.getJwsHeaders().setCritical(criticalHeader); + producer.getJwsHeaders().setHeader(criticalParameter1, criticalValue1); + producer.getJwsHeaders().setHeader(criticalParameter2, criticalValue2); + producer.getJwsHeaders().setHeader(criticalParameter3, criticalValue3); + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + signedJws = producer.getSignedEncodedJws(); + consumer = new JwsCompactConsumer(signedJws); + assertTrue(consumer.validateCriticalHeaders()); + + criticalHeader = new ArrayList<String>(); + criticalHeader.add(criticalParameter); + criticalHeader.add(criticalParameter); + producer = new JwsCompactProducer(payload); + producer.getJwsHeaders().setSignatureAlgorithm(SignatureAlgorithm.HS512); + producer.getJwsHeaders().setHeader(criticalParameter, criticalValue); + producer.getJwsHeaders().setCritical(criticalHeader); + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + signedJws = producer.getSignedEncodedJws(); + consumer = new JwsCompactConsumer(signedJws); + assertFalse(consumer.validateCriticalHeaders()); + } +} + 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/jws/JwsCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java new file mode 100644 index 0000000..7292a52 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java @@ -0,0 +1,317 @@ +/** + * 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.jws; + +import java.security.PrivateKey; +import java.security.Security; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.rs.security.jose.common.JoseConstants; +import org.apache.cxf.rs.security.jose.common.JoseHeaders; +import org.apache.cxf.rs.security.jose.common.JoseType; +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.KeyOperation; +import org.apache.cxf.rs.security.jose.jwk.KeyType; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReaderWriter; +import org.apache.cxf.rt.security.crypto.CryptoUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import org.junit.Assert; +import org.junit.Test; + +public class JwsCompactReaderWriterTest extends Assert { + + public static final String TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD = + "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9..GsyM6AQJbQHY8aQKCbZSPJHzMRWo3HKIlcDuXof7nqs"; + public static final String UNSIGNED_PLAIN_DOCUMENT = "$.02"; + + public static final String ENCODED_TOKEN_SIGNED_BY_MAC = + "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9" + + ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + + ".dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + + + private static final String ENCODED_MAC_KEY = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75" + + "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"; + + private static final String ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC = + "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIU" + + "zI1NiIsDQogImp3ayI6eyJrdHkiOiJvY3QiLA0KICJrZXlfb3BzIjpbDQogInNpZ24iLA0KICJ2ZXJpZnkiDQogXX19" + + ".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + + ".8cFZqb15gEDYRZqSzUu23nQnKNynru1ADByRPvmmOq8"; + + private static final String RSA_MODULUS_ENCODED = "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddx" + + "HmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMs" + + "D1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSH" + + "SXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdV" + + "MTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8" + + "NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ"; + private static final String RSA_PUBLIC_EXPONENT_ENCODED = "AQAB"; + private static final String RSA_PRIVATE_EXPONENT_ENCODED = + "Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97I" + + "jlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0" + + "BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn" + + "439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYT" + + "CBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLh" + + "BOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ"; + private static final String ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY = + "eyJhbGciOiJSUzI1NiJ9" + + "." + + "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt" + + "cGxlLmNvbS9pc19yb290Ijp0cnVlfQ" + + "." + + "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7" + + "AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4" + + "BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K" + + "0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqv" + + "hJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrB" + + "p0igcN_IoypGlUPQGe77Rw"; + + private static final String EC_PRIVATE_KEY_ENCODED = + "jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI"; + private static final String EC_X_POINT_ENCODED = + "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU"; + private static final String EC_Y_POINT_ENCODED = + "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"; + + private static boolean isJava6() { + String version = System.getProperty("java.version"); + return 1.6D == Double.parseDouble(version.substring(0, 3)); + } + + @Test + public void testWriteJwsSignedByMacSpecExample() throws Exception { + JwsHeaders headers = new JwsHeaders(); + headers.setType(JoseType.JWT); + headers.setSignatureAlgorithm(SignatureAlgorithm.HS256); + JwsCompactProducer jws = initSpecJwtTokenWriter(headers); + jws.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + + assertEquals(ENCODED_TOKEN_SIGNED_BY_MAC, jws.getSignedEncodedJws()); + } + @Test + public void testWriteReadJwsUnencodedPayload() throws Exception { + JwsHeaders headers = new JwsHeaders(SignatureAlgorithm.HS256); + headers.setPayloadEncodingStatus(false); + JwsCompactProducer producer = new JwsCompactProducer(headers, + UNSIGNED_PLAIN_DOCUMENT, + true); + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + assertEquals(TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD, producer.getSignedEncodedJws()); + JwsCompactConsumer consumer = + new JwsCompactConsumer(TOKEN_WITH_DETACHED_UNENCODED_PAYLOAD, UNSIGNED_PLAIN_DOCUMENT); + + assertTrue(consumer.verifySignatureWith( + new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, SignatureAlgorithm.HS256))); + } + @Test + public void testWriteReadJwsUnsigned() throws Exception { + JwsHeaders headers = new JwsHeaders(JoseType.JWT); + headers.setSignatureAlgorithm(SignatureAlgorithm.NONE); + + JwtClaims claims = new JwtClaims(); + claims.setIssuer("https://jwt-idp.example.com"); + claims.setSubject("mailto:[email protected]"); + claims.setAudience("https://jwt-rp.example.net"); + claims.setNotBefore(1300815780L); + claims.setExpiryTime(1300819380L); + claims.setClaim("http://claims.example.com/member", true); + + JwsCompactProducer writer = new JwsJwtCompactProducer(headers, claims); + String signed = writer.getSignedEncodedJws(); + + JwsJwtCompactConsumer reader = new JwsJwtCompactConsumer(signed); + assertEquals(0, reader.getDecodedSignature().length); + + JwtToken token = reader.getJwtToken(); + assertEquals(new JwtToken(headers, claims), token); + } + + @Test + public void testReadJwsSignedByMacSpecExample() throws Exception { + JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(ENCODED_TOKEN_SIGNED_BY_MAC); + assertTrue(jws.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256))); + JwtToken token = jws.getJwtToken(); + JwsHeaders headers = new JwsHeaders(token.getHeaders()); + assertEquals(JoseType.JWT, headers.getType()); + assertEquals(SignatureAlgorithm.HS256, headers.getSignatureAlgorithm()); + validateSpecClaim(token.getClaims()); + } + + @Test + public void testWriteJwsWithJwkSignedByMac() throws Exception { + JsonWebKey key = new JsonWebKey(); + key.setKeyType(KeyType.OCTET); + key.setKeyOperation(Arrays.asList( + new KeyOperation[]{KeyOperation.SIGN, KeyOperation.VERIFY})); + doTestWriteJwsWithJwkSignedByMac(key); + } + + @Test + public void testWriteJwsWithJwkAsMapSignedByMac() throws Exception { + Map<String, Object> map = new LinkedHashMap<String, Object>(); + map.put(JsonWebKey.KEY_TYPE, JsonWebKey.KEY_TYPE_OCTET); + map.put(JsonWebKey.KEY_OPERATIONS, + new KeyOperation[]{KeyOperation.SIGN, KeyOperation.VERIFY}); + doTestWriteJwsWithJwkSignedByMac(map); + } + + private void doTestWriteJwsWithJwkSignedByMac(Object jsonWebKey) throws Exception { + JwsHeaders headers = new JwsHeaders(); + headers.setType(JoseType.JWT); + headers.setSignatureAlgorithm(SignatureAlgorithm.HS256); + headers.setHeader(JoseConstants.HEADER_JSON_WEB_KEY, jsonWebKey); + + JwtClaims claims = new JwtClaims(); + claims.setIssuer("joe"); + claims.setExpiryTime(1300819380L); + claims.setClaim("http://example.com/is_root", Boolean.TRUE); + + JwtToken token = new JwtToken(headers, claims); + JwsCompactProducer jws = new JwsJwtCompactProducer(token, getWriter()); + jws.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, SignatureAlgorithm.HS256)); + + assertEquals(ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC, jws.getSignedEncodedJws()); + } + + @Test + public void testReadJwsWithJwkSignedByMac() throws Exception { + JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(ENCODED_TOKEN_WITH_JSON_KEY_SIGNED_BY_MAC); + assertTrue(jws.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY, + SignatureAlgorithm.HS256))); + JwtToken token = jws.getJwtToken(); + JwsHeaders headers = new JwsHeaders(token.getHeaders()); + assertEquals(JoseType.JWT, headers.getType()); + assertEquals(SignatureAlgorithm.HS256, headers.getSignatureAlgorithm()); + + JsonWebKey key = headers.getJsonWebKey(); + assertEquals(KeyType.OCTET, key.getKeyType()); + List<KeyOperation> keyOps = key.getKeyOperation(); + assertEquals(2, keyOps.size()); + assertEquals(KeyOperation.SIGN, keyOps.get(0)); + assertEquals(KeyOperation.VERIFY, keyOps.get(1)); + + validateSpecClaim(token.getClaims()); + } + + private void validateSpecClaim(JwtClaims claims) { + assertEquals("joe", claims.getIssuer()); + assertEquals(Long.valueOf(1300819380), claims.getExpiryTime()); + assertEquals(Boolean.TRUE, claims.getClaim("http://example.com/is_root")); + } + + @Test + public void testWriteJwsSignedByPrivateKey() throws Exception { + JwsHeaders headers = new JwsHeaders(); + headers.setSignatureAlgorithm(SignatureAlgorithm.RS256); + JwsCompactProducer jws = initSpecJwtTokenWriter(headers); + PrivateKey key = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED); + jws.signWith(new PrivateKeyJwsSignatureProvider(key, SignatureAlgorithm.RS256)); + + assertEquals(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY, jws.getSignedEncodedJws()); + } + @Test + public void testJwsPsSha() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + try { + JwsHeaders outHeaders = new JwsHeaders(); + outHeaders.setSignatureAlgorithm(SignatureAlgorithm.PS256); + JwsCompactProducer producer = initSpecJwtTokenWriter(outHeaders); + PrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED); + String signed = producer.signWith( + new PrivateKeyJwsSignatureProvider(privateKey, SignatureAlgorithm.PS256)); + + JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(signed); + RSAPublicKey key = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED, RSA_PUBLIC_EXPONENT_ENCODED); + assertTrue(jws.verifySignatureWith(new PublicKeyJwsSignatureVerifier(key, SignatureAlgorithm.PS256))); + JwtToken token = jws.getJwtToken(); + JwsHeaders inHeaders = new JwsHeaders(token.getHeaders()); + assertEquals(SignatureAlgorithm.PS256, + inHeaders.getSignatureAlgorithm()); + validateSpecClaim(token.getClaims()); + } finally { + Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); + } + } + + @Test + public void testWriteReadJwsSignedByESPrivateKey() throws Exception { + if (isJava6()) { + return; + } + + JwsHeaders headers = new JwsHeaders(); + headers.setSignatureAlgorithm(SignatureAlgorithm.ES256); + JwsCompactProducer jws = initSpecJwtTokenWriter(headers); + ECPrivateKey privateKey = CryptoUtils.getECPrivateKey(JsonWebKey.EC_CURVE_P256, + EC_PRIVATE_KEY_ENCODED); + jws.signWith(new EcDsaJwsSignatureProvider(privateKey, SignatureAlgorithm.ES256)); + String signedJws = jws.getSignedEncodedJws(); + + ECPublicKey publicKey = CryptoUtils.getECPublicKey(JsonWebKey.EC_CURVE_P256, + EC_X_POINT_ENCODED, + EC_Y_POINT_ENCODED); + JwsJwtCompactConsumer jwsConsumer = new JwsJwtCompactConsumer(signedJws); + assertTrue(jwsConsumer.verifySignatureWith(new EcDsaJwsSignatureVerifier(publicKey, + SignatureAlgorithm.ES256))); + JwtToken token = jwsConsumer.getJwtToken(); + JwsHeaders headersReceived = new JwsHeaders(token.getHeaders()); + assertEquals(SignatureAlgorithm.ES256, headersReceived.getSignatureAlgorithm()); + validateSpecClaim(token.getClaims()); + } + + @Test + public void testReadJwsSignedByPrivateKey() throws Exception { + JwsJwtCompactConsumer jws = new JwsJwtCompactConsumer(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY); + RSAPublicKey key = CryptoUtils.getRSAPublicKey(RSA_MODULUS_ENCODED, RSA_PUBLIC_EXPONENT_ENCODED); + assertTrue(jws.verifySignatureWith(new PublicKeyJwsSignatureVerifier(key, SignatureAlgorithm.RS256))); + JwtToken token = jws.getJwtToken(); + JwsHeaders headers = new JwsHeaders(token.getHeaders()); + assertEquals(SignatureAlgorithm.RS256, headers.getSignatureAlgorithm()); + validateSpecClaim(token.getClaims()); + } + + private JwsCompactProducer initSpecJwtTokenWriter(JoseHeaders headers) throws Exception { + + JwtClaims claims = new JwtClaims(); + claims.setIssuer("joe"); + claims.setExpiryTime(1300819380L); + claims.setClaim("http://example.com/is_root", Boolean.TRUE); + + JwtToken token = new JwtToken(headers, claims); + return new JwsJwtCompactProducer(token, getWriter()); + } + + + private JwtTokenReaderWriter getWriter() { + JwtTokenReaderWriter jsonWriter = new JwtTokenReaderWriter(); + jsonWriter.setFormat(true); + return jsonWriter; + } +} 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/jws/JwsJsonConsumerTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java new file mode 100644 index 0000000..a5e35b0 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonConsumerTest.java @@ -0,0 +1,98 @@ +/** + * 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.jws; + +import java.io.InputStream; +import java.util.List; + +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.junit.Assert; +import org.junit.Test; + +public class JwsJsonConsumerTest extends Assert { + private static final String DUAL_SIGNED_DOCUMENT = + "{\"payload\":\n" + + "\t\"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ\",\n" + + "\t\"signatures\":[\n" + + "\t\t\t{\"protected\":\"eyJhbGciOiJSUzI1NiJ9\",\n" + + "\t\t\t \"header\":\n" + + "\t\t\t\t{\"kid\":\"2010-12-29\"},\n" + + "\t\t\t \"signature\":\n" + + "\t\t\t\t\"cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5" + + "jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb" + + "1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOY" + + "EUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw\"},\n" + + "\t\t\t{\"protected\":\"eyJhbGciOiJFUzI1NiJ9\",\n" + + "\t\t\t \"header\":\n" + + "\t\t\t\t{\"kid\":\"e9bc097a-ce51-4036-9562-d2ade882db0d\"},\n" + + "\t\t\t \"signature\":\n" + + "\t\t\t\t\"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q\"}]\n" + + "}"; + + private static final String KID_OF_THE_FIRST_SIGNER = "2010-12-29"; + private static final String KID_OF_THE_SECOND_SIGNER = "e9bc097a-ce51-4036-9562-d2ade882db0d"; + + private static boolean isJava6() { + String version = System.getProperty("java.version"); + return 1.6D == Double.parseDouble(version.substring(0, 3)); + } + + @Test + public void testVerifySignedWithProtectedHeaderOnlyUnencodedPayload() { + JwsJsonConsumer consumer = + new JwsJsonConsumer(JwsJsonProducerTest.SIGNED_JWS_JSON_FLAT_UNENCODED_DOCUMENT); + assertEquals(JwsJsonProducerTest.UNSIGNED_PLAIN_DOCUMENT, consumer.getJwsPayload()); + assertEquals(JwsJsonProducerTest.UNSIGNED_PLAIN_DOCUMENT, consumer.getDecodedJwsPayload()); + assertTrue(consumer.verifySignatureWith( + new HmacJwsSignatureVerifier(JwsJsonProducerTest.ENCODED_MAC_KEY_1, SignatureAlgorithm.HS256))); + } + + @Test + public void testVerifyDualSignedDocument() throws Exception { + if (isJava6()) { + return; + } + + JwsJsonConsumer consumer = new JwsJsonConsumer(DUAL_SIGNED_DOCUMENT); + JsonWebKeys jwks = readKeySet("jwkPublicJsonConsumerSet.txt"); + + List<JwsJsonSignatureEntry> sigEntries = consumer.getSignatureEntries(); + assertEquals(2, sigEntries.size()); + // 1st signature + String firstKid = (String)sigEntries.get(0).getKeyId(); + assertEquals(KID_OF_THE_FIRST_SIGNER, firstKid); + JsonWebKey rsaKey = jwks.getKey(firstKid); + assertNotNull(rsaKey); + assertTrue(sigEntries.get(0).verifySignatureWith(rsaKey)); + // 2nd signature + String secondKid = (String)sigEntries.get(1).getKeyId(); + assertEquals(KID_OF_THE_SECOND_SIGNER, secondKid); + JsonWebKey ecKey = jwks.getKey(secondKid); + assertNotNull(ecKey); + assertTrue(sigEntries.get(1).verifySignatureWith(ecKey)); + } + public JsonWebKeys readKeySet(String fileName) throws Exception { + InputStream is = JwsJsonConsumerTest.class.getResourceAsStream(fileName); + return JwkUtils.readJwkSet(is); + } +} 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/jws/JwsJsonProducerTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProducerTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProducerTest.java new file mode 100644 index 0000000..5895dcb --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsJsonProducerTest.java @@ -0,0 +1,126 @@ +/** + * 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.jws; +import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm; + +import org.junit.Assert; +import org.junit.Test; + +public class JwsJsonProducerTest extends Assert { + + public static final String ENCODED_MAC_KEY_1 = "AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75" + + "aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"; + + public static final String ENCODED_MAC_KEY_2 = "09Y_RK7l5rAY9QY7EblYQNuYbu9cy1j7ovCbkeIyAKN8LIeRL-3H8g" + + "c8kZSYzAQ1uTRC_egZ_8cgZSZa9T5nmQ"; + + public static final String UNSIGNED_PLAIN_JSON_DOCUMENT = "{" + + " \"from\": \"user\"," + " \"to\": \"developer\"," + + " \"msg\": \"good job!\" " + "}"; + + public static final String UNSIGNED_PLAIN_DOCUMENT = "$.02"; + + public static final String UNSIGNED_PLAIN_JSON_DOCUMENT_AS_B64URL = "eyAiZnJvbSI6ICJ1c2VyIiwgInRvIjogI" + + "mRldmVsb3BlciIsICJtc2ciOiAiZ29vZCBqb2IhIiB9"; + + + public static final String SIGNED_JWS_JSON_DOCUMENT = "{" + + "\"payload\":\"" + + UNSIGNED_PLAIN_JSON_DOCUMENT_AS_B64URL + + "\",\"signatures\":[{\"protected\":\"eyJhbGciOiJIUzI1NiJ9\",\"signature\":" + + "\"NNksREOsFCI1nUQEqzCe6XZFa-bRAge2XXMMAU2Jj2I\"}]}"; + + public static final String SIGNED_JWS_JSON_FLAT_DOCUMENT = "{" + + "\"payload\":\"" + + UNSIGNED_PLAIN_JSON_DOCUMENT_AS_B64URL + + "\",\"protected\":\"eyJhbGciOiJIUzI1NiJ9\",\"signature\":" + + "\"NNksREOsFCI1nUQEqzCe6XZFa-bRAge2XXMMAU2Jj2I\"}"; + + public static final String SIGNED_JWS_JSON_FLAT_UNENCODED_DOCUMENT = "{" + + "\"payload\":\"" + UNSIGNED_PLAIN_DOCUMENT + "\"," + + "\"protected\":\"eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9\"," + + "\"signature\":" + "\"GsyM6AQJbQHY8aQKCbZSPJHzMRWo3HKIlcDuXof7nqs\"}"; + + public static final String DUAL_SIGNED_JWS_JSON_DOCUMENT = "{" + + "\"payload\":\"" + + UNSIGNED_PLAIN_JSON_DOCUMENT_AS_B64URL + + "\",\"signatures\":[{\"protected\":\"eyJhbGciOiJIUzI1NiJ9\"," + + "\"signature\":\"NNksREOsFCI1nUQEqzCe6XZFa-bRAge2XXMMAU2Jj2I\"}," + + "{\"protected\":\"eyJhbGciOiJIUzI1NiJ9\"," + + "\"signature\":\"KY2r_Gubar7G86fVyrA7I2-69KA7faKDmebfCCmibdI\"}]}"; + + @Test + public void testSignPlainJsonDocumentPayloadConstruction() { + JwsJsonProducer producer = new JwsJsonProducer(UNSIGNED_PLAIN_JSON_DOCUMENT); + + assertEquals(UNSIGNED_PLAIN_JSON_DOCUMENT_AS_B64URL, + producer.getUnsignedEncodedPayload()); + } + + + @Test + public void testSignWithProtectedHeaderOnly() { + JwsJsonProducer producer = new JwsJsonProducer(UNSIGNED_PLAIN_JSON_DOCUMENT); + JwsHeaders headerEntries = new JwsHeaders(); + headerEntries.setSignatureAlgorithm(SignatureAlgorithm.HS256); + + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY_1, SignatureAlgorithm.HS256), + headerEntries); + assertEquals(SIGNED_JWS_JSON_DOCUMENT, + producer.getJwsJsonSignedDocument()); + } + @Test + public void testSignWithProtectedHeaderOnlyUnencodedPayload() { + JwsJsonProducer producer = new JwsJsonProducer(UNSIGNED_PLAIN_DOCUMENT, true); + JwsHeaders headers = new JwsHeaders(); + headers.setSignatureAlgorithm(SignatureAlgorithm.HS256); + headers.setPayloadEncodingStatus(false); + + + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY_1, SignatureAlgorithm.HS256), + headers); + assertEquals(SIGNED_JWS_JSON_FLAT_UNENCODED_DOCUMENT, + producer.getJwsJsonSignedDocument()); + } + @Test + public void testSignWithProtectedHeaderOnlyFlat() { + JwsJsonProducer producer = new JwsJsonProducer(UNSIGNED_PLAIN_JSON_DOCUMENT, true); + JwsHeaders headerEntries = new JwsHeaders(); + headerEntries.setSignatureAlgorithm(SignatureAlgorithm.HS256); + + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY_1, SignatureAlgorithm.HS256), + headerEntries); + assertEquals(SIGNED_JWS_JSON_FLAT_DOCUMENT, + producer.getJwsJsonSignedDocument()); + } + @Test + public void testDualSignWithProtectedHeaderOnly() { + JwsJsonProducer producer = new JwsJsonProducer(UNSIGNED_PLAIN_JSON_DOCUMENT); + JwsHeaders headerEntries = new JwsHeaders(); + headerEntries.setSignatureAlgorithm(SignatureAlgorithm.HS256); + + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY_1, SignatureAlgorithm.HS256), + headerEntries); + producer.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY_2, SignatureAlgorithm.HS256), + headerEntries); + assertEquals(DUAL_SIGNED_JWS_JSON_DOCUMENT, + producer.getJwsJsonSignedDocument()); + } + +} 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/jws/jwkPublicJsonConsumerSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt new file mode 100644 index 0000000..ba8e723 --- /dev/null +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/jwkPublicJsonConsumerSet.txt @@ -0,0 +1,18 @@ +{"keys": + [ + {"kty":"RSA", + "n": "ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ", + "e":"AQAB", + "alg":"RS256", + "kid":"2010-12-29"}, + + {"kty":"EC", + "alg":"ES256", + "crv":"P-256", + "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0", + "kid":"e9bc097a-ce51-4036-9562-d2ade882db0d" + } + + ] + } \ No newline at end of file
