You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2015/02/10 14:34:48 UTC
cxf git commit: [CXF-6085] Prototyping the actual JweJson producer
code, using the patch from Daniel Torkian as the input
Repository: cxf
Updated Branches:
refs/heads/master 7944123fb -> 8891549dc
[CXF-6085] Prototyping the actual JweJson producer code, using the patch from Daniel Torkian as the input
Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/8891549d
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/8891549d
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/8891549d
Branch: refs/heads/master
Commit: 8891549dc9f6a10e879430d18836459a17a94100
Parents: 7944123
Author: Sergey Beryozkin <sb...@talend.com>
Authored: Tue Feb 10 13:34:31 2015 +0000
Committer: Sergey Beryozkin <sb...@talend.com>
Committed: Tue Feb 10 13:34:31 2015 +0000
----------------------------------------------------------------------
.../jose/jaxrs/JweWriterInterceptor.java | 4 +-
.../jose/jwe/AbstractJweEncryption.java | 39 +++--
.../security/jose/jwe/JweEncryptionInput.java | 60 +++++++
.../jose/jwe/JweEncryptionProvider.java | 8 +-
.../jose/jwe/JweJsonEncryptionEntry.java | 56 +++++++
.../rs/security/jose/jwe/JweJsonProducer.java | 168 +++++++++++++++++++
6 files changed, 314 insertions(+), 21 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf/blob/8891549d/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java
index ca4f5c9..7291edd 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jaxrs/JweWriterInterceptor.java
@@ -35,6 +35,7 @@ import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.jaxrs.utils.JAXRSUtils;
import org.apache.cxf.rs.security.jose.JoseConstants;
import org.apache.cxf.rs.security.jose.jwe.JweCompactProducer;
+import org.apache.cxf.rs.security.jose.jwe.JweEncryptionInput;
import org.apache.cxf.rs.security.jose.jwe.JweEncryptionProvider;
import org.apache.cxf.rs.security.jose.jwe.JweEncryptionState;
import org.apache.cxf.rs.security.jose.jwe.JweHeaders;
@@ -70,7 +71,8 @@ public class JweWriterInterceptor implements WriterInterceptor {
}
if (useJweOutputStream) {
- JweEncryptionState encryption = theEncryptionProvider.createJweEncryptionState(jweHeaders, null);
+ JweEncryptionState encryption =
+ theEncryptionProvider.createJweEncryptionState(new JweEncryptionInput(jweHeaders));
try {
JweCompactProducer.startJweContent(actualOs,
encryption.getHeaders(),
http://git-wip-us.apache.org/repos/asf/cxf/blob/8891549d/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AbstractJweEncryption.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AbstractJweEncryption.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AbstractJweEncryption.java
index 9103ecf..e0d7cf6 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AbstractJweEncryption.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/AbstractJweEncryption.java
@@ -107,8 +107,8 @@ public abstract class AbstractJweEncryption implements JweEncryptionProvider {
return writer;
}
@Override
- public JweEncryptionState createJweEncryptionState(JweHeaders jweHeaders, byte[] aad) {
- JweEncryptionInternal state = getInternalState(jweHeaders, aad);
+ public JweEncryptionState createJweEncryptionState(JweEncryptionInput jweInput) {
+ JweEncryptionInternal state = getInternalState(jweInput.getJweHeaders(), jweInput);
Cipher c = CryptoUtils.initCipher(createCekSecretKey(state), state.keyProps,
Cipher.ENCRYPT_MODE);
return new JweEncryptionState(c,
@@ -130,7 +130,7 @@ public abstract class AbstractJweEncryption implements JweEncryptionProvider {
return theCek;
}
- private JweEncryptionInternal getInternalState(JweHeaders jweHeaders, byte[] aad) {
+ private JweEncryptionInternal getInternalState(JweHeaders jweInHeaders, JweEncryptionInput jweInput) {
JweHeaders theHeaders = new JweHeaders();
if (getKeyAlgorithm() != null) {
theHeaders.setKeyEncryptionAlgorithm(getKeyAlgorithm());
@@ -138,33 +138,35 @@ public abstract class AbstractJweEncryption implements JweEncryptionProvider {
theHeaders.setContentEncryptionAlgorithm(getContentAlgorithm());
JweHeaders protectedHeaders = null;
- if (jweHeaders != null) {
- if (jweHeaders.getKeyEncryptionAlgorithm() != null
+ if (jweInHeaders != null) {
+ if (jweInHeaders.getKeyEncryptionAlgorithm() != null
&& (getKeyAlgorithm() == null
- || !getKeyAlgorithm().equals(jweHeaders.getKeyEncryptionAlgorithm()))
- || jweHeaders.getAlgorithm() != null
- && !getContentAlgorithm().equals(jweHeaders.getAlgorithm())) {
+ || !getKeyAlgorithm().equals(jweInHeaders.getKeyEncryptionAlgorithm()))
+ || jweInHeaders.getAlgorithm() != null
+ && !getContentAlgorithm().equals(jweInHeaders.getAlgorithm())) {
throw new SecurityException();
}
- theHeaders.asMap().putAll(jweHeaders.asMap());
- if (jweHeaders.getProtectedHeaders() != null
- && !jweHeaders.asMap().entrySet().containsAll(theHeaders.asMap().entrySet())) {
- jweHeaders.getProtectedHeaders().asMap().putAll(theHeaders.asMap());
+ theHeaders.asMap().putAll(jweInHeaders.asMap());
+ if (jweInHeaders.getProtectedHeaders() != null
+ && !jweInHeaders.asMap().entrySet().containsAll(theHeaders.asMap().entrySet())) {
+ jweInHeaders.getProtectedHeaders().asMap().putAll(theHeaders.asMap());
}
- protectedHeaders = jweHeaders.getProtectedHeaders() != null
- ? jweHeaders.getProtectedHeaders() : theHeaders;
+ protectedHeaders = jweInHeaders.getProtectedHeaders() != null
+ ? jweInHeaders.getProtectedHeaders() : theHeaders;
} else {
protectedHeaders = theHeaders;
}
- byte[] theCek = getContentEncryptionKey(theHeaders);
+ byte[] theCek = jweInput != null && jweInput.getCek() != null
+ ? jweInput.getCek() : getContentEncryptionKey(theHeaders);
String contentEncryptionAlgoJavaName = Algorithm.toJavaName(getContentEncryptionAlgoJwt());
KeyProperties keyProps = new KeyProperties(contentEncryptionAlgoJavaName);
keyProps.setCompressionSupported(compressionRequired(theHeaders));
- byte[] theIv = getContentEncryptionAlgorithm().getInitVector();
+ byte[] theIv = jweInput != null && jweInput.getIv() != null
+ ? jweInput.getIv() : getContentEncryptionAlgorithm().getInitVector();
AlgorithmParameterSpec specParams = getAlgorithmParameterSpec(theIv);
keyProps.setAlgoSpec(specParams);
byte[] jweContentEncryptionKey =
@@ -173,7 +175,8 @@ public abstract class AbstractJweEncryption implements JweEncryptionProvider {
String protectedHeadersJson = writer.headersToJson(protectedHeaders);
- byte[] additionalEncryptionParam = getAAD(protectedHeadersJson, aad);
+ byte[] additionalEncryptionParam = getAAD(protectedHeadersJson,
+ jweInput == null ? null : jweInput.getAad());
keyProps.setAdditionalData(additionalEncryptionParam);
JweEncryptionInternal state = new JweEncryptionInternal();
@@ -183,7 +186,7 @@ public abstract class AbstractJweEncryption implements JweEncryptionProvider {
state.secretKey = theCek;
state.theIv = theIv;
state.protectedHeadersJson = protectedHeadersJson;
- state.aad = aad;
+ state.aad = jweInput != null ? jweInput.getAad() : null;
return state;
}
private boolean compressionRequired(JweHeaders theHeaders) {
http://git-wip-us.apache.org/repos/asf/cxf/blob/8891549d/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionInput.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionInput.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionInput.java
new file mode 100644
index 0000000..cb07be3
--- /dev/null
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionInput.java
@@ -0,0 +1,60 @@
+/**
+ * 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 class JweEncryptionInput {
+ private JweHeaders jweHeaders;
+ private byte[] cek;
+ private byte[] iv;
+ private byte[] aad;
+ public JweEncryptionInput(JweHeaders jweHeaders) {
+ this.jweHeaders = jweHeaders;
+ }
+ public JweEncryptionInput(JweHeaders jweHeaders,
+ byte[] cek,
+ byte[] iv) {
+ this(jweHeaders, cek, iv, null);
+
+ }
+ public JweEncryptionInput(JweHeaders jweHeaders,
+ byte[] aad) {
+ this(jweHeaders, null, null, aad);
+ }
+ public JweEncryptionInput(JweHeaders jweHeaders,
+ byte[] cek,
+ byte[] iv,
+ byte[] aad) {
+ this(jweHeaders);
+ this.cek = cek;
+ this.iv = iv;
+ this.aad = aad;
+ }
+ public JweHeaders getJweHeaders() {
+ return jweHeaders;
+ }
+ public byte[] getCek() {
+ return cek;
+ }
+ public byte[] getIv() {
+ return iv;
+ }
+ public byte[] getAad() {
+ return aad;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cxf/blob/8891549d/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionProvider.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionProvider.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionProvider.java
index df4a0d9..25b931a 100644
--- a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionProvider.java
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweEncryptionProvider.java
@@ -21,9 +21,13 @@ package org.apache.cxf.rs.security.jose.jwe;
public interface JweEncryptionProvider extends JweKeyProperties {
+ /**
+ * JWE compact encryption
+ */
String encrypt(byte[] jweContent, JweHeaders jweHeaders);
/**
- * Prepare JWE state (optional operation)
+ * Prepare JWE state for completing either
+ * JWE compact or JSON encryption
*/
- JweEncryptionState createJweEncryptionState(JweHeaders jweHeaders, byte[] aad);
+ JweEncryptionState createJweEncryptionState(JweEncryptionInput jweInput);
}
http://git-wip-us.apache.org/repos/asf/cxf/blob/8891549d/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonEncryptionEntry.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonEncryptionEntry.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonEncryptionEntry.java
new file mode 100644
index 0000000..cdba53e
--- /dev/null
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonEncryptionEntry.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.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.cxf.jaxrs.provider.json.JsonMapObjectReaderWriter;
+
+public class JweJsonEncryptionEntry {
+ private JweHeaders unprotectedHeader;
+ private String encodedEncryptedKey;
+ public JweJsonEncryptionEntry(String encodedEncryptedKey) {
+ this(null, encodedEncryptedKey);
+ }
+ public JweJsonEncryptionEntry(JweHeaders unprotectedHeader, String encodedEncryptedKey) {
+ this.unprotectedHeader = unprotectedHeader;
+ this.encodedEncryptedKey = encodedEncryptedKey;
+ }
+ public JweHeaders getUnprotectedHeader() {
+ return unprotectedHeader;
+ }
+ public String getEncodedEncryptedKey() {
+ return encodedEncryptedKey;
+ }
+ public String toJson() {
+ JsonMapObjectReaderWriter jsonWriter = new JsonMapObjectReaderWriter();
+ Map<String, Object> recipientsEntry = new LinkedHashMap<String, Object>();
+ if (unprotectedHeader != null) {
+ recipientsEntry.put("header", this.unprotectedHeader);
+ }
+ if (encodedEncryptedKey != null) {
+ recipientsEntry.put("encrypted_key", this.encodedEncryptedKey);
+ }
+ return jsonWriter.toJson(recipientsEntry);
+ }
+ public String toString() {
+ return toJson();
+ }
+}
http://git-wip-us.apache.org/repos/asf/cxf/blob/8891549d/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducer.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducer.java b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducer.java
new file mode 100644
index 0000000..8879e24
--- /dev/null
+++ b/rt/rs/security/jose/src/main/java/org/apache/cxf/rs/security/jose/jwe/JweJsonProducer.java
@@ -0,0 +1,168 @@
+/**
+ * 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.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.cxf.common.util.Base64UrlUtility;
+import org.apache.cxf.common.util.crypto.CryptoUtils;
+import org.apache.cxf.rs.security.jose.JoseHeadersReaderWriter;
+
+public class JweJsonProducer {
+ private JoseHeadersReaderWriter writer = new JoseHeadersReaderWriter();
+ private JweHeaders protectedHeader;
+ private JweHeaders unprotectedHeader;
+ private byte[] content;
+ private byte[] aad;
+ public JweJsonProducer(JweHeaders protectedHeader, byte[] content) {
+ this.protectedHeader = protectedHeader;
+ this.content = content;
+ }
+ public JweJsonProducer(JweHeaders protectedHeader, byte[] content, byte[] aad) {
+ this(protectedHeader, content);
+ this.aad = aad;
+ }
+ public JweJsonProducer(JweHeaders protectedHeader, JweHeaders unprotectedHeader,
+ byte[] content, byte[] aad) {
+ this(protectedHeader, content, aad);
+ this.unprotectedHeader = unprotectedHeader;
+ }
+
+ public String encryptWith(List<JweEncryptionProvider> encryptors) {
+ return encryptWith(encryptors, null);
+ }
+ public String encryptWith(List<JweEncryptionProvider> encryptors,
+ List<JweHeaders> recepientUnprotected) {
+ checkAndGetContentAlgorithm(encryptors);
+ if (recepientUnprotected != null
+ && recepientUnprotected.size() != encryptors.size()) {
+ throw new IllegalArgumentException();
+ }
+ //TODO: determine the actual cek and iv length based on the algo
+ byte[] cek = CryptoUtils.generateSecureRandomBytes(32);
+ byte[] iv = CryptoUtils.generateSecureRandomBytes(16);
+ JweHeaders unionHeaders = new JweHeaders();
+ unionHeaders.setProtectedHeaders(protectedHeader);
+ if (protectedHeader != null) {
+ unionHeaders.asMap().putAll(protectedHeader.asMap());
+ }
+ if (unprotectedHeader != null) {
+ if (!Collections.disjoint(unionHeaders.asMap().keySet(),
+ unprotectedHeader.asMap().keySet())) {
+ throw new SecurityException("Protected and unprotected headers have duplicate values");
+ }
+ unionHeaders.asMap().putAll(unprotectedHeader.asMap());
+ }
+
+ List<JweJsonEncryptionEntry> entries = new ArrayList<JweJsonEncryptionEntry>(encryptors.size());
+ Map<String, Object> jweJsonMap = new LinkedHashMap<String, Object>();
+ if (protectedHeader != null) {
+ jweJsonMap.put("protected",
+ Base64UrlUtility.encode(writer.toJson(protectedHeader)));
+ }
+ if (unprotectedHeader != null) {
+ jweJsonMap.put("unprotected", unprotectedHeader);
+ }
+ byte[] cipherText = null;
+ byte[] authTag = null;
+ for (int i = 0; i < encryptors.size(); i++) {
+ JweEncryptionProvider encryptor = encryptors.get(i);
+ JweHeaders perRecipientUnprotected =
+ recepientUnprotected == null ? null : recepientUnprotected.get(i);
+ JweHeaders jsonHeaders = null;
+ if (recepientUnprotected != null) {
+ if (!Collections.disjoint(unionHeaders.asMap().keySet(),
+ perRecipientUnprotected.asMap().keySet())) {
+ throw new SecurityException("Protected and unprotected headers have duplicate values");
+ }
+ jsonHeaders = new JweHeaders(unionHeaders.asMap());
+ jsonHeaders.asMap().putAll(unprotectedHeader.asMap());
+ } else {
+ jsonHeaders = null;
+ }
+ JweEncryptionInput input = new JweEncryptionInput(unionHeaders,
+ cek,
+ iv,
+ aad);
+
+ JweEncryptionState state = encryptor.createJweEncryptionState(input);
+ try {
+ byte[] currentCipherOutput = state.getCipher().doFinal(content);
+ byte[] currentCipherText = null;
+ byte[] currentAuthTag = null;
+ if (state.getAuthTagProducer() != null) {
+ currentCipherText = currentCipherOutput;
+ state.getAuthTagProducer().update(content, 0, content.length);
+ currentAuthTag = state.getAuthTagProducer().getTag();
+ } else {
+ final int authTagLengthBits = 128;
+ final int cipherTextLen = currentCipherOutput.length - authTagLengthBits / 8;
+ currentCipherText = Arrays.copyOf(currentCipherOutput, cipherTextLen);
+ currentAuthTag = Arrays.copyOfRange(currentCipherOutput, cipherTextLen, authTagLengthBits / 8);
+ if (cipherText == null) {
+ cipherText = currentCipherText;
+ } else if (!Arrays.equals(cipherText, currentCipherText)) {
+ throw new SecurityException();
+ }
+ if (authTag == null) {
+ authTag = currentAuthTag;
+ } else if (!Arrays.equals(authTag, currentAuthTag)) {
+ throw new SecurityException();
+ }
+ }
+
+ byte[] encryptedCek = state.getContentEncryptionKey();
+ if (encryptedCek == null && encryptor.getKeyAlgorithm() != null) {
+ // can be null only if it is the direct key encryption
+ throw new SecurityException();
+ }
+ String encodedCek = encryptedCek == null ? null : Base64UrlUtility.encode(encryptedCek);
+ entries.add(new JweJsonEncryptionEntry(perRecipientUnprotected, encodedCek));
+ } catch (Exception ex) {
+ throw new SecurityException(ex);
+ }
+ }
+ jweJsonMap.put("recipients", entries);
+ if (aad != null) {
+ jweJsonMap.put("aad", Base64UrlUtility.encode(aad));
+ }
+ jweJsonMap.put("iv", Base64UrlUtility.encode(iv));
+ jweJsonMap.put("ciphertext", Base64UrlUtility.encode(cipherText));
+ jweJsonMap.put("tag", Base64UrlUtility.encode(authTag));
+ return writer.toJson(jweJsonMap);
+ }
+ private String checkAndGetContentAlgorithm(List<JweEncryptionProvider> encryptors) {
+ Set<String> set = new HashSet<String>();
+ for (JweEncryptionProvider encryptor : encryptors) {
+ set.add(encryptor.getContentAlgorithm());
+ }
+ if (set.size() != 1) {
+ throw new SecurityException("Invalid content encryption algorithm");
+ }
+ return set.iterator().next();
+ }
+
+}