This is an automated email from the ASF dual-hosted git repository.

fanningpj pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/poi.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 811eb4a4a8 support reading HSSFWorkbook with password passed as param 
(#1016)
811eb4a4a8 is described below

commit 811eb4a4a829ebacf45f092378d2f338b157841a
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Feb 20 15:06:33 2026 +0100

    support reading HSSFWorkbook with password passed as param (#1016)
    
    * support reading HSSFWorkbook with password passed as param
    
    Update HSSFWorkbook.java
    
    * npe issue
    
    * remove one use of Biff8EncryptionKey
    
    * extractor api
    
    * support changing passwords
---
 .../apache/poi/hssf/extractor/ExcelExtractor.java  |  14 +++
 .../org/apache/poi/hssf/record/RecordFactory.java  |  21 +++-
 .../poi/hssf/record/RecordFactoryInputStream.java  |  82 ++++++++++-----
 .../apache/poi/hssf/usermodel/HSSFWorkbook.java    | 113 ++++++++++++++++++++-
 .../poi/hssf/usermodel/HSSFWorkbookFactory.java    |  39 ++-----
 .../org/apache/poi/hssf/HSSFTestDataSamples.java   |   9 ++
 .../poi/hssf/extractor/TestExcelExtractor.java     |  15 +++
 .../poi/hssf/usermodel/TestHSSFWorkbook.java       |  38 +++++++
 8 files changed, 267 insertions(+), 64 deletions(-)

diff --git 
a/poi/src/main/java/org/apache/poi/hssf/extractor/ExcelExtractor.java 
b/poi/src/main/java/org/apache/poi/hssf/extractor/ExcelExtractor.java
index fc0e7aa43c..02315ffb29 100644
--- a/poi/src/main/java/org/apache/poi/hssf/extractor/ExcelExtractor.java
+++ b/poi/src/main/java/org/apache/poi/hssf/extractor/ExcelExtractor.java
@@ -72,10 +72,24 @@ public class ExcelExtractor implements 
POIOLE2TextExtractor, org.apache.poi.ss.e
         this(fs.getRoot());
     }
 
+    /**
+     * @since 6.0.0
+     */
+    public ExcelExtractor(POIFSFileSystem fs, char[] password) throws 
IOException {
+        this(fs.getRoot(), password);
+    }
+
     public ExcelExtractor(DirectoryNode dir) throws IOException {
         this(new HSSFWorkbook(dir, true));
     }
 
+    /**
+     * @since 6.0.0
+     */
+    public ExcelExtractor(DirectoryNode dir, char[] password) throws 
IOException {
+        this(new HSSFWorkbook(dir, true, password));
+    }
+
     private static final class CommandParseException extends Exception {
         public CommandParseException(String msg) {
             super(msg);
diff --git a/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java 
b/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java
index 08b49f5be4..f0f1c42f82 100644
--- a/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java
+++ b/poi/src/main/java/org/apache/poi/hssf/record/RecordFactory.java
@@ -172,19 +172,31 @@ public final class RecordFactory {
      * Create an array of records from an input stream
      *
      * @param in the InputStream from which the records will be obtained
-     *
      * @return a list of Records created from the InputStream
-     *
      * @throws org.apache.poi.util.RecordFormatException on error processing 
the InputStream
      */
     public static List<org.apache.poi.hssf.record.Record> 
createRecords(InputStream in) throws RecordFormatException {
+        return createRecords(in, null);
+    }
+
+    /**
+     * Create an array of records from an input stream
+     *
+     * @param in the InputStream from which the records will be obtained
+     * @param password in char array format (can be null)
+     * @return a list of Records created from the InputStream
+     * @throws org.apache.poi.util.RecordFormatException on error processing 
the InputStream
+     * @since 6.0.0
+     */
+    public static List<org.apache.poi.hssf.record.Record> createRecords(
+            InputStream in, char[] password) throws RecordFormatException {
 
         List<org.apache.poi.hssf.record.Record> records = new 
ArrayList<>(NUM_RECORDS);
 
-        RecordFactoryInputStream recStream = new RecordFactoryInputStream(in, 
true);
+        RecordFactoryInputStream recStream = new RecordFactoryInputStream(in, 
true, password);
 
         Record record;
-        while ((record = recStream.nextRecord())!=null) {
+        while ((record = recStream.nextRecord()) != null) {
             records.add(record);
 
             IOUtils.safelyAllocateCheck(records.size(), MAX_NUMBER_OF_RECORDS);
@@ -192,4 +204,5 @@ public final class RecordFactory {
 
         return records;
     }
+
 }
diff --git 
a/poi/src/main/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java 
b/poi/src/main/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java
index 46077788b4..a5cedea15d 100644
--- a/poi/src/main/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java
+++ b/poi/src/main/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java
@@ -102,19 +102,39 @@ public final class RecordFactoryInputStream {
             _lastRecord = rec;
         }
 
+        /**
+         * This method requires that you store the password in {@link 
Biff8EncryptionKey}.
+         * Since 6.0.0, we have overloaded methods where you can pass the 
password as a param instead.
+         */
         @SuppressWarnings({"squid:S2068"})
         public RecordInputStream createDecryptingStream(InputStream original) {
-            String userPassword = Biff8EncryptionKey.getCurrentUserPassword();
-            if (userPassword == null) {
-                userPassword = Decryptor.DEFAULT_PASSWORD;
-            }
+            return createDecryptingStream(original, (String) null);
+        }
 
+        /**
+         * @since 6.0.0
+         */
+        public RecordInputStream createDecryptingStream(InputStream original, 
char[] password) {
+            final String pwdString = password == null ? null : new 
String(password);
+            return createDecryptingStream(original, pwdString);
+        }
+
+        /**
+         * @since 6.0.0
+         */
+        public RecordInputStream createDecryptingStream(InputStream original, 
String password) {
+            if (password == null) {
+                password = Biff8EncryptionKey.getCurrentUserPassword();
+                if (password == null) {
+                    password = Decryptor.DEFAULT_PASSWORD;
+                }
+            }
             EncryptionInfo info = _filePassRec.getEncryptionInfo();
             try {
-                if (!info.getDecryptor().verifyPassword(userPassword)) {
+                if (!info.getDecryptor().verifyPassword(password)) {
                     throw new EncryptedDocumentException(
-                            (Decryptor.DEFAULT_PASSWORD.equals(userPassword) ? 
"Default" : "Supplied")
-                            + " password is invalid for 
salt/verifier/verifierHash");
+                            (Decryptor.DEFAULT_PASSWORD.equals(password) ? 
"Default" : "Supplied")
+                                    + " password is invalid for 
salt/verifier/verifierHash");
                 }
             } catch (GeneralSecurityException e) {
                 throw new EncryptedDocumentException(e);
@@ -176,17 +196,29 @@ public final class RecordFactoryInputStream {
 
     /**
      * @param in the InputStream to read from
-     * 
      * @param shouldIncludeContinueRecords caller can pass <code>false</code> 
if loose
      * {@link ContinueRecord}s should be skipped (this is sometimes useful in 
event based
      * processing).
      */
     public RecordFactoryInputStream(InputStream in, boolean 
shouldIncludeContinueRecords) {
+        this(in, shouldIncludeContinueRecords, null);
+    }
+
+    /**
+     * @param in the InputStream to read from
+     * @param shouldIncludeContinueRecords caller can pass <code>false</code> 
if loose
+     * @param password password in char array format (can be null)
+     * {@link ContinueRecord}s should be skipped (this is sometimes useful in 
event based
+     * processing).
+     * @since 6.0.0
+     */
+    public RecordFactoryInputStream(InputStream in, boolean 
shouldIncludeContinueRecords,
+                                    char[] password) {
         RecordInputStream rs = new RecordInputStream(in);
         List<org.apache.poi.hssf.record.Record> records = new ArrayList<>();
         StreamEncryptionInfo sei = new StreamEncryptionInfo(rs, records);
         if (sei.hasEncryption()) {
-            rs = sei.createDecryptingStream(in);
+            rs = sei.createDecryptingStream(in, password);
         } else {
             // typical case - non-encrypted stream
         }
@@ -201,22 +233,22 @@ public final class RecordFactoryInputStream {
         _lastRecord = sei.getLastRecord();
 
         /*
-        * How to recognise end of stream?
-        * In the best case, the underlying input stream (in) ends just after 
the last EOF record
-        * Usually however, the stream is padded with an arbitrary byte count.  
Excel and most apps
-        * reliably use zeros for padding and if this were always the case, 
this code could just
-        * skip all the (zero sized) records with sid==0.  However, bug 46987 
shows a file with
-        * non-zero padding that is read OK by Excel (Excel also fixes the 
padding).
-        *
-        * So to properly detect the workbook end of stream, this code has to 
identify the last
-        * EOF record.  This is not so easy because the worbook bof+eof pair do 
not bracket the
-        * whole stream.  The worksheets follow the workbook, but it is not 
easy to tell how many
-        * sheet sub-streams should be present.  Hence we are looking for an 
EOF record that is not
-        * immediately followed by a BOF record.  One extra complication is 
that bof+eof sub-
-        * streams can be nested within worksheet streams and it's not clear in 
these cases what
-        * record might follow any EOF record.  So we also need to keep track 
of the bof/eof
-        * nesting level.
-        */
+         * How to recognise end of stream?
+         * In the best case, the underlying input stream (in) ends just after 
the last EOF record
+         * Usually however, the stream is padded with an arbitrary byte count. 
 Excel and most apps
+         * reliably use zeros for padding and if this were always the case, 
this code could just
+         * skip all the (zero sized) records with sid==0.  However, bug 46987 
shows a file with
+         * non-zero padding that is read OK by Excel (Excel also fixes the 
padding).
+         *
+         * So to properly detect the workbook end of stream, this code has to 
identify the last
+         * EOF record.  This is not so easy because the workbook bof+eof pair 
do not bracket the
+         * whole stream.  The worksheets follow the workbook, but it is not 
easy to tell how many
+         * sheet sub-streams should be present.  Hence we are looking for an 
EOF record that is not
+         * immediately followed by a BOF record.  One extra complication is 
that bof+eof sub-
+         * streams can be nested within worksheet streams and it's not clear 
in these cases what
+         * record might follow any EOF record.  So we also need to keep track 
of the bof/eof
+         * nesting level.
+         */
         _bofDepth = sei.hasBOFRecord() ? 1 : 0;
         _lastRecordWasEOFLevelZero = false;
     }
diff --git a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java 
b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 1d2506b5bb..21cbb9ca6d 100644
--- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -205,6 +205,12 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
      */
     private final UDFFinder _udfFinder = new 
IndexedUDFFinder(AggregatingUDFFinder.DEFAULT);
 
+    /**
+     * The password used to decrypt this workbook when writing out.
+     * @since 6.0.0
+     */
+    private char[] outputPasswordChars;
+
     public static HSSFWorkbook create(InternalWorkbook book) {
         return new HSSFWorkbook(book);
     }
@@ -287,6 +293,27 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
         this(fs.getRoot(), fs, preserveNodes);
     }
 
+    /**
+     * Given a POI POIFSFileSystem object, read in its Workbook and populate
+     * the high and low level models.  If you're reading in a workbook... 
start here!
+     *
+     * @param fs            the POI filesystem that contains the Workbook 
stream.
+     * @param preserveNodes whether to preserve other nodes, such as
+     *                      macros.  This takes more memory, so only say yes 
if you
+     *                      need to. If set, will store all of the 
POIFSFileSystem
+     *                      in memory
+     * @param password      in char array format (can be null)
+     * @throws IOException if the stream cannot be read
+     * @throws IllegalStateException a number of runtime exceptions can be 
thrown, especially if there are problems with the
+     * input format
+     * @since 6.0.0
+     * @see POIFSFileSystem
+     */
+    public HSSFWorkbook(POIFSFileSystem fs, boolean preserveNodes, char[] 
password)
+            throws IOException {
+        this(fs.getRoot(), preserveNodes, password);
+    }
+
     public static String getWorkbookDirEntryName(DirectoryNode directory) {
         if (directory.hasEntryCaseInsensitive(WORKBOOK)) {
             return WORKBOOK;
@@ -359,6 +386,28 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
      */
     public HSSFWorkbook(DirectoryNode directory, boolean preserveNodes)
             throws IOException {
+        this(directory, preserveNodes, null);
+    }
+
+    /**
+     * given a POI POIFSFileSystem object, and a specific directory
+     * within it, read in its Workbook and populate the high and
+     * low level models.  If you're reading in a workbook...start here.
+     *
+     * @param directory     the POI filesystem directory to process from
+     * @param preserveNodes whether to preserve other nodes, such as
+     *                      macros.  This takes more memory, so only say yes 
if you
+     *                      need to. If set, will store all of the 
POIFSFileSystem
+     *                      in memory
+     * @param password      in char array format (can be null)
+     * @throws IOException if the stream cannot be read
+     * @throws IllegalStateException a number of runtime exceptions can be 
thrown, especially if there are problems with the
+     * input format
+     * @see POIFSFileSystem
+     * @since 6.0.0
+     */
+    public HSSFWorkbook(DirectoryNode directory, boolean preserveNodes, char[] 
password)
+            throws IOException {
         super(directory);
         String workbookName = getWorkbookDirEntryName(directory);
 
@@ -377,7 +426,7 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
         //  it happens to be spelled.
         InputStream stream = directory.createDocumentInputStream(workbookName);
 
-        List<org.apache.poi.hssf.record.Record> records = 
RecordFactory.createRecords(stream);
+        List<org.apache.poi.hssf.record.Record> records = 
RecordFactory.createRecords(stream, password);
 
         workbook = InternalWorkbook.createWorkbook(records);
         setPropertiesFromWorkbook(workbook);
@@ -420,6 +469,24 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
         this(s, true);
     }
 
+    /**
+     * Companion to HSSFWorkbook(POIFSFileSystem), this constructs the
+     * POI filesystem around your {@link InputStream}, including all nodes.
+     * <p>This calls {@link #HSSFWorkbook(InputStream, boolean)} with
+     * preserve nodes set to true.
+     *
+     * @throws IOException if the stream cannot be read
+     * @throws IllegalStateException a number of runtime exceptions can be 
thrown, especially if there are problems with the
+     * input format
+     * @see #HSSFWorkbook(InputStream, boolean)
+     * @see #HSSFWorkbook(POIFSFileSystem)
+     * @see POIFSFileSystem
+     * @since 6.0.0
+     */
+    public HSSFWorkbook(InputStream s, char[] password) throws IOException {
+        this(s, true, password);
+    }
+
     /**
      * Companion to HSSFWorkbook(POIFSFileSystem), this constructs the
      * POI filesystem around your {@link InputStream}.
@@ -441,9 +508,30 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
     }
 
     /**
-     * used internally to set the workbook properties.
+     * Companion to HSSFWorkbook(POIFSFileSystem), this constructs the
+     * POI filesystem around your {@link InputStream}.
+     *
+     * @param s             the POI filesystem that contains the Workbook 
stream.
+     * @param preserveNodes whether to preserve other nodes, such as
+     *                      macros.  This takes more memory, so only say yes 
if you
+     *                      need to.
+     * @param password      in char array format (can be null)
+     * @throws IOException if the stream cannot be read
+     * @throws IllegalStateException a number of runtime exceptions can be 
thrown, especially if there are problems with the
+     * input format
+     * @see POIFSFileSystem
+     * @see #HSSFWorkbook(POIFSFileSystem)
+     * @since 6.0.0
      */
+    @SuppressWarnings("resource")   // POIFSFileSystem always closes the stream
+    public HSSFWorkbook(InputStream s, boolean preserveNodes, char[] password)
+            throws IOException {
+        this(new POIFSFileSystem(s).getRoot(), preserveNodes, password);
+    }
 
+    /**
+     * used internally to set the workbook properties.
+     */
     private void setPropertiesFromWorkbook(InternalWorkbook book) {
         this.workbook = book;
 
@@ -1482,6 +1570,15 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
         }
     }
 
+    /**
+     * Set the password to be used to password protect the spreadsheet when we 
output the data.
+     * @param password as a char array (null is supported and means use @{link 
Biff8EncryptionKey}
+     *                 and no password if none set there)
+     * @since 6.0.0
+     */
+    public void setOutputPassword(final char[] password) {
+        this.outputPasswordChars = password;
+    }
 
     /**
      * Method getBytes - get the bytes of just the HSSF portions of the XLS 
file.
@@ -1496,7 +1593,14 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
         HSSFSheet[] sheets = getSheets();
         int nSheets = sheets.length;
 
-        updateEncryptionInfo();
+        String pwdString;
+        if (outputPasswordChars != null) {
+            pwdString = new String(outputPasswordChars);
+        } else {
+            // from POI 6.0.0, using Biff8EncryptionKey is discouraged
+            pwdString = Biff8EncryptionKey.getCurrentUserPassword();
+        }
+        updateEncryptionInfo(pwdString);
 
         // before getting the workbook size we must tell the sheets that
         // serialization is about to occur.
@@ -2329,12 +2433,11 @@ public final class HSSFWorkbook extends POIDocument 
implements Workbook {
     }
 
 
-    private void updateEncryptionInfo() {
+    private void updateEncryptionInfo(String password) {
         // 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) {
diff --git 
a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java 
b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java
index bda0dd6e8d..5dd5585130 100644
--- a/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java
+++ b/poi/src/main/java/org/apache/poi/hssf/usermodel/HSSFWorkbookFactory.java
@@ -21,7 +21,6 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 
-import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.FileMagic;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@@ -70,18 +69,8 @@ public class HSSFWorkbookFactory implements WorkbookProvider 
{
     @SuppressWarnings("java:S2093")
     @Override
     public HSSFWorkbook create(final DirectoryNode root, String password) 
throws IOException {
-        boolean passwordSet = false;
-        if (password != null) {
-            Biff8EncryptionKey.setCurrentUserPassword(password);
-            passwordSet = true;
-        }
-        try {
-            return new HSSFWorkbook(root, true);
-        } finally {
-            if (passwordSet) {
-                Biff8EncryptionKey.setCurrentUserPassword(null);
-            }
-        }
+        final char[] passwordChars = password == null ? null : 
password.toCharArray();
+        return new HSSFWorkbook(root, true, passwordChars);
     }
 
     @Override
@@ -98,24 +87,14 @@ public class HSSFWorkbookFactory implements 
WorkbookProvider {
     @Override
     @SuppressWarnings({"java:S2095","java:S2093"})
     public Workbook create(File file, String password, boolean readOnly) 
throws IOException {
-        boolean passwordSet = false;
-        if (password != null) {
-            Biff8EncryptionKey.setCurrentUserPassword(password);
-            passwordSet = true;
-        }
+        POIFSFileSystem fs = new POIFSFileSystem(file, readOnly);
         try {
-            POIFSFileSystem fs = new POIFSFileSystem(file, readOnly);
-            try {
-                return new HSSFWorkbook(fs, true);
-            } catch (RuntimeException e) {
-                // we need to close the filesystem if we encounter an 
exception to not leak file handles
-                fs.close();
-                throw e;
-            }
-        } finally {
-            if (passwordSet) {
-                Biff8EncryptionKey.setCurrentUserPassword(null);
-            }
+            final char[] passwordChars = password == null ? null : 
password.toCharArray();
+            return new HSSFWorkbook(fs, true, passwordChars);
+        } catch (RuntimeException e) {
+            // we need to close the filesystem if we encounter an exception to 
not leak file handles
+            fs.close();
+            throw e;
         }
     }
 }
diff --git a/poi/src/test/java/org/apache/poi/hssf/HSSFTestDataSamples.java 
b/poi/src/test/java/org/apache/poi/hssf/HSSFTestDataSamples.java
index 32fdc0fc52..74b8277c89 100644
--- a/poi/src/test/java/org/apache/poi/hssf/HSSFTestDataSamples.java
+++ b/poi/src/test/java/org/apache/poi/hssf/HSSFTestDataSamples.java
@@ -49,6 +49,15 @@ public final class HSSFTestDataSamples {
             throw new RuntimeException(e);
         }
     }
+
+    public static HSSFWorkbook openSampleWorkbook(String sampleFileName, 
char[] password) {
+        try (InputStream stream = _inst.openResourceAsStream(sampleFileName)){
+            return new HSSFWorkbook(stream, password);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /**
      * Writes a spreadsheet to a {@code ByteArrayOutputStream} and reads it 
back
      * from a {@code ByteArrayInputStream}.<p>
diff --git 
a/poi/src/test/java/org/apache/poi/hssf/extractor/TestExcelExtractor.java 
b/poi/src/test/java/org/apache/poi/hssf/extractor/TestExcelExtractor.java
index 8e27789f97..a9d3748997 100644
--- a/poi/src/test/java/org/apache/poi/hssf/extractor/TestExcelExtractor.java
+++ b/poi/src/test/java/org/apache/poi/hssf/extractor/TestExcelExtractor.java
@@ -46,6 +46,13 @@ final class TestExcelExtractor {
         return new ExcelExtractor(fs);
     }
 
+    private static ExcelExtractor createExtractor(String sampleFileName, 
String password) throws IOException {
+        File file = HSSFTestDataSamples.getSampleFile(sampleFileName);
+        POIFSFileSystem fs = new POIFSFileSystem(file);
+        final char[] passwordChars = password == null ? null : 
password.toCharArray();
+        return new ExcelExtractor(fs, passwordChars);
+    }
+
     @Test
     void testSimple() throws IOException {
         try (ExcelExtractor extractor = createExtractor("Simple.xls")) {
@@ -335,6 +342,14 @@ final class TestExcelExtractor {
 
     @Test
     void testPassword() throws IOException {
+        try (ExcelExtractor extractor = createExtractor("password.xls", 
"password")) {
+            String text = extractor.getText();
+            assertContains(text, "ZIP");
+        }
+    }
+
+    @Test
+    void testPasswordWithBiff8EncryptionKey() throws IOException {
         Biff8EncryptionKey.setCurrentUserPassword("password");
         try (ExcelExtractor extractor = createExtractor("password.xls")) {
             String text = extractor.getText();
diff --git 
a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java 
b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java
index 9d99627674..2b9452ccf4 100644
--- a/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java
+++ b/poi/src/test/java/org/apache/poi/hssf/usermodel/TestHSSFWorkbook.java
@@ -1249,6 +1249,44 @@ public final class TestHSSFWorkbook extends 
BaseTestWorkbook {
         }
     }
 
+    @Test
+    void testPassword() throws Exception {
+        try (HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(
+                "xor-encryption-abc.xls", "abc".toCharArray())) {
+            validateXorEncryptionDoc(wb);
+            try (UnsynchronizedByteArrayOutputStream baos = 
UnsynchronizedByteArrayOutputStream.builder().get()) {
+                // testing that when we write that no password is applied
+                wb.write(baos);
+                try (HSSFWorkbook wbOut = new 
HSSFWorkbook(baos.toInputStream())) {
+                    validateXorEncryptionDoc(wbOut);
+                }
+            }
+        }
+    }
+
+    @Test
+    void testChangePassword() throws Exception {
+        try (HSSFWorkbook wb = HSSFTestDataSamples.openSampleWorkbook(
+                "xor-encryption-abc.xls", "abc".toCharArray())) {
+            validateXorEncryptionDoc(wb);
+            String newPassword = "newPassword";
+            try (UnsynchronizedByteArrayOutputStream baos = 
UnsynchronizedByteArrayOutputStream.builder().get()) {
+                // testing that when we write that the newPassword is applied
+                wb.setOutputPassword(newPassword.toCharArray());
+                wb.write(baos);
+                try (HSSFWorkbook wbOut = new 
HSSFWorkbook(baos.toInputStream(), newPassword.toCharArray())) {
+                    validateXorEncryptionDoc(wbOut);
+                }
+            }
+        }
+    }
+
+    private void validateXorEncryptionDoc(HSSFWorkbook wb) {
+        HSSFSheet sheet = wb.getSheetAt(0);
+        double value = sheet.getRow(0).getCell(0).getNumericCellValue();
+        assertEquals(1.0, value);
+    }
+
     private static class WrappedStream extends FilterInputStream {
         private boolean closed;
 


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

Reply via email to