cxf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From serg...@apache.org
Subject git commit: [CXF-5311] Initial attempt at JWE encrypting directly into output stream, cleanup to follow
Date Wed, 25 Jun 2014 15:54:11 GMT
Repository: cxf
Updated Branches:
  refs/heads/master 7454e5a34 -> 27e80bbff


[CXF-5311] Initial attempt at JWE encrypting directly into output stream, cleanup to follow


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/27e80bbf
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/27e80bbf
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/27e80bbf

Branch: refs/heads/master
Commit: 27e80bbffacc5d119d53f203ed1044a406969497
Parents: 7454e5a
Author: Sergey Beryozkin <sberyozkin@talend.com>
Authored: Wed Jun 25 16:53:53 2014 +0100
Committer: Sergey Beryozkin <sberyozkin@talend.com>
Committed: Wed Jun 25 16:53:53 2014 +0100

----------------------------------------------------------------------
 .../oauth2/jwe/AbstractJweEncryptor.java        |  79 +++++++----
 .../security/oauth2/jwe/JweCompactProducer.java |  57 ++++----
 .../rs/security/oauth2/jwe/JweEncryptor.java    |   2 +-
 .../oauth2/jwe/JweEncryptorWorkerState.java     |  43 ++++++
 .../rs/security/oauth2/jwe/JweOutputStream.java | 133 +++++++++++++++++++
 .../oauth2/jwe/WrappedKeyJweEncryptor.java      |   7 +
 .../oauth2/jwt/jaxrs/JweWriterInterceptor.java  |  31 ++++-
 .../oauth2/jwe/JweCompactReaderWriterTest.java  |   6 +-
 .../oauth2/utils/crypto/CryptoUtils.java        |   6 +-
 .../jaxrs/security/jwt/JAXRSJweJwsTest.java     |   4 +-
 10 files changed, 307 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/AbstractJweEncryptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/AbstractJweEncryptor.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/AbstractJweEncryptor.java
index 64e29da..0835836 100644
--- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/AbstractJweEncryptor.java
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/AbstractJweEncryptor.java
@@ -18,9 +18,10 @@
  */
 package org.apache.cxf.rs.security.oauth2.jwe;
 
-import java.io.UnsupportedEncodingException;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.concurrent.atomic.AtomicInteger;
 
+import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
 
 import org.apache.cxf.rs.security.oauth2.jwt.Algorithm;
@@ -36,6 +37,7 @@ public abstract class AbstractJweEncryptor implements JweEncryptor {
     private JwtHeadersWriter writer = new JwtTokenReaderWriter();
     private byte[] cek;
     private byte[] iv;
+    private AtomicInteger providedIvUsageCount;
     private int authTagLen = DEFAULT_AUTH_TAG_LENGTH;
     
     protected AbstractJweEncryptor(SecretKey cek, byte[] iv) {
@@ -47,6 +49,9 @@ public abstract class AbstractJweEncryptor implements JweEncryptor {
         this.headers = headers;
         this.cek = cek;
         this.iv = iv;
+        if (iv != null && iv.length > 0) {
+            providedIvUsageCount = new AtomicInteger();
+        }
     }
     protected AbstractJweEncryptor(JweHeaders headers, byte[] cek, byte[] iv, int authTagLen)
{
         this(headers, cek, iv);
@@ -68,7 +73,13 @@ public abstract class AbstractJweEncryptor implements JweEncryptor {
     }
     
     protected byte[] getContentEncryptionCipherInitVector() {
-        return iv == null ? CryptoUtils.generateSecureRandomBytes(DEFAULT_IV_SIZE) : iv;
+        if (iv == null) {
+            return CryptoUtils.generateSecureRandomBytes(DEFAULT_IV_SIZE);
+        } else if (iv.length > 0 && providedIvUsageCount.addAndGet(1) > 1)
{
+            throw new SecurityException();
+        } else {
+            return iv;
+        }
     }
     
     protected byte[] getContentEncryptionKey() {
@@ -91,6 +102,35 @@ public abstract class AbstractJweEncryptor implements JweEncryptor {
         return headers;
     }
     public String encrypt(byte[] content, String contentType) {
+        JweEncryptorInternalState state = getInternalState(contentType);
+        
+        byte[] cipherText = CryptoUtils.encryptBytes(
+            content, 
+            state.secretKey,
+            state.keyProps);
+        
+        
+        JweCompactProducer producer = new JweCompactProducer(state.theHeaders, 
+                                             writer,                
+                                             state.jweContentEncryptionKey,
+                                             state.theIv,
+                                             cipherText,
+                                             getAuthTagLen());
+        return producer.getJweContent();
+    }
+    
+    public JweEncryptorWorkerState newWorkerState(String contentType) {
+        JweEncryptorInternalState state = getInternalState(contentType);
+        String jweStart = JweCompactProducer.startJweContent(state.theHeaders, 
+                                           writer, 
+                                           state.jweContentEncryptionKey, 
+                                           state.theIv);
+        Cipher c = CryptoUtils.initCipher(state.secretKey, state.keyProps, 
+                                          Cipher.ENCRYPT_MODE);
+        return new JweEncryptorWorkerState(jweStart, c, getAuthTagLen());
+    }
+    
+    private JweEncryptorInternalState getInternalState(String contentType) {
         JweHeaders theHeaders = headers;
         if (contentType != null) {
             theHeaders = new JweHeaders(theHeaders.asMap());
@@ -106,29 +146,22 @@ public abstract class AbstractJweEncryptor implements JweEncryptor {
         byte[] theIv = getContentEncryptionCipherInitVector();
         AlgorithmParameterSpec specParams = getContentEncryptionCipherSpec(theIv);
         keyProps.setAlgoSpec(specParams);
-        
-        byte[] cipherText = CryptoUtils.encryptBytes(
-            content, 
-            CryptoUtils.createSecretKeySpec(theCek, contentEncryptionAlgoJavaName),
-            keyProps);
-        
         byte[] jweContentEncryptionKey = getEncryptedContentEncryptionKey(theCek);
-        JweCompactProducer producer = new JweCompactProducer(theHeaders, 
-                                             writer,                
-                                             jweContentEncryptionKey,
-                                             theIv,
-                                             cipherText,
-                                             getAuthTagLen());
-        return producer.getJweContent();
+        JweEncryptorInternalState state = new JweEncryptorInternalState();
+        state.theHeaders = theHeaders;
+        state.jweContentEncryptionKey = jweContentEncryptionKey;
+        state.keyProps = keyProps;
+        state.secretKey = CryptoUtils.createSecretKeySpec(theCek, 
+                                        contentEncryptionAlgoJavaName);
+        state.theIv = theIv;
+        return state;
     }
     
-    public String encryptText(String text, String contentType) {
-        try {
-            return encrypt(text.getBytes("UTF-8"), contentType);
-        } catch (UnsupportedEncodingException ex) {
-            throw new SecurityException(ex);
-        }
+    private static class JweEncryptorInternalState {
+        JweHeaders theHeaders;
+        byte[] jweContentEncryptionKey;
+        byte[] theIv;
+        KeyProperties keyProps;
+        SecretKey secretKey;
     }
-    
-    
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactProducer.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactProducer.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactProducer.java
index 82945b7..bb0a24d 100644
--- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactProducer.java
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactProducer.java
@@ -25,9 +25,7 @@ import org.apache.cxf.rs.security.oauth2.utils.Base64UrlUtility;
 
 
 public class JweCompactProducer {
-    private String encodedHeaders;
-    private String encodedContentEncryptionKey;
-    private String encodedInitVector;
+    private StringBuilder jweContentBuilder;
     private String encodedEncryptedContent;
     private String encodedAuthTag;
     public JweCompactProducer(JweHeaders headers,
@@ -44,10 +42,12 @@ public class JweCompactProducer {
                        byte[] encryptedContentEncryptionKey,
                        byte[] cipherInitVector,
                        byte[] encryptedContentNoTag,
-                       byte[] authenticationTag) {    
+                       byte[] authenticationTag) {
+        jweContentBuilder = startJweContent(new StringBuilder(), headers, writer, 
+                                   encryptedContentEncryptionKey, cipherInitVector);
         this.encodedEncryptedContent = Base64UrlUtility.encode(encryptedContentNoTag);
         this.encodedAuthTag = Base64UrlUtility.encode(authenticationTag);
-        finalizeInit(headers, writer, encryptedContentEncryptionKey, cipherInitVector);
+        
     }
     
     public JweCompactProducer(JweHeaders headers,
@@ -63,7 +63,9 @@ public class JweCompactProducer {
                        byte[] encryptedContentEncryptionKey,
                        byte[] cipherInitVector,
                        byte[] encryptedContentWithTag,
-                       int authTagLengthBits) {    
+                       int authTagLengthBits) {
+        jweContentBuilder = startJweContent(new StringBuilder(), headers, writer,
+                                   encryptedContentEncryptionKey, cipherInitVector);
         this.encodedEncryptedContent = Base64UrlUtility.encodeChunk(
             encryptedContentWithTag, 
             0, 
@@ -71,29 +73,36 @@ public class JweCompactProducer {
         this.encodedAuthTag = Base64UrlUtility.encodeChunk(
             encryptedContentWithTag, 
             encryptedContentWithTag.length - authTagLengthBits / 8, 
-            encryptedContentWithTag.length);
-        finalizeInit(headers, writer, encryptedContentEncryptionKey, cipherInitVector);
+            authTagLengthBits / 8);
+        
     }
-    
-    private void finalizeInit(JweHeaders headers,
-                              JwtHeadersWriter writer, 
-                              byte[] encryptedContentEncryptionKey,
-                              byte[] cipherInitVector) {
+    public static String startJweContent(JweHeaders headers,
+                                                JwtHeadersWriter writer, 
+                                                byte[] encryptedContentEncryptionKey,
+                                                byte[] cipherInitVector) {
+        return startJweContent(new StringBuilder(), 
+                               headers, writer, encryptedContentEncryptionKey, cipherInitVector).toString();
      
+    }
+    public static StringBuilder startJweContent(StringBuilder sb,
+                                        JweHeaders headers,
+                                        JwtHeadersWriter writer, 
+                                        byte[] encryptedContentEncryptionKey,
+                                        byte[] cipherInitVector) {
         writer = writer == null ? new JwtTokenReaderWriter() : writer;
-        this.encodedHeaders = Base64UrlUtility.encode(writer.headersToJson(headers));
-        this.encodedContentEncryptionKey = Base64UrlUtility.encode(encryptedContentEncryptionKey);
-        this.encodedInitVector = Base64UrlUtility.encode(cipherInitVector);
+        String encodedHeaders = Base64UrlUtility.encode(writer.headersToJson(headers));
+        String encodedContentEncryptionKey = Base64UrlUtility.encode(encryptedContentEncryptionKey);
+        String encodedInitVector = Base64UrlUtility.encode(cipherInitVector);
+        sb.append(encodedHeaders)
+            .append('.')
+            .append(encodedContentEncryptionKey == null ? "" : encodedContentEncryptionKey)
+            .append('.')
+            .append(encodedInitVector == null ? "" : encodedInitVector)
+            .append('.');
+        return sb;
     }
     
     public String getJweContent() {
-        StringBuilder sb = new StringBuilder();
-        return sb.append(encodedHeaders)
-                 .append('.')
-                 .append(encodedContentEncryptionKey == null ? "" : encodedContentEncryptionKey)
-                 .append('.')
-                 .append(encodedInitVector == null ? "" : encodedInitVector)
-                 .append('.')
-                 .append(encodedEncryptedContent)
+        return jweContentBuilder.append(encodedEncryptedContent)
                  .append('.')
                  .append(encodedAuthTag)
                  .toString();

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptor.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptor.java
index f8eb013..b2acb17 100644
--- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptor.java
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptor.java
@@ -21,5 +21,5 @@ package org.apache.cxf.rs.security.oauth2.jwe;
 
 public interface JweEncryptor {
     String encrypt(byte[] jweContent, String contentType);
-    String encryptText(String jweContent, String contentType);
+    JweEncryptorWorkerState newWorkerState(String contentType);
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptorWorkerState.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptorWorkerState.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptorWorkerState.java
new file mode 100644
index 0000000..e73cdf5
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweEncryptorWorkerState.java
@@ -0,0 +1,43 @@
+/**
+ * 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.oauth2.jwe;
+
+import javax.crypto.Cipher;
+
+public class JweEncryptorWorkerState {
+    private String jweContentStart;
+    private Cipher encryptingCipher;
+    private int authTagLengthBits;
+    
+    public JweEncryptorWorkerState(String jweContentStart, Cipher encryptingCipher, int authTagLengthBits)
{
+        this.jweContentStart = jweContentStart;
+        this.encryptingCipher = encryptingCipher;
+        this.authTagLengthBits = authTagLengthBits;
+    }
+    public Cipher getEncryptingCipher() {
+        return encryptingCipher;
+    }
+    public int getAuthTagLengthBits() {
+        return authTagLengthBits;
+    }
+    public String getJweContentStart() {
+        return jweContentStart;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweOutputStream.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweOutputStream.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweOutputStream.java
new file mode 100644
index 0000000..babe28d
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/JweOutputStream.java
@@ -0,0 +1,133 @@
+/**
+ * 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.oauth2.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 int authTagLengthBits;
+    private byte[] lastRawDataChunk;
+    private byte[] lastEncryptedDataChunk;
+    private boolean flushed;
+    public JweOutputStream(OutputStream out, JweEncryptorWorkerState state) throws IOException
{
+        super(out);
+        this.encryptingCipher = state.getEncryptingCipher();
+        this.blockSize = encryptingCipher.getBlockSize(); 
+        this.authTagLengthBits = state.getAuthTagLengthBits();
+        out.write(state.getJweContentStart().getBytes("UTF-8"));
+    }
+
+    @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;
+        for (; offset + blockSize <= len; offset += blockSize, off += blockSize) {
+            encryptAndWrite(b, off, blockSize);
+        }
+        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);
+        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; 
+        encodeAndWriteFinally(theChunk, off, lenToEncode - rem);
+        
+        if (rem > 0) {
+            lastEncryptedDataChunk = newArray(theChunk, lenToEncode - rem, rem);
+        } else {
+            lastEncryptedDataChunk = null;
+        }
+    }
+    private void encodeAndWriteFinally(byte[] chunk, int off, int len) throws IOException
{
+        String encoded = Base64UrlUtility.encodeChunk(chunk, off, len);
+        byte[] encodedBytes = encoded.getBytes("UTF-8");
+        out.write(encodedBytes, 0, encodedBytes.length);
+    }
+    
+    @Override
+    public void flush() throws IOException {
+        if (flushed) {
+            return;
+        }
+        try {
+            byte[] finalBytes = lastRawDataChunk == null 
+                ? encryptingCipher.doFinal()
+                : encryptingCipher.doFinal(lastRawDataChunk, 0, lastRawDataChunk.length);
+            encodeAndWrite(finalBytes, 0, finalBytes.length - authTagLengthBits / 8, true);
+            out.write('.');
+            encodeAndWrite(finalBytes, finalBytes.length - authTagLengthBits / 8, 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/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/WrappedKeyJweEncryptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/WrappedKeyJweEncryptor.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/WrappedKeyJweEncryptor.java
index 6f9154a..998be77 100644
--- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/WrappedKeyJweEncryptor.java
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwe/WrappedKeyJweEncryptor.java
@@ -19,6 +19,7 @@
 package org.apache.cxf.rs.security.oauth2.jwe;
 
 import java.security.Key;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.cxf.rs.security.oauth2.jwt.Algorithm;
 import org.apache.cxf.rs.security.oauth2.jwt.JwtHeadersWriter;
@@ -28,6 +29,7 @@ import org.apache.cxf.rs.security.oauth2.utils.crypto.KeyProperties;
 public class WrappedKeyJweEncryptor extends AbstractJweEncryptor {
     private Key cekEncryptionKey;
     private boolean wrap;
+    private AtomicInteger providedCekUsageCount;
     public WrappedKeyJweEncryptor(JweHeaders headers, Key cekEncryptionKey) {
         this(headers, cekEncryptionKey, null, null);
     }
@@ -44,6 +46,9 @@ public class WrappedKeyJweEncryptor extends AbstractJweEncryptor {
         super(headers, cek, iv, authTagLen, writer);
         this.cekEncryptionKey = cekEncryptionKey;
         this.wrap = wrap;
+        if (cek != null) {
+            providedCekUsageCount = new AtomicInteger();
+        }
     }
     protected byte[] getContentEncryptionKey() {
         byte[] theCek = super.getContentEncryptionKey();
@@ -52,6 +57,8 @@ public class WrappedKeyJweEncryptor extends AbstractJweEncryptor {
             String algoJwt = getContentEncryptionAlgoJwt();
             theCek = CryptoUtils.getSecretKey(Algorithm.stripAlgoProperties(algoJava), 
                 Algorithm.valueOf(algoJwt).getKeySizeBits()).getEncoded();
+        } else if (providedCekUsageCount.addAndGet(1) > 1) {
+            throw new SecurityException();
         }
         return theCek;
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweWriterInterceptor.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweWriterInterceptor.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweWriterInterceptor.java
index 65dbb5f..8459052 100644
--- a/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweWriterInterceptor.java
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/main/java/org/apache/cxf/rs/security/oauth2/jwt/jaxrs/JweWriterInterceptor.java
@@ -37,7 +37,9 @@ import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 import org.apache.cxf.jaxrs.utils.ResourceUtils;
 import org.apache.cxf.message.Message;
 import org.apache.cxf.rs.security.oauth2.jwe.JweEncryptor;
+import org.apache.cxf.rs.security.oauth2.jwe.JweEncryptorWorkerState;
 import org.apache.cxf.rs.security.oauth2.jwe.JweHeaders;
+import org.apache.cxf.rs.security.oauth2.jwe.JweOutputStream;
 import org.apache.cxf.rs.security.oauth2.jwe.WrappedKeyJweEncryptor;
 import org.apache.cxf.rs.security.oauth2.jwt.Algorithm;
 import org.apache.cxf.rs.security.oauth2.utils.crypto.CryptoUtils;
@@ -49,15 +51,13 @@ public class JweWriterInterceptor implements WriterInterceptor {
     private static final String JSON_WEB_ENCRYPTION_ZIP_ALGO_PROP = "rs.security.jwe.zip.algorithm";
     private JweEncryptor encryptor;
     private boolean contentTypeRequired = true;
-    
+    private boolean useJweOutputStream;
     @Override
     public void aroundWriteTo(WriterInterceptorContext ctx) throws IOException, WebApplicationException
{
         OutputStream actualOs = ctx.getOutputStream();
-        CachedOutputStream cos = new CachedOutputStream(); 
-        ctx.setOutputStream(cos);
-        ctx.proceed();
         
         JweEncryptor theEncryptor = getInitializedEncryptor();
+        
         String ctString = null;
         if (contentTypeRequired) {
             MediaType mt = ctx.getMediaType();
@@ -65,9 +65,22 @@ public class JweWriterInterceptor implements WriterInterceptor {
                 ctString = JAXRSUtils.mediaTypeToString(mt);
             }
         }
-        String jweContent = theEncryptor.encrypt(cos.getBytes(), ctString);
-        IOUtils.copy(new ByteArrayInputStream(jweContent.getBytes("UTF-8")), actualOs);
-        actualOs.flush();
+        
+        
+        if (useJweOutputStream) {
+            JweEncryptorWorkerState state = theEncryptor.newWorkerState(ctString);
+            JweOutputStream jweStream = new JweOutputStream(actualOs, state); 
+            ctx.setOutputStream(jweStream);
+            ctx.proceed();
+            jweStream.flush();
+        } else {
+            CachedOutputStream cos = new CachedOutputStream(); 
+            ctx.setOutputStream(cos);
+            ctx.proceed();
+            String jweContent = theEncryptor.encrypt(cos.getBytes(), ctString);
+            IOUtils.copy(new ByteArrayInputStream(jweContent.getBytes("UTF-8")), actualOs);
+            actualOs.flush();
+        }
     }
     
     protected JweEncryptor getInitializedEncryptor() {
@@ -97,5 +110,9 @@ public class JweWriterInterceptor implements WriterInterceptor {
             throw new SecurityException(ex);
         }
     }
+
+    public void setUseJweOutputStream(boolean useJweOutputStream) {
+        this.useJweOutputStream = useJweOutputStream;
+    }
     
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
b/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
index e2de7f6..2a80395 100644
--- a/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
+++ b/rt/rs/security/oauth-parent/oauth2-jwt/src/test/java/org/apache/cxf/rs/security/oauth2/jwe/JweCompactReaderWriterTest.java
@@ -73,7 +73,7 @@ public class JweCompactReaderWriterTest extends Assert {
     public static void unregisterBouncyCastleIfNeeded() throws Exception {
         Security.removeProvider(BouncyCastleProvider.class.getName());    
     }
-    
+        
     @Test
     public void testEncryptDecryptSpecExample() throws Exception {
         final String specPlainText = "The true sign of intelligence is not knowledge but
imagination.";
@@ -110,11 +110,11 @@ public class JweCompactReaderWriterTest extends Assert {
                                                         key, 
                                                         jwtKeyName, 
                                                         INIT_VECTOR);
-        return encryptor.encryptText(content, null);
+        return encryptor.encrypt(content.getBytes("UTF-8"), null);
     }
     private String encryptContentDirect(SecretKey key, String content) throws Exception {
         DirectKeyJweEncryptor encryptor = new DirectKeyJweEncryptor(key, INIT_VECTOR);
-        return encryptor.encryptText(content, null);
+        return encryptor.encrypt(content.getBytes("UTF-8"), null);
     }
     private void decrypt(String jweContent, String plainContent, boolean unwrap) throws Exception
{
         RSAPrivateKey privateKey = CryptoUtils.getRSAPrivateKey(RSA_MODULUS_ENCODED, RSA_PRIVATE_EXPONENT_ENCODED);

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/crypto/CryptoUtils.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/crypto/CryptoUtils.java
b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/crypto/CryptoUtils.java
index 1d59467..99e46d8 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/crypto/CryptoUtils.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/crypto/CryptoUtils.java
@@ -474,7 +474,7 @@ public final class CryptoUtils {
                              new KeyProperties(wrapperKeyAlgo));
     }
     
-    public static byte[] wrapSecretKey(SecretKey secretKey,
+    public static byte[] wrapSecretKey(Key secretKey,
                                        Key wrapperKey,
                                        KeyProperties keyProps)  throws SecurityException
{
         try {
@@ -533,13 +533,15 @@ public final class CryptoUtils {
                 }
                 boolean updateRequired = keyProps != null && keyProps.getAdditionalData()
!= null;
                 int offset = 0;
-                for (; offset + blockSize < bytes.length; offset += blockSize) {
+                for (; offset + blockSize <= bytes.length; offset += blockSize) {
                     byte[] next = !updateRequired ? c.doFinal(bytes, offset, blockSize) 
                         : c.update(bytes, offset, blockSize);
                     result = addToResult(result, next);
                 }
                 if (offset < bytes.length) {
                     result = addToResult(result, c.doFinal(bytes, offset, bytes.length -
offset));
+                } else {
+                    result = addToResult(result, c.doFinal());
                 }
             }
             if (compressionSupported && mode == Cipher.DECRYPT_MODE) {

http://git-wip-us.apache.org/repos/asf/cxf/blob/27e80bbf/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
----------------------------------------------------------------------
diff --git a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
index a86a3b2..15f40dd 100644
--- a/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
+++ b/systests/rs-security/src/test/java/org/apache/cxf/systest/jaxrs/security/jwt/JAXRSJweJwsTest.java
@@ -82,7 +82,9 @@ public class JAXRSJweJwsTest extends AbstractBusClientServerTestBase {
         bean.setServiceClass(BookStore.class);
         bean.setAddress(address);
         List<Object> providers = new LinkedList<Object>();
-        providers.add(new JweWriterInterceptor());
+        JweWriterInterceptor jweWriter = new JweWriterInterceptor();
+        jweWriter.setUseJweOutputStream(true);
+        providers.add(jweWriter);
         providers.add(new JweClientResponseFilter());
         providers.add(new JwsWriterInterceptor());
         providers.add(new JwsClientResponseFilter());


Mime
View raw message