From 2ceb5ff55346ee97c3541c9e7bcd8bc5847be9b4 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Wed, 17 Mar 2021 22:51:58 +0000 Subject: #65192 - Allow change of EncryptionMode git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1887764 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/poi/POIDocument.java | 10 ++--- .../apache/poi/hssf/usermodel/HSSFWorkbook.java | 42 ++++++++++++++++++++ .../poi/poifs/crypt/tests/TestHxxFEncryption.java | 45 ++++++++++++++++++++++ 3 files changed, 91 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java index 519da97e3a..e2f613af64 100644 --- a/src/java/org/apache/poi/POIDocument.java +++ b/src/java/org/apache/poi/POIDocument.java @@ -271,7 +271,8 @@ public abstract class POIDocument implements Closeable { */ protected void writeProperties(POIFSFileSystem outFS, List 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 @@ public abstract class POIDocument implements Closeable { return; } + // Only CryptoAPI encryption supports encrypted property sets + // create empty document summary writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, newDocumentSummaryInformation(), outFS); @@ -289,11 +292,6 @@ public abstract class POIDocument implements Closeable { 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 d6e5c6329f..bd86c2857f 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 final class HSSFWorkbook extends POIDocument implements Workbook { 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 548e12ced9..bf5aebb590 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.apache.poi.POIDataSamples.getSpreadSheetInstance; 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 @@ class TestHxxFEncryption { 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()); + } + } } -- cgit v1.2.3