#65192 - Allow change of EncryptionMode

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1887764 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java
index 519da97..e2f613a 100644
--- a/src/java/org/apache/poi/POIDocument.java
+++ b/src/java/org/apache/poi/POIDocument.java
@@ -271,7 +271,8 @@
      */
     protected void writeProperties(POIFSFileSystem outFS, List<String> writtenEntries) throws IOException {
         final EncryptionInfo ei = getEncryptionInfo();
-        final boolean encryptProps = (ei != null && ei.isDocPropsEncrypted());
+        Encryptor encGen = (ei == null) ? null : ei.getEncryptor();
+        final boolean encryptProps = (ei != null && ei.isDocPropsEncrypted() && encGen instanceof CryptoAPIEncryptor);
         try (POIFSFileSystem tmpFS = new POIFSFileSystem()) {
             final POIFSFileSystem fs = (encryptProps) ? tmpFS : outFS;
 
@@ -282,6 +283,8 @@
                 return;
             }
 
+            // Only CryptoAPI encryption supports encrypted property sets
+
             // create empty document summary
             writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, newDocumentSummaryInformation(), outFS);
 
@@ -289,11 +292,6 @@
             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);
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index d6e5c63..bd86c28 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -2255,4 +2255,46 @@
     public HSSFEvaluationWorkbook createEvaluationWorkbook() {
         return HSSFEvaluationWorkbook.create(this);
     }
+
+    /**
+     * Sets the encryption mode - if not set, defaults to {@link EncryptionMode#cryptoAPI}.
+     * Allowed modes are {@code null} = remove encryption - same as {@code Biff8EncryptionKey.setCurrentUserPassword(null)},
+     * {@link EncryptionMode#cryptoAPI}, {@link EncryptionMode#binaryRC4}, {@link EncryptionMode#xor}.
+     * Use {@link EncryptionMode#binaryRC4} for Libre Office, but better use OOXML format/encryption for real security.
+     *
+     * @param mode the encryption mode
+     */
+    public void setEncryptionMode(EncryptionMode mode) {
+        if (mode == null) {
+            Biff8EncryptionKey.setCurrentUserPassword(null);
+            return;
+        }
+        if (mode != EncryptionMode.xor && mode != EncryptionMode.binaryRC4 && mode != EncryptionMode.cryptoAPI) {
+            throw new IllegalArgumentException("Only xor, binaryRC4 and cryptoAPI are supported.");
+        }
+
+        FilePassRecord oldFPR = (FilePassRecord)getInternalWorkbook().findFirstRecordBySid(FilePassRecord.sid);
+        EncryptionMode oldMode = (oldFPR == null) ? null : oldFPR.getEncryptionInfo().getEncryptionMode();
+        if (mode == oldMode) {
+            return;
+        }
+
+        // Properties need to be cached, when we change the encryption mode
+        readProperties();
+        WorkbookRecordList wrl = getInternalWorkbook().getWorkbookRecordList();
+        if (oldFPR != null) {
+            wrl.remove(oldFPR);
+        }
+
+        FilePassRecord newFPR = new FilePassRecord(mode);
+        wrl.add(1, newFPR);
+    }
+
+    /**
+     * @return the encryption mode or {@code null} if unset
+     */
+    public EncryptionMode getEncryptionMode() {
+        FilePassRecord r = (FilePassRecord)getInternalWorkbook().findFirstRecordBySid(FilePassRecord.sid);
+        return (r == null) ? null : r.getEncryptionInfo().getEncryptionMode();
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/tests/TestHxxFEncryption.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/tests/TestHxxFEncryption.java
index 548e12c..bf5aebb 100644
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/tests/TestHxxFEncryption.java
+++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/tests/TestHxxFEncryption.java
@@ -23,23 +23,34 @@
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.stream.Stream;
 
+import org.apache.poi.hssf.model.WorkbookRecordList;
+import org.apache.poi.hssf.record.FilePassRecord;
+
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.POIDocument;
 import org.apache.poi.extractor.ExtractorFactory;
 import org.apache.poi.extractor.POITextExtractor;
+import org.apache.poi.hssf.record.Record;
 import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
 import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4EncryptionHeader;
 import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
 import org.apache.poi.poifs.storage.RawDataUtil;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -167,4 +178,38 @@
             Biff8EncryptionKey.setCurrentUserPassword(null);
         }
     }
+
+    @Test
+    public void changeEncryptionMode() throws IOException {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(10_000);
+
+        try (HSSFWorkbook wb = new HSSFWorkbook()) {
+            wb.createSheet().createRow(1).createCell(1).setCellValue("Test");
+            Biff8EncryptionKey.setCurrentUserPassword("test1");
+            wb.write(bos);
+        }
+
+        try (HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()))) {
+            assertEquals(EncryptionMode.cryptoAPI, wb.getEncryptionMode());
+            wb.setEncryptionMode(EncryptionMode.binaryRC4);
+            Biff8EncryptionKey.setCurrentUserPassword("test2");
+            bos.reset();
+            wb.write(bos);
+        }
+
+        try (HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()))) {
+            assertEquals(EncryptionMode.binaryRC4, wb.getEncryptionMode());
+            wb.setEncryptionMode(null);
+            bos.reset();
+            wb.write(bos);
+        }
+
+        assertNull(Biff8EncryptionKey.getCurrentUserPassword());
+
+        try (HSSFWorkbook wb = new HSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()))) {
+            assertNull(wb.getEncryptionMode());
+            wb.setEncryptionMode(null);
+            assertEquals("Test", wb.getSheetAt(0).getRow(1).getCell(1).getStringCellValue());
+        }
+    }
 }