You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2016/08/03 23:54:02 UTC

svn commit: r1755127 [1/3] - in /poi/branches/hssf_cryptoapi/src: java/org/apache/poi/ java/org/apache/poi/hssf/record/ java/org/apache/poi/poifs/crypt/ java/org/apache/poi/poifs/crypt/binaryrc4/ java/org/apache/poi/poifs/crypt/cryptoapi/ java/org/apac...

Author: kiwiwings
Date: Wed Aug  3 23:54:01 2016
New Revision: 1755127

URL: http://svn.apache.org/viewvc?rev=1755127&view=rev
Log:
Preparations for hssf_cryptoapi:
- Add cloneable
- Change existing hslf cryptoapi to streaming

Added:
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java   (with props)
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java   (with props)
Modified:
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/POIDocument.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/hssf/record/RecordInputStream.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Decryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Encryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/standard/StandardDecryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionInfoBuilder.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptor.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/util/LittleEndianByteArrayInputStream.java
    poi/branches/hssf_cryptoapi/src/java/org/apache/poi/util/LittleEndianByteArrayOutputStream.java
    poi/branches/hssf_cryptoapi/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java
    poi/branches/hssf_cryptoapi/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java
    poi/branches/hssf_cryptoapi/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java
    poi/branches/hssf_cryptoapi/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java
    poi/branches/hssf_cryptoapi/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java
    poi/branches/hssf_cryptoapi/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestAgileEncryptionParameters.java
    poi/branches/hssf_cryptoapi/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
    poi/branches/hssf_cryptoapi/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
    poi/branches/hssf_cryptoapi/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/POIDocument.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/POIDocument.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/POIDocument.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/POIDocument.java Wed Aug  3 23:54:01 2016
@@ -32,6 +32,7 @@ import org.apache.poi.hpsf.PropertySet;
 import org.apache.poi.hpsf.PropertySetFactory;
 import org.apache.poi.hpsf.SummaryInformation;
 import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
@@ -59,6 +60,8 @@ public abstract class POIDocument implem
 
     /* Have the property streams been read yet? (Only done on-demand) */
     private boolean initialized = false;
+
+    private static final String[] encryptedStreamNames = { "EncryptedSummary" };
     
     /**
      * Constructs a POIDocument with the given directory node.
@@ -195,13 +198,18 @@ public abstract class POIDocument implem
         try {
             if (encryptionInfo != null) {
                 step = "getting encrypted";
-                InputStream is = encryptionInfo.getDecryptor().getDataStream(directory);
-                try {
-                    encPoifs = new NPOIFSFileSystem(is);
-                    dirNode = encPoifs.getRoot();
-                } finally {
-                    is.close();
+                String encryptedStream = null;
+                for (String s : encryptedStreamNames) {
+                    if (dirNode.hasEntry(s)) {
+                        encryptedStream = s;
+                    }
+                }
+                if (encryptedStream == null) {
+                    throw new EncryptedDocumentException("can't find matching encrypted property stream");
                 }
+                CryptoAPIDecryptor dec = (CryptoAPIDecryptor)encryptionInfo.getDecryptor();
+                encPoifs = dec.getSummaryEntries(dirNode, encryptedStream);
+                dirNode = encPoifs.getRoot();
             }
             
             //directory can be null when creating new documents

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/hssf/record/RecordInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/hssf/record/RecordInputStream.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/hssf/record/RecordInputStream.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/hssf/record/RecordInputStream.java Wed Aug  3 23:54:01 2016
@@ -18,13 +18,14 @@
 package org.apache.poi.hssf.record;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Locale;
 
 import org.apache.poi.hssf.dev.BiffViewer;
 import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
 import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
-import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndianConsts;
 import org.apache.poi.util.LittleEndianInput;
 import org.apache.poi.util.LittleEndianInputStream;
@@ -91,6 +92,10 @@ public final class RecordInputStream imp
 	 * index within the data section of the current BIFF record
 	 */
 	private int _currentDataOffset;
+	/**
+	 * index within the data section when mark() was called
+	 */
+	private int _markedDataOffset;
 
 	private static final class SimpleHeaderInput implements BiffHeaderInput {
 
@@ -123,8 +128,8 @@ public final class RecordInputStream imp
 			_bhi = new SimpleHeaderInput(in);
 		} else {
 			Biff8DecryptingStream bds = new Biff8DecryptingStream(in, initialOffset, key);
+            _dataInput = bds;
 			_bhi = bds;
-			_dataInput = bds;
 		}
 		_nextSid = readNextSid();
 	}
@@ -491,4 +496,31 @@ public final class RecordInputStream imp
     public int getNextSid() {
         return _nextSid;
     }
+
+    /**
+     * Mark the stream position - experimental function 
+     *
+     * @param readlimit the read ahead limit
+     * 
+     * @see InputStream#mark(int)
+     */
+    @Internal
+    public void mark(int readlimit) {
+        ((InputStream)_dataInput).mark(readlimit);
+        _markedDataOffset = _currentDataOffset;
+    }
+    
+    /**
+     * Resets the stream position to the previously marked position.
+     * Experimental function - this only works, when nextRecord() wasn't called in the meantime.
+     *
+     * @throws IOException if marking is not supported
+     * 
+     * @see InputStream#reset()
+     */
+    @Internal
+    public void reset() throws IOException {
+        ((InputStream)_dataInput).reset();
+        _currentDataOffset = _markedDataOffset;
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherInputStream.java Wed Aug  3 23:54:01 2016
@@ -16,6 +16,7 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.GeneralSecurityException;
@@ -29,54 +30,81 @@ import org.apache.poi.util.LittleEndianI
 
 @Internal
 public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
-    private final int chunkSize;
-    private final int chunkMask;
-    private final int chunkBits;
-    
-    private int _lastIndex = 0;
-    private long _pos = 0;
-    private long _size;
-    private byte[] _chunk;
-    private Cipher _cipher;
+    private final int _chunkSize;
+    private final int _chunkBits;
+    
+    private final long _size;
+    private final byte[] _chunk;
+    private final Cipher _cipher;
+
+    private int _lastIndex;
+    private long _pos;
+    private boolean _chunkIsValid = false;
 
     public ChunkedCipherInputStream(LittleEndianInput stream, long size, int chunkSize)
-        throws GeneralSecurityException {
+    throws GeneralSecurityException {
+        this(stream, size, chunkSize, 0);
+    }
+
+    public ChunkedCipherInputStream(LittleEndianInput stream, long size, int chunkSize, int initialPos)
+    throws GeneralSecurityException {
         super((InputStream)stream);
         _size = size;
-        this.chunkSize = chunkSize;
-        chunkMask = chunkSize-1;
-        chunkBits = Integer.bitCount(chunkMask);
+        _pos = initialPos;
+        this._chunkSize = chunkSize;
+        if (chunkSize == -1) {
+            _chunk = new byte[4096];
+        } else {
+            _chunk = new byte[chunkSize];
+        }
+        _chunkBits = Integer.bitCount(_chunk.length-1);
+        _lastIndex = (int)(_pos >> _chunkBits);
+        _cipher = initCipherForBlock(null, _lastIndex);
+    }
+    
+    public final Cipher initCipherForBlock(int block) throws IOException, GeneralSecurityException {
+        if (_chunkSize != -1) {
+            throw new GeneralSecurityException("the cipher block can only be set for streaming encryption, e.g. CryptoAPI...");
+        }
         
-        _cipher = initCipherForBlock(null, 0);
+        _chunkIsValid = false;
+        return initCipherForBlock(_cipher, block);
     }
     
     protected abstract Cipher initCipherForBlock(Cipher existing, int block)
     throws GeneralSecurityException;
 
+    @Override
     public int read() throws IOException {
         byte[] b = new byte[1];
-        if (read(b) == 1)
+        if (read(b) == 1) {
             return b[0];
+        }
         return -1;
     }
 
     // do not implement! -> recursion
     // public int read(byte[] b) throws IOException;
 
+    @Override
     public int read(byte[] b, int off, int len) throws IOException {
         int total = 0;
         
-        if (available() <= 0) return -1;
+        if (available() <= 0) {
+            return -1;
+        }
 
+        final int chunkMask = getChunkMask();
         while (len > 0) {
-            if (_chunk == null) {
+            if (!_chunkIsValid) {
                 try {
-                    _chunk = nextChunk();
+                    nextChunk();
+                    _chunkIsValid = true;
                 } catch (GeneralSecurityException e) {
                     throw new EncryptedDocumentException(e.getMessage(), e);
                 }
             }
-            int count = (int)(chunkSize - (_pos & chunkMask));
+            int count = (int)(_chunk.length - (_pos & chunkMask));
             int avail = available();
             if (avail == 0) {
                 return total;
@@ -86,8 +114,9 @@ public abstract class ChunkedCipherInput
             off += count;
             len -= count;
             _pos += count;
-            if ((_pos & chunkMask) == 0)
-                _chunk = null;
+            if ((_pos & chunkMask) == 0) {
+                _chunkIsValid = false;
+            }
             total += count;
         }
 
@@ -95,18 +124,28 @@ public abstract class ChunkedCipherInput
     }
 
     @Override
-    public long skip(long n) throws IOException {
+    public long skip(final long n) throws IOException {
         long start = _pos;
-        long skip = Math.min(available(), n);
+        long skip = Math.min(remainingBytes(), n);
 
-        if ((((_pos + skip) ^ start) & ~chunkMask) != 0)
-            _chunk = null;
+        if ((((_pos + skip) ^ start) & ~getChunkMask()) != 0) {
+            _chunkIsValid = false;
+        }
         _pos += skip;
         return skip;
     }
 
     @Override
     public int available() {
+        return remainingBytes();
+    }
+    
+    /**
+     * Helper method for forbidden available call - we know the size beforehand, so it's ok ...
+     *
+     * @return the remaining byte until EOF
+     */
+    private int remainingBytes() {
         return (int)(_size - _pos);
     }
     
@@ -125,17 +164,37 @@ public abstract class ChunkedCipherInput
         throw new UnsupportedOperationException();
     }
 
-    private byte[] nextChunk() throws GeneralSecurityException, IOException {
-        int index = (int)(_pos >> chunkBits);
-        initCipherForBlock(_cipher, index);
+    private int getChunkMask() {
+        return _chunk.length-1;
+    }
+    
+    private void nextChunk() throws GeneralSecurityException, IOException {
+        if (_chunkSize != -1) {
+            int index = (int)(_pos >> _chunkBits);
+            initCipherForBlock(_cipher, index);
         
-        if (_lastIndex != index) {
-            super.skip((index - _lastIndex) << chunkBits);
+            if (_lastIndex != index) {
+                super.skip((index - _lastIndex) << _chunkBits);
+            }
+
+            _lastIndex = index + 1;
         }
 
-        byte[] block = new byte[Math.min(super.available(), chunkSize)];
-        super.read(block, 0, block.length);
-        _lastIndex = index + 1;
-        return _cipher.doFinal(block);
+        final int todo = (int)Math.min(_size, _chunk.length);
+        int readBytes = 0, totalBytes = 0;
+        do {
+            readBytes = super.read(_chunk, totalBytes, todo-totalBytes);
+            totalBytes += Math.max(0, readBytes);
+        } while (readBytes != -1 && totalBytes < todo);
+
+        if (readBytes == -1 && _pos+totalBytes < _size) {
+            throw new EOFException("buffer underrun");
+        }
+
+        if (_chunkSize == -1) {
+            _cipher.update(_chunk, 0, totalBytes, _chunk);
+        } else {
+            _cipher.doFinal(_chunk, 0, totalBytes, _chunk);
+        }
     }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java Wed Aug  3 23:54:01 2016
@@ -32,6 +32,7 @@ import org.apache.poi.EncryptedDocumentE
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
 import org.apache.poi.poifs.filesystem.POIFSWriterListener;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianConsts;
@@ -41,137 +42,177 @@ import org.apache.poi.util.TempFile;
 
 @Internal
 public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
-    private static final POILogger logger = POILogFactory.getLogger(ChunkedCipherOutputStream.class);
-    
-    protected final int chunkSize;
-    protected final int chunkMask;
-    protected final int chunkBits;
-    
+    private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class);
+    private static final int STREAMING = -1;
+
+    protected final int _chunkSize;
+    protected final int _chunkBits;
+
     private final byte[] _chunk;
-    private final File fileOut;
-    private final DirectoryNode dir;
+    private final File _fileOut;
+    private final DirectoryNode _dir;
 
     private long _pos = 0;
     private Cipher _cipher;
-    
+
     public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException {
         super(null);
-        this.chunkSize = chunkSize;
-        chunkMask = chunkSize-1;
-        chunkBits = Integer.bitCount(chunkMask);
-        _chunk = new byte[chunkSize];
-
-        fileOut = TempFile.createTempFile("encrypted_package", "crypt");
-        fileOut.deleteOnExit();
-        this.out = new FileOutputStream(fileOut);
-        this.dir = dir;
+        this._chunkSize = chunkSize;
+        int cs = chunkSize == STREAMING ? 4096 : chunkSize;
+        _chunk = new byte[cs];
+        _chunkBits = Integer.bitCount(cs-1);
+        _fileOut = TempFile.createTempFile("encrypted_package", "crypt");
+        _fileOut.deleteOnExit();
+        this.out = new FileOutputStream(_fileOut);
+        this._dir = dir;
+        _cipher = initCipherForBlock(null, 0, false);
+    }
+
+    public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException {
+        super(stream);
+        this._chunkSize = chunkSize;
+        int cs = chunkSize == STREAMING ? 4096 : chunkSize;
+        _chunk = new byte[cs];
+        _chunkBits = Integer.bitCount(cs-1);
+        _fileOut = null;
+        _dir = null;
         _cipher = initCipherForBlock(null, 0, false);
     }
 
+    public final Cipher initCipherForBlock(int block, boolean lastChunk) throws IOException, GeneralSecurityException {
+        return initCipherForBlock(_cipher, block, lastChunk);
+    }
+
     protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk)
-    throws GeneralSecurityException;    
-    
-    @SuppressWarnings("hiding")
+    throws IOException, GeneralSecurityException;
+
     protected abstract void calculateChecksum(File fileOut, int oleStreamSize)
     throws GeneralSecurityException, IOException;
-    
-    @SuppressWarnings("hiding")
+
     protected abstract void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
     throws IOException, GeneralSecurityException;
 
+    @Override
     public void write(int b) throws IOException {
         write(new byte[]{(byte)b});
     }
 
+    @Override
     public void write(byte[] b) throws IOException {
         write(b, 0, b.length);
     }
 
+    @Override
     public void write(byte[] b, int off, int len)
     throws IOException {
-        if (len == 0) return;
-        
+        if (len == 0) {
+            return;
+        }
+
         if (len < 0 || b.length < off+len) {
             throw new IOException("not enough bytes in your input buffer");
         }
-        
+
+        final int chunkMask = getChunkMask();
         while (len > 0) {
             int posInChunk = (int)(_pos & chunkMask);
-            int nextLen = Math.min(chunkSize-posInChunk, len);
+            int nextLen = Math.min(_chunk.length-posInChunk, len);
             System.arraycopy(b, off, _chunk, posInChunk, nextLen);
             _pos += nextLen;
             off += nextLen;
             len -= nextLen;
             if ((_pos & chunkMask) == 0) {
-                try {
-                    writeChunk();
-                } catch (GeneralSecurityException e) {
-                    throw new IOException(e);
-                }
+                writeChunk(len > 0);
             }
         }
     }
 
-    protected void writeChunk() throws IOException, GeneralSecurityException {
-        int posInChunk = (int)(_pos & chunkMask);
+    private int getChunkMask() {
+        return _chunk.length-1;
+    }
+
+    protected void writeChunk(boolean continued) throws IOException {
+        if (_pos == 0) {
+            return;
+        }
+
+        int posInChunk = (int)(_pos & getChunkMask());
+
         // normally posInChunk is 0, i.e. on the next chunk (-> index-1)
         // but if called on close(), posInChunk is somewhere within the chunk data
-        int index = (int)(_pos >> chunkBits);
+        int index = (int)(_pos >> _chunkBits);
         boolean lastChunk;
         if (posInChunk==0) {
             index--;
-            posInChunk = chunkSize;
+            posInChunk = _chunk.length;
             lastChunk = false;
         } else {
             // pad the last chunk
             lastChunk = true;
         }
 
-        _cipher = initCipherForBlock(_cipher, index, lastChunk);
+        int ciLen;
+        try {
+            if (_chunkSize == STREAMING) {
+                if (continued) {
+                    ciLen = _cipher.update(_chunk, 0, posInChunk, _chunk);
+                } else {
+                    ciLen = _cipher.doFinal(_chunk, 0, posInChunk, _chunk);
+                }
+
+                // reset stream (not only) in case we were interrupted by plain stream parts
+                _pos = 0;
+            } else {
+                _cipher = initCipherForBlock(_cipher, index, lastChunk);
+                ciLen = _cipher.doFinal(_chunk, 0, posInChunk, _chunk);
+            }
+        } catch (GeneralSecurityException e) {
+            throw new IOException("can't re-/initialize cipher", e);
+        }
 
-        int ciLen = _cipher.doFinal(_chunk, 0, posInChunk, _chunk);
         out.write(_chunk, 0, ciLen);
     }
-    
+
+    @Override
     public void close() throws IOException {
         try {
-            writeChunk();
+            writeChunk(false);
 
             super.close();
-            
-            int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
-            calculateChecksum(fileOut, (int)_pos);
-            dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());
-            createEncryptionInfoEntry(dir, fileOut);
+
+            if (_fileOut != null) {
+                int oleStreamSize = (int)(_fileOut.length()+LittleEndianConsts.LONG_SIZE);
+                calculateChecksum(_fileOut, (int)_pos);
+                _dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());
+                createEncryptionInfoEntry(_dir, _fileOut);
+            }
         } catch (GeneralSecurityException e) {
             throw new IOException(e);
         }
     }
 
     private class EncryptedPackageWriter implements POIFSWriterListener {
+        @Override
         public void processPOIFSWriterEvent(POIFSWriterEvent event) {
             try {
                 OutputStream os = event.getStream();
-                byte buf[] = new byte[chunkSize];
-    
-                // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data 
-                // encrypted within the EncryptedData field, not including the size of the StreamSize field. 
-                // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this 
+
+                // StreamSize (8 bytes): An unsigned integer that specifies the number of bytes used by data
+                // encrypted within the EncryptedData field, not including the size of the StreamSize field.
+                // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
                 // value, depending on the block size of the chosen encryption algorithm
+                byte buf[] = new byte[LittleEndianConsts.LONG_SIZE];
                 LittleEndian.putLong(buf, 0, _pos);
-                os.write(buf, 0, LittleEndian.LONG_SIZE);
+                os.write(buf);
 
-                FileInputStream fis = new FileInputStream(fileOut);
-                int readBytes;
-                while ((readBytes = fis.read(buf)) != -1) {
-                    os.write(buf, 0, readBytes);
-                }
+                FileInputStream fis = new FileInputStream(_fileOut);
+                IOUtils.copy(fis, os);
                 fis.close();
 
                 os.close();
-                
-                if (!fileOut.delete()) {
-                    logger.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);
+
+                if (!_fileOut.delete()) {
+                    LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+_fileOut);
                 }
             } catch (IOException e) {
                 throw new EncryptedDocumentException(e);

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Decryptor.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Decryptor.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Decryptor.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Decryptor.java Wed Aug  3 23:54:01 2016
@@ -20,24 +20,26 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.security.GeneralSecurityException;
 
+import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
 import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.LittleEndianInput;
 
-public abstract class Decryptor {
+public abstract class Decryptor implements Cloneable {
     public static final String DEFAULT_PASSWORD="VelvetSweatshop";
     public static final String DEFAULT_POIFS_ENTRY="EncryptedPackage";
     
-    protected final EncryptionInfoBuilder builder;
+    protected EncryptionInfo encryptionInfo;
     private SecretKey secretKey;
     private byte[] verifier, integrityHmacKey, integrityHmacValue;
 
-    protected Decryptor(EncryptionInfoBuilder builder) {
-        this.builder = builder;
+    protected Decryptor() {
     }
     
     /**
@@ -54,6 +56,45 @@ public abstract class Decryptor {
     public abstract InputStream getDataStream(DirectoryNode dir)
         throws IOException, GeneralSecurityException;
 
+    /**
+     * Wraps a stream for decryption<p>
+     * 
+     * As we are handling streams and don't know the total length beforehand,
+     * it's the callers duty to care for the length of the entries.
+     *
+     * @param stream the stream to be wrapped
+     * @param initialPos initial/current byte position within the stream
+     * @return decrypted stream
+     */
+    public InputStream getDataStream(LittleEndianInput stream, int size, int initialPos)
+        throws IOException, GeneralSecurityException {
+        throw new RuntimeException("this decryptor doesn't support reading from a stream");
+    }
+
+    /**
+     * Sets the chunk size of the data stream.
+     * Needs to be set before the data stream is requested.
+     * When not set, the implementation uses method specific default values
+     *
+     * @param chunkSize the chunk size, i.e. the block size with the same encryption key
+     */
+    public void setChunkSize(int chunkSize) {
+        throw new RuntimeException("this decryptor doesn't support changing the chunk size");
+    }
+
+    /**
+     * Initializes a cipher object for a given block index for encryption
+     *
+     * @param cipher may be null, otherwise the given instance is reset to the new block index
+     * @param block the block index, e.g. the persist/slide id (hslf)
+     * @return a new cipher object, if cipher was null, otherwise the reinitialized cipher
+     * @throws GeneralSecurityException
+     */
+    public Cipher initCipherForBlock(Cipher cipher, int block)
+    throws GeneralSecurityException {
+        throw new RuntimeException("this decryptor doesn't support initCipherForBlock");
+    }
+    
     public abstract boolean verifyPassword(String password)
         throws GeneralSecurityException;
 
@@ -85,9 +126,11 @@ public abstract class Decryptor {
     public InputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
         return getDataStream(fs.getRoot());
     }
+
     public InputStream getDataStream(OPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
         return getDataStream(fs.getRoot());
     }
+
     public InputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException {
         return getDataStream(fs.getRoot());
     }
@@ -126,10 +169,29 @@ public abstract class Decryptor {
     }
 
     protected int getBlockSizeInBytes() {
-        return builder.getHeader().getBlockSize();
+        return encryptionInfo.getHeader().getBlockSize();
     }
     
     protected int getKeySizeInBytes() {
-        return builder.getHeader().getKeySize()/8;
+        return encryptionInfo.getHeader().getKeySize()/8;
+    }
+    
+    public EncryptionInfo getEncryptionInfo() {
+        return encryptionInfo;
+    }
+
+    public void setEncryptionInfo(EncryptionInfo encryptionInfo) {
+        this.encryptionInfo = encryptionInfo;
+    }
+
+    @Override
+    public Decryptor clone() throws CloneNotSupportedException {
+        Decryptor other = (Decryptor)super.clone();
+        other.integrityHmacKey = integrityHmacKey.clone();
+        other.integrityHmacValue = integrityHmacValue.clone();
+        other.verifier = verifier.clone();
+        other.secretKey = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
+        // encryptionInfo is set from outside
+        return other;
     }
 }
\ No newline at end of file

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java Wed Aug  3 23:54:01 2016
@@ -16,12 +16,11 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt;
 
-
 /**
  * Reads and processes OOXML Encryption Headers
  * The constants are largely based on ZIP constants.
  */
-public abstract class EncryptionHeader {
+public abstract class EncryptionHeader implements Cloneable {
     public static final int ALGORITHM_RC4 = CipherAlgorithm.rc4.ecmaId;
     public static final int ALGORITHM_AES_128 = CipherAlgorithm.aes128.ecmaId;
     public static final int ALGORITHM_AES_192 = CipherAlgorithm.aes192.ecmaId;
@@ -132,4 +131,11 @@ public abstract class EncryptionHeader {
     protected void setCspName(String cspName) {
         this.cspName = cspName;
     }
+
+    @Override
+    public EncryptionHeader clone() throws CloneNotSupportedException {
+        EncryptionHeader other = (EncryptionHeader)super.clone();
+        other.keySalt = (keySalt == null) ? null : keySalt.clone();
+        return other;
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java Wed Aug  3 23:54:01 2016
@@ -34,15 +34,15 @@ import org.apache.poi.util.LittleEndianI
 
 /**
  */
-public class EncryptionInfo {
+public class EncryptionInfo implements Cloneable {
     private final int versionMajor;
     private final int versionMinor;
     private final int encryptionFlags;
     
-    private final EncryptionHeader header;
-    private final EncryptionVerifier verifier;
-    private final Decryptor decryptor;
-    private final Encryptor encryptor;
+    private EncryptionHeader header;
+    private EncryptionVerifier verifier;
+    private Decryptor decryptor;
+    private Encryptor encryptor;
 
     /**
      * A flag that specifies whether CryptoAPI RC4 or ECMA-376 encryption
@@ -96,11 +96,10 @@ public class EncryptionInfo {
 
     public EncryptionInfo(LittleEndianInput dis, boolean isCryptoAPI) throws IOException {
         final EncryptionMode encryptionMode;
-        versionMajor = dis.readShort();
-        versionMinor = dis.readShort();
+        versionMajor = dis.readUShort();
+        versionMinor = dis.readUShort();
 
-        if (!isCryptoAPI
-            && versionMajor == binaryRC4.versionMajor
+        if (   versionMajor == binaryRC4.versionMajor
             && versionMinor == binaryRC4.versionMinor) {
             encryptionMode = binaryRC4;
             encryptionFlags = -1;
@@ -138,10 +137,6 @@ public class EncryptionInfo {
         }
 
         eib.initialize(this, dis);
-        header = eib.getHeader();
-        verifier = eib.getVerifier();
-        decryptor = eib.getDecryptor();
-        encryptor = eib.getEncryptor();
     }
     
     /**
@@ -187,11 +182,6 @@ public class EncryptionInfo {
         }
         
         eib.initialize(this, cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
-        
-        header = eib.getHeader();
-        verifier = eib.getVerifier();
-        decryptor = eib.getDecryptor();
-        encryptor = eib.getEncryptor();
     }
 
     protected static EncryptionInfoBuilder getBuilder(EncryptionMode encryptionMode)
@@ -229,4 +219,32 @@ public class EncryptionInfo {
     public Encryptor getEncryptor() {
         return encryptor;
     }
-}
+
+    public void setHeader(EncryptionHeader header) {
+        this.header = header;
+    }
+
+    public void setVerifier(EncryptionVerifier verifier) {
+        this.verifier = verifier;
+    }
+
+    public void setDecryptor(Decryptor decryptor) {
+        this.decryptor = decryptor;
+    }
+
+    public void setEncryptor(Encryptor encryptor) {
+        this.encryptor = encryptor;
+    }
+
+    @Override
+    public EncryptionInfo clone() throws CloneNotSupportedException {
+        EncryptionInfo other = (EncryptionInfo)super.clone();
+        other.header = header.clone();
+        other.verifier = verifier.clone();
+        other.decryptor = decryptor.clone();
+        other.decryptor.setEncryptionInfo(other);
+        other.encryptor = encryptor.clone();
+        other.encryptor.setEncryptionInfo(other);
+        return other;
+    }
+}
\ No newline at end of file

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java Wed Aug  3 23:54:01 2016
@@ -30,24 +30,4 @@ public interface EncryptionInfoBuilder {
      * initialize the builder from scratch
      */
     void initialize(EncryptionInfo ei, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode);
-
-    /**
-     * @return the header data
-     */
-    EncryptionHeader getHeader();
-
-    /**
-     * @return the verifier data
-     */
-    EncryptionVerifier getVerifier();
-
-    /**
-     * @return the decryptor
-     */
-    Decryptor getDecryptor();
-
-    /**
-     * @return the encryptor
-     */
-    Encryptor getEncryptor();
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java Wed Aug  3 23:54:01 2016
@@ -16,11 +16,10 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt;
 
-
 /**
  * Used when checking if a key is valid for a document 
  */
-public abstract class EncryptionVerifier {
+public abstract class EncryptionVerifier implements Cloneable {
     private byte[] salt;
     private byte[] encryptedVerifier;
     private byte[] encryptedVerifierHash;
@@ -105,5 +104,13 @@ public abstract class EncryptionVerifier
         this.hashAlgorithm = hashAlgorithm;
     }
     
-    
+    @Override
+    public EncryptionVerifier clone() throws CloneNotSupportedException {
+        EncryptionVerifier other = (EncryptionVerifier)super.clone();
+        other.salt = (salt == null) ? null : salt.clone();
+        other.encryptedVerifier = (encryptedVerifier == null) ? null : encryptedVerifier.clone();
+        other.encryptedVerifierHash = (encryptedVerifierHash == null) ? null : encryptedVerifierHash.clone();
+        other.encryptedKey = (encryptedKey == null) ? null : encryptedKey.clone();
+        return other;
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Encryptor.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Encryptor.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Encryptor.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/Encryptor.java Wed Aug  3 23:54:01 2016
@@ -21,14 +21,16 @@ import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 
 import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
 import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
-public abstract class Encryptor {
+public abstract class Encryptor implements Cloneable {
     protected static final String DEFAULT_POIFS_ENTRY = Decryptor.DEFAULT_POIFS_ENTRY;
+    private EncryptionInfo encryptionInfo;
     private SecretKey secretKey;
     
     /**
@@ -66,4 +68,20 @@ public abstract class Encryptor {
     protected void setSecretKey(SecretKey secretKey) {
         this.secretKey = secretKey;
     }
+
+    public EncryptionInfo getEncryptionInfo() {
+        return encryptionInfo;
+    }
+
+    public void setEncryptionInfo(EncryptionInfo encryptionInfo) {
+        this.encryptionInfo = encryptionInfo;
+    }
+
+    @Override
+    public Encryptor clone() throws CloneNotSupportedException {
+        Encryptor other = (Encryptor)super.clone();
+        other.secretKey = new SecretKeySpec(secretKey.getEncoded(), secretKey.getAlgorithm());
+        // encryptionInfo is set from outside
+        return other;
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Decryptor.java Wed Aug  3 23:54:01 2016
@@ -29,36 +29,45 @@ import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.poifs.crypt.*;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianInput;
 import org.apache.poi.util.StringUtil;
 
-public class BinaryRC4Decryptor extends Decryptor {
+public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
     private long _length = -1L;
+    private int _chunkSize = 512;
     
     private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream {
 
+        @Override
         protected Cipher initCipherForBlock(Cipher existing, int block)
                 throws GeneralSecurityException {
-            return BinaryRC4Decryptor.initCipherForBlock(existing, block, builder, getSecretKey(), Cipher.DECRYPT_MODE);
+            return BinaryRC4Decryptor.this.initCipherForBlock(existing, block);
         }
 
         public BinaryRC4CipherInputStream(DocumentInputStream stream, long size)
                 throws GeneralSecurityException {
-            super(stream, size, 512);
+            super(stream, size, _chunkSize);
         }
+
+        public BinaryRC4CipherInputStream(LittleEndianInput stream)
+                throws GeneralSecurityException {
+            super(stream, Integer.MAX_VALUE, _chunkSize);
+        }    
     }
 
-    protected BinaryRC4Decryptor(BinaryRC4EncryptionInfoBuilder builder) {
-        super(builder);
+    protected BinaryRC4Decryptor() {
     }
 
+    @Override
     public boolean verifyPassword(String password) {
-        EncryptionVerifier ver = builder.getVerifier();
+        EncryptionVerifier ver = getEncryptionInfo().getVerifier();
         SecretKey skey = generateSecretKey(password, ver);
         try {
-            Cipher cipher = initCipherForBlock(null, 0, builder, skey, Cipher.DECRYPT_MODE);
+            Cipher cipher = initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.DECRYPT_MODE);
             byte encryptedVerifier[] = ver.getEncryptedVerifier();
             byte verifier[] = new byte[encryptedVerifier.length];
             cipher.update(encryptedVerifier, 0, encryptedVerifier.length, verifier);
@@ -78,17 +87,23 @@ public class BinaryRC4Decryptor extends
         return false;
     }
 
+    @Override
+    public Cipher initCipherForBlock(Cipher cipher, int block)
+    throws GeneralSecurityException {
+        return initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.DECRYPT_MODE);
+    }    
+    
     protected static Cipher initCipherForBlock(Cipher cipher, int block,
-        EncryptionInfoBuilder builder, SecretKey skey, int encryptMode)
+        EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
     throws GeneralSecurityException {
-        EncryptionVerifier ver = builder.getVerifier();
+        EncryptionVerifier ver = encryptionInfo.getVerifier();
         HashAlgorithm hashAlgo = ver.getHashAlgorithm();
         byte blockKey[] = new byte[4];
         LittleEndian.putUInt(blockKey, 0, block);
         byte encKey[] = CryptoFunctions.generateKey(skey.getEncoded(), hashAlgo, blockKey, 16);
         SecretKey key = new SecretKeySpec(encKey, skey.getAlgorithm());
         if (cipher == null) {
-            EncryptionHeader em = builder.getHeader();
+            EncryptionHeader em = encryptionInfo.getHeader();
             cipher = CryptoFunctions.getCipher(key, em.getCipherAlgorithm(), null, null, encryptMode);
         } else {
             cipher.init(encryptMode, key);
@@ -96,10 +111,10 @@ public class BinaryRC4Decryptor extends
         return cipher;
     }
 
-    protected static SecretKey generateSecretKey(String password,
-            EncryptionVerifier ver) {
-        if (password.length() > 255)
+    protected static SecretKey generateSecretKey(String password, EncryptionVerifier ver) {
+        if (password.length() > 255) {
             password = password.substring(0, 255);
+        }
         HashAlgorithm hashAlgo = ver.getHashAlgorithm();
         MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
         byte hash[] = hashAlg.digest(StringUtil.getToUnicodeLE(password));
@@ -116,15 +131,22 @@ public class BinaryRC4Decryptor extends
         return skey;
     }
 
+    @Override
     @SuppressWarnings("resource")
-    public InputStream getDataStream(DirectoryNode dir) throws IOException,
+    public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException,
             GeneralSecurityException {
         DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);
         _length = dis.readLong();
-        BinaryRC4CipherInputStream cipherStream = new BinaryRC4CipherInputStream(dis, _length);
-        return cipherStream;
+        return new BinaryRC4CipherInputStream(dis, _length);
+    }
+    
+    public InputStream getDataStream(LittleEndianInput stream)
+            throws IOException, GeneralSecurityException {
+        return new BinaryRC4CipherInputStream(stream);
     }
+    
 
+    @Override
     public long getLength() {
         if (_length == -1L) {
             throw new IllegalStateException("Decryptor.getDataStream() was not called");
@@ -132,4 +154,14 @@ public class BinaryRC4Decryptor extends
         
         return _length;
     }
+
+    @Override
+    public void setChunkSize(int chunkSize) {
+        _chunkSize = chunkSize;
+    }
+    
+    @Override
+    public BinaryRC4Decryptor clone() throws CloneNotSupportedException {
+        return (BinaryRC4Decryptor)super.clone();
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionHeader.java Wed Aug  3 23:54:01 2016
@@ -24,8 +24,7 @@ import org.apache.poi.poifs.crypt.HashAl
 import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 
-public class BinaryRC4EncryptionHeader extends EncryptionHeader implements
-        EncryptionRecord {
+public class BinaryRC4EncryptionHeader extends EncryptionHeader implements EncryptionRecord, Cloneable {
 
     protected BinaryRC4EncryptionHeader() {
         setCipherAlgorithm(CipherAlgorithm.rc4);
@@ -39,6 +38,14 @@ public class BinaryRC4EncryptionHeader e
         setChainingMode(null);
     }
 
+    @Override
     public void write(LittleEndianByteArrayOutputStream littleendianbytearrayoutputstream) {
     }
+
+    @Override
+    public BinaryRC4EncryptionHeader clone() throws CloneNotSupportedException {
+        return (BinaryRC4EncryptionHeader)super.clone();
+    }
+    
+    
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionInfoBuilder.java Wed Aug  3 23:54:01 2016
@@ -23,55 +23,37 @@ import org.apache.poi.util.LittleEndianI
 
 public class BinaryRC4EncryptionInfoBuilder implements EncryptionInfoBuilder {
 
-    EncryptionInfo info;
-    BinaryRC4EncryptionHeader header;
-    BinaryRC4EncryptionVerifier verifier;
-    BinaryRC4Decryptor decryptor;
-    BinaryRC4Encryptor encryptor;
-
     public BinaryRC4EncryptionInfoBuilder() {
     }
 
+    @Override
     public void initialize(EncryptionInfo info, LittleEndianInput dis)
     throws IOException {
-        this.info = info;
         int vMajor = info.getVersionMajor();
         int vMinor = info.getVersionMinor();
         assert (vMajor == 1 && vMinor == 1);
 
-        header = new BinaryRC4EncryptionHeader();
-        verifier = new BinaryRC4EncryptionVerifier(dis);
-        decryptor = new BinaryRC4Decryptor(this);
-        encryptor = new BinaryRC4Encryptor(this);
+        info.setHeader(new BinaryRC4EncryptionHeader());
+        info.setVerifier(new BinaryRC4EncryptionVerifier(dis));
+        Decryptor dec = new BinaryRC4Decryptor();
+        dec.setEncryptionInfo(info);
+        info.setDecryptor(dec);
+        Encryptor enc = new BinaryRC4Encryptor();
+        enc.setEncryptionInfo(info);
+        info.setEncryptor(enc);
     }
 
+    @Override
     public void initialize(EncryptionInfo info,
         CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm,
         int keyBits, int blockSize, ChainingMode chainingMode) {
-        this.info = info;
-        header = new BinaryRC4EncryptionHeader();
-        verifier = new BinaryRC4EncryptionVerifier();
-        decryptor = new BinaryRC4Decryptor(this);
-        encryptor = new BinaryRC4Encryptor(this);
-    }
-
-    public BinaryRC4EncryptionHeader getHeader() {
-        return header;
-    }
-
-    public BinaryRC4EncryptionVerifier getVerifier() {
-        return verifier;
-    }
-
-    public BinaryRC4Decryptor getDecryptor() {
-        return decryptor;
-    }
-
-    public BinaryRC4Encryptor getEncryptor() {
-        return encryptor;
-    }
-
-    public EncryptionInfo getEncryptionInfo() {
-        return info;
+        info.setHeader(new BinaryRC4EncryptionHeader());
+        info.setVerifier(new BinaryRC4EncryptionVerifier());
+        Decryptor dec = new BinaryRC4Decryptor();
+        dec.setEncryptionInfo(info);
+        info.setDecryptor(dec);
+        Encryptor enc = new BinaryRC4Encryptor();
+        enc.setEncryptionInfo(info);
+        info.setEncryptor(enc);
     }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4EncryptionVerifier.java Wed Aug  3 23:54:01 2016
@@ -23,7 +23,7 @@ import org.apache.poi.poifs.crypt.standa
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 import org.apache.poi.util.LittleEndianInput;
 
-public class BinaryRC4EncryptionVerifier extends EncryptionVerifier implements EncryptionRecord {
+public class BinaryRC4EncryptionVerifier extends EncryptionVerifier implements EncryptionRecord, Cloneable {
 
     protected BinaryRC4EncryptionVerifier() {
         setSpinCount(-1);
@@ -50,6 +50,7 @@ public class BinaryRC4EncryptionVerifier
         setHashAlgorithm(HashAlgorithm.md5);
     }
 
+    @Override
     protected void setSalt(byte salt[]) {
         if (salt == null || salt.length != 16) {
             throw new EncryptedDocumentException("invalid verifier salt");
@@ -58,14 +59,17 @@ public class BinaryRC4EncryptionVerifier
         super.setSalt(salt);
     }
 
+    @Override
     protected void setEncryptedVerifier(byte encryptedVerifier[]) {
         super.setEncryptedVerifier(encryptedVerifier);
     }
 
+    @Override
     protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
         super.setEncryptedVerifierHash(encryptedVerifierHash);
     }
 
+    @Override
     public void write(LittleEndianByteArrayOutputStream bos) {
         byte salt[] = getSalt();
         assert (salt.length == 16);
@@ -78,4 +82,8 @@ public class BinaryRC4EncryptionVerifier
         bos.write(encryptedVerifierHash);
     }
 
+    @Override
+    public BinaryRC4EncryptionVerifier clone() throws CloneNotSupportedException {
+        return (BinaryRC4EncryptionVerifier)super.clone();
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/binaryrc4/BinaryRC4Encryptor.java Wed Aug  3 23:54:01 2016
@@ -34,39 +34,17 @@ import org.apache.poi.poifs.crypt.Crypto
 import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
 import org.apache.poi.poifs.crypt.EncryptionInfo;
 import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 
-public class BinaryRC4Encryptor extends Encryptor {
+public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
 
-    private final BinaryRC4EncryptionInfoBuilder builder;
-    
-    protected class BinaryRC4CipherOutputStream extends ChunkedCipherOutputStream {
-
-        protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk)
-        throws GeneralSecurityException {
-            return BinaryRC4Decryptor.initCipherForBlock(cipher, block, builder, getSecretKey(), Cipher.ENCRYPT_MODE);
-        }
-
-        protected void calculateChecksum(File file, int i) {
-        }
-
-        protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
-        throws IOException, GeneralSecurityException {
-            BinaryRC4Encryptor.this.createEncryptionInfoEntry(dir);
-        }
-
-        public BinaryRC4CipherOutputStream(DirectoryNode dir)
-        throws IOException, GeneralSecurityException {
-            super(dir, 512);
-        }
-    }
-
-    protected BinaryRC4Encryptor(BinaryRC4EncryptionInfoBuilder builder) {
-        this.builder = builder;
+    protected BinaryRC4Encryptor() {
     }
 
+    @Override
     public void confirmPassword(String password) {
         Random r = new SecureRandom();
         byte salt[] = new byte[16];
@@ -76,20 +54,20 @@ public class BinaryRC4Encryptor extends
         confirmPassword(password, null, null, verifier, salt, null);
     }
 
+    @Override
     public void confirmPassword(String password, byte keySpec[],
             byte keySalt[], byte verifier[], byte verifierSalt[],
             byte integritySalt[]) {
-        BinaryRC4EncryptionVerifier ver = builder.getVerifier();
+        BinaryRC4EncryptionVerifier ver = (BinaryRC4EncryptionVerifier)getEncryptionInfo().getVerifier();
         ver.setSalt(verifierSalt);
         SecretKey skey = BinaryRC4Decryptor.generateSecretKey(password, ver);
         setSecretKey(skey);
         try {
-            Cipher cipher = BinaryRC4Decryptor.initCipherForBlock(null, 0, builder, skey, Cipher.ENCRYPT_MODE);
+            Cipher cipher = BinaryRC4Decryptor.initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.ENCRYPT_MODE);
             byte encryptedVerifier[] = new byte[16];
             cipher.update(verifier, 0, 16, encryptedVerifier);
             ver.setEncryptedVerifier(encryptedVerifier);
-            org.apache.poi.poifs.crypt.HashAlgorithm hashAlgo = ver
-                    .getHashAlgorithm();
+            HashAlgorithm hashAlgo = ver.getHashAlgorithm();
             MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
             byte calcVerifierHash[] = hashAlg.digest(verifier);
             byte encryptedVerifierHash[] = cipher.doFinal(calcVerifierHash);
@@ -99,6 +77,7 @@ public class BinaryRC4Encryptor extends
         }
     }
 
+    @Override
     public OutputStream getDataStream(DirectoryNode dir)
     throws IOException, GeneralSecurityException {
         OutputStream countStream = new BinaryRC4CipherOutputStream(dir);
@@ -106,15 +85,16 @@ public class BinaryRC4Encryptor extends
     }
 
     protected int getKeySizeInBytes() {
-        return builder.getHeader().getKeySize() / 8;
+        return getEncryptionInfo().getHeader().getKeySize() / 8;
     }
 
     protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
         DataSpaceMapUtils.addDefaultDataSpace(dir);
-        final EncryptionInfo info = builder.getEncryptionInfo();
-        final BinaryRC4EncryptionHeader header = builder.getHeader();
-        final BinaryRC4EncryptionVerifier verifier = builder.getVerifier();
+        final EncryptionInfo info = getEncryptionInfo();
+        final BinaryRC4EncryptionHeader header = (BinaryRC4EncryptionHeader)info.getHeader();
+        final BinaryRC4EncryptionVerifier verifier = (BinaryRC4EncryptionVerifier)info.getVerifier();
         EncryptionRecord er = new EncryptionRecord() {
+            @Override
             public void write(LittleEndianByteArrayOutputStream bos) {
                 bos.writeShort(info.getVersionMajor());
                 bos.writeShort(info.getVersionMinor());
@@ -124,4 +104,33 @@ public class BinaryRC4Encryptor extends
         };
         DataSpaceMapUtils.createEncryptionEntry(dir, "EncryptionInfo", er);
     }
+
+    @Override
+    public BinaryRC4Encryptor clone() throws CloneNotSupportedException {
+        return (BinaryRC4Encryptor)super.clone();
+    }
+
+    protected class BinaryRC4CipherOutputStream extends ChunkedCipherOutputStream {
+
+        @Override
+        protected Cipher initCipherForBlock(Cipher cipher, int block, boolean lastChunk)
+        throws GeneralSecurityException {
+            return BinaryRC4Decryptor.initCipherForBlock(cipher, block, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
+        }
+
+        @Override
+        protected void calculateChecksum(File file, int i) {
+        }
+
+        @Override
+        protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
+        throws IOException, GeneralSecurityException {
+            BinaryRC4Encryptor.this.createEncryptionInfoEntry(dir);
+        }
+
+        public BinaryRC4CipherOutputStream(DirectoryNode dir)
+        throws IOException, GeneralSecurityException {
+            super(dir, 512);
+        }
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java Wed Aug  3 23:54:01 2016
@@ -17,7 +17,6 @@
 
 package org.apache.poi.poifs.crypt.cryptoapi;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -27,79 +26,34 @@ import java.util.Arrays;
 
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
 import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
 import org.apache.poi.poifs.crypt.CryptoFunctions;
 import org.apache.poi.poifs.crypt.Decryptor;
 import org.apache.poi.poifs.crypt.EncryptionHeader;
-import org.apache.poi.poifs.crypt.EncryptionInfoBuilder;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
 import org.apache.poi.poifs.crypt.EncryptionVerifier;
 import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 import org.apache.poi.poifs.filesystem.DocumentNode;
-import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.BitFieldFactory;
 import org.apache.poi.util.BoundedInputStream;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianInput;
 import org.apache.poi.util.LittleEndianInputStream;
 import org.apache.poi.util.StringUtil;
 
-public class CryptoAPIDecryptor extends Decryptor {
+public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
 
     private long _length;
+    private int _chunkSize = -1;
     
-    private class SeekableByteArrayInputStream extends ByteArrayInputStream {
-        Cipher cipher;
-        byte oneByte[] = { 0 };
-        
-        public void seek(int newpos) {
-            if (newpos > count) {
-                throw new ArrayIndexOutOfBoundsException(newpos);
-            }
-            
-            this.pos = newpos;
-            mark = newpos;
-        }
-
-        public void setBlock(int block) throws GeneralSecurityException {
-            cipher = initCipherForBlock(cipher, block);
-        }
-
-        public synchronized int read() {
-            int ch = super.read();
-            if (ch == -1) return -1;
-            oneByte[0] = (byte) ch;
-            try {
-                cipher.update(oneByte, 0, 1, oneByte);
-            } catch (ShortBufferException e) {
-                throw new EncryptedDocumentException(e);
-            }
-            return oneByte[0];
-        }
-
-        public synchronized int read(byte b[], int off, int len) {
-            int readLen = super.read(b, off, len);
-            if (readLen ==-1) return -1;
-            try {
-                cipher.update(b, off, readLen, b, off);
-            } catch (ShortBufferException e) {
-                throw new EncryptedDocumentException(e);
-            }
-            return readLen;
-        }
-
-        public SeekableByteArrayInputStream(byte buf[])
-        throws GeneralSecurityException {
-            super(buf);
-            cipher = initCipherForBlock(null, 0);
-        }
-    }
-
     static class StreamDescriptorEntry {
         static BitField flagStream = BitFieldFactory.getInstance(1);
         
@@ -111,16 +65,16 @@ public class CryptoAPIDecryptor extends
         String streamName;
     }
 
-    protected CryptoAPIDecryptor(CryptoAPIEncryptionInfoBuilder builder) {
-        super(builder);
+    protected CryptoAPIDecryptor() {
         _length = -1L;
     }
 
+    @Override
     public boolean verifyPassword(String password) {
-        EncryptionVerifier ver = builder.getVerifier();
+        EncryptionVerifier ver = getEncryptionInfo().getVerifier();
         SecretKey skey = generateSecretKey(password, ver);
         try {
-            Cipher cipher = initCipherForBlock(null, 0, builder, skey, Cipher.DECRYPT_MODE);
+            Cipher cipher = initCipherForBlock(null, 0, getEncryptionInfo(), skey, Cipher.DECRYPT_MODE);
             byte encryptedVerifier[] = ver.getEncryptedVerifier();
             byte verifier[] = new byte[encryptedVerifier.length];
             cipher.update(encryptedVerifier, 0, encryptedVerifier.length, verifier);
@@ -140,30 +94,25 @@ public class CryptoAPIDecryptor extends
         return false;
     }
 
-    /**
-     * Initializes a cipher object for a given block index for decryption
-     *
-     * @param cipher may be null, otherwise the given instance is reset to the new block index
-     * @param block the block index, e.g. the persist/slide id (hslf)
-     * @return a new cipher object, if cipher was null, otherwise the reinitialized cipher
-     * @throws GeneralSecurityException
-     */
+    @Override
     public Cipher initCipherForBlock(Cipher cipher, int block)
     throws GeneralSecurityException {
-        return initCipherForBlock(cipher, block, builder, getSecretKey(), Cipher.DECRYPT_MODE);
+        EncryptionInfo ei = getEncryptionInfo();
+        SecretKey sk = getSecretKey();
+        return initCipherForBlock(cipher, block, ei, sk, Cipher.DECRYPT_MODE);
     }
 
     protected static Cipher initCipherForBlock(Cipher cipher, int block,
-        EncryptionInfoBuilder builder, SecretKey skey, int encryptMode)
+        EncryptionInfo encryptionInfo, SecretKey skey, int encryptMode)
     throws GeneralSecurityException {
-        EncryptionVerifier ver = builder.getVerifier();
+        EncryptionVerifier ver = encryptionInfo.getVerifier();
         HashAlgorithm hashAlgo = ver.getHashAlgorithm();
         byte blockKey[] = new byte[4];
         LittleEndian.putUInt(blockKey, 0, block);
         MessageDigest hashAlg = CryptoFunctions.getMessageDigest(hashAlgo);
         hashAlg.update(skey.getEncoded());
         byte encKey[] = hashAlg.digest(blockKey);
-        EncryptionHeader header = builder.getHeader();
+        EncryptionHeader header = encryptionInfo.getHeader();
         int keyBits = header.getKeySize();
         encKey = CryptoFunctions.getBlock0(encKey, keyBits / 8);
         if (keyBits == 40) {
@@ -190,6 +139,18 @@ public class CryptoAPIDecryptor extends
         return skey;
     }
 
+    @Override
+    public ChunkedCipherInputStream getDataStream(DirectoryNode dir)
+    throws IOException, GeneralSecurityException {
+        throw new IOException("not supported");
+    }
+
+    @Override
+    public ChunkedCipherInputStream getDataStream(LittleEndianInput stream, int size, int initialPos)
+            throws IOException, GeneralSecurityException {
+        return new CryptoAPICipherInputStream(stream, size, initialPos);
+    }
+    
     /**
      * Decrypt the Document-/SummaryInformation and other optionally streams.
      * Opposed to other crypto modes, cryptoapi is record based and can't be used
@@ -197,15 +158,17 @@ public class CryptoAPIDecryptor extends
      * 
      * @see <a href="http://msdn.microsoft.com/en-us/library/dd943321(v=office.12).aspx">2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream</a>
      */
-    public InputStream getDataStream(DirectoryNode dir)
+    public POIFSFileSystem getSummaryEntries(DirectoryNode root, String encryptedStream)
     throws IOException, GeneralSecurityException {
-        NPOIFSFileSystem fsOut = new NPOIFSFileSystem();
-        DocumentNode es = (DocumentNode) dir.getEntry("EncryptedSummary");
-        DocumentInputStream dis = dir.createDocumentInputStream(es);
+        POIFSFileSystem fsOut = new POIFSFileSystem();
+        // HSLF: encryptedStream
+        // HSSF: encryption
+        DocumentNode es = (DocumentNode) root.getEntry(encryptedStream);
+        DocumentInputStream dis = root.createDocumentInputStream(es);
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         IOUtils.copy(dis, bos);
         dis.close();
-        SeekableByteArrayInputStream sbis = new SeekableByteArrayInputStream(bos.toByteArray());
+        CryptoAPIDocumentInputStream sbis = new CryptoAPIDocumentInputStream(this, bos.toByteArray());
         LittleEndianInputStream leis = new LittleEndianInputStream(sbis);
         int streamDescriptorArrayOffset = (int) leis.readUInt();
         /* int streamDescriptorArraySize = (int) */ leis.readUInt();
@@ -239,21 +202,40 @@ public class CryptoAPIDecryptor extends
         leis.close();
         sbis.close();
         sbis = null;
-        bos.reset();
-        fsOut.writeFilesystem(bos);
-        fsOut.close();
-        _length = bos.size();
-        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
-        return bis;
+        return fsOut;
     }
 
     /**
      * @return the length of the stream returned by {@link #getDataStream(DirectoryNode)}
      */
+    @Override
     public long getLength() {
         if (_length == -1L) {
             throw new IllegalStateException("Decryptor.getDataStream() was not called");
         }
         return _length;
     }
+
+    public void setChunkSize(int chunkSize) {
+        _chunkSize = chunkSize;
+    }
+    
+    @Override
+    public CryptoAPIDecryptor clone() throws CloneNotSupportedException {
+        return (CryptoAPIDecryptor)super.clone();
+    }
+
+    private class CryptoAPICipherInputStream extends ChunkedCipherInputStream {
+
+        @Override
+        protected Cipher initCipherForBlock(Cipher existing, int block)
+                throws GeneralSecurityException {
+            return CryptoAPIDecryptor.this.initCipherForBlock(existing, block);
+        }
+
+        public CryptoAPICipherInputStream(LittleEndianInput stream, long size, int initialPos)
+                throws GeneralSecurityException {
+            super(stream, size, _chunkSize, initialPos);
+        }    
+    }
 }

Added: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java?rev=1755127&view=auto
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java (added)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java Wed Aug  3 23:54:01 2016
@@ -0,0 +1,86 @@
+/* ====================================================================
+   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.poi.poifs.crypt.cryptoapi;
+
+import java.io.ByteArrayInputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.Cipher;
+import javax.crypto.ShortBufferException;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.util.Internal;
+
+/**
+ * A seekable InputStream, which is used to decrypt/extract the document entries
+ * within the encrypted stream 
+ */
+@Internal
+/* package */ class CryptoAPIDocumentInputStream extends ByteArrayInputStream {
+    private Cipher cipher;
+    private final CryptoAPIDecryptor decryptor;
+    private byte oneByte[] = { 0 };
+    
+    public void seek(int newpos) {
+        if (newpos > count) {
+            throw new ArrayIndexOutOfBoundsException(newpos);
+        }
+        
+        this.pos = newpos;
+        mark = newpos;
+    }
+
+    public void setBlock(int block) throws GeneralSecurityException {
+        cipher = decryptor.initCipherForBlock(cipher, block);
+    }
+
+    @Override
+    public synchronized int read() {
+        int ch = super.read();
+        if (ch == -1) {
+            return -1;
+        }
+        oneByte[0] = (byte) ch;
+        try {
+            cipher.update(oneByte, 0, 1, oneByte);
+        } catch (ShortBufferException e) {
+            throw new EncryptedDocumentException(e);
+        }
+        return oneByte[0];
+    }
+
+    @Override
+    public synchronized int read(byte b[], int off, int len) {
+        int readLen = super.read(b, off, len);
+        if (readLen ==-1) {
+            return -1;
+        }
+        try {
+            cipher.update(b, off, readLen, b, off);
+        } catch (ShortBufferException e) {
+            throw new EncryptedDocumentException(e);
+        }
+        return readLen;
+    }
+
+    public CryptoAPIDocumentInputStream(CryptoAPIDecryptor decryptor, byte buf[])
+    throws GeneralSecurityException {
+        super(buf);
+        this.decryptor = decryptor;
+        cipher = decryptor.initCipherForBlock(null, 0);
+    }
+}
\ No newline at end of file

Propchange: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java?rev=1755127&view=auto
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java (added)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java Wed Aug  3 23:54:01 2016
@@ -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.poi.poifs.crypt.cryptoapi;
+
+import java.io.ByteArrayOutputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.Cipher;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.util.Internal;
+
+/**
+ * An OutputStream for the document entries within the encrypted stream
+ */
+@Internal
+/* package */ class CryptoAPIDocumentOutputStream extends ByteArrayOutputStream {
+    private Cipher cipher;
+    private CryptoAPIEncryptor encryptor;
+    private byte oneByte[] = { 0 };
+
+    public CryptoAPIDocumentOutputStream(CryptoAPIEncryptor encryptor) throws GeneralSecurityException {
+        this.encryptor = encryptor;
+        setBlock(0);
+    }
+    
+    public byte[] getBuf() {
+        return buf;
+    }
+    
+    public void setSize(int count) {
+        this.count = count;
+    }
+    
+    public void setBlock(int block) throws GeneralSecurityException {
+        cipher = encryptor.initCipherForBlock(cipher, block);
+    }
+    
+    @Override
+    public void write(int b) {
+        try {
+            oneByte[0] = (byte)b;
+            cipher.update(oneByte, 0, 1, oneByte, 0);
+            super.write(oneByte);
+        } catch (Exception e) {
+            throw new EncryptedDocumentException(e);
+        }
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len) {
+        try {
+            cipher.update(b, off, len, b, off);
+            super.write(b, off, len);
+        } catch (Exception e) {
+            throw new EncryptedDocumentException(e);
+        }
+    }
+
+}
\ No newline at end of file

Propchange: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDocumentOutputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionHeader.java Wed Aug  3 23:54:01 2016
@@ -27,7 +27,7 @@ import org.apache.poi.poifs.crypt.HashAl
 import org.apache.poi.poifs.crypt.standard.StandardEncryptionHeader;
 import org.apache.poi.util.LittleEndianInput;
 
-public class CryptoAPIEncryptionHeader extends StandardEncryptionHeader {
+public class CryptoAPIEncryptionHeader extends StandardEncryptionHeader implements Cloneable {
 
     public CryptoAPIEncryptionHeader(LittleEndianInput is) throws IOException {
         super(is);
@@ -39,6 +39,7 @@ public class CryptoAPIEncryptionHeader e
         super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
     }
 
+    @Override
     public void setKeySize(int keyBits) {
         // Microsoft Base Cryptographic Provider is limited up to 40 bits
         // http://msdn.microsoft.com/en-us/library/windows/desktop/aa375599(v=vs.85).aspx
@@ -59,4 +60,9 @@ public class CryptoAPIEncryptionHeader e
             setCspName(CipherProvider.rc4.cipherProviderName);
         }
     }
+
+    @Override
+    public CryptoAPIEncryptionHeader clone() throws CloneNotSupportedException {
+        return (CryptoAPIEncryptionHeader)super.clone();
+    }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionInfoBuilder.java Wed Aug  3 23:54:01 2016
@@ -23,63 +23,52 @@ import org.apache.poi.poifs.crypt.*;
 import org.apache.poi.util.LittleEndianInput;
 
 public class CryptoAPIEncryptionInfoBuilder implements EncryptionInfoBuilder {
-    EncryptionInfo info;
-    CryptoAPIEncryptionHeader header;
-    CryptoAPIEncryptionVerifier verifier;
-    CryptoAPIDecryptor decryptor;
-    CryptoAPIEncryptor encryptor;
-
     public CryptoAPIEncryptionInfoBuilder() {
     }
 
     /**
      * initialize the builder from a stream
      */
+    @Override
     public void initialize(EncryptionInfo info, LittleEndianInput dis)
     throws IOException {
-        this.info = info;
         /* int hSize = */ dis.readInt();
-        header = new CryptoAPIEncryptionHeader(dis);
-        verifier = new CryptoAPIEncryptionVerifier(dis, header);
-        decryptor = new CryptoAPIDecryptor(this);
-        encryptor = new CryptoAPIEncryptor(this);
+        CryptoAPIEncryptionHeader header = new CryptoAPIEncryptionHeader(dis);
+        info.setHeader(header);
+        info.setVerifier(new CryptoAPIEncryptionVerifier(dis, header));
+        CryptoAPIDecryptor dec = new CryptoAPIDecryptor();
+        dec.setEncryptionInfo(info);
+        info.setDecryptor(dec);
+        CryptoAPIEncryptor enc = new CryptoAPIEncryptor();
+        enc.setEncryptionInfo(info);
+        info.setEncryptor(enc);
     }
 
     /**
      * initialize the builder from scratch
      */
+    @Override
     public void initialize(EncryptionInfo info,
             CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm,
             int keyBits, int blockSize, ChainingMode chainingMode) {
-        this.info = info;
-        if (cipherAlgorithm == null) cipherAlgorithm = CipherAlgorithm.rc4;
-        if (hashAlgorithm == null) hashAlgorithm = HashAlgorithm.sha1;
-        if (keyBits == -1) keyBits = 0x28; 
+        if (cipherAlgorithm == null) {
+            cipherAlgorithm = CipherAlgorithm.rc4;
+        }
+        if (hashAlgorithm == null) {
+            hashAlgorithm = HashAlgorithm.sha1;
+        }
+        if (keyBits == -1) {
+            keyBits = 0x28;
+        } 
         assert(cipherAlgorithm == CipherAlgorithm.rc4 && hashAlgorithm == HashAlgorithm.sha1);
         
-        header = new CryptoAPIEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
-        verifier = new CryptoAPIEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
-        decryptor = new CryptoAPIDecryptor(this);
-        encryptor = new CryptoAPIEncryptor(this);
-    }
-
-    public CryptoAPIEncryptionHeader getHeader() {
-        return header;
-    }
-
-    public CryptoAPIEncryptionVerifier getVerifier() {
-        return verifier;
-    }
-
-    public CryptoAPIDecryptor getDecryptor() {
-        return decryptor;
-    }
-
-    public CryptoAPIEncryptor getEncryptor() {
-        return encryptor;
-    }
-
-    public EncryptionInfo getEncryptionInfo() {
-        return info;
+        info.setHeader(new CryptoAPIEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+        info.setVerifier(new CryptoAPIEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+        CryptoAPIDecryptor dec = new CryptoAPIDecryptor();
+        dec.setEncryptionInfo(info);
+        info.setDecryptor(dec);
+        CryptoAPIEncryptor enc = new CryptoAPIEncryptor();
+        enc.setEncryptionInfo(info);
+        info.setEncryptor(enc);
     }
 }

Modified: poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java?rev=1755127&r1=1755126&r2=1755127&view=diff
==============================================================================
--- poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java (original)
+++ poi/branches/hssf_cryptoapi/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptionVerifier.java Wed Aug  3 23:54:01 2016
@@ -23,7 +23,7 @@ import org.apache.poi.poifs.crypt.HashAl
 import org.apache.poi.poifs.crypt.standard.StandardEncryptionVerifier;
 import org.apache.poi.util.LittleEndianInput;
 
-public class CryptoAPIEncryptionVerifier extends StandardEncryptionVerifier {
+public class CryptoAPIEncryptionVerifier extends StandardEncryptionVerifier implements Cloneable {
 
     protected CryptoAPIEncryptionVerifier(LittleEndianInput is,
             CryptoAPIEncryptionHeader header) {
@@ -36,15 +36,23 @@ public class CryptoAPIEncryptionVerifier
         super(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
     }
 
+    @Override
     protected void setSalt(byte salt[]) {
         super.setSalt(salt);
     }
 
+    @Override
     protected void setEncryptedVerifier(byte encryptedVerifier[]) {
         super.setEncryptedVerifier(encryptedVerifier);
     }
 
+    @Override
     protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
         super.setEncryptedVerifierHash(encryptedVerifierHash);
     }
+
+    @Override
+    public CryptoAPIEncryptionVerifier clone() throws CloneNotSupportedException {
+        return (CryptoAPIEncryptionVerifier)super.clone();
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org