You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by on...@apache.org on 2016/10/09 12:58:46 UTC

svn commit: r1763969 - in /poi/trunk/src/ooxml: java/org/apache/poi/openxml4j/util/ testcases/org/apache/poi/poifs/crypt/ testcases/org/apache/poi/xssf/streaming/

Author: onealj
Date: Sun Oct  9 12:58:46 2016
New Revision: 1763969

URL: http://svn.apache.org/viewvc?rev=1763969&view=rev
Log:
bug 60153: patch from PJ Fanning to demonstrate that SXSSFWorkbook SheetDataWriter can write encrypted temporary files to disk and the workbook can be AES encrypted when written to disk for a fully secure disk environment

Added:
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource.java   (with props)
Modified:
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java?rev=1763969&r1=1763968&r2=1763969&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/util/ZipEntrySource.java Sun Oct  9 12:58:46 2016
@@ -16,6 +16,7 @@
 ==================================================================== */
 package org.apache.poi.openxml4j.util;
 
+import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Enumeration;
@@ -28,7 +29,7 @@ import java.util.zip.ZipEntry;
  *  needing to worry about ZipFile vs ZipInputStream
  *  being annoyingly very different.
  */
-public interface ZipEntrySource {
+public interface ZipEntrySource extends Closeable {
 	/**
 	 * Returns an Enumeration of all the Entries
 	 */
@@ -44,6 +45,7 @@ public interface ZipEntrySource {
 	 * Indicates we are done with reading, and 
 	 *  resources may be freed
 	 */
+	@Override
 	public void close() throws IOException;
 	
 	/**

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java?rev=1763969&r1=1763968&r2=1763969&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java Sun Oct  9 12:58:46 2016
@@ -69,8 +69,10 @@ public class AesZipFileZipEntrySource im
 
     @Override
     public void close() throws IOException {
-        zipFile.close();
-        tmpFile.delete();
+        if(!closed) {
+            zipFile.close();
+            tmpFile.delete();
+        }
         closed = true;
     }
     

Added: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource.java?rev=1763969&view=auto
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource.java (added)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource.java Sun Oct  9 12:58:46 2016
@@ -0,0 +1,49 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+package org.apache.poi.xssf.streaming;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.poi.xssf.streaming.TestSXSSFWorkbookWithCustomZipEntrySource.SXSSFWorkbookWithCustomZipEntrySource;
+import org.apache.poi.xssf.streaming.TestSXSSFWorkbookWithCustomZipEntrySource.SheetDataWriterWithDecorator;
+
+// a class to record a list of temporary files that are written to disk
+// afterwards, a test function can check whether these files were encrypted or not
+public class TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbookWithCustomZipEntrySource {
+
+    private final List<File> tempFiles = new ArrayList<File>();
+
+    List<File> getTempFiles() {
+        return new ArrayList<File>(tempFiles);
+    }
+    
+    @Override
+    protected SheetDataWriter createSheetDataWriter() throws IOException {
+        return new TempFileRecordingSheetDataWriterWithDecorator();
+    }
+
+    class TempFileRecordingSheetDataWriterWithDecorator extends SheetDataWriterWithDecorator {
+
+        TempFileRecordingSheetDataWriterWithDecorator() throws IOException {
+            super();
+            tempFiles.add(getTempFile());
+        }
+    }
+}
\ No newline at end of file

Propchange: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java?rev=1763969&r1=1763968&r2=1763969&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java Sun Oct  9 12:58:46 2016
@@ -19,10 +19,14 @@
 
 package org.apache.poi.xssf.streaming;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -30,19 +34,25 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
 import javax.crypto.CipherOutputStream;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
 import org.apache.poi.openxml4j.util.ZipEntrySource;
 import org.apache.poi.poifs.crypt.AesZipFileZipEntrySource;
 import org.apache.poi.poifs.crypt.ChainingMode;
 import org.apache.poi.poifs.crypt.CipherAlgorithm;
 import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.util.IOUtils;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
+import org.apache.poi.util.TempFile;
 import org.apache.poi.xssf.usermodel.XSSFCell;
 import org.apache.poi.xssf.usermodel.XSSFRow;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
@@ -54,11 +64,13 @@ import org.junit.Test;
  * is encrypted, but the final saved workbook is not encrypted
  */
 public final class TestSXSSFWorkbookWithCustomZipEntrySource {
-
+    
+    final String sheetName = "TestSheet1";
+    final String cellValue = "customZipEntrySource";
+    
+    // write an unencrypted workbook to disk, but any temporary files are encrypted
     @Test
-    public void customZipEntrySource() throws IOException, GeneralSecurityException {
-        final String sheetName = "TestSheet1";
-        final String cellValue = "customZipEntrySource";
+    public void customZipEntrySource() throws IOException {
         SXSSFWorkbookWithCustomZipEntrySource workbook = new SXSSFWorkbookWithCustomZipEntrySource();
         SXSSFSheet sheet1 = workbook.createSheet(sheetName);
         SXSSFRow row1 = sheet1.createRow(1);
@@ -77,6 +89,54 @@ public final class TestSXSSFWorkbookWith
         xwb.close();
     }
     
+    // write an encrypted workbook to disk, and encrypt any temporary files as well
+    @Test
+    public void customZipEntrySourceForWriteAndRead() throws IOException, GeneralSecurityException, InvalidFormatException {
+        SXSSFWorkbookWithCustomZipEntrySource workbook = new SXSSFWorkbookWithCustomZipEntrySource();
+        SXSSFSheet sheet1 = workbook.createSheet(sheetName);
+        SXSSFRow row1 = sheet1.createRow(1);
+        SXSSFCell cell1 = row1.createCell(1);
+        cell1.setCellValue(cellValue);
+        EncryptedTempData tempData = new EncryptedTempData();
+        workbook.write(tempData.getOutputStream());
+        workbook.close();
+        workbook.dispose();
+        ZipEntrySource zipEntrySource = AesZipFileZipEntrySource.createZipEntrySource(tempData.getInputStream());
+        tempData.dispose();
+        OPCPackage opc = OPCPackage.open(zipEntrySource);
+        XSSFWorkbook xwb = new XSSFWorkbook(opc);
+        zipEntrySource.close();
+        XSSFSheet xs1 = xwb.getSheetAt(0);
+        assertEquals(sheetName, xs1.getSheetName());
+        XSSFRow xr1 = xs1.getRow(1);
+        XSSFCell xc1 = xr1.getCell(1);
+        assertEquals(cellValue, xc1.getStringCellValue());
+        xwb.close();
+        opc.close();
+    }
+    
+    @Test
+    public void validateTempFilesAreEncrypted() throws IOException {
+        TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource workbook = new TempFileRecordingSXSSFWorkbookWithCustomZipEntrySource();
+        SXSSFSheet sheet1 = workbook.createSheet(sheetName);
+        SXSSFRow row1 = sheet1.createRow(1);
+        SXSSFCell cell1 = row1.createCell(1);
+        cell1.setCellValue(cellValue);
+        ByteArrayOutputStream os = new ByteArrayOutputStream(8192);
+        workbook.write(os);
+        workbook.close();
+        List<File> tempFiles = workbook.getTempFiles();
+        assertEquals(1, tempFiles.size());
+        File tempFile = tempFiles.get(0);
+        assertTrue("tempFile exists?", tempFile.exists());
+        byte[] data = IOUtils.toByteArray(new FileInputStream(tempFile));
+        String text = new String(data, UTF_8);
+        assertFalse(text.contains(sheetName));
+        assertFalse(text.contains(cellValue));
+        workbook.dispose();
+        assertFalse("tempFile deleted after dispose?", tempFile.exists());
+    }
+    
     static class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook {
 
         private static final POILogger logger = POILogFactory.getLogger(SXSSFWorkbookWithCustomZipEntrySource.class);
@@ -84,17 +144,19 @@ public final class TestSXSSFWorkbookWith
         @Override
         public void write(OutputStream stream) throws IOException {
             flushSheets();
-            ByteArrayOutputStream os = new ByteArrayOutputStream();
+            EncryptedTempData tempData = new EncryptedTempData();
+            OutputStream os = tempData.getOutputStream();
             getXSSFWorkbook().write(os);
+            os.close();
             ZipEntrySource source = null;
             try {
                 // provide ZipEntrySource to poi which decrypts on the fly
-                source = AesZipFileZipEntrySource.createZipEntrySource(new ByteArrayInputStream(os.toByteArray()));
+                source = AesZipFileZipEntrySource.createZipEntrySource(tempData.getInputStream());
                 injectData(source, stream);
             } catch (GeneralSecurityException e) {
                 throw new IOException(e);
             } finally {
-                source.close();
+                IOUtils.closeQuietly(source);
             }
         }
 
@@ -107,6 +169,7 @@ public final class TestSXSSFWorkbookWith
         }
     }
     
+    
     static class SheetDataWriterWithDecorator extends SheetDataWriter {
         final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
         SecretKeySpec skeySpec;
@@ -141,4 +204,37 @@ public final class TestSXSSFWorkbookWith
         }
         
     }
+    
+    // a class to save and read an AES-encrypted workbook
+    static class EncryptedTempData {
+        final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
+        final SecretKeySpec skeySpec;
+        final byte[] ivBytes;
+        final File tempFile;
+        
+        EncryptedTempData() throws IOException {
+            SecureRandom sr = new SecureRandom();
+            ivBytes = new byte[16];
+            byte[] keyBytes = new byte[16];
+            sr.nextBytes(ivBytes);
+            sr.nextBytes(keyBytes);
+            skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId);
+            tempFile = TempFile.createTempFile("poi-temp-data", ".tmp");
+        }
+
+        OutputStream getOutputStream() throws IOException {
+            Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, null);
+            return new CipherOutputStream(new FileOutputStream(tempFile), ciEnc);
+        }
+
+        InputStream getInputStream() throws IOException {
+            Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, null);
+            return new CipherInputStream(new FileInputStream(tempFile), ciDec);
+        }
+        
+        void dispose() {
+            tempFile.delete();
+        }
+    }
+    
 }



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