http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenWriter.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenWriter.java new file mode 100644 index 0000000..a2bd02f --- /dev/null +++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtTokenWriter.java @@ -0,0 +1,30 @@ +/** + * 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 org.apache.cxf.rs.security.jose.JoseHeadersWriter; + + + +public interface JwtTokenWriter extends JoseHeadersWriter { + + String claimsToJson(JwtClaims claims); + JwtTokenJson tokenToJson(JwtToken token); + +}
http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java new file mode 100644 index 0000000..b62dc87 --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java @@ -0,0 +1,211 @@ +/** + * 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.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.common.util.crypto.CryptoUtils; +import org.apache.cxf.rs.security.jose.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.jose.jws.JwsCompactReaderWriterTest; +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 + private 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}; + private static final String RSA_MODULUS_ENCODED_A1 = "oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW" + + "cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S" + + "psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a" + + "sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS" + + "tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj" + + "YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw"; + private static final String RSA_PUBLIC_EXPONENT_ENCODED_A1 = "AQAB"; + private 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"; + + private 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 + private 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}; + private static final byte[] INIT_VECTOR_A3 = { + 3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101}; + private static final String KEY_ENCRYPTION_KEY_A3 = "GawgguFyGrWKav7AX4VKUg"; + private static final String JWE_OUTPUT_A3 = + "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0" + + ".6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ" + + ".AxY8DCtDaGlsbGljb3RoZQ" + + ".KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY" + + ".U0m_YmjN04DJvceFICbCVQ"; + + @BeforeClass + public static void registerBouncyCastleIfNeeded() throws Exception { + try { + // Java 8 apparently has it + Cipher.getInstance(Algorithm.AES_GCM_ALGO_JAVA); + } catch (Throwable t) { + // Oracle Java 7 + Security.addProvider(new BouncyCastleProvider()); + } + } + @AfterClass + public static void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.class.getName()); + } + + @Test + public void testEncryptDecryptAesWrapA128CBCHS256() throws Exception { + final String specPlainText = "Live long and prosper."; + JweHeaders headers = new JweHeaders(); + headers.setAlgorithm(Algorithm.A128KW.getJwtName()); + headers.setContentEncryptionAlgorithm(Algorithm.A128CBC_HS256.getJwtName()); + + byte[] cekEncryptionKey = Base64UrlUtility.decode(KEY_ENCRYPTION_KEY_A3); + + AesWrapKeyEncryptionAlgorithm keyEncryption = + new AesWrapKeyEncryptionAlgorithm(cekEncryptionKey, Algorithm.A128KW.getJwtName()); + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(headers, + 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 testEncryptDecryptAesGcmWrapA128CBCHS256() throws Exception { + final String specPlainText = "Live long and prosper."; + JweHeaders headers = new JweHeaders(); + headers.setAlgorithm(JoseConstants.A128GCMKW_ALGO); + headers.setContentEncryptionAlgorithm(Algorithm.A128CBC_HS256.getJwtName()); + + byte[] cekEncryptionKey = Base64UrlUtility.decode(KEY_ENCRYPTION_KEY_A3); + + AesGcmWrapKeyEncryptionAlgorithm keyEncryption = + new AesGcmWrapKeyEncryptionAlgorithm(cekEncryptionKey, JoseConstants.A128GCMKW_ALGO); + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(headers, + 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 { + 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 { + 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 { + 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 = Algorithm.A128GCM.getJwtName(); + } else { + jwtKeyName = Algorithm.toJwtName(key.getAlgorithm(), key.getEncoded().length * 8); + } + KeyEncryptionAlgorithm keyEncryptionAlgo = new RSAOaepKeyEncryptionAlgorithm(publicKey, + Algorithm.RSA_OAEP.getJwtName()); + ContentEncryptionAlgorithm contentEncryptionAlgo = + new AesGcmContentEncryptionAlgorithm(key == null ? null : key.getEncoded(), INIT_VECTOR_A1, jwtKeyName); + JweEncryptionProvider encryptor = new WrappedKeyJweEncryption(keyEncryptionAlgo, contentEncryptionAlgo); + return encryptor.encrypt(content.getBytes("UTF-8"), null); + } + private String encryptContentDirect(SecretKey key, String content) throws Exception { + DirectKeyJweEncryption encryptor = new DirectKeyJweEncryption( + new AesGcmContentEncryptionAlgorithm(key, INIT_VECTOR_A1, JoseConstants.A128GCM_ALGO)); + 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); + JweDecryptionProvider decryptor = new WrappedKeyJweDecryption(new RSAOaepKeyDecryptionAlgorithm(privateKey), + new AesGcmContentDecryptionAlgorithm()); + String decryptedText = decryptor.decrypt(jweContent).getContentText(); + assertEquals(decryptedText, plainContent); + } + private void decryptDirect(SecretKey key, String jweContent, String plainContent) throws Exception { + DirectKeyJweDecryption decryptor = new DirectKeyJweDecryption(key, new AesGcmContentDecryptionAlgorithm()); + 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/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java new file mode 100644 index 0000000..05d53c2 --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java @@ -0,0 +1,80 @@ +/** + * 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.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +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 { + @Before + public void registerBouncyCastleIfNeeded() throws Exception { + Security.addProvider(new BouncyCastleProvider()); + } + @After + public void unregisterBouncyCastleIfNeeded() throws Exception { + Security.removeProvider(BouncyCastleProvider.class.getName()); + } + @Test + public void testEncryptDecryptPbesHmacAesWrapA128CBCHS256() throws Exception { + final String specPlainText = "Live long and prosper."; + JweHeaders headers = new JweHeaders(); + headers.setAlgorithm(JoseConstants.PBES2_HS256_A128KW_ALGO); + headers.setContentEncryptionAlgorithm(Algorithm.A128CBC_HS256.getJwtName()); + final String password = "Thus from my lips, by yours, my sin is purged."; + KeyEncryptionAlgorithm keyEncryption = + new PbesHmacAesWrapKeyEncryptionAlgorithm(password, JoseConstants.PBES2_HS256_A128KW_ALGO); + JweEncryptionProvider encryption = new AesCbcHmacJweEncryption(headers, 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 { + final String specPlainText = "Live long and prosper."; + JweHeaders headers = new JweHeaders(); + headers.setAlgorithm(JoseConstants.PBES2_HS256_A128KW_ALGO); + headers.setContentEncryptionAlgorithm(Algorithm.A128GCM.getJwtName()); + final String password = "Thus from my lips, by yours, my sin is purged."; + KeyEncryptionAlgorithm keyEncryption = + new PbesHmacAesWrapKeyEncryptionAlgorithm(password, JoseConstants.PBES2_HS256_A128KW_ALGO); + JweEncryptionProvider encryption = new WrappedKeyJweEncryption(headers, + keyEncryption, + new AesGcmContentEncryptionAlgorithm(Algorithm.A128GCM.getJwtName())); + String jweContent = encryption.encrypt(specPlainText.getBytes("UTF-8"), null); + PbesHmacAesWrapKeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); + JweDecryptionProvider decryption = new WrappedKeyJweDecryption(keyDecryption, + new AesGcmContentDecryptionAlgorithm()); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + + } +} + http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java new file mode 100644 index 0000000..eb660ae --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java @@ -0,0 +1,222 @@ +/** + * 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.util.List; +import java.util.Map; + +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.rs.security.jose.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +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(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("jwkPublicSet.txt"); + Map<String, JsonWebKey> keysMap = jwks.getKeyIdMap(); + assertEquals(2, 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 { + 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(Algorithm.PBES2_HS256_A128KW.getJwtName(), c.getJweHeaders().getKeyEncryptionAlgorithm()); + assertEquals(Algorithm.A128CBC_HS256.getJwtName(), 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.class.getName()); + } + } + @Test + public void testEncryptDecryptPrivateKey() throws Exception { + 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(Algorithm.PBES2_HS256_A128KW.getJwtName(), c.getJweHeaders().getKeyEncryptionAlgorithm()); + assertEquals(Algorithm.A128CBC_HS256.getJwtName(), 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.class.getName()); + } + } + + @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.getKid()); + assertEquals(JsonWebKey.KEY_TYPE_OCTET, key.getKeyType()); + assertEquals(JoseConstants.A128KW_ALGO, key.getAlgorithm()); + } + private void validateSecretHmacKey(JsonWebKey key) { + assertEquals(HMAC_SECRET_VALUE, key.getProperty(JsonWebKey.OCTET_KEY_VALUE)); + assertEquals(HMAC_KID_VALUE, key.getKid()); + assertEquals(JsonWebKey.KEY_TYPE_OCTET, key.getKeyType()); + assertEquals(JoseConstants.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.getKid()); + assertEquals(JsonWebKey.KEY_TYPE_RSA, key.getKeyType()); + assertEquals(JoseConstants.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.getKid()); + assertEquals(JsonWebKey.KEY_TYPE_ELLIPTIC, key.getKeyType()); + assertEquals(EC_CURVE_VALUE, key.getProperty(JsonWebKey.EC_CURVE)); + assertEquals(JsonWebKey.PUBLIC_KEY_USE_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/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt b/rt/rs/security/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/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/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt new file mode 100644 index 0000000..5a4a839 --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt @@ -0,0 +1,17 @@ +{"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"} + + ] + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt b/rt/rs/security/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/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/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java new file mode 100644 index 0000000..0cc0a07 --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactHeaderTest.java @@ -0,0 +1,147 @@ +/** + * 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.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))); + } + + @Test + public void verifyJwsWithTwoAlgHeaderFieldsBogusFieldFirst() throws Exception { + JwsCompactConsumer jwsConsumer = new JwsCompactConsumer(TWO_ALG_HEADER_FIELDS_IN_JWS_BOGUS_FIRST); + + assertFalse(jwsConsumer.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY))); + } + + @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))); + } + + @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))); + + assertFalse(jwsConsumerAltered.verifySignatureWith(new HmacJwsSignatureVerifier(ENCODED_MAC_KEY))); + } + + +} + http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java new file mode 100644 index 0000000..bf868bc --- /dev/null +++ b/rt/rs/security/jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java @@ -0,0 +1,256 @@ +/** + * 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.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.common.util.crypto.CryptoUtils; +import org.apache.cxf.rs.security.jose.JoseConstants; +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; +import org.apache.cxf.rs.security.jose.jwt.JwtClaims; +import org.apache.cxf.rs.security.jose.jwt.JwtHeaders; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReaderWriter; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenWriter; + +import org.junit.Assert; +import org.junit.Test; + +public class JwsCompactReaderWriterTest extends Assert { + + 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"; + @Test + public void testWriteJwsSignedByMacSpecExample() throws Exception { + JwtHeaders headers = new JwtHeaders(Algorithm.HmacSHA256.getJwtName()); + JwsCompactProducer jws = initSpecJwtTokenWriter(headers); + jws.signWith(new HmacJwsSignatureProvider(ENCODED_MAC_KEY, Algorithm.HmacSHA256.getJwtName())); + + assertEquals(ENCODED_TOKEN_SIGNED_BY_MAC, jws.getSignedEncodedJws()); + + } + + @Test + public void testWriteReadJwsUnsigned() throws Exception { + JwtHeaders headers = new JwtHeaders(JoseConstants.PLAIN_TEXT_ALGO); + + JwtClaims claims = new JwtClaims(); + claims.setIssuer("https://jwt-idp.example.com"); + claims.setSubject("mailto:m...@example.com"); + 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))); + JwtToken token = jws.getJwtToken(); + JwtHeaders headers = token.getHeaders(); + assertEquals(JoseConstants.TYPE_JWT, headers.getType()); + assertEquals(Algorithm.HmacSHA256.getJwtName(), headers.getAlgorithm()); + validateSpecClaim(token.getClaims()); + } + + @Test + public void testWriteJwsWithJwkSignedByMac() throws Exception { + JsonWebKey key = new JsonWebKey(); + key.setKeyType(JsonWebKey.KEY_TYPE_OCTET); + key.setKeyOperation(Arrays.asList( + new String[]{JsonWebKey.KEY_OPER_SIGN, JsonWebKey.KEY_OPER_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 String[]{JsonWebKey.KEY_OPER_SIGN, JsonWebKey.KEY_OPER_VERIFY}); + doTestWriteJwsWithJwkSignedByMac(map); + } + + private void doTestWriteJwsWithJwkSignedByMac(Object jsonWebKey) throws Exception { + JwtHeaders headers = new JwtHeaders(Algorithm.HmacSHA256.getJwtName()); + + 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, Algorithm.HmacSHA256.getJwtName())); + + 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))); + JwtToken token = jws.getJwtToken(); + JwtHeaders headers = token.getHeaders(); + assertEquals(JoseConstants.TYPE_JWT, headers.getType()); + assertEquals(Algorithm.HmacSHA256.getJwtName(), headers.getAlgorithm()); + + JsonWebKey key = headers.getJsonWebKey(); + assertEquals(JsonWebKey.KEY_TYPE_OCTET, key.getKeyType()); + List<String> keyOps = key.getKeyOperation(); + assertEquals(2, keyOps.size()); + assertEquals(JsonWebKey.KEY_OPER_SIGN, keyOps.get(0)); + assertEquals(JsonWebKey.KEY_OPER_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 { + JwtHeaders headers = new JwtHeaders(); + headers.setAlgorithm(Algorithm.SHA256withRSA.getJwtName()); + JwsCompactProducer jws = initSpecJwtTokenWriter(headers); + PrivateKey key = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED); + jws.signWith(new PrivateKeyJwsSignatureProvider(key, Algorithm.SHA256withRSA.getJwtName())); + + assertEquals(ENCODED_TOKEN_SIGNED_BY_PRIVATE_KEY, jws.getSignedEncodedJws()); + } + + @Test + public void testWriteReadJwsSignedByESPrivateKey() throws Exception { + JwtHeaders headers = new JwtHeaders(); + headers.setAlgorithm(Algorithm.SHA256withECDSA.getJwtName()); + JwsCompactProducer jws = initSpecJwtTokenWriter(headers); + ECPrivateKey privateKey = CryptoUtils.getECPrivateKey(JsonWebKey.EC_CURVE_P256, + EC_PRIVATE_KEY_ENCODED); + jws.signWith(new EcDsaJwsSignatureProvider(privateKey, Algorithm.SHA256withECDSA.getJwtName())); + 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))); + JwtToken token = jwsConsumer.getJwtToken(); + JwtHeaders headersReceived = token.getHeaders(); + assertEquals(Algorithm.SHA256withECDSA.getJwtName(), headersReceived.getAlgorithm()); + 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))); + JwtToken token = jws.getJwtToken(); + JwtHeaders headers = token.getHeaders(); + assertEquals(Algorithm.SHA256withRSA.getJwtName(), headers.getAlgorithm()); + validateSpecClaim(token.getClaims()); + } + + private JwsCompactProducer initSpecJwtTokenWriter(JwtHeaders 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 JwtTokenWriter getWriter() { + JwtTokenReaderWriter jsonWriter = new JwtTokenReaderWriter(); + jsonWriter.setFormat(true); + return jsonWriter; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/pom.xml ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/pom.xml b/rt/rs/security/oauth-parent/oauth2-jose/pom.xml index f372d4c..4c6858c 100644 --- a/rt/rs/security/oauth-parent/oauth2-jose/pom.xml +++ b/rt/rs/security/oauth-parent/oauth2-jose/pom.xml @@ -37,11 +37,9 @@ <version>${project.version}</version> </dependency> <dependency> - <groupId>org.bouncycastle</groupId> - <artifactId>bcprov-ext-jdk15on</artifactId> - <version>${cxf.bcprov.version}</version> - <scope>provided</scope> - <optional>true</optional> + <groupId>org.apache.cxf</groupId> + <artifactId>cxf-rt-rs-security-jose</artifactId> + <version>${project.version}</version> </dependency> <!--test dependencies--> <dependency> http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObject.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObject.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObject.java deleted file mode 100644 index 1db9f1c..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObject.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * 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; - -import java.util.LinkedHashMap; -import java.util.Map; - -public abstract class AbstractJoseObject { - protected Map<String, Object> values = new LinkedHashMap<String, Object>(); - - protected AbstractJoseObject() { - - } - - protected AbstractJoseObject(Map<String, Object> values) { - this.values = values; - } - - protected void setValue(String name, Object value) { - values.put(name, value); - } - - protected Object getValue(String name) { - return values.get(name); - } - - public Map<String, Object> asMap() { - return new LinkedHashMap<String, Object>(values); - } - - protected Long getLongDate(String name) { - Object object = getValue(name); - return object instanceof Long ? (Long)object : Long.valueOf(object.toString()); - } - - public int hashCode() { - return values.hashCode(); - } - - public boolean equals(Object obj) { - return obj instanceof AbstractJoseObject && ((AbstractJoseObject)obj).values.equals(this.values); - } - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObjectReaderWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObjectReaderWriter.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObjectReaderWriter.java deleted file mode 100644 index d1cbcb7..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/AbstractJoseObjectReaderWriter.java +++ /dev/null @@ -1,230 +0,0 @@ -/** - * 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; - -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.apache.cxf.rs.security.jose.jwt.JwtConstants; - - - -public class AbstractJoseObjectReaderWriter { - private static final Set<String> DATE_PROPERTIES = - new HashSet<String>(Arrays.asList(JwtConstants.CLAIM_EXPIRY, - JwtConstants.CLAIM_ISSUED_AT, - JwtConstants.CLAIM_NOT_BEFORE)); - private boolean format; - - protected String toJson(AbstractJoseObject jwt) { - StringBuilder sb = new StringBuilder(); - toJsonInternal(sb, jwt.asMap()); - return sb.toString(); - } - - protected void toJsonInternal(StringBuilder sb, Map<String, Object> map) { - sb.append("{"); - for (Iterator<Map.Entry<String, Object>> it = map.entrySet().iterator(); it.hasNext();) { - Map.Entry<String, Object> entry = it.next(); - sb.append("\"").append(entry.getKey()).append("\""); - sb.append(":"); - toJsonInternal(sb, entry.getValue(), it.hasNext()); - } - sb.append("}"); - } - - protected void toJsonInternal(StringBuilder sb, Object[] array) { - toJsonInternal(sb, Arrays.asList(array)); - } - - protected void toJsonInternal(StringBuilder sb, Collection<?> coll) { - sb.append("["); - formatIfNeeded(sb); - for (Iterator<?> iter = coll.iterator(); iter.hasNext();) { - toJsonInternal(sb, iter.next(), iter.hasNext()); - } - formatIfNeeded(sb); - sb.append("]"); - } - - @SuppressWarnings("unchecked") - protected void toJsonInternal(StringBuilder sb, Object value, boolean hasNext) { - if (AbstractJoseObject.class.isAssignableFrom(value.getClass())) { - sb.append(toJson((AbstractJoseObject)value)); - } else if (value.getClass().isArray()) { - toJsonInternal(sb, (Object[])value); - } else if (Collection.class.isAssignableFrom(value.getClass())) { - toJsonInternal(sb, (Collection<?>)value); - } else if (Map.class.isAssignableFrom(value.getClass())) { - toJsonInternal(sb, (Map<String, Object>)value); - } else { - if (value.getClass() == String.class) { - sb.append("\""); - } - sb.append(value); - if (value.getClass() == String.class) { - sb.append("\""); - } - } - if (hasNext) { - sb.append(","); - formatIfNeeded(sb); - } - - } - - protected void formatIfNeeded(StringBuilder sb) { - if (format) { - sb.append("\r\n "); - } - } - - protected void fromJsonInternal(AbstractJoseObject jwt, String json) { - String theJson = json.trim(); - JoseObjectSettable joseObject = new JoseObjectSettable(jwt); - readJwtObjectAsMap(joseObject, theJson.substring(1, theJson.length() - 1)); - } - - - - protected void readJwtObjectAsMap(Settable values, String json) { - for (int i = 0; i < json.length(); i++) { - if (isWhiteSpace(json.charAt(i))) { - continue; - } - - int closingQuote = json.indexOf('"', i + 1); - int from = json.charAt(i) == '"' ? i + 1 : i; - String name = json.substring(from, closingQuote); - int sepIndex = json.indexOf(':', closingQuote + 1); - - int j = 1; - while (isWhiteSpace(json.charAt(sepIndex + j))) { - j++; - } - if (json.charAt(sepIndex + j) == '{') { - int closingIndex = getClosingIndex(json, '{', '}', sepIndex + j); - String newJson = json.substring(sepIndex + j + 1, closingIndex); - MapSettable nextMap = new MapSettable(); - readJwtObjectAsMap(nextMap, newJson); - values.put(name, nextMap.map); - i = closingIndex + 1; - } else if (json.charAt(sepIndex + j) == '[') { - int closingIndex = getClosingIndex(json, '[', ']', sepIndex + j); - String newJson = json.substring(sepIndex + j + 1, closingIndex); - values.put(name, readJwtObjectAsList(newJson)); - i = closingIndex + 1; - } else { - int commaIndex = getCommaIndex(json, sepIndex + j); - Object value = readPrimitiveValue(json, sepIndex + j, commaIndex); - if (DATE_PROPERTIES.contains(name)) { - value = Long.valueOf(value.toString()); - } - values.put(name, value); - i = commaIndex + 1; - } - - } - } - protected List<Object> readJwtObjectAsList(String json) { - List<Object> values = new LinkedList<Object>(); - for (int i = 0; i < json.length(); i++) { - if (isWhiteSpace(json.charAt(i))) { - continue; - } - if (json.charAt(i) == '{') { - int closingIndex = getClosingIndex(json, '{', '}', i); - MapSettable nextMap = new MapSettable(); - readJwtObjectAsMap(nextMap, json.substring(i + 1, closingIndex)); - values.add(nextMap.map); - i = closingIndex + 1; - } else { - int commaIndex = getCommaIndex(json, i); - Object value = readPrimitiveValue(json, i, commaIndex); - values.add(value); - i = commaIndex + 1; - } - } - - return values; - } - protected Object readPrimitiveValue(String json, int from, int to) { - Object value = json.substring(from, to); - String valueStr = value.toString().trim(); - if (valueStr.startsWith("\"")) { - value = valueStr.substring(1, valueStr.length() - 1); - } else if ("true".equals(value) || "false".equals(value)) { - value = Boolean.valueOf(valueStr); - } - return value; - } - - protected static int getCommaIndex(String json, int from) { - int commaIndex = json.indexOf(",", from); - if (commaIndex == -1) { - commaIndex = json.length(); - } - return commaIndex; - } - protected int getClosingIndex(String json, char openChar, char closeChar, int from) { - int nextOpenIndex = json.indexOf(openChar, from + 1); - int closingIndex = json.indexOf(closeChar, from + 1); - while (nextOpenIndex != -1 && nextOpenIndex < closingIndex) { - nextOpenIndex = json.indexOf(openChar, closingIndex + 1); - closingIndex = json.indexOf(closeChar, closingIndex + 1); - } - return closingIndex; - } - protected boolean isWhiteSpace(char jsonChar) { - return jsonChar == ' ' || jsonChar == '\r' || jsonChar == '\n' || jsonChar == '\t'; - } - - public void setFormat(boolean format) { - this.format = format; - } - - private interface Settable { - void put(String key, Object value); - } - private static class MapSettable implements Settable { - private Map<String, Object> map = new LinkedHashMap<String, Object>(); - public void put(String key, Object value) { - map.put(key, value); - } - - } - private static class JoseObjectSettable implements Settable { - private AbstractJoseObject jose; - public JoseObjectSettable(AbstractJoseObject jose) { - this.jose = jose; - } - public void put(String key, Object value) { - jose.setValue(key, value); - } - } - - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java deleted file mode 100644 index a0b6a5b..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseConstants.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * 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; - -public final class JoseConstants { - public static final String HEADER_TYPE = "typ"; - public static final String HEADER_ALGORITHM = "alg"; - public static final String HEADER_CONTENT_TYPE = "cty"; - public static final String HEADER_CRITICAL = "crit"; - - public static final String HEADER_KEY_ID = "kid"; - public static final String HEADER_X509_URL = "x5u"; - public static final String HEADER_X509_CHAIN = "x5c"; - public static final String HEADER_X509_THUMBPRINT = "x5t"; - public static final String HEADER_X509_THUMBPRINT_SHA256 = "x5t#S256"; - public static final String HEADER_JSON_WEB_KEY = "jwk"; - public static final String HEADER_JSON_WEB_KEY_SET = "jku"; - - public static final String JWE_HEADER_KEY_ENC_ALGORITHM = HEADER_ALGORITHM; - public static final String JWE_HEADER_CONTENT_ENC_ALGORITHM = "enc"; - public static final String JWE_HEADER_ZIP_ALGORITHM = "zip"; - public static final String DEFLATE_ZIP_ALGORITHM = "DEF"; - - public static final String TYPE_JWT = "JWT"; - public static final String TYPE_JOSE = "JOSE"; - public static final String TYPE_JOSE_JSON = "JOSE+JSON"; - public static final String MEDIA_TYPE_JOSE_JSON = "application/jose+json"; - - public static final String PLAIN_TEXT_ALGO = "none"; - public static final String HMAC_SHA_256_ALGO = "HS256"; - public static final String HMAC_SHA_384_ALGO = "HS384"; - public static final String HMAC_SHA_512_ALGO = "HS512"; - public static final String RS_SHA_256_ALGO = "RS256"; - public static final String RS_SHA_384_ALGO = "RS384"; - public static final String RS_SHA_512_ALGO = "RS512"; - public static final String ES_SHA_256_ALGO = "ES256"; - public static final String ES_SHA_384_ALGO = "ES384"; - public static final String ES_SHA_512_ALGO = "ES512"; - - // Key Encryption - public static final String RSA_OAEP_ALGO = "RSA-OAEP"; - public static final String RSA_OAEP_256_ALGO = "RSA-OAEP-256"; - public static final String RSA_1_5_ALGO = "RSA1_5"; - public static final String A128KW_ALGO = "A128KW"; - public static final String A192KW_ALGO = "A192KW"; - public static final String A256KW_ALGO = "A256KW"; - public static final String A128GCMKW_ALGO = "A128GCMKW"; - public static final String A192GCMKW_ALGO = "A192GCMKW"; - public static final String A256GCMKW_ALGO = "A256GCMKW"; - public static final String PBES2_HS256_A128KW_ALGO = "PBES2-HS256+A128KW"; - public static final String PBES2_HS384_A192KW_ALGO = "PBES2-HS384+A192KW"; - public static final String PBES2_HS512_A256KW_ALGO = "PBES2-HS512+A256KW"; - // Content Encryption - public static final String A128CBC_HS256_ALGO = "A128CBC-HS256"; - public static final String A192CBC_HS384_ALGO = "A192CBC-HS384"; - public static final String A256CBC_HS512_ALGO = "A256CBC-HS512"; - public static final String A128GCM_ALGO = "A128GCM"; - public static final String A192GCM_ALGO = "A192GCM"; - public static final String A256GCM_ALGO = "A256GCM"; - - private JoseConstants() { - - } -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java deleted file mode 100644 index 3577669..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeaders.java +++ /dev/null @@ -1,174 +0,0 @@ -/** - * 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; - -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.apache.cxf.helpers.CastUtils; -import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; - -public class JoseHeaders extends AbstractJoseObject { - private Map<String, Integer> headerUpdateCount; - public JoseHeaders() { - } - - public JoseHeaders(Map<String, Object> values) { - super(values); - } - - public void setType(String type) { - setHeader(JoseConstants.HEADER_TYPE, type); - } - - public String getType() { - return (String)getHeader(JoseConstants.HEADER_TYPE); - } - - public void setContentType(String type) { - setHeader(JoseConstants.HEADER_CONTENT_TYPE, type); - } - - public String getContentType() { - return (String)getHeader(JoseConstants.HEADER_CONTENT_TYPE); - } - - public void setAlgorithm(String algo) { - setHeader(JoseConstants.HEADER_ALGORITHM, algo); - } - - public String getAlgorithm() { - return (String)getHeader(JoseConstants.HEADER_ALGORITHM); - } - - public void setKeyId(String kid) { - setHeader(JoseConstants.HEADER_KEY_ID, kid); - } - - public String getKeyId() { - return (String)getHeader(JoseConstants.HEADER_KEY_ID); - } - - public void setX509Url(String x509Url) { - setHeader(JoseConstants.HEADER_X509_URL, x509Url); - } - - public String getX509Url() { - return (String)getHeader(JoseConstants.HEADER_X509_URL); - } - - public void setX509Chain(String x509Chain) { - setHeader(JoseConstants.HEADER_X509_CHAIN, x509Chain); - } - - public String getX509Chain() { - return (String)getHeader(JoseConstants.HEADER_X509_CHAIN); - } - - public void setX509Thumbprint(String x509Thumbprint) { - setHeader(JoseConstants.HEADER_X509_THUMBPRINT, x509Thumbprint); - } - - public String getX509Thumbprint() { - return (String)getHeader(JoseConstants.HEADER_X509_THUMBPRINT); - } - - public void setX509ThumbprintSHA256(String x509Thumbprint) { - setHeader(JoseConstants.HEADER_X509_THUMBPRINT_SHA256, x509Thumbprint); - } - - public String getX509ThumbprintSHA256() { - return (String)super.getValue(JoseConstants.HEADER_X509_THUMBPRINT_SHA256); - } - - public void setCritical(List<String> crit) { - setHeader(JoseConstants.HEADER_CRITICAL, crit); - } - - public List<String> getCritical() { - return CastUtils.cast((List<?>)getHeader(JoseConstants.HEADER_CRITICAL)); - } - - public void setJsonWebKey(JsonWebKey key) { - setHeader(JoseConstants.HEADER_JSON_WEB_KEY, key); - } - - public JsonWebKey getJsonWebKey() { - Object jsonWebKey = getValue(JoseConstants.HEADER_JSON_WEB_KEY); - if (jsonWebKey == null || jsonWebKey instanceof JsonWebKey) { - return (JsonWebKey)jsonWebKey; - } - Map<String, Object> map = CastUtils.cast((Map<?, ?>)jsonWebKey); - return new JsonWebKey(map); - } - - public JoseHeaders setHeader(String name, Object value) { - setValue(name, value); - return this; - } - - protected void setValue(String name, Object value) { - if (super.values.containsKey(name)) { - if (headerUpdateCount == null) { - headerUpdateCount = new LinkedHashMap<String, Integer>(); - } - Integer count = headerUpdateCount.get(name); - count = count == null ? 2 : count++; - headerUpdateCount.put(name, count); - } - super.setValue(name, value); - } - - public Object getHeader(String name) { - return getValue(name); - } - - public JoseHeaders setIntegerHeader(String name, Integer value) { - setHeader(name, value); - return this; - } - - public Integer getIntegerHeader(String name) { - Object value = getHeader(name); - if (value != null) { - return value instanceof Integer ? (Integer)value : Integer.parseInt(value.toString()); - } else { - return null; - } - } - public JoseHeaders setLongHeader(String name, Long value) { - setHeader(name, value); - return this; - } - - public Long getLongHeader(String name) { - Object value = getHeader(name); - if (value != null) { - return value instanceof Long ? (Long)value : Long.parseLong(value.toString()); - } else { - return null; - } - } - public Map<String, Object> getHeaderUpdateCount() { - return headerUpdateCount == null ? null : Collections.<String, Object>unmodifiableMap(headerUpdateCount); - } -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReader.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReader.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReader.java deleted file mode 100644 index a99ca23..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReader.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * 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; - - -public interface JoseHeadersReader { - JoseHeaders fromJsonHeaders(String jsonHeaders); -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReaderWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReaderWriter.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReaderWriter.java deleted file mode 100644 index be20ca3..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersReaderWriter.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * 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; - - - - - -public class JoseHeadersReaderWriter extends AbstractJoseObjectReaderWriter - implements JoseHeadersReader, JoseHeadersWriter { - @Override - public String headersToJson(JoseHeaders headers) { - return toJson(headers); - } - - @Override - public JoseHeaders fromJsonHeaders(String headersJson) { - JoseHeaders headers = new JoseHeaders(); - fromJsonInternal(headers, headersJson); - return headers; - } - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersWriter.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersWriter.java deleted file mode 100644 index 54a77e7..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseHeadersWriter.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * 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; - -public interface JoseHeadersWriter { - - String headersToJson(JoseHeaders headers); - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/8be78044/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java deleted file mode 100644 index 9abbe79..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/JoseUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 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; - -public final class JoseUtils { - private JoseUtils() { - - } - - public static String checkContentType(String contentType, String defaultType) { - if (contentType != null) { - int paramIndex = contentType.indexOf(';'); - String typeWithoutParams = paramIndex == -1 ? contentType : contentType.substring(0, paramIndex); - if (typeWithoutParams.indexOf('/') == -1) { - contentType = "application/" + contentType; - } - } else { - contentType = defaultType; - } - return contentType; - } - public static String expandContentType(String contentType) { - int paramIndex = contentType.indexOf(';'); - String typeWithoutParams = paramIndex == -1 ? contentType : contentType.substring(0, paramIndex); - if (typeWithoutParams.indexOf('/') == -1) { - contentType = "application/" + contentType; - } - return contentType; - } -}