http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/Constants.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/Constants.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/Constants.java new file mode 100644 index 0000000..8714aac --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/Constants.java @@ -0,0 +1,33 @@ +/** + * 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.grant; + +public final class Constants { + public static final String JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer"; + public static final String CLIENT_GRANT_ASSERTION_PARAM = "assertion"; + + public static final String CLIENT_AUTH_ASSERTION_PARAM = "client_assertion"; + public static final String CLIENT_AUTH_ASSERTION_TYPE = "client_assertion_type"; + public static final String CLIENT_AUTH_JWT_BEARER = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + + + private Constants() { + + } +}
http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerClientCredentialsGrant.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerClientCredentialsGrant.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerClientCredentialsGrant.java new file mode 100644 index 0000000..f5f86a8 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerClientCredentialsGrant.java @@ -0,0 +1,41 @@ +/** + * 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.grant; + +import javax.ws.rs.core.MultivaluedMap; + +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; + +public class JwtBearerClientCredentialsGrant extends AbstractJwtBearerGrant { + + private static final long serialVersionUID = 4801583498206813025L; + + public JwtBearerClientCredentialsGrant(String assertion, boolean encoded, String scope) { + super(OAuthConstants.CLIENT_CREDENTIALS_GRANT, assertion, encoded, scope); + } + + public MultivaluedMap<String, String> toMap() { + MultivaluedMap<String, String> map = initMap(); + map.putSingle(Constants.CLIENT_AUTH_ASSERTION_TYPE, Constants.CLIENT_AUTH_JWT_BEARER); + map.putSingle(Constants.CLIENT_AUTH_ASSERTION_PARAM, encodeAssertion()); + addScope(map); + return map; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrant.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrant.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrant.java new file mode 100644 index 0000000..49041b9 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrant.java @@ -0,0 +1,48 @@ +/** + * 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.grant; + +import javax.ws.rs.core.MultivaluedMap; + +public class JwtBearerGrant extends AbstractJwtBearerGrant { + private static final long serialVersionUID = -7296527609343431294L; + + public JwtBearerGrant(String assertion) { + this(assertion, false); + } + + public JwtBearerGrant(String assertion, boolean encoded) { + this(assertion, false, null); + } + + public JwtBearerGrant(String assertion, String scope) { + this(assertion, false, scope); + } + + public JwtBearerGrant(String assertion, boolean encoded, String scope) { + super(Constants.JWT_BEARER_GRANT, assertion, encoded, scope); + } + + public MultivaluedMap<String, String> toMap() { + MultivaluedMap<String, String> map = initMap(); + map.putSingle(Constants.CLIENT_GRANT_ASSERTION_PARAM, encodeAssertion()); + addScope(map); + return map; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java new file mode 100644 index 0000000..c3a2283 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtBearerGrantHandler.java @@ -0,0 +1,91 @@ +/** + * 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.grant; + +import java.util.Arrays; + +import javax.ws.rs.core.MultivaluedMap; + +import org.apache.cxf.jaxrs.utils.HttpUtils; +import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReader; +import org.apache.cxf.rs.security.oauth2.common.Client; +import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants; +import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils; + +/** + * The "JWT Bearer" grant handler + */ +public class JwtBearerGrantHandler extends AbstractJwtHandler { + private static final String ENCODED_JWT_BEARER_GRANT; + static { + // AccessTokenService may be configured with the form provider + // which will not decode by default - so listing both the actual + // and encoded grant type value will help + ENCODED_JWT_BEARER_GRANT = HttpUtils.urlEncode(Constants.JWT_BEARER_GRANT, "UTF-8"); + } + private JwtTokenReader jwtReader; + public JwtBearerGrantHandler() { + super(Arrays.asList(Constants.JWT_BEARER_GRANT, ENCODED_JWT_BEARER_GRANT)); + } + + @Override + public ServerAccessToken createAccessToken(Client client, MultivaluedMap<String, String> params) + throws OAuthServiceException { + String assertion = params.getFirst(Constants.CLIENT_GRANT_ASSERTION_PARAM); + if (assertion == null) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT); + } + try { + JwsJwtCompactConsumer jwsReader = getJwsReader(assertion); + JwtToken jwtToken = jwsReader.getJwtToken(); + super.validateSignature(jwtToken.getHeaders(), + jwsReader.getUnsignedEncodedPayload(), + jwsReader.getDecodedSignature()); + + + super.validateClaims(client, jwtToken.getClaims()); + UserSubject grantSubject = new UserSubject(jwtToken.getClaims().getSubject()); + + return doCreateAccessToken(client, + grantSubject, + Constants.JWT_BEARER_GRANT, + OAuthUtils.parseScope(params.getFirst(OAuthConstants.SCOPE))); + } catch (OAuthServiceException ex) { + throw ex; + } catch (Exception ex) { + throw new OAuthServiceException(OAuthConstants.INVALID_GRANT, ex); + } + + } + + protected JwsJwtCompactConsumer getJwsReader(String assertion) { + return new JwsJwtCompactConsumer(assertion, jwtReader); + } + + public void setJwtReader(JwtTokenReader tokenReader) { + this.jwtReader = tokenReader; + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtUserSubject.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtUserSubject.java b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtUserSubject.java new file mode 100644 index 0000000..18eaae1 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/grant/JwtUserSubject.java @@ -0,0 +1,34 @@ +/** + * 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.grant; + +import org.apache.cxf.rs.security.jose.jwt.JwtToken; +import org.apache.cxf.rs.security.oauth2.common.UserSubject; + +public class JwtUserSubject extends UserSubject { + private static final long serialVersionUID = -1135272749329239037L; + private JwtToken token; + public JwtUserSubject(String user, JwtToken token) { + super(user); + this.token = token; + } + public JwtToken getToken() { + return token; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java b/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java new file mode 100644 index 0000000..ca49a38 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JweCompactReaderWriterTest.java @@ -0,0 +1,209 @@ +/** + * 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.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.jose.jws.JwsCompactReaderWriterTest; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; +import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class JweCompactReaderWriterTest extends Assert { + // A1 example + 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(JwtConstants.A128GCMKW_ALGO); + headers.setContentEncryptionAlgorithm(Algorithm.A128CBC_HS256.getJwtName()); + + byte[] cekEncryptionKey = Base64UrlUtility.decode(KEY_ENCRYPTION_KEY_A3); + + AesGcmWrapKeyEncryptionAlgorithm keyEncryption = + new AesGcmWrapKeyEncryptionAlgorithm(cekEncryptionKey, JwtConstants.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(key, INIT_VECTOR_A1); + 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)); + 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); + 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/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java b/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java new file mode 100644 index 0000000..af5ae37 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwe/JwePbeHmacAesWrapTest.java @@ -0,0 +1,79 @@ +/** + * 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.Algorithm; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +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(JwtConstants.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, JwtConstants.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(JwtConstants.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, JwtConstants.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, null, null); + String decryptedText = decryption.decrypt(jweContent).getContentText(); + assertEquals(specPlainText, decryptedText); + + } +} + http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java b/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeyTest.java new file mode 100644 index 0000000..d0fadf3 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-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.jwa.Algorithm; +import org.apache.cxf.rs.security.jose.jwe.JweCompactConsumer; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +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(JwtConstants.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(JwtConstants.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(JwtConstants.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/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPrivateSet.txt b/rt/rs/security/oauth-parent/oauth2-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/oauth-parent/oauth2-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/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkPublicSet.txt b/rt/rs/security/oauth-parent/oauth2-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/oauth-parent/oauth2-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/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jwk/jwkSecretSet.txt b/rt/rs/security/oauth-parent/oauth2-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/oauth-parent/oauth2-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/4640cf1e/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java b/rt/rs/security/oauth-parent/oauth2-jose/src/test/java/org/apache/cxf/rs/security/jose/jws/JwsCompactReaderWriterTest.java new file mode 100644 index 0000000..5a08733 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-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.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.JwtConstants; +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.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; + +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(JwtConstants.PLAIN_TEXT_ALGO); + + 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))); + JwtToken token = jws.getJwtToken(); + JwtHeaders headers = token.getHeaders(); + assertEquals(JwtConstants.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(JwtConstants.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(JwtConstants.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 PublicKeyJwsSignatureVerifier(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/4640cf1e/rt/rs/security/oauth-parent/oauth2-jwt/pom.xml ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/pom.xml b/rt/rs/security/oauth-parent/oauth2-jwt/pom.xml deleted file mode 100644 index cc51bad..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jwt/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ -<?xml version="1.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. ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - <artifactId>cxf-rt-rs-security-oauth2-jose</artifactId> - <packaging>bundle</packaging> - <name>Apache CXF Runtime OAuth 2.0 JOSE</name> - <description>Apache CXF Runtime OAuth 2.0 JOSE</description> - <url>http://cxf.apache.org</url> - <parent> - <artifactId>cxf-rt-rs-security-oauth-parent</artifactId> - <groupId>org.apache.cxf</groupId> - <version>3.1.0-SNAPSHOT</version> - <relativePath>../pom.xml</relativePath> - </parent> - <dependencies> - <dependency> - <groupId>org.apache.cxf</groupId> - <artifactId>cxf-rt-rs-security-oauth2</artifactId> - <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> - </dependency> - <!--test dependencies--> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.easymock</groupId> - <artifactId>easymock</artifactId> - <scope>test</scope> - </dependency> - </dependencies> -</project> http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java deleted file mode 100644 index 1906622..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJweDecryptingFilter.java +++ /dev/null @@ -1,118 +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.jaxrs; - -import java.io.IOException; -import java.io.InputStream; -import java.security.interfaces.RSAPrivateKey; -import java.util.Properties; - -import org.apache.cxf.Bus; -import org.apache.cxf.helpers.IOUtils; -import org.apache.cxf.jaxrs.utils.JAXRSUtils; -import org.apache.cxf.jaxrs.utils.ResourceUtils; -import org.apache.cxf.message.Message; -import org.apache.cxf.message.MessageUtils; -import org.apache.cxf.rs.security.jose.jwa.Algorithm; -import org.apache.cxf.rs.security.jose.jwe.AesCbcHmacJweDecryption; -import org.apache.cxf.rs.security.jose.jwe.JweCryptoProperties; -import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; -import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; -import org.apache.cxf.rs.security.jose.jwe.JweHeaders; -import org.apache.cxf.rs.security.jose.jwe.JweUtils; -import org.apache.cxf.rs.security.jose.jwe.KeyDecryptionAlgorithm; -import org.apache.cxf.rs.security.jose.jwe.RSAOaepKeyDecryptionAlgorithm; -import org.apache.cxf.rs.security.jose.jwe.WrappedKeyJweDecryption; -import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; -import org.apache.cxf.rs.security.jose.jwk.JwkUtils; -import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; - -public class AbstractJweDecryptingFilter { - private static final String RSSEC_ENCRYPTION_IN_PROPS = "rs.security.encryption.in.properties"; - private static final String RSSEC_ENCRYPTION_PROPS = "rs.security.encryption.properties"; - private static final String JSON_WEB_ENCRYPTION_CEK_ALGO_PROP = "rs.security.jwe.content.encryption.algorithm"; - private JweDecryptionProvider decryption; - private JweCryptoProperties cryptoProperties; - private String defaultMediaType; - protected JweDecryptionOutput decrypt(InputStream is) throws IOException { - JweDecryptionProvider theDecryptor = getInitializedDecryptionProvider(); - JweDecryptionOutput out = theDecryptor.decrypt(new String(IOUtils.readBytesFromStream(is), "UTF-8")); - validateHeaders(out.getHeaders()); - return out; - } - - protected void validateHeaders(JweHeaders headers) { - // complete - } - public void setDecryptionProvider(JweDecryptionProvider decryptor) { - this.decryption = decryptor; - } - protected JweDecryptionProvider getInitializedDecryptionProvider() { - if (decryption != null) { - return decryption; - } - Message m = JAXRSUtils.getCurrentMessage(); - String propLoc = - (String)MessageUtils.getContextualProperty(m, RSSEC_ENCRYPTION_IN_PROPS, RSSEC_ENCRYPTION_PROPS); - if (propLoc == null) { - throw new SecurityException(); - } - Bus bus = m.getExchange().getBus(); - try { - KeyDecryptionAlgorithm keyDecryptionProvider = null; - Properties props = ResourceUtils.loadProperties(propLoc, bus); - if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(CryptoUtils.RSSEC_KEY_STORE_TYPE))) { - JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_ENCRYPT); - keyDecryptionProvider = JweUtils.getKeyDecryptionAlgorithm(jwk); - } else { - keyDecryptionProvider = new RSAOaepKeyDecryptionAlgorithm( - (RSAPrivateKey)CryptoUtils.loadPrivateKey(m, props, CryptoUtils.RSSEC_DECRYPT_KEY_PSWD_PROVIDER)); - } - if (keyDecryptionProvider == null) { - throw new SecurityException(); - } - String contentEncryptionAlgo = props.getProperty(JSON_WEB_ENCRYPTION_CEK_ALGO_PROP); - boolean isAesHmac = Algorithm.isAesCbcHmac(contentEncryptionAlgo); - if (isAesHmac) { - return new AesCbcHmacJweDecryption(keyDecryptionProvider); - } else { - return new WrappedKeyJweDecryption(keyDecryptionProvider, cryptoProperties, null); - } - - } catch (SecurityException ex) { - throw ex; - } catch (Exception ex) { - throw new SecurityException(ex); - } - - } - - public void setCryptoProperties(JweCryptoProperties cryptoProperties) { - this.cryptoProperties = cryptoProperties; - } - - public String getDefaultMediaType() { - return defaultMediaType; - } - - public void setDefaultMediaType(String defaultMediaType) { - this.defaultMediaType = defaultMediaType; - } - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java deleted file mode 100644 index ac9366a..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsReaderProvider.java +++ /dev/null @@ -1,103 +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.jaxrs; - -import java.security.interfaces.RSAPublicKey; -import java.util.Properties; - -import org.apache.cxf.Bus; -import org.apache.cxf.jaxrs.utils.JAXRSUtils; -import org.apache.cxf.jaxrs.utils.ResourceUtils; -import org.apache.cxf.message.Message; -import org.apache.cxf.message.MessageUtils; -import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; -import org.apache.cxf.rs.security.jose.jwk.JwkUtils; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureProperties; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureVerifier; -import org.apache.cxf.rs.security.jose.jws.JwsUtils; -import org.apache.cxf.rs.security.jose.jws.PublicKeyJwsSignatureVerifier; -import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; - -public class AbstractJwsReaderProvider { - private static final String RSSEC_SIGNATURE_IN_PROPS = "rs.security.signature.in.properties"; - private static final String RSSEC_SIGNATURE_PROPS = "rs.security.signature.properties"; - private static final String JSON_WEB_SIGNATURE_ALGO_PROP = "rs.security.jws.content.signature.algorithm"; - - - private JwsSignatureVerifier sigVerifier; - private JwsSignatureProperties sigProperties; - private String defaultMediaType; - - public void setSignatureVerifier(JwsSignatureVerifier signatureVerifier) { - this.sigVerifier = signatureVerifier; - } - - public void setSignatureProperties(JwsSignatureProperties signatureProperties) { - this.sigProperties = signatureProperties; - } - - public JwsSignatureProperties getSigProperties() { - return sigProperties; - } - - protected JwsSignatureVerifier getInitializedSigVerifier() { - if (sigVerifier != null) { - return sigVerifier; - } - - Message m = JAXRSUtils.getCurrentMessage(); - String propLoc = - (String)MessageUtils.getContextualProperty(m, RSSEC_SIGNATURE_IN_PROPS, RSSEC_SIGNATURE_PROPS); - if (propLoc == null) { - throw new SecurityException(); - } - Bus bus = m.getExchange().getBus(); - try { - Properties props = ResourceUtils.loadProperties(propLoc, bus); - JwsSignatureVerifier theVerifier = null; - String rsaSignatureAlgo = null; - if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(CryptoUtils.RSSEC_KEY_STORE_TYPE))) { - JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_VERIFY); - rsaSignatureAlgo = getSignatureAlgo(props, jwk.getAlgorithm()); - theVerifier = JwsUtils.getSignatureVerifier(jwk, rsaSignatureAlgo); - - } else { - theVerifier = new PublicKeyJwsSignatureVerifier( - (RSAPublicKey)CryptoUtils.loadPublicKey(m, props)); - } - return theVerifier; - } catch (SecurityException ex) { - throw ex; - } catch (Exception ex) { - throw new SecurityException(ex); - } - } - - public String getDefaultMediaType() { - return defaultMediaType; - } - - public void setDefaultMediaType(String defaultMediaType) { - this.defaultMediaType = defaultMediaType; - } - - private String getSignatureAlgo(Properties props, String algo) { - return algo == null ? props.getProperty(JSON_WEB_SIGNATURE_ALGO_PROP) : algo; - } -} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java deleted file mode 100644 index d2fc2ae..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/AbstractJwsWriterProvider.java +++ /dev/null @@ -1,95 +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.jaxrs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.security.interfaces.RSAPrivateKey; -import java.util.Properties; - -import org.apache.cxf.helpers.IOUtils; -import org.apache.cxf.jaxrs.utils.JAXRSUtils; -import org.apache.cxf.jaxrs.utils.ResourceUtils; -import org.apache.cxf.message.Message; -import org.apache.cxf.message.MessageUtils; -import org.apache.cxf.rs.security.jose.jwk.JsonWebKey; -import org.apache.cxf.rs.security.jose.jwk.JwkUtils; -import org.apache.cxf.rs.security.jose.jws.JwsCompactProducer; -import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider; -import org.apache.cxf.rs.security.jose.jws.JwsUtils; -import org.apache.cxf.rs.security.jose.jws.PrivateKeyJwsSignatureProvider; -import org.apache.cxf.rs.security.jose.jwt.JwtHeaders; -import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; - -public class AbstractJwsWriterProvider { - private static final String RSSEC_SIGNATURE_OUT_PROPS = "rs.security.signature.out.properties"; - private static final String RSSEC_SIGNATURE_PROPS = "rs.security.signature.properties"; - private static final String JSON_WEB_SIGNATURE_ALGO_PROP = "rs.security.jws.content.signature.algorithm"; - - private JwsSignatureProvider sigProvider; - - public void setSignatureProvider(JwsSignatureProvider signatureProvider) { - this.sigProvider = signatureProvider; - } - - protected JwsSignatureProvider getInitializedSigProvider(JwtHeaders headers) { - if (sigProvider != null) { - return sigProvider; - } - Message m = JAXRSUtils.getCurrentMessage(); - String propLoc = - (String)MessageUtils.getContextualProperty(m, RSSEC_SIGNATURE_OUT_PROPS, RSSEC_SIGNATURE_PROPS); - if (propLoc == null) { - throw new SecurityException(); - } - try { - Properties props = ResourceUtils.loadProperties(propLoc, m.getExchange().getBus()); - JwsSignatureProvider theSigProvider = null; - String rsaSignatureAlgo = null; - if (JwkUtils.JWK_KEY_STORE_TYPE.equals(props.get(CryptoUtils.RSSEC_KEY_STORE_TYPE))) { - JsonWebKey jwk = JwkUtils.loadJsonWebKey(m, props, JsonWebKey.KEY_OPER_SIGN); - rsaSignatureAlgo = getSignatureAlgo(props, jwk.getAlgorithm()); - theSigProvider = JwsUtils.getSignatureProvider(jwk, rsaSignatureAlgo); - } else { - rsaSignatureAlgo = getSignatureAlgo(props, null); - RSAPrivateKey pk = (RSAPrivateKey)CryptoUtils.loadPrivateKey(m, props, - CryptoUtils.RSSEC_SIG_KEY_PSWD_PROVIDER); - theSigProvider = new PrivateKeyJwsSignatureProvider(pk, rsaSignatureAlgo); - } - if (theSigProvider == null) { - throw new SecurityException(); - } - headers.setAlgorithm(rsaSignatureAlgo); - return theSigProvider; - } catch (SecurityException ex) { - throw ex; - } catch (Exception ex) { - throw new SecurityException(ex); - } - } - protected void writeJws(JwsCompactProducer p, JwsSignatureProvider theSigProvider, OutputStream os) - throws IOException { - p.signWith(theSigProvider); - IOUtils.copy(new ByteArrayInputStream(p.getSignedEncodedJws().getBytes("UTF-8")), os); - } - private String getSignatureAlgo(Properties props, String algo) { - return algo == null ? props.getProperty(JSON_WEB_SIGNATURE_ALGO_PROP) : algo; - } -} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweClientResponseFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweClientResponseFilter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweClientResponseFilter.java deleted file mode 100644 index 596d3db..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweClientResponseFilter.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.jaxrs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import javax.annotation.Priority; -import javax.ws.rs.client.ClientRequestContext; -import javax.ws.rs.client.ClientResponseContext; -import javax.ws.rs.client.ClientResponseFilter; - -import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; -import org.apache.cxf.rs.security.jose.jwt.JwtUtils; - -@Priority(Priorities.JWE_CLIENT_READ_PRIORITY) -public class JweClientResponseFilter extends AbstractJweDecryptingFilter implements ClientResponseFilter { - @Override - public void filter(ClientRequestContext req, ClientResponseContext res) throws IOException { - JweDecryptionOutput out = decrypt(res.getEntityStream()); - byte[] bytes = out.getContent(); - res.setEntityStream(new ByteArrayInputStream(bytes)); - res.getHeaders().putSingle("Content-Length", Integer.toString(bytes.length)); - String ct = JwtUtils.checkContentType(out.getHeaders().getContentType(), getDefaultMediaType()); - if (ct != null) { - res.getHeaders().putSingle("Content-Type", ct); - } - } - -} http://git-wip-us.apache.org/repos/asf/cxf/blob/4640cf1e/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweContainerRequestFilter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweContainerRequestFilter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweContainerRequestFilter.java deleted file mode 100644 index 449a3a3..0000000 --- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweContainerRequestFilter.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.jaxrs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; - -import javax.annotation.Priority; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerRequestFilter; -import javax.ws.rs.container.PreMatching; - -import org.apache.cxf.rs.security.jose.jwe.JweDecryptionOutput; -import org.apache.cxf.rs.security.jose.jwt.JwtUtils; - -@PreMatching -@Priority(Priorities.JWE_SERVER_READ_PRIORITY) -public class JweContainerRequestFilter extends AbstractJweDecryptingFilter implements ContainerRequestFilter { - @Override - public void filter(ContainerRequestContext context) throws IOException { - JweDecryptionOutput out = decrypt(context.getEntityStream()); - byte[] bytes = out.getContent(); - context.setEntityStream(new ByteArrayInputStream(bytes)); - context.getHeaders().putSingle("Content-Length", Integer.toString(bytes.length)); - String ct = JwtUtils.checkContentType(out.getHeaders().getContentType(), getDefaultMediaType()); - if (ct != null) { - context.getHeaders().putSingle("Content-Type", ct); - } - } -}
