http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweHeaders.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweHeaders.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweHeaders.java new file mode 100644 index 0000000..7fe91d6 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweHeaders.java @@ -0,0 +1,102 @@ +/** + * 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.io.UnsupportedEncodingException; +import java.util.Map; + +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.JwtHeadersWriter; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; + + + + +public class JweHeaders extends JwtHeaders { + + public JweHeaders() { + } + + public JweHeaders(Map<String, Object> values) { + super(values); + } + public JweHeaders(String keyEncAlgo, String ctEncAlgo) { + this(keyEncAlgo, ctEncAlgo, false); + } + public JweHeaders(String ctEncAlgo) { + this(null, ctEncAlgo, false); + } + public JweHeaders(String ctEncAlgo, boolean deflate) { + this(null, ctEncAlgo, deflate); + } + public JweHeaders(String keyEncAlgo, String ctEncAlgo, boolean deflate) { + init(keyEncAlgo, ctEncAlgo, deflate); + } + private void init(String keyEncAlgo, String ctEncAlgo, boolean deflate) { + if (keyEncAlgo != null) { + setKeyEncryptionAlgorithm(keyEncAlgo); + } + setContentEncryptionAlgorithm(ctEncAlgo); + if (deflate) { + setZipAlgorithm(JwtConstants.DEFLATE_ZIP_ALGORITHM); + } + } + + public void setKeyEncryptionAlgorithm(String type) { + super.setAlgorithm(type); + } + + public String getKeyEncryptionAlgorithm() { + return super.getAlgorithm(); + } + + public void setContentEncryptionAlgorithm(String type) { + setHeader(JwtConstants.JWE_HEADER_CONTENT_ENC_ALGORITHM, type); + } + + public String getContentEncryptionAlgorithm() { + return (String)getHeader(JwtConstants.JWE_HEADER_CONTENT_ENC_ALGORITHM); + } + + public void setZipAlgorithm(String type) { + setHeader(JwtConstants.JWE_HEADER_ZIP_ALGORITHM, type); + } + + public String getZipAlgorithm() { + return (String)getHeader(JwtConstants.JWE_HEADER_ZIP_ALGORITHM); + } + + @Override + public JwtHeaders setHeader(String name, Object value) { + return (JwtHeaders)super.setHeader(name, value); + } + public byte[] toCipherAdditionalAuthData(JwtHeadersWriter writer) { + return toCipherAdditionalAuthData(writer.headersToJson(this)); + } + public static byte[] toCipherAdditionalAuthData(String headersJson) { + try { + String base64UrlHeadersInJson = Base64UrlUtility.encode(headersJson.getBytes("UTF-8")); + return base64UrlHeadersInJson.getBytes("US-ASCII"); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } + } +}
http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweOutputStream.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweOutputStream.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweOutputStream.java new file mode 100644 index 0000000..5abe38c --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweOutputStream.java @@ -0,0 +1,145 @@ +/** + * 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.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; + +import javax.crypto.Cipher; + +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; + +public class JweOutputStream extends FilterOutputStream { + private Cipher encryptingCipher; + private int blockSize; + private AuthenticationTagProducer authTagProducer; + private byte[] lastRawDataChunk; + private byte[] lastEncryptedDataChunk; + private boolean flushed; + public JweOutputStream(OutputStream out, + Cipher encryptingCipher, + AuthenticationTagProducer authTagProducer) { + super(out); + this.encryptingCipher = encryptingCipher; + this.blockSize = encryptingCipher.getBlockSize(); + this.authTagProducer = authTagProducer; + } + + @Override + public void write(int value) throws IOException { + byte[] bytes = ByteBuffer.allocate(Integer.SIZE / 8).putInt(value).array(); + write(bytes, 0, bytes.length); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + if (lastRawDataChunk != null) { + int remaining = blockSize - lastRawDataChunk.length; + int lenToCopy = remaining < len ? remaining : len; + lastRawDataChunk = newArray(lastRawDataChunk, 0, lastRawDataChunk.length, b, off, lenToCopy); + off = off + lenToCopy; + len -= lenToCopy; + if (lastRawDataChunk.length < blockSize) { + return; + } else { + encryptAndWrite(lastRawDataChunk, 0, lastRawDataChunk.length); + lastRawDataChunk = null; + } + } + int offset = 0; + int chunkSize = blockSize > len ? blockSize : blockSize * (len / blockSize); + for (; offset + chunkSize <= len; offset += chunkSize, off += chunkSize) { + encryptAndWrite(b, off, chunkSize); + } + if (offset < len) { + lastRawDataChunk = newArray(b, off, len - offset); + } + + } + + private void encryptAndWrite(byte[] chunk, int off, int len) throws IOException { + byte[] encrypted = encryptingCipher.update(chunk, off, len); + if (authTagProducer != null) { + authTagProducer.update(encrypted, 0, encrypted.length); + } + encodeAndWrite(encrypted, 0, encrypted.length, false); + } + private void encodeAndWrite(byte[] encryptedChunk, int off, int len, boolean finalWrite) throws IOException { + byte[] theChunk = lastEncryptedDataChunk; + int lenToEncode = len; + if (theChunk != null) { + theChunk = newArray(theChunk, 0, theChunk.length, encryptedChunk, off, len); + lenToEncode = theChunk.length; + off = 0; + } else { + theChunk = encryptedChunk; + } + int rem = finalWrite ? 0 : lenToEncode % 3; + Base64UrlUtility.encodeAndStream(theChunk, off, lenToEncode - rem, out); + + if (rem > 0) { + lastEncryptedDataChunk = newArray(theChunk, lenToEncode - rem, rem); + } else { + lastEncryptedDataChunk = null; + } + } + + @Override + public void flush() throws IOException { + if (flushed) { + return; + } + try { + byte[] finalBytes = lastRawDataChunk == null + ? encryptingCipher.doFinal() + : encryptingCipher.doFinal(lastRawDataChunk, 0, lastRawDataChunk.length); + final int authTagLengthBits = 128; + if (authTagProducer != null) { + authTagProducer.update(finalBytes, 0, finalBytes.length); + encodeAndWrite(finalBytes, 0, finalBytes.length, true); + } else { + encodeAndWrite(finalBytes, 0, finalBytes.length - authTagLengthBits / 8, true); + } + out.write(new byte[]{'.'}); + + if (authTagProducer == null) { + encodeAndWrite(finalBytes, finalBytes.length - authTagLengthBits / 8, authTagLengthBits / 8, true); + } else { + byte[] authTag = authTagProducer.getTag(); + encodeAndWrite(authTag, 0, authTagLengthBits / 8, true); + } + } catch (Exception ex) { + throw new SecurityException(); + } + flushed = true; + } + private byte[] newArray(byte[] src, int srcPos, int srcLen) { + byte[] buf = new byte[srcLen]; + System.arraycopy(src, srcPos, buf, 0, srcLen); + return buf; + } + private byte[] newArray(byte[] src, int srcPos, int srcLen, byte[] src2, int srcPos2, int srcLen2) { + byte[] buf = new byte[srcLen + srcLen2]; + System.arraycopy(src, srcPos, buf, 0, srcLen); + System.arraycopy(src2, srcPos2, buf, srcLen, srcLen2); + return buf; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyDecryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyDecryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyDecryptionAlgorithm.java new file mode 100644 index 0000000..9932ab2 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyDecryptionAlgorithm.java @@ -0,0 +1,24 @@ +/** + * 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; + + +public interface KeyDecryptionAlgorithm { + byte[] getDecryptedContentEncryptionKey(JweCompactConsumer consumer); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyEncryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyEncryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyEncryptionAlgorithm.java new file mode 100644 index 0000000..a6a147b --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/KeyEncryptionAlgorithm.java @@ -0,0 +1,24 @@ +/** + * 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; + + +public interface KeyEncryptionAlgorithm { + byte[] getEncryptedContentEncryptionKey(JweHeaders headers, byte[] cek); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java new file mode 100644 index 0000000..ac66535 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyDecryptionAlgorithm.java @@ -0,0 +1,54 @@ +/** + * 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 org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; + +public class PbesHmacAesWrapKeyDecryptionAlgorithm implements KeyDecryptionAlgorithm { + private byte[] password; + public PbesHmacAesWrapKeyDecryptionAlgorithm(String password) { + this(PbesHmacAesWrapKeyEncryptionAlgorithm.stringToBytes(password)); + } + public PbesHmacAesWrapKeyDecryptionAlgorithm(char[] password) { + this(PbesHmacAesWrapKeyEncryptionAlgorithm.charsToBytes(password)); + } + public PbesHmacAesWrapKeyDecryptionAlgorithm(byte[] password) { + this.password = password; + } + @Override + public byte[] getDecryptedContentEncryptionKey(JweCompactConsumer consumer) { + byte[] saltInput = getDecodedBytes(consumer, "p2s"); + int pbesCount = consumer.getJweHeaders().getIntegerHeader("p2c"); + String keyAlgoJwt = consumer.getJweHeaders().getAlgorithm(); + int keySize = PbesHmacAesWrapKeyEncryptionAlgorithm.getKeySize(keyAlgoJwt); + byte[] derivedKey = PbesHmacAesWrapKeyEncryptionAlgorithm + .createDerivedKey(keyAlgoJwt, keySize, password, saltInput, pbesCount); + KeyDecryptionAlgorithm aesWrap = new AesWrapKeyDecryptionAlgorithm(derivedKey); + return aesWrap.getDecryptedContentEncryptionKey(consumer); + } + private byte[] getDecodedBytes(JweCompactConsumer consumer, String headerName) { + try { + Object headerValue = consumer.getJweHeaders().getHeader(headerName); + return Base64UrlUtility.decode(headerValue.toString()); + } catch (Exception ex) { + throw new SecurityException(ex); + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java new file mode 100644 index 0000000..b67332d --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/PbesHmacAesWrapKeyEncryptionAlgorithm.java @@ -0,0 +1,169 @@ +/** + * 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.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; +import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA384Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; + +public class PbesHmacAesWrapKeyEncryptionAlgorithm implements KeyEncryptionAlgorithm { + private static final Set<String> SUPPORTED_ALGORITHMS = new HashSet<String>( + Arrays.asList(Algorithm.PBES2_HS256_A128KW.getJwtName(), + Algorithm.PBES2_HS384_A192KW.getJwtName(), + Algorithm.PBES2_HS512_A256KW.getJwtName())); + private static final Map<String, Integer> PBES_HMAC_MAP; + private static final Map<String, String> PBES_AES_MAP; + private static final Map<String, Integer> DERIVED_KEY_SIZE_MAP; + static { + PBES_HMAC_MAP = new HashMap<String, Integer>(); + PBES_HMAC_MAP.put(Algorithm.PBES2_HS256_A128KW.getJwtName(), 256); + PBES_HMAC_MAP.put(Algorithm.PBES2_HS384_A192KW.getJwtName(), 384); + PBES_HMAC_MAP.put(Algorithm.PBES2_HS512_A256KW.getJwtName(), 512); + + PBES_AES_MAP = new HashMap<String, String>(); + PBES_AES_MAP.put(Algorithm.PBES2_HS256_A128KW.getJwtName(), Algorithm.A128KW.getJwtName()); + PBES_AES_MAP.put(Algorithm.PBES2_HS384_A192KW.getJwtName(), Algorithm.A192KW.getJwtName()); + PBES_AES_MAP.put(Algorithm.PBES2_HS512_A256KW.getJwtName(), Algorithm.A256KW.getJwtName()); + + DERIVED_KEY_SIZE_MAP = new HashMap<String, Integer>(); + DERIVED_KEY_SIZE_MAP.put(Algorithm.PBES2_HS256_A128KW.getJwtName(), 16); + DERIVED_KEY_SIZE_MAP.put(Algorithm.PBES2_HS384_A192KW.getJwtName(), 24); + DERIVED_KEY_SIZE_MAP.put(Algorithm.PBES2_HS512_A256KW.getJwtName(), 32); + } + + + private byte[] password; + private int pbesCount; + private String keyAlgoJwt; + public PbesHmacAesWrapKeyEncryptionAlgorithm(String password, String keyAlgoJwt) { + this(stringToBytes(password), keyAlgoJwt); + } + public PbesHmacAesWrapKeyEncryptionAlgorithm(String password, int pbesCount, String keyAlgoJwt) { + this(stringToBytes(password), pbesCount, keyAlgoJwt); + } + public PbesHmacAesWrapKeyEncryptionAlgorithm(char[] password, String keyAlgoJwt) { + this(password, 4096, keyAlgoJwt); + } + public PbesHmacAesWrapKeyEncryptionAlgorithm(char[] password, int pbesCount, String keyAlgoJwt) { + this(charsToBytes(password), pbesCount, keyAlgoJwt); + } + public PbesHmacAesWrapKeyEncryptionAlgorithm(byte[] password, String keyAlgoJwt) { + this(password, 4096, keyAlgoJwt); + } + public PbesHmacAesWrapKeyEncryptionAlgorithm(byte[] password, int pbesCount, String keyAlgoJwt) { + this.password = password; + this.keyAlgoJwt = validateKeyAlgorithm(keyAlgoJwt); + this.pbesCount = validatePbesCount(pbesCount); + } + + @Override + public byte[] getEncryptedContentEncryptionKey(JweHeaders headers, byte[] cek) { + int keySize = getKeySize(keyAlgoJwt); + byte[] saltInput = CryptoUtils.generateSecureRandomBytes(keySize); + byte[] derivedKey = createDerivedKey(keyAlgoJwt, keySize, password, saltInput, pbesCount); + + headers.setHeader("p2s", Base64UrlUtility.encode(saltInput)); + headers.setIntegerHeader("p2c", pbesCount); + + final String aesAlgoJwt = PBES_AES_MAP.get(keyAlgoJwt); + KeyEncryptionAlgorithm aesWrap = new AesWrapKeyEncryptionAlgorithm(derivedKey, aesAlgoJwt) { + protected void checkAlgorithms(JweHeaders headers, String defaultAlgo) { + // complete + } + protected String getKeyEncryptionAlgoJava(JweHeaders headers) { + return Algorithm.AES_WRAP_ALGO_JAVA; + } + }; + return aesWrap.getEncryptedContentEncryptionKey(headers, cek); + + + } + static int getKeySize(String keyAlgoJwt) { + return DERIVED_KEY_SIZE_MAP.get(keyAlgoJwt); + } + static byte[] createDerivedKey(String keyAlgoJwt, int keySize, + byte[] password, byte[] saltInput, int pbesCount) { + byte[] saltValue = createSaltValue(keyAlgoJwt, saltInput); + Digest digest = null; + int macSigSize = PBES_HMAC_MAP.get(keyAlgoJwt); + if (macSigSize == 256) { + digest = new SHA256Digest(); + } else if (macSigSize == 384) { + digest = new SHA384Digest(); + } else { + digest = new SHA512Digest(); + } + PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(digest); + gen.init(password, saltValue, pbesCount); + return ((KeyParameter) gen.generateDerivedParameters(keySize * 8)).getKey(); + } + + + private static byte[] createSaltValue(String keyAlgoJwt, byte[] saltInput) { + byte[] algoBytes = stringToBytes(keyAlgoJwt); + byte[] saltValue = new byte[algoBytes.length + 1 + saltInput.length]; + System.arraycopy(algoBytes, 0, saltValue, 0, algoBytes.length); + saltValue[algoBytes.length] = 0; + System.arraycopy(saltInput, 0, saltValue, algoBytes.length + 1, saltInput.length); + return saltValue; + } + static String validateKeyAlgorithm(String algo) { + if (!SUPPORTED_ALGORITHMS.contains(algo)) { + throw new SecurityException(); + } + return algo; + } + static int validatePbesCount(int count) { + if (count < 1000) { + throw new SecurityException(); + } + return count; + } + + static byte[] stringToBytes(String str) { + try { + return str.getBytes("UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new SecurityException(ex); + } + } + static byte[] charsToBytes(char[] chars) { + ByteBuffer bb = Charset.forName("UTF-8").encode(CharBuffer.wrap(chars)); + byte[] b = new byte[bb.remaining()]; + bb.get(b); + return b; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyDecryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyDecryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyDecryptionAlgorithm.java new file mode 100644 index 0000000..c0e2f28 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyDecryptionAlgorithm.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.jwe; + +import java.security.interfaces.RSAPrivateKey; + +public class RSAOaepKeyDecryptionAlgorithm extends WrappedKeyDecryptionAlgorithm { + public RSAOaepKeyDecryptionAlgorithm(RSAPrivateKey privateKey) { + this(privateKey, true); + } + public RSAOaepKeyDecryptionAlgorithm(RSAPrivateKey privateKey, boolean unwrap) { + super(privateKey, unwrap); + } + protected int getKeyCipherBlockSize() { + return ((RSAPrivateKey)getCekDecryptionKey()).getModulus().toByteArray().length; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyEncryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyEncryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyEncryptionAlgorithm.java new file mode 100644 index 0000000..b658e36 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/RSAOaepKeyEncryptionAlgorithm.java @@ -0,0 +1,39 @@ +/** + * 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.interfaces.RSAPublicKey; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.cxf.rs.security.jose.jwa.Algorithm; + +public class RSAOaepKeyEncryptionAlgorithm extends AbstractWrapKeyEncryptionAlgorithm { + private static final Set<String> SUPPORTED_ALGORITHMS = new HashSet<String>( + Arrays.asList(Algorithm.RSA_OAEP.getJwtName(), + Algorithm.RSA_OAEP_256.getJwtName())); + public RSAOaepKeyEncryptionAlgorithm(RSAPublicKey publicKey, String jweAlgo) { + this(publicKey, jweAlgo, true); + } + public RSAOaepKeyEncryptionAlgorithm(RSAPublicKey publicKey, String jweAlgo, boolean wrap) { + super(publicKey, jweAlgo, wrap, SUPPORTED_ALGORITHMS); + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyDecryptionAlgorithm.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyDecryptionAlgorithm.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyDecryptionAlgorithm.java new file mode 100644 index 0000000..789e8cd --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyDecryptionAlgorithm.java @@ -0,0 +1,74 @@ +/** + * 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.Key; +import java.security.spec.AlgorithmParameterSpec; + +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; +import org.apache.cxf.rs.security.oauth2.utils.crypto.KeyProperties; + +public class WrappedKeyDecryptionAlgorithm implements KeyDecryptionAlgorithm { + private Key cekDecryptionKey; + private boolean unwrap; + public WrappedKeyDecryptionAlgorithm(Key cekDecryptionKey) { + this(cekDecryptionKey, true); + } + public WrappedKeyDecryptionAlgorithm(Key cekDecryptionKey, boolean unwrap) { + this.cekDecryptionKey = cekDecryptionKey; + this.unwrap = unwrap; + } + public byte[] getDecryptedContentEncryptionKey(JweCompactConsumer consumer) { + KeyProperties keyProps = new KeyProperties(getKeyEncryptionAlgorithm(consumer)); + AlgorithmParameterSpec spec = getAlgorithmParameterSpec(consumer); + if (spec != null) { + keyProps.setAlgoSpec(spec); + } + if (!unwrap) { + keyProps.setBlockSize(getKeyCipherBlockSize()); + return CryptoUtils.decryptBytes(getEncryptedContentEncryptionKey(consumer), + getCekDecryptionKey(), keyProps); + } else { + return CryptoUtils.unwrapSecretKey(getEncryptedContentEncryptionKey(consumer), + getContentEncryptionAlgorithm(consumer), + getCekDecryptionKey(), + keyProps).getEncoded(); + } + } + + protected Key getCekDecryptionKey() { + return cekDecryptionKey; + } + protected int getKeyCipherBlockSize() { + return -1; + } + protected String getKeyEncryptionAlgorithm(JweCompactConsumer consumer) { + return Algorithm.toJavaName(consumer.getJweHeaders().getKeyEncryptionAlgorithm()); + } + protected String getContentEncryptionAlgorithm(JweCompactConsumer consumer) { + return Algorithm.toJavaName(consumer.getJweHeaders().getContentEncryptionAlgorithm()); + } + protected AlgorithmParameterSpec getAlgorithmParameterSpec(JweCompactConsumer consumer) { + return null; + } + protected byte[] getEncryptedContentEncryptionKey(JweCompactConsumer consumer) { + return consumer.getEncryptedContentEncryptionKey(); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweDecryption.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweDecryption.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweDecryption.java new file mode 100644 index 0000000..b14effc --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweDecryption.java @@ -0,0 +1,56 @@ +/** + * 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.Key; + +import org.apache.cxf.rs.security.jose.jwt.JwtHeadersReader; + +public class WrappedKeyJweDecryption extends AbstractJweDecryption { + public WrappedKeyJweDecryption(Key cekDecryptionKey) { + this(cekDecryptionKey, true); + } + public WrappedKeyJweDecryption(Key cekDecryptionKey, boolean unwrap) { + this(cekDecryptionKey, unwrap, null); + } + public WrappedKeyJweDecryption(Key cekDecryptionKey, JweCryptoProperties props) { + this(cekDecryptionKey, true, props); + } + public WrappedKeyJweDecryption(Key cekDecryptionKey, boolean unwrap, + JweCryptoProperties props) { + this(cekDecryptionKey, unwrap, props, null); + } + public WrappedKeyJweDecryption(Key cekDecryptionKey, boolean unwrap, + JweCryptoProperties props, JwtHeadersReader reader) { + this(new WrappedKeyDecryptionAlgorithm(cekDecryptionKey, unwrap), + props, reader); + } + public WrappedKeyJweDecryption(KeyDecryptionAlgorithm keyDecryptionAlgo) { + this(keyDecryptionAlgo, null, null); + } + public WrappedKeyJweDecryption(KeyDecryptionAlgorithm keyDecryptionAlgo, + JweCryptoProperties props, JwtHeadersReader reader) { + this(keyDecryptionAlgo, props, reader, new AesGcmContentDecryptionAlgorithm()); + } + public WrappedKeyJweDecryption(KeyDecryptionAlgorithm keyDecryptionAlgo, + JweCryptoProperties props, JwtHeadersReader reader, + ContentDecryptionAlgorithm cipherProps) { + super(props, reader, keyDecryptionAlgo, cipherProps); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweEncryption.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweEncryption.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweEncryption.java new file mode 100644 index 0000000..98bad90 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwe/WrappedKeyJweEncryption.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.jwe; + +import org.apache.cxf.rs.security.jose.jwt.JwtHeadersWriter; + +public class WrappedKeyJweEncryption extends AbstractJweEncryption { + public WrappedKeyJweEncryption(JweHeaders headers, + KeyEncryptionAlgorithm keyEncryptionAlgorithm) { + this(headers, null, null, keyEncryptionAlgorithm); + } + public WrappedKeyJweEncryption(JweHeaders headers, byte[] cek, + byte[] iv, KeyEncryptionAlgorithm keyEncryptionAlgorithm) { + this(headers, cek, iv, keyEncryptionAlgorithm, null); + } + public WrappedKeyJweEncryption(JweHeaders headers, + byte[] cek, + byte[] iv, + KeyEncryptionAlgorithm keyEncryptionAlgorithm, + JwtHeadersWriter writer) { + super(headers, new AesGcmContentEncryptionAlgorithm(cek, iv), keyEncryptionAlgorithm, writer); + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.java new file mode 100644 index 0000000..be300f4 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/DefaultJwkReaderWriter.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.jwk; + +import org.apache.cxf.rs.security.jose.jwt.AbstractJwtObjectReaderWriter; + + + + +public class DefaultJwkReaderWriter extends AbstractJwtObjectReaderWriter + implements JwkReaderWriter { + @Override + public String jwkSetToJson(JsonWebKeys jwks) { + return toJson(jwks); + } + @Override + public JsonWebKeys jsonToJwkSet(String jwksJson) { + JsonWebKeys jwks = new JsonWebKeys(); + fromJsonInternal(jwks, jwksJson); + return jwks; + } + @Override + public String jwkToJson(JsonWebKey jwk) { + return toJson(jwk); + } + @Override + public JsonWebKey jsonToJwk(String jwkJson) { + JsonWebKey jwk = new JsonWebKey(); + fromJsonInternal(jwk, jwkJson); + return jwk; + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKey.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKey.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKey.java new file mode 100644 index 0000000..510e7a7 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKey.java @@ -0,0 +1,213 @@ +/** + * 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.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.List; +import java.util.Map; + +import javax.crypto.SecretKey; + +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.jose.jwt.AbstractJwtObject; +import org.apache.cxf.rs.security.jose.jwt.JwtConstants; +import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; + + +public class JsonWebKey extends AbstractJwtObject { + + public static final String KEY_TYPE = "kty"; + public static final String PUBLIC_KEY_USE = "use"; + public static final String KEY_OPERATIONS = "key_ops"; + public static final String KEY_ALGO = JwtConstants.HEADER_ALGORITHM; + public static final String KEY_ID = JwtConstants.HEADER_KEY_ID; + public static final String X509_URL = JwtConstants.HEADER_X509_URL; + public static final String X509_CHAIN = JwtConstants.HEADER_X509_CHAIN; + public static final String X509_THUMBPRINT = JwtConstants.HEADER_X509_THUMBPRINT; + public static final String X509_THUMBPRINT_SHA256 = JwtConstants.HEADER_X509_THUMBPRINT_SHA256; + + public static final String KEY_TYPE_RSA = "RSA"; + public static final String RSA_MODULUS = "n"; + public static final String RSA_PUBLIC_EXP = "e"; + public static final String RSA_PRIVATE_EXP = "d"; + public static final String RSA_FIRST_PRIME_FACTOR = "p"; + public static final String RSA_SECOND_PRIME_FACTOR = "q"; + public static final String RSA_FIRST_PRIME_CRT = "dp"; + public static final String RSA_SECOND_PRIME_CRT = "dq"; + public static final String RSA_FIRST_CRT_COEFFICIENT = "qi"; + + public static final String KEY_TYPE_OCTET = "oct"; + public static final String OCTET_KEY_VALUE = "k"; + + public static final String KEY_TYPE_ELLIPTIC = "EC"; + public static final String EC_CURVE = "crv"; + public static final String EC_CURVE_P256 = "P-256"; + public static final String EC_CURVE_P384 = "P-384"; + public static final String EC_CURVE_P512 = "P-512"; + public static final String EC_X_COORDINATE = "x"; + public static final String EC_Y_COORDINATE = "y"; + public static final String EC_PRIVATE_KEY = "d"; + + public static final String PUBLIC_KEY_USE_SIGN = "sig"; + public static final String PUBLIC_KEY_USE_ENCRYPT = "enc"; + + public static final String KEY_OPER_SIGN = "sign"; + public static final String KEY_OPER_VERIFY = "verify"; + public static final String KEY_OPER_ENCRYPT = "encrypt"; + public static final String KEY_OPER_DECRYPT = "decrypt"; + + public JsonWebKey() { + + } + + public JsonWebKey(Map<String, Object> values) { + super(values); + } + + public void setKeyType(String keyType) { + super.setValue(KEY_TYPE, keyType); + } + + public String getKeyType() { + return (String)super.getValue(KEY_TYPE); + } + + public void setPublicKeyUse(String use) { + super.setValue(PUBLIC_KEY_USE, use); + } + + public String getPublicKeyUse() { + return (String)super.getValue(PUBLIC_KEY_USE); + } + + public void setKeyOperation(List<String> keyOperation) { + super.setValue(KEY_OPERATIONS, keyOperation); + } + + public List<String> getKeyOperation() { + return CastUtils.cast((List<?>)super.getValue(KEY_OPERATIONS)); + } + + public void setAlgorithm(String algorithm) { + super.setValue(KEY_ALGO, algorithm); + } + + public String getAlgorithm() { + return (String)super.getValue(KEY_ALGO); + } + + public void setKid(String kid) { + super.setValue(KEY_ID, kid); + } + + public String getKid() { + return (String)super.getValue(KEY_ID); + } + + public void setX509Url(String x509Url) { + super.setValue(X509_URL, x509Url); + } + + public String getX509Url() { + return (String)super.getValue(X509_URL); + } + + public void setX509Chain(String x509Chain) { + super.setValue(X509_CHAIN, x509Chain); + } + + public String getX509Chain() { + return (String)super.getValue(X509_CHAIN); + } + + public void setX509Thumbprint(String x509Thumbprint) { + super.setValue(X509_THUMBPRINT, x509Thumbprint); + } + + public String getX509Thumbprint() { + return (String)super.getValue(X509_THUMBPRINT); + } + + public void setX509ThumbprintSHA256(String x509Thumbprint) { + super.setValue(X509_THUMBPRINT_SHA256, x509Thumbprint); + } + + public String getX509ThumbprintSHA256() { + return (String)super.getValue(X509_THUMBPRINT_SHA256); + } + + public JsonWebKey setProperty(String name, Object value) { + super.setValue(name, value); + return this; + } + + public Object getProperty(String name) { + return super.getValue(name); + } + + public RSAPublicKey toRSAPublicKey() { + String encodedModulus = (String)super.getValue(RSA_MODULUS); + String encodedPublicExponent = (String)super.getValue(RSA_PUBLIC_EXP); + return CryptoUtils.getRSAPublicKey(encodedModulus, encodedPublicExponent); + } + public RSAPrivateKey toRSAPrivateKey() { + String encodedModulus = (String)super.getValue(RSA_MODULUS); + String encodedPrivateExponent = (String)super.getValue(RSA_PRIVATE_EXP); + String encodedPrimeP = (String)super.getValue(RSA_FIRST_PRIME_FACTOR); + if (encodedPrimeP == null) { + return CryptoUtils.getRSAPrivateKey(encodedModulus, encodedPrivateExponent); + } else { + String encodedPublicExponent = (String)super.getValue(RSA_PUBLIC_EXP); + String encodedPrimeQ = (String)super.getValue(RSA_SECOND_PRIME_FACTOR); + String encodedPrimeExpP = (String)super.getValue(RSA_FIRST_PRIME_CRT); + String encodedPrimeExpQ = (String)super.getValue(RSA_SECOND_PRIME_CRT); + String encodedCrtCoefficient = (String)super.getValue(RSA_FIRST_CRT_COEFFICIENT); + return CryptoUtils.getRSAPrivateKey(encodedModulus, + encodedPublicExponent, + encodedPrivateExponent, + encodedPrimeP, + encodedPrimeQ, + encodedPrimeExpP, + encodedPrimeExpQ, + encodedCrtCoefficient); + } + } + public ECPublicKey toECPublicKey() { + String eCurve = (String)super.getValue(EC_CURVE); + String encodedXCoord = (String)super.getValue(EC_X_COORDINATE); + String encodedYCoord = (String)super.getValue(EC_Y_COORDINATE); + return CryptoUtils.getECPublicKey(eCurve, encodedXCoord, encodedYCoord); + } + public ECPrivateKey toECPrivateKey() { + String eCurve = (String)super.getValue(EC_CURVE); + String encodedPrivateKey = (String)super.getValue(EC_PRIVATE_KEY); + return CryptoUtils.getECPrivateKey(eCurve, encodedPrivateKey); + } + + public SecretKey toSecretKey() { + return CryptoUtils.createSecretKeySpec((String)getProperty(OCTET_KEY_VALUE), + Algorithm.toJavaName(getAlgorithm())); + } + + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java new file mode 100644 index 0000000..3c79454 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JsonWebKeys.java @@ -0,0 +1,131 @@ +/** + * 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.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.apache.cxf.helpers.CastUtils; +import org.apache.cxf.rs.security.jose.jwt.AbstractJwtObject; + +public class JsonWebKeys extends AbstractJwtObject { + public static final String KEYS_PROPERTY = "keys"; + public List<JsonWebKey> getKeys() { + List<?> list = (List<?>)super.getValue(KEYS_PROPERTY); + if (list != null && !list.isEmpty()) { + Object first = list.get(0); + if (first instanceof JsonWebKey) { + return CastUtils.cast(list); + } else { + List<JsonWebKey> keys = new LinkedList<JsonWebKey>(); + List<Map<String, Object>> listOfMaps = + CastUtils.cast((List<?>)super.getValue(KEYS_PROPERTY)); + for (Map<String, Object> map : listOfMaps) { + keys.add(new JsonWebKey(map)); + } + return keys; + } + } else { + return null; + } + } + + public void setKeys(List<JsonWebKey> keys) { + super.setValue(KEYS_PROPERTY, keys); + } + + public Map<String, JsonWebKey> getKeyIdMap() { + List<JsonWebKey> keys = getKeys(); + if (keys == null) { + return Collections.emptyMap(); + } + Map<String, JsonWebKey> map = new LinkedHashMap<String, JsonWebKey>(); + for (JsonWebKey key : keys) { + String kid = key.getKid(); + if (kid != null) { + map.put(kid, key); + } + } + return map; + } + public JsonWebKey getKey(String kid) { + return getKeyIdMap().get(kid); + } + public Map<String, List<JsonWebKey>> getKeyTypeMap() { + return getKeyPropertyMap(JsonWebKey.KEY_TYPE); + } + public Map<String, List<JsonWebKey>> getKeyUseMap() { + return getKeyPropertyMap(JsonWebKey.PUBLIC_KEY_USE); + } + private Map<String, List<JsonWebKey>> getKeyPropertyMap(String propertyName) { + List<JsonWebKey> keys = getKeys(); + if (keys == null) { + return Collections.emptyMap(); + } + Map<String, List<JsonWebKey>> map = new LinkedHashMap<String, List<JsonWebKey>>(); + for (JsonWebKey key : keys) { + String propValue = (String)key.getProperty(propertyName); + if (propValue != null) { + List<JsonWebKey> list = map.get(propValue); + if (list == null) { + list = new LinkedList<JsonWebKey>(); + map.put(propValue, list); + } + list.add(key); + } + } + return map; + } + public Map<String, List<JsonWebKey>> getKeyOperationMap() { + List<JsonWebKey> keys = getKeys(); + if (keys == null) { + return Collections.emptyMap(); + } + Map<String, List<JsonWebKey>> map = new LinkedHashMap<String, List<JsonWebKey>>(); + for (JsonWebKey key : keys) { + List<String> ops = key.getKeyOperation(); + if (ops != null) { + for (String op : ops) { + List<JsonWebKey> list = map.get(op); + if (list == null) { + list = new LinkedList<JsonWebKey>(); + map.put(op, list); + } + list.add(key); + } + } + } + return map; + } + public List<JsonWebKey> getKeys(String keyType) { + return getKeyTypeMap().get(keyType); + } + public List<JsonWebKey> getRsaKeys() { + return getKeyTypeMap().get(JsonWebKey.KEY_TYPE_RSA); + } + public List<JsonWebKey> getEllipticKeys() { + return getKeyTypeMap().get(JsonWebKey.KEY_TYPE_ELLIPTIC); + } + public List<JsonWebKey> getSecretKeys() { + return getKeyTypeMap().get(JsonWebKey.KEY_TYPE_OCTET); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java new file mode 100644 index 0000000..679b7aa --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkReaderWriter.java @@ -0,0 +1,27 @@ +/** + * 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; + + +public interface JwkReaderWriter { + String jwkToJson(JsonWebKey jwk); + JsonWebKey jsonToJwk(String jwkJson); + String jwkSetToJson(JsonWebKeys jwkSet); + JsonWebKeys jsonToJwkSet(String jwkSetJson); +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java new file mode 100644 index 0000000..9661bdb --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jwk/JwkUtils.java @@ -0,0 +1,190 @@ +/** + * 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.io.UnsupportedEncodingException; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.apache.cxf.Bus; +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.jaxrs.utils.ResourceUtils; +import org.apache.cxf.message.Message; +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.AesCbcHmacJweEncryption; +import org.apache.cxf.rs.security.jose.jwe.JweDecryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider; +import org.apache.cxf.rs.security.jose.jwe.KeyDecryptionAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.KeyEncryptionAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.PbesHmacAesWrapKeyDecryptionAlgorithm; +import org.apache.cxf.rs.security.jose.jwe.PbesHmacAesWrapKeyEncryptionAlgorithm; +import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils; +import org.apache.cxf.rs.security.oauth2.utils.crypto.PrivateKeyPasswordProvider; + +public final class JwkUtils { + public static final String JWK_KEY_STORE_TYPE = "jwk"; + public static final String RSSEC_KEY_STORE_JWKSET = "rs.security.keystore.jwkset"; + public static final String RSSEC_KEY_STORE_JWKKEY = "rs.security.keystore.jwkkey"; + private JwkUtils() { + + } + public static String encryptJwkSet(JsonWebKeys jwkSet, char[] password) { + return encryptJwkSet(jwkSet, password, new DefaultJwkReaderWriter()); + } + public static String encryptJwkSet(JsonWebKeys jwkSet, char[] password, JwkReaderWriter writer) { + return encryptJwkSet(jwkSet, createDefaultEncryption(password), writer); + } + public static String encryptJwkSet(JsonWebKeys jwkSet, JweEncryptionProvider jwe, JwkReaderWriter writer) { + return jwe.encrypt(stringToBytes(writer.jwkSetToJson(jwkSet)), "jwk-set+json"); + } + public static JsonWebKeys decryptJwkSet(String jsonJwkSet, char[] password) { + return decryptJwkSet(jsonJwkSet, password, new DefaultJwkReaderWriter()); + } + public static JsonWebKeys decryptJwkSet(String jsonJwkSet, char[] password, JwkReaderWriter reader) { + return decryptJwkSet(jsonJwkSet, createDefaultDecryption(password), reader); + } + public static JsonWebKeys decryptJwkSet(String jsonJwkSet, JweDecryptionProvider jwe, JwkReaderWriter reader) { + return reader.jsonToJwkSet(jwe.decrypt(jsonJwkSet).getContentText()); + } + public static String encryptJwkKey(JsonWebKey jwk, char[] password) { + return encryptJwkKey(jwk, password, new DefaultJwkReaderWriter()); + } + public static String encryptJwkKey(JsonWebKey jwkKey, char[] password, JwkReaderWriter writer) { + return encryptJwkKey(jwkKey, createDefaultEncryption(password), writer); + } + public static String encryptJwkKey(JsonWebKey jwkKey, JweEncryptionProvider jwe, JwkReaderWriter writer) { + return jwe.encrypt(stringToBytes(writer.jwkToJson(jwkKey)), "jwk+json"); + } + public static JsonWebKey decryptJwkKey(String jsonJwkKey, char[] password) { + return decryptJwkKey(jsonJwkKey, password, new DefaultJwkReaderWriter()); + } + public static JsonWebKey decryptJwkKey(String jsonJwkKey, char[] password, JwkReaderWriter reader) { + return decryptJwkKey(jsonJwkKey, createDefaultDecryption(password), reader); + } + public static JsonWebKey decryptJwkKey(String jsonJwkKey, JweDecryptionProvider jwe, JwkReaderWriter reader) { + return reader.jsonToJwk(jwe.decrypt(jsonJwkKey).getContentText()); + } + private static JweEncryptionProvider createDefaultEncryption(char[] password) { + KeyEncryptionAlgorithm keyEncryption = + new PbesHmacAesWrapKeyEncryptionAlgorithm(password, Algorithm.PBES2_HS256_A128KW.getJwtName()); + return new AesCbcHmacJweEncryption(Algorithm.PBES2_HS256_A128KW.getJwtName(), + Algorithm.A128CBC_HS256.getJwtName(), + keyEncryption); + } + private static JweDecryptionProvider createDefaultDecryption(char[] password) { + KeyDecryptionAlgorithm keyDecryption = new PbesHmacAesWrapKeyDecryptionAlgorithm(password); + return new AesCbcHmacJweDecryption(keyDecryption); + } + public static JsonWebKeys loadJwkSet(Message m, Properties props, PrivateKeyPasswordProvider cb) { + return loadJwkSet(m, props, cb, new DefaultJwkReaderWriter()); + } + public static JsonWebKeys loadJwkSet(Message m, Properties props, PrivateKeyPasswordProvider cb, + JwkReaderWriter reader) { + JsonWebKeys jwkSet = (JsonWebKeys)m.getExchange().get(props.get(CryptoUtils.RSSEC_KEY_STORE_FILE)); + if (jwkSet == null) { + jwkSet = loadJwkSet(props, m.getExchange().getBus(), cb, reader); + m.getExchange().put((String)props.get(CryptoUtils.RSSEC_KEY_STORE_FILE), jwkSet); + } + return jwkSet; + } + public static JsonWebKeys loadJwkSet(Properties props, Bus bus, PrivateKeyPasswordProvider cb) { + return loadJwkSet(props, bus, cb, new DefaultJwkReaderWriter()); + } + public static JsonWebKeys loadJwkSet(Properties props, Bus bus, PrivateKeyPasswordProvider cb, + JwkReaderWriter reader) { + JweDecryptionProvider decryption = cb != null + ? new AesCbcHmacJweDecryption(new PbesHmacAesWrapKeyDecryptionAlgorithm(cb.getPassword(props))) : null; + return loadJwkSet(props, bus, decryption, reader); + } + public static JsonWebKeys loadJwkSet(Properties props, Bus bus, JweDecryptionProvider jwe, JwkReaderWriter reader) { + String keyContent = null; + String keyStoreLoc = props.getProperty(CryptoUtils.RSSEC_KEY_STORE_FILE); + if (keyStoreLoc != null) { + try { + InputStream is = ResourceUtils.getResourceStream(keyStoreLoc, bus); + keyContent = IOUtils.readStringFromStream(is); + } catch (Exception ex) { + throw new SecurityException(ex); + } + } else { + keyContent = props.getProperty(RSSEC_KEY_STORE_JWKSET); + if (keyContent == null) { + keyContent = props.getProperty(RSSEC_KEY_STORE_JWKKEY); + } + } + if (jwe != null) { + keyContent = jwe.decrypt(keyContent).getContentText(); + } + if (props.getProperty(RSSEC_KEY_STORE_JWKKEY) == null) { + return reader.jsonToJwkSet(keyContent); + } else { + JsonWebKey key = reader.jsonToJwk(keyContent); + JsonWebKeys keys = new JsonWebKeys(); + keys.setKeys(Collections.singletonList(key)); + return keys; + } + } + public static JsonWebKey loadJsonWebKey(Message m, Properties props, String keyOper) { + return loadJsonWebKey(m, props, keyOper, new DefaultJwkReaderWriter()); + } + public static JsonWebKey loadJsonWebKey(Message m, Properties props, String keyOper, JwkReaderWriter reader) { + PrivateKeyPasswordProvider cb = + (PrivateKeyPasswordProvider)m.getContextualProperty(CryptoUtils.RSSEC_KEY_PSWD_PROVIDER); + if (cb == null && keyOper != null) { + String propName = keyOper.equals(JsonWebKey.KEY_OPER_SIGN) ? CryptoUtils.RSSEC_SIG_KEY_PSWD_PROVIDER + : keyOper.equals(JsonWebKey.KEY_OPER_ENCRYPT) ? CryptoUtils.RSSEC_DECRYPT_KEY_PSWD_PROVIDER : null; + if (propName != null) { + cb = (PrivateKeyPasswordProvider)m.getContextualProperty(propName); + } + } + JsonWebKeys jwkSet = loadJwkSet(m, props, cb, reader); + String kid = props.getProperty(CryptoUtils.RSSEC_KEY_STORE_ALIAS); + if (kid == null && keyOper != null) { + String keyIdProp = null; + if (keyOper.equals(JsonWebKey.KEY_OPER_ENCRYPT)) { + keyIdProp = CryptoUtils.RSSEC_KEY_STORE_ALIAS + ".jwe"; + } else if (keyOper.equals(JsonWebKey.KEY_OPER_SIGN) + || keyOper.equals(JsonWebKey.KEY_OPER_VERIFY)) { + keyIdProp = CryptoUtils.RSSEC_KEY_STORE_ALIAS + ".jws"; + } + if (keyIdProp != null) { + kid = props.getProperty(keyIdProp); + } + } + if (kid != null) { + return jwkSet.getKey(kid); + } else if (keyOper != null) { + List<JsonWebKey> keys = jwkSet.getKeyUseMap().get(keyOper); + if (keys != null && keys.size() == 1) { + return keys.get(0); + } + } + return null; + } + private static byte[] stringToBytes(String str) { + try { + return str.getBytes("UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new SecurityException(ex); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/AbstractJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/AbstractJwsSignatureProvider.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/AbstractJwsSignatureProvider.java new file mode 100644 index 0000000..04516a3 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/AbstractJwsSignatureProvider.java @@ -0,0 +1,62 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jws; + +import java.util.Set; + +import org.apache.cxf.rs.security.jose.jwt.JwtHeaders; + +public abstract class AbstractJwsSignatureProvider implements JwsSignatureProvider { + private Set<String> supportedAlgorithms; + private String defaultJwtAlgorithm; + + protected AbstractJwsSignatureProvider(Set<String> supportedAlgorithms) { + this.supportedAlgorithms = supportedAlgorithms; + } + + protected JwtHeaders prepareHeaders(JwtHeaders headers) { + if (headers == null) { + headers = new JwtHeaders(); + } + String algo = headers.getAlgorithm(); + if (algo != null) { + checkAlgorithm(algo); + } else { + headers.setAlgorithm(defaultJwtAlgorithm); + } + return headers; + } + + @Override + public JwsSignature createJwsSignature(JwtHeaders headers) { + return doCreateJwsSignature(prepareHeaders(headers)); + } + + protected abstract JwsSignature doCreateJwsSignature(JwtHeaders headers); + + public void setDefaultJwtAlgorithm(String algo) { + this.defaultJwtAlgorithm = algo; + } + protected void checkAlgorithm(String algo) { + if (algo == null || !supportedAlgorithms.contains(algo)) { + throw new SecurityException(); + } + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java new file mode 100644 index 0000000..f1547b5 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/EcDsaJwsSignatureProvider.java @@ -0,0 +1,45 @@ +/** + * 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.SecureRandom; +import java.security.interfaces.ECPrivateKey; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.cxf.rs.security.jose.jwa.Algorithm; + +public class EcDsaJwsSignatureProvider extends PrivateKeyJwsSignatureProvider { + private static final Set<String> SUPPORTED_ALGORITHMS = new HashSet<String>( + Arrays.asList(Algorithm.SHA256withECDSA.getJwtName(), + Algorithm.SHA384withECDSA.getJwtName(), + Algorithm.SHA512withECDSA.getJwtName())); + + public EcDsaJwsSignatureProvider(ECPrivateKey key) { + this(key, null); + } + public EcDsaJwsSignatureProvider(ECPrivateKey key, AlgorithmParameterSpec spec) { + this(key, null, spec); + } + public EcDsaJwsSignatureProvider(ECPrivateKey key, SecureRandom random, AlgorithmParameterSpec spec) { + super(key, random, spec, SUPPORTED_ALGORITHMS); + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureProvider.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureProvider.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureProvider.java new file mode 100644 index 0000000..38ed06a --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/HmacJwsSignatureProvider.java @@ -0,0 +1,89 @@ +/** + * 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.spec.AlgorithmParameterSpec; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.crypto.Mac; + +import org.apache.cxf.common.util.Base64Exception; +import org.apache.cxf.rs.security.jose.jwa.Algorithm; +import org.apache.cxf.rs.security.jose.jwt.JwtHeaders; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; +import org.apache.cxf.rs.security.oauth2.utils.crypto.HmacUtils; + +public class HmacJwsSignatureProvider extends AbstractJwsSignatureProvider implements JwsSignatureVerifier { + private static final Set<String> SUPPORTED_ALGORITHMS = new HashSet<String>( + Arrays.asList(Algorithm.HmacSHA256.getJwtName(), + Algorithm.HmacSHA384.getJwtName(), + Algorithm.HmacSHA512.getJwtName())); + private byte[] key; + private AlgorithmParameterSpec hmacSpec; + + public HmacJwsSignatureProvider(byte[] key) { + this(key, null); + } + public HmacJwsSignatureProvider(byte[] key, AlgorithmParameterSpec spec) { + super(SUPPORTED_ALGORITHMS); + this.key = key; + this.hmacSpec = spec; + } + public HmacJwsSignatureProvider(String encodedKey) { + super(SUPPORTED_ALGORITHMS); + try { + this.key = Base64UrlUtility.decode(encodedKey); + } catch (Base64Exception ex) { + throw new SecurityException(); + } + } + + @Override + public boolean verify(JwtHeaders headers, String unsignedText, byte[] signature) { + byte[] expected = computeMac(headers, unsignedText); + return Arrays.equals(expected, signature); + } + + private byte[] computeMac(JwtHeaders headers, String text) { + return HmacUtils.computeHmac(key, + Algorithm.toJavaName(headers.getAlgorithm()), + hmacSpec, + text); + } + protected JwsSignature doCreateJwsSignature(JwtHeaders headers) { + final Mac mac = HmacUtils.getInitializedMac(key, Algorithm.toJavaName(headers.getAlgorithm()), + hmacSpec); + return new JwsSignature() { + + @Override + public void update(byte[] src, int off, int len) { + mac.update(src, off, len); + } + + @Override + public byte[] sign() { + return mac.doFinal(); + } + + }; + } + +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java new file mode 100644 index 0000000..422473f --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactConsumer.java @@ -0,0 +1,122 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jws; + +import java.io.UnsupportedEncodingException; + +import org.apache.cxf.common.util.Base64Exception; +import org.apache.cxf.rs.security.jose.jwt.JwtHeaders; +import org.apache.cxf.rs.security.jose.jwt.JwtHeadersReader; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReader; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReaderWriter; +import org.apache.cxf.rs.security.oauth2.provider.OAuthServiceException; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; + +public class JwsCompactConsumer { + private JwtHeadersReader reader = new JwtTokenReaderWriter(); + private String encodedSequence; + private String encodedSignature; + private String headersJson; + private String jwsPayload; + private JwsSignatureProperties props; + public JwsCompactConsumer(String encodedJws) { + this(encodedJws, null, null); + } + public JwsCompactConsumer(String encodedJws, JwsSignatureProperties props) { + this(encodedJws, props, null); + } + public JwsCompactConsumer(String encodedJws, JwtTokenReader r) { + this(encodedJws, null, r); + } + public JwsCompactConsumer(String encodedJws, JwsSignatureProperties props, JwtHeadersReader r) { + if (r != null) { + this.reader = r; + } + this.props = props; + String[] parts = encodedJws.split("\\."); + if (parts.length != 3) { + if (parts.length == 2 && encodedJws.endsWith(".")) { + encodedSignature = ""; + } else { + throw new OAuthServiceException("Invalid JWS Compact sequence"); + } + } else { + encodedSignature = parts[2]; + } + headersJson = decodeToString(parts[0]); + jwsPayload = decodeToString(parts[1]); + encodedSequence = parts[0] + "." + parts[1]; + + } + public String getUnsignedEncodedPayload() { + return encodedSequence; + } + public String getEncodedSignature() { + return encodedSignature; + } + public String getDecodedJsonHeaders() { + return headersJson; + } + public String getDecodedJwsPayload() { + return jwsPayload; + } + public byte[] getDecodedJwsPayloadBytes() { + try { + return jwsPayload.getBytes("UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new SecurityException(ex); + } + } + public byte[] getDecodedSignature() { + return encodedSignature.isEmpty() ? new byte[]{} : decode(encodedSignature); + } + public JwtHeaders getJwtHeaders() { + return getReader().fromJsonHeaders(headersJson); + } + public boolean verifySignatureWith(JwsSignatureVerifier validator) { + enforceJweSignatureProperties(); + if (!validator.verify(getJwtHeaders(), getUnsignedEncodedPayload(), getDecodedSignature())) { + throw new SecurityException(); + } + return true; + } + private void enforceJweSignatureProperties() { + if (props != null) { + //TODO: + } + } + private static String decodeToString(String encoded) { + try { + return new String(decode(encoded), "UTF-8"); + } catch (UnsupportedEncodingException ex) { + throw new SecurityException(ex); + } + + } + protected JwtHeadersReader getReader() { + return reader; + } + private static byte[] decode(String encoded) { + try { + return Base64UrlUtility.decode(encoded); + } catch (Base64Exception ex) { + throw new SecurityException(ex); + } + } +} http://git-wip-us.apache.org/repos/asf/cxf/blob/30dec871/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java ---------------------------------------------------------------------- diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java new file mode 100644 index 0000000..ef4dd52 --- /dev/null +++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/jose/jws/JwsCompactProducer.java @@ -0,0 +1,109 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.rs.security.jose.jws; + +import org.apache.cxf.common.util.StringUtils; +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.JwtHeadersWriter; +import org.apache.cxf.rs.security.jose.jwt.JwtTokenReaderWriter; +import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility; + +public class JwsCompactProducer { + private JwtHeadersWriter writer = new JwtTokenReaderWriter(); + private JwtHeaders headers; + private String plainJwsPayload; + private String signature; + private String plainRep; + + public JwsCompactProducer(String plainJwsPayload) { + this(null, null, plainJwsPayload); + } + public JwsCompactProducer(JwtHeaders headers, String plainJwsPayload) { + this(headers, null, plainJwsPayload); + } + public JwsCompactProducer(JwtHeaders headers, JwtHeadersWriter w, String plainJwsPayload) { + this.headers = headers; + if (w != null) { + this.writer = w; + } + this.plainJwsPayload = plainJwsPayload; + } + public JwtHeaders getHeaders() { + if (headers == null) { + headers = new JwtHeaders(); + } + return headers; + } + public String getUnsignedEncodedJws() { + checkAlgorithm(); + if (plainRep == null) { + plainRep = Base64UrlUtility.encode(writer.headersToJson(getHeaders())) + + "." + + Base64UrlUtility.encode(plainJwsPayload); + } + return plainRep; + } + + public String getSignedEncodedJws() { + checkAlgorithm(); + boolean noSignature = StringUtils.isEmpty(signature); + if (noSignature && !isPlainText()) { + throw new IllegalStateException("Signature is not available"); + } + return getUnsignedEncodedJws() + "." + (noSignature ? "" : signature); + } + + public String signWith(JwsSignatureProvider signer) { + JwsSignature worker = signer.createJwsSignature(getHeaders()); + try { + byte[] bytes = getUnsignedEncodedJws().getBytes("UTF-8"); + worker.update(bytes, 0, bytes.length); + signWith(worker.sign()); + return getSignedEncodedJws(); + } catch (Exception ex) { + throw new SecurityException(); + } + } + + public String signWith(String signatureText) { + setEncodedSignature(Base64UrlUtility.encode(signatureText)); + return getSignedEncodedJws(); + } + + public String signWith(byte[] signatureOctets) { + setEncodedSignature(Base64UrlUtility.encode(signatureOctets)); + return getSignedEncodedJws(); + } + + private void setEncodedSignature(String sig) { + this.signature = sig; + } + private boolean isPlainText() { + return JwtConstants.PLAIN_TEXT_ALGO.equals(getAlgorithm()); + } + private String getAlgorithm() { + return getHeaders().getAlgorithm(); + } + private void checkAlgorithm() { + if (getAlgorithm() == null) { + throw new IllegalStateException("Algorithm header is not set"); + } + } +}