Author: kiwiwings
Date: Wed Jun 14 17:21:50 2017
New Revision: 1798722

URL: http://svn.apache.org/viewvc?rev=1798722&view=rev
Log:
#61162 - En-/decryption support for HWPF
- encryption support for HWPF
- refactor/unify EncryptionInfo handling in H**F classes
- remove Iterable interface from WorkbookRecordList - use getRecords() instead, 
to prevent modifications over the Iterator

Added:
    
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java
      - copied, changed from r1798721, 
poi/trunk/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java
Removed:
    
poi/trunk/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java
Modified:
    poi/site/src/documentation/content/xdocs/encryption.xml
    poi/trunk/build.xml
    poi/trunk/src/java/org/apache/poi/POIDocument.java
    poi/trunk/src/java/org/apache/poi/POIOLE2TextExtractor.java
    poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
    poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java
    poi/trunk/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
    poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
    
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
    
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
    poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java
    
poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/model/io/HWPFFileSystem.java

Modified: poi/site/src/documentation/content/xdocs/encryption.xml
URL: 
http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/encryption.xml?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/encryption.xml (original)
+++ poi/site/src/documentation/content/xdocs/encryption.xml Wed Jun 14 17:21:50 
2017
@@ -63,73 +63,55 @@
     
     <table>
         <tr>
-            <td>Encryption</td>
-            <td>HSSF</td>
-            <td>HSLF</td>
-            <td>HWPF</td>
-            <td>XSSF</td>
-            <td>XSLF</td>
-            <td>XWPF</td>
+            <th>Encryption</th>
+            <th>HSSF</th>
+            <th>HSLF</th>
+            <th>HWPF</th>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/dd949802(v=office.12).aspx">XOR 
obfuscation *)</link></td>
             <td class="feature-yes">Yes (Writing since 3.16)</td>
             <td class="feature-na">N/A</td>
             <td class="feature-no">No</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/dd909583(v=office.12).aspx">40-bit
 RC4 encryption</link></td>
             <td class="feature-yes">Yes (Writing since 3.16)</td>
             <td class="feature-na">N/A</td>
-            <td class="feature-partly">Read (since 3.17)</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
+            <td class="feature-yes">Yes (since 3.17)</td>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/dd910113(v=office.12).aspx">Office
 Binary Document RC4 CryptoAPI Encryption</link></td>
             <td class="feature-yes">Yes (Since 3.16)</td>
             <td class="feature-yes">Yes</td>
-            <td class="feature-partly">Read (since 3.17)</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
+            <td class="feature-yes">Yes (since 3.17)</td>
+        </tr>
+        <tr>
+               <th/>
+            <th>XSSF</th>
+            <th>XSLF</th>
+            <th>XWPF</th>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/dd907466(v=office.12).aspx">Office
 Binary Document RC4 Encryption **)</link></td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/dd906131(v=office.12).aspx">ECMA-376
 Standard Encryption</link></td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/dd906131(v=office.12).aspx">ECMA-376
 Agile Encryption</link></td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
         </tr>
         <tr>
             <td><link 
href="https://msdn.microsoft.com/en-us/library/ms757845(v=vs.85).aspx">ECMA-376 
XML Signature</link></td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
-            <td class="feature-na">N/A</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>
             <td class="feature-yes">Yes</td>

Modified: poi/trunk/build.xml
URL: 
http://svn.apache.org/viewvc/poi/trunk/build.xml?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/build.xml (original)
+++ poi/trunk/build.xml Wed Jun 14 17:21:50 2017
@@ -463,6 +463,7 @@ under the License.
         <exclude name="**/TestExtractorFactory.java"/>
         <exclude name="**/OutlookTextExtactor.java"/>
         <exclude name="**/TestEmbedOLEPackage.java"/>
+       <exclude name="**/TestHxxFEncryption.java"/>
     </patternset>
 
     <!-- Prints POI's Ant usage help -->

Modified: poi/trunk/src/java/org/apache/poi/POIDocument.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/POIDocument.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/POIDocument.java (original)
+++ poi/trunk/src/java/org/apache/poi/POIDocument.java Wed Jun 14 17:21:50 2017
@@ -24,6 +24,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.GeneralSecurityException;
 import java.util.List;
 
 import org.apache.poi.hpsf.DocumentSummaryInformation;
@@ -31,12 +32,15 @@ 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.Encryptor;
 import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
 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.IOUtils;
 import org.apache.poi.util.Internal;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
@@ -60,8 +64,6 @@ public abstract class POIDocument implem
     /* Have the property streams been read yet? (Only done on-demand) */
     private boolean initialized;
 
-    private static final String[] encryptedStreamNames = { "EncryptedSummary" 
};
-    
     /**
      * Constructs a POIDocument with the given directory node.
      *
@@ -103,7 +105,9 @@ public abstract class POIDocument implem
      *      if it could not be read for this document.
      */
     public DocumentSummaryInformation getDocumentSummaryInformation() {
-        if(!initialized) readProperties();
+        if(!initialized) {
+            readProperties();
+        }
         return dsInf;
     }
 
@@ -114,7 +118,9 @@ public abstract class POIDocument implem
      *      if it could not be read for this document.
      */
     public SummaryInformation getSummaryInformation() {
-        if(!initialized) readProperties();
+        if(!initialized) {
+            readProperties();
+        }
         return sInf;
     }
        
@@ -128,7 +134,9 @@ public abstract class POIDocument implem
      *  then nothing will happen.
      */
     public void createInformationProperties() {
-        if (!initialized) readProperties();
+        if (!initialized) {
+            readProperties();
+        }
         if (sInf == null) {
             sInf = PropertySetFactory.newSummaryInformation();
         }
@@ -144,32 +152,40 @@ public abstract class POIDocument implem
      *  it will remain null;
      */
     protected void readProperties() {
-        PropertySet ps;
-
-        // DocumentSummaryInformation
-        ps = getPropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
-        if (ps instanceof DocumentSummaryInformation) {
-            dsInf = (DocumentSummaryInformation)ps;
-        } else if (ps != null) {
-            logger.log(POILogger.WARN, "DocumentSummaryInformation property 
set came back with wrong class - ", ps.getClass());
-        } else {
-            logger.log(POILogger.WARN, "DocumentSummaryInformation property 
set came back as null");
-        }
-
-        // SummaryInformation
-        ps = getPropertySet(SummaryInformation.DEFAULT_STREAM_NAME);
-        if (ps instanceof SummaryInformation) {
-            sInf = (SummaryInformation)ps;
-        } else if (ps != null) {
-            logger.log(POILogger.WARN, "SummaryInformation property set came 
back with wrong class - ", ps.getClass());
-        } else {
-            logger.log(POILogger.WARN, "SummaryInformation property set came 
back as null");
+        if (initialized) {
+            return;
+        }
+        DocumentSummaryInformation dsi = 
readPropertySet(DocumentSummaryInformation.class, 
DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+        if (dsi != null) {
+            dsInf = dsi;
+        }
+        SummaryInformation si = readPropertySet(SummaryInformation.class, 
SummaryInformation.DEFAULT_STREAM_NAME);
+        if (si != null) {
+            sInf = si;
         }
 
         // Mark the fact that we've now loaded up the properties
         initialized = true;
     }
 
+    @SuppressWarnings("unchecked")
+    private <T> T readPropertySet(Class<T> clazz, String name) {
+        String localName = 
clazz.getName().substring(clazz.getName().lastIndexOf('.')+1);
+        try {
+            PropertySet ps = getPropertySet(name);
+            if (clazz.isInstance(ps)) {
+                return (T)ps;
+            } else if (ps != null) {
+                logger.log(POILogger.WARN, localName+" property set came back 
with wrong class - "+ps.getClass().getName());
+            } else {
+                logger.log(POILogger.WARN, localName+" property set came back 
as null");
+            }
+        } catch (IOException e) {
+            logger.log(POILogger.ERROR, "can't retrieve property set", e);
+        }
+        return null;
+    }
+    
     /** 
      * For a given named property entry, either return it or null if
      *  if it wasn't found
@@ -177,8 +193,8 @@ public abstract class POIDocument implem
      *  @param setName The property to read
      *  @return The value of the given property or null if it wasn't found.
      */
-    protected PropertySet getPropertySet(String setName) {
-        return getPropertySet(setName, null);
+    protected PropertySet getPropertySet(String setName) throws IOException {
+        return getPropertySet(setName, getEncryptionInfo());
     }
     
     /** 
@@ -189,7 +205,7 @@ public abstract class POIDocument implem
      *  @param encryptionInfo the encryption descriptor in case of cryptoAPI 
encryption
      *  @return The value of the given property or null if it wasn't found.
      */
-    protected PropertySet getPropertySet(String setName, EncryptionInfo 
encryptionInfo) {
+    protected PropertySet getPropertySet(String setName, EncryptionInfo 
encryptionInfo) throws IOException {
         DirectoryNode dirNode = directory;
         
         NPOIFSFileSystem encPoifs = null;
@@ -197,14 +213,9 @@ public abstract class POIDocument implem
         try {
             if (encryptionInfo != null && 
encryptionInfo.isDocPropsEncrypted()) {
                 step = "getting encrypted";
-                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");
+                String encryptedStream = getEncryptedPropertyStreamName();
+                if (!dirNode.hasEntry(encryptedStream)) {
+                    throw new EncryptedDocumentException("can't find encrypted 
property stream '"+encryptedStream+"'");
                 }
                 CryptoAPIDecryptor dec = 
(CryptoAPIDecryptor)encryptionInfo.getDecryptor();
                 encPoifs = dec.getSummaryEntries(dirNode, encryptedStream);
@@ -226,17 +237,12 @@ public abstract class POIDocument implem
             } finally {
                 dis.close();
             }
+        } catch (IOException e) {
+            throw e;
         } catch (Exception e) {
-            logger.log(POILogger.WARN, "Error "+step+" property set with name 
" + setName, e);
-            return null;
+            throw new IOException("Error "+step+" property set with name " + 
setName, e);
         } finally {
-            if (encPoifs != null) {
-                try {
-                    encPoifs.close();
-                } catch(IOException e) {
-                    logger.log(POILogger.WARN, "Error closing encrypted 
property poifs", e);
-                }
-            }
+            IOUtils.closeQuietly(encPoifs);
         }
     }
     
@@ -271,20 +277,48 @@ public abstract class POIDocument implem
      *      {@link NPOIFSFileSystem} occurs
      */
     protected void writeProperties(NPOIFSFileSystem outFS, List<String> 
writtenEntries) throws IOException {
+        EncryptionInfo ei = getEncryptionInfo();
+        final boolean encryptProps = (ei != null && ei.isDocPropsEncrypted());
+        NPOIFSFileSystem fs = (encryptProps) ? new NPOIFSFileSystem() : outFS;
+        
         SummaryInformation si = getSummaryInformation();
         if (si != null) {
-            writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, si, 
outFS);
+            writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, si, fs);
             if(writtenEntries != null) {
                 writtenEntries.add(SummaryInformation.DEFAULT_STREAM_NAME);
             }
         }
         DocumentSummaryInformation dsi = getDocumentSummaryInformation();
         if (dsi != null) {
-            writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, 
dsi, outFS);
+            writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, 
dsi, fs);
             if(writtenEntries != null) {
                 
writtenEntries.add(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
             }
         }
+
+        if (!encryptProps) {
+            return;
+        }
+
+        // create empty document summary
+        dsi = PropertySetFactory.newDocumentSummaryInformation();
+        writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, dsi, 
outFS);
+        // remove summary, if previously available
+        if (outFS.getRoot().hasEntry(SummaryInformation.DEFAULT_STREAM_NAME)) {
+            
outFS.getRoot().getEntry(SummaryInformation.DEFAULT_STREAM_NAME).delete();
+        }
+        Encryptor encGen = ei.getEncryptor();
+        if (!(encGen instanceof CryptoAPIEncryptor)) {
+            throw new EncryptedDocumentException("Using 
"+ei.getEncryptionMode()+" encryption. Only CryptoAPI encryption supports 
encrypted property sets!");
+        }
+        CryptoAPIEncryptor enc = (CryptoAPIEncryptor)encGen;
+        try {
+            enc.setSummaryEntries(outFS.getRoot(), 
getEncryptedPropertyStreamName(), fs);
+        } catch (GeneralSecurityException e) {
+            throw new IOException(e);
+        } finally {
+            fs.close();
+        }
     }
        
     /**
@@ -443,4 +477,18 @@ public abstract class POIDocument implem
         directory = newDirectory;
         return dn;
     }
+
+    /**
+     * @return the stream name of the property set collection, if the document 
is encrypted
+     */
+    protected String getEncryptedPropertyStreamName() {
+        return "encryption";
+    }
+
+    /**
+     * @return the encryption info if the document is encrypted, otherwise 
{@code null}
+     */
+    public EncryptionInfo getEncryptionInfo() throws IOException {
+        return null;
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/POIOLE2TextExtractor.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/POIOLE2TextExtractor.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/POIOLE2TextExtractor.java (original)
+++ poi/trunk/src/java/org/apache/poi/POIOLE2TextExtractor.java Wed Jun 14 
17:21:50 2017
@@ -27,7 +27,7 @@ import org.apache.poi.poifs.filesystem.D
  * You will typically find the implementation of
  *  a given format's text extractor under
  *  org.apache.poi.[format].extractor .
- *  
+ *
  * @see org.apache.poi.hssf.extractor.ExcelExtractor
  * @see org.apache.poi.hslf.extractor.PowerPointExtractor
  * @see org.apache.poi.hdgf.extractor.VisioTextExtractor
@@ -39,12 +39,12 @@ public abstract class POIOLE2TextExtract
 
        /**
         * Creates a new text extractor for the given document
-        * 
+        *
         * @param document The POIDocument to use in this extractor.
         */
        public POIOLE2TextExtractor(POIDocument document) {
                this.document = document;
-               
+
                // Ensure any underlying resources, such as open files,
                //  will get cleaned up if the user calls #close()
                setFilesystem(document);
@@ -54,17 +54,17 @@ public abstract class POIOLE2TextExtract
         * Creates a new text extractor, using the same
         *  document as another text extractor. Normally
         *  only used by properties extractors.
-        * 
+        *
         * @param otherExtractor the extractor which document to be used
         */
        protected POIOLE2TextExtractor(POIOLE2TextExtractor otherExtractor) {
                this.document = otherExtractor.document;
        }
-       
+
        /**
         * Returns the document information metadata for the document
-        * 
-     * @return The Document Summary Information or null 
+        *
+     * @return The Document Summary Information or null
      *      if it could not be read for this document.
         */
        public DocumentSummaryInformation getDocSummaryInformation() {
@@ -72,7 +72,7 @@ public abstract class POIOLE2TextExtract
        }
        /**
         * Returns the summary information metadata for the document.
-        * 
+        *
      * @return The Summary information for the document or null
      *      if it could not be read for this document.
         */
@@ -83,7 +83,7 @@ public abstract class POIOLE2TextExtract
        /**
         * Returns an HPSF powered text extractor for the
         *  document properties metadata, such as title and author.
-        *  
+        *
         * @return an instance of POIExtractor that can extract meta-data.
         */
        @Override
@@ -96,8 +96,16 @@ public abstract class POIOLE2TextExtract
         *
         * @return the DirectoryEntry that is associated with the POIDocument 
of this extractor.
         */
-    public DirectoryEntry getRoot()
-    {
+    public DirectoryEntry getRoot() {
         return document.getDirectory();
     }
-}
+
+    /**
+     * Return the underlying POIDocument
+     *
+     * @return the underlying POIDocument
+     */
+    public POIDocument getDocument() {
+        return document;
+    }
+}
\ No newline at end of file

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/InternalWorkbook.java Wed Jun 
14 17:21:50 2017
@@ -20,16 +20,12 @@ package org.apache.poi.hssf.model;
 import static org.apache.poi.util.POILogger.DEBUG;
 
 import java.security.AccessControlException;
-import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 
-import javax.crypto.SecretKey;
-
-import org.apache.poi.EncryptedDocumentException;
 import org.apache.poi.ddf.EscherBSERecord;
 import org.apache.poi.ddf.EscherBoolProperty;
 import org.apache.poi.ddf.EscherContainerRecord;
@@ -57,7 +53,6 @@ import org.apache.poi.hssf.record.Escher
 import org.apache.poi.hssf.record.ExtSSTRecord;
 import org.apache.poi.hssf.record.ExtendedFormatRecord;
 import org.apache.poi.hssf.record.ExternSheetRecord;
-import org.apache.poi.hssf.record.FilePassRecord;
 import org.apache.poi.hssf.record.FileSharingRecord;
 import org.apache.poi.hssf.record.FnGroupCountRecord;
 import org.apache.poi.hssf.record.FontRecord;
@@ -88,13 +83,8 @@ import org.apache.poi.hssf.record.Window
 import org.apache.poi.hssf.record.WriteAccessRecord;
 import org.apache.poi.hssf.record.WriteProtectRecord;
 import org.apache.poi.hssf.record.common.UnicodeString;
-import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
 import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
 import org.apache.poi.poifs.crypt.CryptoFunctions;
-import org.apache.poi.poifs.crypt.Decryptor;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.EncryptionMode;
-import org.apache.poi.poifs.crypt.Encryptor;
 import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName;
 import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
 import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheetRange;
@@ -1048,7 +1038,7 @@ public final class InternalWorkbook {
         SSTRecord lSST = null;
         int sstPos = 0;
         boolean wroteBoundSheets = false;
-        for ( Record record : records ) {
+        for ( Record record : records.getRecords() ) {
             int len = 0;
             if (record instanceof SSTRecord) {
                 lSST = (SSTRecord)record;
@@ -1080,8 +1070,6 @@ public final class InternalWorkbook {
      * Include in it ant code that modifies the workbook record stream and 
affects its size.
      */
     public void preSerialize(){
-        updateEncryptionRecord();
-
         // Ensure we have enough tab IDs
         // Can be a few short if new sheets were added
         if(records.getTabpos() > 0) {
@@ -1092,45 +1080,11 @@ public final class InternalWorkbook {
         }
     }
 
-    private void updateEncryptionRecord() {
-        FilePassRecord fpr = 
(FilePassRecord)findFirstRecordBySid(FilePassRecord.sid);
-
-        String password = Biff8EncryptionKey.getCurrentUserPassword();
-        if (password == null) {
-            if (fpr != null) {
-                // need to remove password data
-                records.remove(fpr);
-            }
-        } else {
-            // create password record
-            if (fpr == null) {
-                fpr = new FilePassRecord(EncryptionMode.binaryRC4);
-                records.add(1, fpr);
-            }
-
-            // check if the password has been changed
-            EncryptionInfo ei = fpr.getEncryptionInfo();
-            byte encVer[] = ei.getVerifier().getEncryptedVerifier();
-            try {
-                Decryptor dec = ei.getDecryptor();
-                Encryptor enc = ei.getEncryptor();
-                if (encVer == null || !dec.verifyPassword(password)) {
-                    enc.confirmPassword(password);
-                } else {
-                    SecretKey sk = dec.getSecretKey();
-                    ei.getEncryptor().setSecretKey(sk);
-                }
-            } catch (GeneralSecurityException e) {
-                throw new EncryptedDocumentException("can't validate/update 
encryption setting", e);
-            }
-        }
-    }
-
     public int getSize() {
         int retval = 0;
 
         SSTRecord lSST = null;
-        for ( Record record : records ) {
+        for ( Record record : records.getRecords() ) {
             if (record instanceof SSTRecord) {
                 lSST = (SSTRecord)record;
             }
@@ -1803,7 +1757,7 @@ public final class InternalWorkbook {
      * @return the matching record or {@code null} if it wasn't found
      */
     public Record findFirstRecordBySid(short sid) {
-        for (Record record : records) {
+        for (Record record : records.getRecords() ) {
             if (record.getSid() == sid) {
                 return record;
             }
@@ -1818,7 +1772,7 @@ public final class InternalWorkbook {
      */
     public int findFirstRecordLocBySid(short sid) {
         int index = 0;
-        for (Record record : records) {
+        for (Record record : records.getRecords() ) {
             if (record.getSid() == sid) {
                 return index;
             }
@@ -1837,7 +1791,7 @@ public final class InternalWorkbook {
      */
     public Record findNextRecordBySid(short sid, int pos) {
         int matches = 0;
-        for (Record record : records) {
+        for (Record record : records.getRecords() ) {
             if (record.getSid() == sid && matches++ == pos) {
                 return record;
             }
@@ -1901,7 +1855,7 @@ public final class InternalWorkbook {
         }
 
         // Need to find a DrawingGroupRecord that contains a EscherDggRecord
-        for(Record r : records) {
+        for(Record r : records.getRecords() ) {
             if (!(r instanceof DrawingGroupRecord)) {
                 continue;
             }
@@ -2301,4 +2255,12 @@ public final class InternalWorkbook {
     public boolean changeExternalReference(String oldUrl, String newUrl) {
        return linkTable.changeExternalReference(oldUrl, newUrl);
     }
+
+    /**
+     * Only for internal calls - code based on this is not supported ...
+     */
+    @Internal
+    public WorkbookRecordList getWorkbookRecordList() {
+        return records;
+    }
 }
\ No newline at end of file

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java (original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/LinkTable.java Wed Jun 14 
17:21:50 2017
@@ -522,9 +522,7 @@ final class LinkTable {
         */
        private int findFirstRecordLocBySid(short sid) {
                int index = 0;
-               for (Iterator<Record> iterator = 
_workbookRecordList.iterator(); iterator.hasNext(); ) {
-                       Record record = iterator.next();
-
+               for (Record record : _workbookRecordList.getRecords()) {
                        if (record.getSid() == sid) {
                                return index;
                        }
@@ -639,11 +637,11 @@ final class LinkTable {
         int supLinkIndex = 0;
         // find the posistion of the Add-In SupBookRecord in the workbook 
stream,
         // the created ExternalNameRecord will be appended to it
-        for (Iterator<Record> iterator = _workbookRecordList.iterator(); 
iterator.hasNext(); supLinkIndex++) {
-            Record record = iterator.next();
-            if (record instanceof SupBookRecord) {
-                if (((SupBookRecord) record).isAddInFunctions()) break;
+        for (Record record : _workbookRecordList.getRecords()) {
+            if (record instanceof SupBookRecord && ((SupBookRecord) 
record).isAddInFunctions()) {
+                break;
             }
+            supLinkIndex++;
         }
         int numberOfNames = extBlock.getNumberOfNames();
         // a new name is inserted in the end of the SupBookRecord, after the 
last name

Modified: poi/trunk/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java Wed 
Jun 14 17:21:50 2017
@@ -18,28 +18,37 @@
 package org.apache.poi.hssf.model;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 
 import org.apache.poi.hssf.record.Record;
 
-public final class WorkbookRecordList implements Iterable<Record> {
+public final class WorkbookRecordList {
     private List<Record> records = new ArrayList<Record>();
 
-       private int  protpos     = 0;   // holds the position of the protect 
record.
-       private int  bspos         = 0;   // holds the position of the last 
bound sheet.
-       private int  tabpos       = 0;   // holds the position of the tabid 
record
-       private int  fontpos     = 0;   // hold the position of the last font 
record
-       private int  xfpos         = 0;   // hold the position of the last 
extended font record
-       private int  backuppos   = 0;   // holds the position of the backup 
record.
-       private int  namepos     = 0;   // holds the position of last name 
record
-       private int  supbookpos  = 0;   // holds the position of sup book
-       private int  externsheetPos = 0;// holds the position of the extern 
sheet
-       private int  palettepos  = -1;   // hold the position of the palette, 
if applicable
+    /** holds the position of the protect record */
+       private int protpos            = 0;
+    /** holds the position of the last bound sheet */
+       private int bspos              = 0;
+    /** holds the position of the tabid record */
+       private int tabpos             = 0;
+    /** hold the position of the last font record */
+       private int fontpos            = 0;
+    /** hold the position of the last extended font record */
+       private int xfpos              = 0;
+    /** holds the position of the backup record */
+       private int backuppos      = 0;
+    /** holds the position of last name record */
+       private int namepos            = 0;
+       /** holds the position of sup book */
+       private int supbookpos     = 0;
+       /** holds the position of the extern sheet */
+       private int externsheetPos = 0;
+       /** hold the position of the palette, if applicable */
+       private int palettepos     = -1;
 
 
        public void setRecords(List<Record> records) {
-               this.records = records;
+           this.records = records;
        }
 
        public int size() {
@@ -52,26 +61,13 @@ public final class WorkbookRecordList im
 
        public void add(int pos, Record r) {
                records.add(pos, r);
-               if (getProtpos() >= pos) setProtpos( protpos + 1 );
-               if (getBspos() >= pos) setBspos( bspos + 1 );
-               if (getTabpos() >= pos) setTabpos( tabpos + 1 );
-               if (getFontpos() >= pos) setFontpos( fontpos + 1 );
-               if (getXfpos() >= pos) setXfpos( xfpos + 1 );
-               if (getBackuppos() >= pos) setBackuppos( backuppos + 1 );
-               if (getNamepos() >= pos) setNamepos(namepos+1);
-               if (getSupbookpos() >= pos) setSupbookpos(supbookpos+1);
-               if ((getPalettepos() != -1) && (getPalettepos() >= pos)) 
setPalettepos( palettepos + 1 );
-               if (getExternsheetPos() >= pos) 
setExternsheetPos(getExternsheetPos() + 1);
+               updateRecordPos(pos, true);
        }
 
        public List<Record> getRecords() {
                return records;
        }
 
-       public Iterator<Record> iterator() {
-               return records.iterator();
-       }
-
        /**
         * Find the given record in the record list by identity and removes it
         *
@@ -89,19 +85,9 @@ public final class WorkbookRecordList im
           }
        }
 
-       public void remove( int pos )
-       {
+       public void remove( int pos ) {
                records.remove(pos);
-               if (getProtpos() >= pos) setProtpos( protpos - 1 );
-               if (getBspos() >= pos) setBspos( bspos - 1 );
-               if (getTabpos() >= pos) setTabpos( tabpos - 1 );
-               if (getFontpos() >= pos) setFontpos( fontpos - 1 );
-               if (getXfpos() >= pos) setXfpos( xfpos - 1 );
-               if (getBackuppos() >= pos) setBackuppos( backuppos - 1 );
-               if (getNamepos() >= pos) setNamepos(getNamepos()-1);
-               if (getSupbookpos() >= pos) setSupbookpos(getSupbookpos()-1);
-               if ((getPalettepos() != -1) && (getPalettepos() >= pos)) 
setPalettepos( palettepos - 1 );
-               if (getExternsheetPos() >= pos) setExternsheetPos( 
getExternsheetPos() -1);
+               updateRecordPos(pos, false);
        }
 
        public int getProtpos() {
@@ -160,7 +146,7 @@ public final class WorkbookRecordList im
                this.palettepos = palettepos;
        }
 
-       
+
        /**
         * Returns the namepos.
         * @return int
@@ -208,4 +194,48 @@ public final class WorkbookRecordList im
        public void setExternsheetPos(int externsheetPos) {
                this.externsheetPos = externsheetPos;
        }
+
+    private void updateRecordPos(int pos, boolean add) {
+        int delta = (add) ? 1 : -1;
+        int p = getProtpos();
+        if (p >= pos) {
+            setProtpos( p + delta );
+        }
+        p = getBspos();
+        if (p >= pos) {
+            setBspos( p + delta );
+        }
+        p = getTabpos();
+        if (p >= pos) {
+            setTabpos( p + delta );
+        }
+        p = getFontpos();
+        if (p >= pos) {
+            setFontpos( p + delta );
+        }
+        p = getXfpos();
+        if (p >= pos) {
+            setXfpos( p + delta );
+        }
+        p = getBackuppos();
+        if (p >= pos) {
+            setBackuppos( p + delta );
+        }
+        p = getNamepos();
+        if (p >= pos) {
+            setNamepos(p + delta );
+        }
+        p = getSupbookpos();
+        if (p >= pos) {
+            setSupbookpos(p + delta);
+        }
+        p = getPalettepos();
+        if (p != -1 && p >= pos) {
+            setPalettepos( p + delta );
+        }
+        p = getExternsheetPos();
+        if (p >= pos) {
+            setExternsheetPos( p + delta );
+        }
+    }
 }

Modified: poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java 
(original)
+++ poi/trunk/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java Wed Jun 
14 17:21:50 2017
@@ -30,6 +30,7 @@ import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -52,6 +53,8 @@ import org.apache.poi.ddf.EscherBlipReco
 import org.apache.poi.ddf.EscherMetafileBlip;
 import org.apache.poi.ddf.EscherRecord;
 import org.apache.poi.hpsf.ClassID;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.SummaryInformation;
 import org.apache.poi.hssf.OldExcelFormatException;
 import org.apache.poi.hssf.model.DrawingManager2;
 import org.apache.poi.hssf.model.HSSFFormulaParser;
@@ -59,6 +62,7 @@ import org.apache.poi.hssf.model.Interna
 import org.apache.poi.hssf.model.InternalSheet.UnsupportedBOFType;
 import org.apache.poi.hssf.model.InternalWorkbook;
 import org.apache.poi.hssf.model.RecordStream;
+import org.apache.poi.hssf.model.WorkbookRecordList;
 import org.apache.poi.hssf.record.AbstractEscherHolderRecord;
 import org.apache.poi.hssf.record.BackupRecord;
 import org.apache.poi.hssf.record.BoundSheetRecord;
@@ -77,9 +81,13 @@ import org.apache.poi.hssf.record.Unknow
 import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
 import org.apache.poi.hssf.record.common.UnicodeString;
 import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
 import org.apache.poi.hssf.util.CellReference;
 import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
 import org.apache.poi.poifs.crypt.Decryptor;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.EncryptionVerifier;
 import org.apache.poi.poifs.crypt.Encryptor;
 import org.apache.poi.poifs.filesystem.DirectoryEntry;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
@@ -1454,13 +1462,21 @@ public final class HSSFWorkbook extends
 
         // Write out our HPFS properties, if we have them
         writeProperties(fs, excepts);
-
+        
         if (preserveNodes) {
             // Don't write out the old Workbook, we'll be doing our new one
             // If the file had an "incorrect" name for the workbook stream,
             // don't write the old one as we'll use the correct name shortly
             excepts.addAll(Arrays.asList(WORKBOOK_DIR_ENTRY_NAMES));
 
+            // summary information has been already written via 
writeProperties and might go in a
+            // different stream, if the file is cryptoapi encrypted
+            excepts.addAll(Arrays.asList(
+                DocumentSummaryInformation.DEFAULT_STREAM_NAME,
+                SummaryInformation.DEFAULT_STREAM_NAME,
+                getEncryptedPropertyStreamName()
+            ));
+
             // Copy over all the other nodes to our new poifs
             EntryUtils.copyNodes(
                     new FilteringDirectoryNode(getDirectory(), excepts)
@@ -1520,6 +1536,7 @@ public final class HSSFWorkbook extends
         HSSFSheet[] sheets = getSheets();
         int nSheets = sheets.length;
 
+        updateEncryptionInfo();
 
         // before getting the workbook size we must tell the sheets that
         // serialization is about to occur.
@@ -1566,22 +1583,14 @@ public final class HSSFWorkbook extends
 
     @SuppressWarnings("resource")
     protected void encryptBytes(byte buf[]) {
-        int initialOffset = 0;
-        FilePassRecord fpr = null;
-        for (Record r : workbook.getRecords()) {
-            initialOffset += r.getRecordSize();
-            if (r instanceof FilePassRecord) {
-                fpr = (FilePassRecord)r;
-                break;
-            }
-        }
-        if (fpr == null) {
+        EncryptionInfo ei = getEncryptionInfo();
+        if (ei == null) {
             return;
         }
-        
+        Encryptor enc = ei.getEncryptor();
+        int initialOffset = 0;
         LittleEndianByteArrayInputStream plain = new 
LittleEndianByteArrayInputStream(buf, 0); // NOSONAR
         LittleEndianByteArrayOutputStream leos = new 
LittleEndianByteArrayOutputStream(buf, 0); // NOSONAR
-        Encryptor enc = fpr.getEncryptionInfo().getEncryptor();
         enc.setChunkSize(Biff8DecryptingStream.RC4_REKEYING_INTERVAL);
         byte tmp[] = new byte[1024];
         try {
@@ -2306,4 +2315,50 @@ public final class HSSFWorkbook extends
     public SpreadsheetVersion getSpreadsheetVersion() {
         return SpreadsheetVersion.EXCEL97;
     }
+
+    @Override
+    public EncryptionInfo getEncryptionInfo() {
+        FilePassRecord fpr = 
(FilePassRecord)workbook.findFirstRecordBySid(FilePassRecord.sid);
+        return (fpr != null) ? fpr.getEncryptionInfo() : null;
+    }
+
+
+    private void updateEncryptionInfo() {
+        // make sure, that we've read all the streams ...
+        readProperties();
+        FilePassRecord fpr = 
(FilePassRecord)workbook.findFirstRecordBySid(FilePassRecord.sid);
+
+        String password = Biff8EncryptionKey.getCurrentUserPassword();
+        WorkbookRecordList wrl = workbook.getWorkbookRecordList();
+        if (password == null) {
+            if (fpr != null) {
+                // need to remove password data
+                wrl.remove(fpr);
+            }
+        } else {
+            // create password record
+            if (fpr == null) {
+                fpr = new FilePassRecord(EncryptionMode.cryptoAPI);
+                wrl.add(1, fpr);
+            }
+
+            // check if the password has been changed
+            EncryptionInfo ei = fpr.getEncryptionInfo();
+            EncryptionVerifier ver = ei.getVerifier();
+            byte encVer[] = ver.getEncryptedVerifier();
+            Decryptor dec = ei.getDecryptor();
+            Encryptor enc = ei.getEncryptor();
+            try {
+                if (encVer == null || !dec.verifyPassword(password)) {
+                    enc.confirmPassword(password);
+                } else {
+                    byte verifier[] = dec.getVerifier();
+                    byte salt[] = ver.getSalt();
+                    enc.confirmPassword(password, null, null, verifier, salt, 
null);
+                }
+            } catch (GeneralSecurityException e) {
+                throw new EncryptedDocumentException("can't validate/update 
encryption setting", e);
+            }
+        }
+    }
 }

Modified: 
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- 
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java 
(original)
+++ 
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java 
Wed Jun 14 17:21:50 2017
@@ -153,14 +153,19 @@ public class CryptoAPIDecryptor extends
     /**
      * Decrypt the Document-/SummaryInformation and other optionally streams.
      * Opposed to other crypto modes, cryptoapi is record based and can't be 
used
-     * to stream-decrypt a whole file
+     * to stream-decrypt a whole file.<p>
+     * 
+     * Summary entries are only encrypted within cryptoapi encrypted files.
+     * Binary RC4 encrypted files use non-encrypted/default property sets
+     * 
+     * @param root root directory node of the OLE file containing the 
encrypted properties
+     * @param encryptedStream name of the encrypted stream -
+     *      "encryption" for HSSF/HWPF, "encryptedStream" (or 
encryptedSummary?) for HSLF 
      *
      * @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 POIFSFileSystem getSummaryEntries(DirectoryNode root, String 
encryptedStream)
     throws IOException, GeneralSecurityException {
-        // HSLF: encryptedStream
-        // HSSF: encryption
         DocumentNode es = (DocumentNode) root.getEntry(encryptedStream);
         DocumentInputStream dis = root.createDocumentInputStream(es);
         ByteArrayOutputStream bos = new ByteArrayOutputStream();

Modified: 
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- 
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java 
(original)
+++ 
poi/trunk/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java 
Wed Jun 14 17:21:50 2017
@@ -32,10 +32,6 @@ import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
 
 import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.hpsf.DocumentSummaryInformation;
-import org.apache.poi.hpsf.PropertySetFactory;
-import org.apache.poi.hpsf.SummaryInformation;
-import org.apache.poi.hpsf.WritingNotSupportedException;
 import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
 import org.apache.poi.poifs.crypt.CryptoFunctions;
 import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
@@ -46,6 +42,8 @@ import org.apache.poi.poifs.crypt.crypto
 import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.LittleEndian;
 import org.apache.poi.util.LittleEndianByteArrayOutputStream;
@@ -124,41 +122,34 @@ public class CryptoAPIEncryptor 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 OutputStream getSummaryEntries(DirectoryNode dir)
+    public void setSummaryEntries(DirectoryNode dir, String encryptedStream, 
NPOIFSFileSystem entries)
     throws IOException, GeneralSecurityException {
         CryptoAPIDocumentOutputStream bos = new 
CryptoAPIDocumentOutputStream(this); // NOSONAR
         byte buf[] = new byte[8];
         
         bos.write(buf, 0, 8); // skip header
-        String entryNames[] = {
-            SummaryInformation.DEFAULT_STREAM_NAME,
-            DocumentSummaryInformation.DEFAULT_STREAM_NAME
-        };
-        
         List<StreamDescriptorEntry> descList = new 
ArrayList<StreamDescriptorEntry>();
 
         int block = 0;
-        for (String entryName : entryNames) {
-            if (!dir.hasEntry(entryName)) {
+        for (Entry entry : entries.getRoot()) {
+            if (entry.isDirectoryEntry()) {
                 continue;
             }
             StreamDescriptorEntry descEntry = new StreamDescriptorEntry();
             descEntry.block = block;
             descEntry.streamOffset = bos.size();
-            descEntry.streamName = entryName;
+            descEntry.streamName = entry.getName();
             descEntry.flags = StreamDescriptorEntry.flagStream.setValue(0, 1);
             descEntry.reserved2 = 0;
             
             bos.setBlock(block);
-            DocumentInputStream dis = dir.createDocumentInputStream(entryName);
+            DocumentInputStream dis = dir.createDocumentInputStream(entry);
             IOUtils.copy(dis, bos);
             dis.close();
             
             descEntry.streamSize = bos.size() - descEntry.streamOffset;
             descList.add(descEntry);
             
-            dir.getEntry(entryName).delete();
-            
             block++;
         }
         
@@ -197,16 +188,7 @@ public class CryptoAPIEncryptor extends
         bos.write(buf, 0, 8);
         bos.setSize(savedSize);
         
-        dir.createDocument("EncryptedSummary", new 
ByteArrayInputStream(bos.getBuf(), 0, savedSize));
-        DocumentSummaryInformation dsi = 
PropertySetFactory.newDocumentSummaryInformation();
-        
-        try {
-            dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
-        } catch (WritingNotSupportedException e) {
-            throw new IOException(e);
-        }
-        
-        return bos;
+        dir.createDocument(encryptedStream, new 
ByteArrayInputStream(bos.getBuf(), 0, savedSize));
     }
 
     protected int getKeySizeInBytes() {

Copied: 
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java
 (from r1798721, 
poi/trunk/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java)
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java?p2=poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java&p1=poi/trunk/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java&r1=1798721&r2=1798722&rev=1798722&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java 
(original)
+++ 
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java
 Wed Jun 14 17:21:50 2017
@@ -15,16 +15,34 @@
    limitations under the License.
 ==================================================================== */
 
-package org.apache.poi.hwpf;
+package org.apache.poi.poifs.crypt;
 
+import static org.apache.poi.POIDataSamples.getDocumentInstance;
+import static org.apache.poi.POIDataSamples.getSlideShowInstance;
+import static org.apache.poi.POIDataSamples.getSpreadSheetInstance;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
+import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.Collection;
 
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.POIDocument;
+import org.apache.poi.POIOLE2TextExtractor;
+import org.apache.poi.POITextExtractor;
+import org.apache.poi.extractor.ExtractorFactory;
+import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
 import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
-import org.apache.poi.hwpf.extractor.WordExtractor;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
+import org.apache.poi.poifs.storage.RawDataUtil;
+import org.apache.xmlbeans.XmlException;
 import org.junit.AfterClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -33,37 +51,137 @@ import org.junit.runners.Parameterized.P
 import org.junit.runners.Parameterized.Parameters;
 
 @RunWith(Parameterized.class)
-public class HWPFTestEncryption {
+public class TestHxxFEncryption {
     @AfterClass
     public static void clearPass() {
         Biff8EncryptionKey.setCurrentUserPassword(null);
     }
 
     @Parameter(value = 0)
-    public String file;
+    public POIDataSamples sampleDir;
 
     @Parameter(value = 1)
-    public String password;
+    public String file;
 
     @Parameter(value = 2)
+    public String password;
+
+    @Parameter(value = 3)
     public String expected;
 
-    @Parameters(name="{0}")
-    public static Collection<String[]> data() {
+    @Parameters(name="{1}")
+    public static Collection<Object[]> data() throws IOException {
         return Arrays.asList(
-            new String[]{ "password_tika_binaryrc4.doc", "tika", "This is an 
encrypted Word 2007 File." },
-            new String[]{ "password_password_cryptoapi.doc", "password", "This 
is a test" }
+            // binary rc4
+            new Object[]{ getDocumentInstance(), 
"password_tika_binaryrc4.doc", "tika", "This is an encrypted Word 2007 File." },
+            // cryptoapi
+            new Object[]{ getDocumentInstance(), 
"password_password_cryptoapi.doc", "password", "This is a test" },
+            // binary rc4
+            new Object[]{ getSpreadSheetInstance(), "password.xls", "password",
+                
x("H4sIAAAAAAAAAF1Uu24bMRDs/RULVwkgCUhSpHaZwkDgpHJH8fZ0G/Nx4ZI6y13yG/mRfIb9R5mlZFlIpdPtcnZmdnjPf57/vvx6+f3h6obuv3"+
+                  
"ylbY5bEiVHe1fEpUp5pOgkrK0iabehm7FyoZi1ks8xcvHiQu8h5bLnorTlnUvkJ/YPOHKsLVInAqCs91KakuaxLq4w3g00SgCo9Xou1UnCmSBe"+
+                  
"MhpRY6qHmXVFteQfQJ5yUaaOw4qXwgPVjPGAqhNH5bBHAfTmwqqoSkLdFT/J3nC0eZBRk7yiu5s7yoU+r+9l3tDtm5A3jgt6AQxNOY2ya+U4sK"+
+                  
"XZ+YczbpfSVVuzFOuunKraqIVD2ND3yVXauT3TNthR/O3IJAM7gzTOGeIcXZvj14ahotW8wSognlMu0Yyp/Fi7O6s+CK6haUUjtPCji7MVcgqH"+
+                  
"jh+42tqeqPDMroJ/lBAE4AZbJbJu6Fu35ej42Tw9mYeTwVXoBKJiPeFV94q2rZJAyNEPo/qOdWYLBpq3B2JX8GDZeJ14mZf3tOQWBmpd9yQ7kI"+
+                  
"DCY/jmkj1oGOicFy62r9vutC5uJsVEMFgmAXXfYcC6BRBKNHCybALFJolnrDcPXNLl+K60Vctt09YZT7YgbeOICGJ/ZgC2JztOnm1JhX3eJXni"+
+                  
"U5Bqhezzlu334vD/Ajr3yDGXw5G9IZ6aLmLfQafY42N3J7cjj1LaXOHihSrcC5ThmuYIB5FX5AU8tKlnNG9Dn1EnsdD4KcnPhsSNPRiXtz461b"+
+                  "VZw8Pm6vn0afh4fvr0D5P/+cMuBAAA") },
+            // cryptoapi
+            new Object[]{ getSpreadSheetInstance(), "35897-type4.xls", 
"freedom", "Sheet1\nhello there!" },
+            // cryptoapi (PPT only supports cryptoapi...)
+            new Object[]{ getSlideShowInstance(), "cryptoapi-proc2356.ppt", 
"crypto", "Dominic Salemno" }
         );
     }
 
+    private static String x(String base64) throws IOException {
+        return new String(RawDataUtil.decompress(base64), 
Charset.forName("UTF-8"));
+    }
+    
+    @Test
+    public void extract() throws IOException, OpenXML4JException, XmlException 
{
+        Biff8EncryptionKey.setCurrentUserPassword(password);
+        File f = sampleDir.getFile(file);
+        POITextExtractor te = ExtractorFactory.createExtractor(f);
+        String actual = te.getText().trim();
+        assertEquals(expected, actual);
+        te.close();
+    }
+    
+    @Test
+    public void changePassword() throws IOException, OpenXML4JException, 
XmlException {
+        newPassword("test");
+    }
+    
     @Test
-    public void extract() throws IOException {
+    public void removePassword() throws IOException, OpenXML4JException, 
XmlException {
+        newPassword(null);
+    }
+    
+    public void newPassword(String newPass) throws IOException, 
OpenXML4JException, XmlException {
         Biff8EncryptionKey.setCurrentUserPassword(password);
-        HWPFDocument docD = HWPFTestDataSamples.openSampleFile(file);
-        WordExtractor we = new WordExtractor(docD);
-        String actual = we.getText().trim();
+        File f = sampleDir.getFile(file);
+        POIOLE2TextExtractor te1 = 
(POIOLE2TextExtractor)ExtractorFactory.createExtractor(f);
+        Biff8EncryptionKey.setCurrentUserPassword(newPass);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        POIDocument doc = te1.getDocument();
+        doc.write(bos);
+        doc.close();
+        te1.close();
+        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+        POITextExtractor te2 = ExtractorFactory.createExtractor(bis);
+        String actual = te2.getText().trim();
         assertEquals(expected, actual);
-        we.close();
-        docD.close();
+        te2.close();
+    }
+
+    /** changing the encryption mode and key size in poor mans style - see 
comments below */
+    @Test
+    public void changeEncryption() throws IOException, OpenXML4JException, 
XmlException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Biff8EncryptionKey.setCurrentUserPassword(password);
+        File f = sampleDir.getFile(file);
+        POIOLE2TextExtractor te1 = 
(POIOLE2TextExtractor)ExtractorFactory.createExtractor(f);
+        // first remove encryption
+        Biff8EncryptionKey.setCurrentUserPassword(null);
+        POIDocument doc = te1.getDocument();
+        doc.write(bos);
+        doc.close();
+        te1.close();
+        // then use default setting, which is cryptoapi
+        String newPass = "newPass";
+        POIOLE2TextExtractor te2 = 
(POIOLE2TextExtractor)ExtractorFactory.createExtractor(new 
ByteArrayInputStream(bos.toByteArray()));
+        Biff8EncryptionKey.setCurrentUserPassword(newPass);
+        doc = te2.getDocument();
+        bos.reset();
+        doc.write(bos);
+        doc.close();
+        te2.close();
+        // and finally update cryptoapi setting
+        POIOLE2TextExtractor te3 = 
(POIOLE2TextExtractor)ExtractorFactory.createExtractor(new 
ByteArrayInputStream(bos.toByteArray()));
+        doc = te3.getDocument();
+        // need to cache data (i.e. read all data) before changing the key size
+        if (doc instanceof HSLFSlideShowImpl) {
+            HSLFSlideShowImpl hss = (HSLFSlideShowImpl)doc;
+            hss.getPictureData();
+            hss.getDocumentSummaryInformation();
+        }
+        EncryptionInfo ei = doc.getEncryptionInfo();
+        assertNotNull(ei);
+        assertTrue(ei.getHeader() instanceof CryptoAPIEncryptionHeader);
+        assertEquals(0x28, 
((CryptoAPIEncryptionHeader)ei.getHeader()).getKeySize());
+        ((CryptoAPIEncryptionHeader)ei.getHeader()).setKeySize(0x78);
+        bos.reset();
+        doc.write(bos);
+        doc.close();
+        te3.close();
+        // check the setting
+        POIOLE2TextExtractor te4 = 
(POIOLE2TextExtractor)ExtractorFactory.createExtractor(new 
ByteArrayInputStream(bos.toByteArray()));
+        doc = te4.getDocument();
+        ei = doc.getEncryptionInfo();
+        assertNotNull(ei);
+        assertTrue(ei.getHeader() instanceof CryptoAPIEncryptionHeader);
+        assertEquals(0x78, 
((CryptoAPIEncryptionHeader)ei.getHeader()).getKeySize());
+        doc.close();
+        te4.close();
     }
 }

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
 Wed Jun 14 17:21:50 2017
@@ -40,8 +40,7 @@ import org.apache.poi.poifs.crypt.Chunke
 import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
 import org.apache.poi.poifs.crypt.Decryptor;
 import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
+import org.apache.poi.poifs.crypt.Encryptor;
 import org.apache.poi.util.BitField;
 import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.Internal;
@@ -55,8 +54,7 @@ import org.apache.poi.util.LittleEndianB
 @Internal
 public class HSLFSlideShowEncrypted implements Closeable {
     DocumentEncryptionAtom dea;
-    CryptoAPIEncryptor enc = null;
-    CryptoAPIDecryptor dec = null;
+    EncryptionInfo _encryptionInfo;
 //    Cipher cipher = null;
     ChunkedCipherOutputStream cyos = null;
 
@@ -120,11 +118,15 @@ public class HSLFSlideShowEncrypted impl
         }
         assert(r instanceof DocumentEncryptionAtom);
         this.dea = (DocumentEncryptionAtom)r;
-        decryptInit();
 
         String pass = Biff8EncryptionKey.getCurrentUserPassword();
-        if(!dec.verifyPassword(pass != null ? pass : 
Decryptor.DEFAULT_PASSWORD)) {
-            throw new EncryptedPowerPointFileException("PowerPoint file is 
encrypted. The correct password needs to be set via 
Biff8EncryptionKey.setCurrentUserPassword()");
+        EncryptionInfo ei = getEncryptionInfo();
+        try {
+            if(!ei.getDecryptor().verifyPassword(pass != null ? pass : 
Decryptor.DEFAULT_PASSWORD)) {
+                throw new EncryptedPowerPointFileException("PowerPoint file is 
encrypted. The correct password needs to be set via 
Biff8EncryptionKey.setCurrentUserPassword()");
+            }
+        } catch (GeneralSecurityException e) {
+            throw new EncryptedPowerPointFileException(e);
         }
      }
 
@@ -132,24 +134,10 @@ public class HSLFSlideShowEncrypted impl
         return dea;
     }
 
-    protected void decryptInit() {
-        if (dec != null) {
-            return;
-        }
-        EncryptionInfo ei = dea.getEncryptionInfo();
-        dec = (CryptoAPIDecryptor)ei.getDecryptor();
-    }
-
-    protected void encryptInit() {
-        if (enc != null) {
-            return;
-        }
-        EncryptionInfo ei = dea.getEncryptionInfo();
-        enc = (CryptoAPIEncryptor)ei.getEncryptor();
+    protected EncryptionInfo getEncryptionInfo() {
+        return (dea != null) ? dea.getEncryptionInfo() : null;
     }
 
-
-
     protected OutputStream encryptRecord(OutputStream plainStream, int 
persistId, Record record) {
         boolean isPlain = (dea == null
             || record instanceof UserEditAtom
@@ -166,9 +154,8 @@ public class HSLFSlideShowEncrypted impl
                 return plainStream;
             }
 
-            encryptInit();
-
             if (cyos == null) {
+                Encryptor enc = getEncryptionInfo().getEncryptor();
                 enc.setChunkSize(-1);
                 cyos = enc.getDataStream(plainStream, 0);
             }
@@ -190,12 +177,12 @@ public class HSLFSlideShowEncrypted impl
             return;
         }
 
-        decryptInit();
+        Decryptor dec = getEncryptionInfo().getDecryptor();
         dec.setChunkSize(-1);
         LittleEndianByteArrayInputStream lei = new 
LittleEndianByteArrayInputStream(docstream, offset); // NOSONAR
         ChunkedCipherInputStream ccis = null;
         try {
-            ccis = dec.getDataStream(lei, docstream.length-offset, 0);
+            ccis = (ChunkedCipherInputStream)dec.getDataStream(lei, 
docstream.length-offset, 0);
             ccis.initCipherForBlock(persistId);
 
             // decrypt header and read length to be decrypted
@@ -217,7 +204,8 @@ public class HSLFSlideShowEncrypted impl
         // when reading the picture elements, each time a segment is read, the 
cipher needs
         // to be reset (usually done when calling Cipher.doFinal)
         LittleEndianByteArrayInputStream lei = new 
LittleEndianByteArrayInputStream(pictstream, offset);
-        ChunkedCipherInputStream ccis = dec.getDataStream(lei, len, 0);
+        Decryptor dec = getEncryptionInfo().getDecryptor();
+        ChunkedCipherInputStream ccis = 
(ChunkedCipherInputStream)dec.getDataStream(lei, len, 0);
         readFully(ccis, pictstream, offset, len);
         ccis.close();
         lei.close();
@@ -228,8 +216,6 @@ public class HSLFSlideShowEncrypted impl
             return;
         }
 
-        decryptInit();
-
         try {
             // decrypt header and read length to be decrypted
             decryptPicBytes(pictstream, offset, 8);
@@ -302,12 +288,11 @@ public class HSLFSlideShowEncrypted impl
             return;
         }
 
-        encryptInit();
-
         LittleEndianByteArrayOutputStream los = new 
LittleEndianByteArrayOutputStream(pictstream, offset); // NOSONAR
         ChunkedCipherOutputStream ccos = null;
 
         try {
+            Encryptor enc = getEncryptionInfo().getEncryptor();
             enc.setChunkSize(-1);
             ccos = enc.getDataStream(los, 0);
             int recInst = 
fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
@@ -396,11 +381,10 @@ public class HSLFSlideShowEncrypted impl
             // create password record
             if (dea == null) {
                 dea = new DocumentEncryptionAtom();
-                enc = null;
             }
-            encryptInit();
             EncryptionInfo ei = dea.getEncryptionInfo();
             byte salt[] = ei.getVerifier().getSalt();
+            Encryptor enc = getEncryptionInfo().getEncryptor();
             if (salt == null) {
                 enc.confirmPassword(password);
             } else {

Modified: 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
 (original)
+++ 
poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
 Wed Jun 14 17:21:50 2017
@@ -24,7 +24,6 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.GeneralSecurityException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -34,7 +33,6 @@ import java.util.NavigableMap;
 import java.util.TreeMap;
 
 import org.apache.poi.POIDocument;
-import org.apache.poi.hpsf.PropertySet;
 import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
 import org.apache.poi.hslf.exceptions.HSLFException;
 import org.apache.poi.hslf.record.CurrentUserAtom;
@@ -46,7 +44,7 @@ import org.apache.poi.hslf.record.Positi
 import org.apache.poi.hslf.record.Record;
 import org.apache.poi.hslf.record.RecordTypes;
 import org.apache.poi.hslf.record.UserEditAtom;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentEntry;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
@@ -731,44 +729,15 @@ public final class HSLFSlideShowImpl ext
         }
     }
 
-    /**
-     * For a given named property entry, either return it or null if
-     * if it wasn't found
-     *
-     * @param setName The property to read
-     * @return The value of the given property or null if it wasn't found.
-     */
-    @Override
-    protected PropertySet getPropertySet(String setName) {
-        DocumentEncryptionAtom dea = getDocumentEncryptionAtom();
-        return (dea == null)
-                ? super.getPropertySet(setName)
-                : super.getPropertySet(setName, dea.getEncryptionInfo());
-    }
 
-    /**
-     * Writes out the standard Documment Information Properties (HPSF)
-     *
-     * @param outFS          the POIFSFileSystem to write the properties into
-     * @param writtenEntries a list of POIFS entries to add the property names 
too
-     * @throws IOException if an error when writing to the
-     *                     {@link POIFSFileSystem} occurs
-     */
     @Override
-    protected void writeProperties(NPOIFSFileSystem outFS, List<String> 
writtenEntries) throws IOException {
-        super.writeProperties(outFS, writtenEntries);
+    public EncryptionInfo getEncryptionInfo() throws IOException {
         DocumentEncryptionAtom dea = getDocumentEncryptionAtom();
-        if (dea != null) {
-            CryptoAPIEncryptor enc = (CryptoAPIEncryptor) 
dea.getEncryptionInfo().getEncryptor();
-            try {
-                enc.getSummaryEntries(outFS.getRoot()); // ignore OutputStream
-            } catch (IOException e) {
-                throw e;
-            } catch (GeneralSecurityException e) {
-                throw new IOException(e);
-            }
-        }
+        return (dea != null) ? dea.getEncryptionInfo() : null;
     }
+
+    
+    
     
     /* ******************* adding methods follow ********************* */
 
@@ -893,6 +862,10 @@ public final class HSLFSlideShowImpl ext
         }
     }
 
+    @Override
+    protected String getEncryptedPropertyStreamName() {
+        return "EncryptedSummary";
+    }
 
     private static class BufAccessBAOS extends ByteArrayOutputStream {
         public byte[] getBuf() {

Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
URL: 
http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java?rev=1798722&r1=1798721&r2=1798722&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java 
(original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java Wed Jun 
14 17:21:50 2017
@@ -23,6 +23,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.GeneralSecurityException;
 
 import org.apache.poi.hpsf.DocumentSummaryInformation;
 import org.apache.poi.hpsf.SummaryInformation;
@@ -61,13 +62,18 @@ import org.apache.poi.hwpf.usermodel.Off
 import org.apache.poi.hwpf.usermodel.OfficeDrawingsImpl;
 import org.apache.poi.hwpf.usermodel.Range;
 import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
 import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.Entry;
 import org.apache.poi.poifs.filesystem.EntryUtils;
 import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
 
 /**
  *
@@ -322,6 +328,7 @@ public final class HWPFDocument extends
         _fields = new FieldsImpl(_fieldsTables);
     }
 
+    @Override
     @Internal
     public TextPieceTable getTextTable()
     {
@@ -340,6 +347,7 @@ public final class HWPFDocument extends
         return _dop;
     }
 
+    @Override
     public Range getOverallRange() {
         return new Range(0, _text.length(), this);
     }
@@ -348,6 +356,7 @@ public final class HWPFDocument extends
      * Returns the range which covers the whole of the document, but excludes
      * any headers and footers.
      */
+    @Override
     public Range getRange()
     {
         // // First up, trigger a full-recalculate
@@ -394,8 +403,9 @@ public final class HWPFDocument extends
         {
             int length = getFileInformationBlock()
                     .getSubdocumentTextStreamLength( previos );
-            if ( subdocument == previos )
+            if ( subdocument == previos ) {
                 return new Range( startCp, startCp + length, this );
+            }
             startCp += length;
         }
         throw new UnsupportedOperationException(
@@ -603,34 +613,52 @@ public final class HWPFDocument extends
      * @throws IOException If there is an unexpected IOException from the 
passed
      *         in OutputStream.
      */
+    @Override
     public void write(OutputStream out) throws IOException {
         NPOIFSFileSystem pfs = new NPOIFSFileSystem();
         write(pfs, true);
         pfs.writeFilesystem( out );
     }
-    private void write(NPOIFSFileSystem pfs, boolean copyOtherEntries) throws 
IOException {
-        // initialize our streams for writing.
-        HWPFFileSystem docSys = new HWPFFileSystem();
-        ByteArrayOutputStream wordDocumentStream = 
docSys.getStream(STREAM_WORD_DOCUMENT);
-        ByteArrayOutputStream tableStream = docSys.getStream(STREAM_TABLE_1);
-        //HWPFOutputStream dataStream = docSys.getStream("Data");
-        int tableOffset = 0;
 
-        // FileInformationBlock fib = (FileInformationBlock)_fib.clone();
+    private void write(NPOIFSFileSystem pfs, boolean copyOtherEntries) throws 
IOException {
         // clear the offsets and sizes in our FileInformationBlock.
         _fib.clearOffsetsSizes();
 
         // determine the FileInformationBLock size
         int fibSize = _fib.getSize();
-        fibSize  += POIFSConstants.SMALLER_BIG_BLOCK_SIZE -
-                (fibSize % POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
+        fibSize += POIFSConstants.SMALLER_BIG_BLOCK_SIZE - (fibSize % 
POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
+
+        // initialize our streams for writing.
+        HWPFFileSystem docSys = new HWPFFileSystem();
+        ByteArrayOutputStream wordDocumentStream = 
docSys.getStream(STREAM_WORD_DOCUMENT);
+        ByteArrayOutputStream tableStream = docSys.getStream(STREAM_TABLE_1);
 
         // preserve space for the FileInformationBlock because we will be 
writing
         // it after we write everything else.
         byte[] placeHolder = new byte[fibSize];
         wordDocumentStream.write(placeHolder);
         int mainOffset = wordDocumentStream.size();
+        int tableOffset = 0;
 
+        // write out EncryptionInfo
+        updateEncryptionInfo();
+        EncryptionInfo ei = getEncryptionInfo();
+        if (ei != null) {
+            byte buf[] = new byte[1000];
+            LittleEndianByteArrayOutputStream leos = new 
LittleEndianByteArrayOutputStream(buf, 0);
+            leos.writeShort(ei.getVersionMajor());
+            leos.writeShort(ei.getVersionMinor());
+            if (ei.getEncryptionMode() == EncryptionMode.cryptoAPI) {
+                leos.writeInt(ei.getEncryptionFlags());
+            }
+            
+            ((EncryptionRecord)ei.getHeader()).write(leos);
+            ((EncryptionRecord)ei.getVerifier()).write(leos);
+            tableStream.write(buf, 0, leos.getWriteIndex());
+            tableOffset += leos.getWriteIndex();
+            _fib.getFibBase().setLKey(tableOffset);
+        }
+        
         // write out the StyleSheet.
         _fib.setFcStshf(tableOffset);
         _ss.writeTo(tableStream);
@@ -869,13 +897,7 @@ public final class HWPFDocument extends
         _fib.setCbMac(wordDocumentStream.size());
 
         // make sure that the table, doc and data streams use big blocks.
-        byte[] mainBuf = wordDocumentStream.toByteArray();
-        if (mainBuf.length < 4096)
-        {
-            byte[] tempBuf = new byte[4096];
-            System.arraycopy(mainBuf, 0, tempBuf, 0, mainBuf.length);
-            mainBuf = tempBuf;
-        }
+        byte[] mainBuf = fillUp4096(wordDocumentStream);
 
         // Table1 stream will be used
         _fib.getFibBase().setFWhichTblStm( true );
@@ -884,97 +906,51 @@ public final class HWPFDocument extends
         //_fib.serialize(mainBuf, 0);
         _fib.writeTo(mainBuf, tableStream);
 
-        byte[] tableBuf = tableStream.toByteArray();
-        if (tableBuf.length < 4096)
-        {
-            byte[] tempBuf = new byte[4096];
-            System.arraycopy(tableBuf, 0, tempBuf, 0, tableBuf.length);
-            tableBuf = tempBuf;
-        }
-
-        byte[] dataBuf = _dataStream;
-        if (dataBuf == null)
-        {
-            dataBuf = new byte[4096];
-        }
-        if (dataBuf.length < 4096)
-        {
-            byte[] tempBuf = new byte[4096];
-            System.arraycopy(dataBuf, 0, tempBuf, 0, dataBuf.length);
-            dataBuf = tempBuf;
-        }
-
-        // Create a new document preserving order of entries / Update existing
-        boolean docWritten = false;
-        boolean dataWritten = false;
-        boolean objectPoolWritten = false;
-        boolean tableWritten = false;
-        boolean propertiesWritten = false;
-        for (Entry entry : getDirectory()) {
-            if ( entry.getName().equals( STREAM_WORD_DOCUMENT ) )
-            {
-                if ( !docWritten )
-                {
-                    write(pfs, mainBuf, STREAM_WORD_DOCUMENT);
-                    docWritten = true;
-                }
-            }
-            else if ( entry.getName().equals( STREAM_OBJECT_POOL ) )
-            {
-                if ( !objectPoolWritten )
-                {
-                    if ( copyOtherEntries ) {
-                        _objectPool.writeTo( pfs.getRoot() );
-                    } else {
-                        // Object pool is already there, no need to change/copy
-                    }
-                    objectPoolWritten = true;
-                }
-            }
-            else if ( entry.getName().equals( STREAM_TABLE_0 )
-                    || entry.getName().equals( STREAM_TABLE_1 ) )
-            {
-                if ( !tableWritten )
-                {
-                    write(pfs, tableBuf, STREAM_TABLE_1);
-                    tableWritten = true;
-                }
-            }
-            else if ( entry.getName().equals(
-                    SummaryInformation.DEFAULT_STREAM_NAME )
-                    || entry.getName().equals(
-                            DocumentSummaryInformation.DEFAULT_STREAM_NAME ) )
-            {
-                if ( !propertiesWritten )
-                {
-                    writeProperties( pfs );
-                    propertiesWritten = true;
-                }
-            }
-            else if ( entry.getName().equals( STREAM_DATA ) )
-            {
-                if ( !dataWritten )
-                {
-                    write(pfs, dataBuf, STREAM_DATA);
-                    dataWritten = true;
-                }
-            }
-            else if ( copyOtherEntries )
-            {
-                EntryUtils.copyNodeRecursively( entry, pfs.getRoot() );
-            }
-        }
-
-        if ( !docWritten )
+        byte[] tableBuf = fillUp4096(tableStream);
+        byte[] dataBuf = fillUp4096(_dataStream);
+        
+        // Create a new document - ignoring the order of the old entries
+        if (ei == null) {
             write(pfs, mainBuf, STREAM_WORD_DOCUMENT);
-        if ( !tableWritten )
             write(pfs, tableBuf, STREAM_TABLE_1);
-        if ( !propertiesWritten )
-            writeProperties( pfs );
-        if ( !dataWritten )
             write(pfs, dataBuf, STREAM_DATA);
-        if ( !objectPoolWritten && copyOtherEntries )
-            _objectPool.writeTo( pfs.getRoot() );
+        } else {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+            encryptBytes(mainBuf, FIB_BASE_LEN, bos);
+            write(pfs, bos.toByteArray(), STREAM_WORD_DOCUMENT);
+            bos.reset();
+            encryptBytes(tableBuf, _fib.getFibBase().getLKey(), bos);
+            write(pfs, bos.toByteArray(), STREAM_TABLE_1);
+            bos.reset();
+            encryptBytes(dataBuf, 0, bos);
+            write(pfs, bos.toByteArray(), STREAM_DATA);
+            bos.reset();
+        }
+
+        writeProperties( pfs );
+        
+        if ( copyOtherEntries && ei == null ) {
+            // For encrypted files:
+            // The ObjectPool storage MUST NOT be present and if the file 
contains OLE objects, the storage
+            // objects for the OLE objects MUST be stored in the Data stream 
as specified in sprmCPicLocation.
+            DirectoryNode newRoot = pfs.getRoot();
+            _objectPool.writeTo( newRoot );
+        
+            for (Entry entry : getDirectory()) {
+                String entryName = entry.getName(); 
+                if ( !(
+                    STREAM_WORD_DOCUMENT.equals(entryName) ||
+                    STREAM_TABLE_0.equals(entryName) ||
+                    STREAM_TABLE_1.equals(entryName) ||
+                    STREAM_DATA.equals(entryName) ||
+                    STREAM_OBJECT_POOL.equals(entryName) ||
+                    SummaryInformation.DEFAULT_STREAM_NAME.equals(entryName) ||
+                    
DocumentSummaryInformation.DEFAULT_STREAM_NAME.equals(entryName)
+                ) ) {
+                    EntryUtils.copyNodeRecursively( entry, newRoot );
+                }
+            }
+        }
 
         /*
          * since we updated all references in FIB and etc, using new arrays to
@@ -984,6 +960,43 @@ public final class HWPFDocument extends
         this._tableStream = tableStream.toByteArray();
         this._dataStream = dataBuf;
     }
+        
+    private void encryptBytes(byte[] plain, int encryptOffset, OutputStream 
bos) throws IOException {
+        try {
+            EncryptionInfo ei = getEncryptionInfo();
+            Encryptor enc = ei.getEncryptor();
+            enc.setChunkSize(RC4_REKEYING_INTERVAL);
+            ChunkedCipherOutputStream os = enc.getDataStream(bos, 0);
+            if (encryptOffset > 0) {
+                os.writePlain(plain, 0, encryptOffset);
+            }
+            os.write(plain, encryptOffset, plain.length-encryptOffset);
+            os.close();
+        } catch (GeneralSecurityException e) {
+            throw new IOException(e);
+        }
+    }
+    
+    private static byte[] fillUp4096(byte[] buf) {
+        if (buf == null) {
+            return new byte[4096];
+        } else if (buf.length < 4096) {
+            ByteArrayOutputStream bos = new ByteArrayOutputStream(4096);
+            bos.write(buf, 0, buf.length);
+            return fillUp4096(bos);
+        } else {
+            return buf;
+        }
+    }
+    
+    private static byte[] fillUp4096(ByteArrayOutputStream bos) {
+        int fillSize = 4096 - bos.size();
+        if (fillSize > 0) {
+            bos.write(new byte[fillSize], 0, fillSize);
+        }
+        return bos.toByteArray();
+    }
+    
     private static void write(NPOIFSFileSystem pfs, byte[] data, String name) 
throws IOException {
         pfs.createOrUpdateDocument(new ByteArrayInputStream(data), name);
     }
@@ -1009,9 +1022,8 @@ public final class HWPFDocument extends
                 list.getLFOData() );
     }
 
-  public void delete(int start, int length)
-  {
-    Range r = new Range(start, start + length, this);
-    r.delete();
-  }
-}
+    public void delete(int start, int length) {
+        Range r = new Range(start, start + length, this);
+        r.delete();
+    }
+}
\ No newline at end of file



---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to