Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 3C34E200BA9 for ; Sun, 9 Oct 2016 06:43:25 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 3AB0E160AEF; Sun, 9 Oct 2016 04:43:25 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id D5DE9160ADF for ; Sun, 9 Oct 2016 06:43:23 +0200 (CEST) Received: (qmail 98284 invoked by uid 500); 9 Oct 2016 04:43:23 -0000 Mailing-List: contact commits-help@poi.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@poi.apache.org Delivered-To: mailing list commits@poi.apache.org Received: (qmail 98271 invoked by uid 99); 9 Oct 2016 04:43:23 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd4-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 09 Oct 2016 04:43:22 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd4-us-west.apache.org (ASF Mail Server at spamd4-us-west.apache.org) with ESMTP id 91A08C0405 for ; Sun, 9 Oct 2016 04:43:22 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd4-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -1.199 X-Spam-Level: X-Spam-Status: No, score=-1.199 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd4-us-west.apache.org [10.40.0.11]) (amavisd-new, port 10024) with ESMTP id D2duYLei6BMp for ; Sun, 9 Oct 2016 04:43:17 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with ESMTP id 8966E5FBDB for ; Sun, 9 Oct 2016 04:43:17 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id CB4ADE0069 for ; Sun, 9 Oct 2016 04:43:16 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id D1B413A0A3E for ; Sun, 9 Oct 2016 04:43:15 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1763943 - in /poi/trunk/src/ooxml: java/org/apache/poi/xssf/streaming/ testcases/org/apache/poi/poifs/crypt/ testcases/org/apache/poi/xssf/streaming/ Date: Sun, 09 Oct 2016 04:43:15 -0000 To: commits@poi.apache.org From: onealj@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20161009044315.D1B413A0A3E@svn01-us-west.apache.org> archived-at: Sun, 09 Oct 2016 04:43:25 -0000 Author: onealj Date: Sun Oct 9 04:43:14 2016 New Revision: 1763943 URL: http://svn.apache.org/viewvc?rev=1763943&view=rev Log: bug 60153: encrypt SXSSF temporary files; patch from PJ Fanning Added: 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/xssf/streaming/GZIPSheetDataWriter.java poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSecureTempZip.java Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/GZIPSheetDataWriter.java URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/GZIPSheetDataWriter.java?rev=1763943&r1=1763942&r2=1763943&view=diff ============================================================================== --- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/GZIPSheetDataWriter.java (original) +++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/GZIPSheetDataWriter.java Sun Oct 9 04:43:14 2016 @@ -24,8 +24,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStreamWriter; -import java.io.Writer; +import java.io.OutputStream; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -56,22 +55,14 @@ public class GZIPSheetDataWriter extends return TempFile.createTempFile("poi-sxssf-sheet-xml", ".gz"); } - /** - * @return a wrapped instance of GZIPOutputStream - */ @Override - public Writer createWriter(File fd)throws IOException { - return new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(fd)), "UTF-8"); + protected InputStream decorateInputStream(FileInputStream fis) throws IOException { + return new GZIPInputStream(fis); } - - /** - * @return a GZIPInputStream stream to read the compressed temp file - */ @Override - public InputStream getWorksheetXMLInputStream() throws IOException { - File fd = getTempFile(); - return new GZIPInputStream(new FileInputStream(fd)); + protected OutputStream decorateOutputStream(FileOutputStream fos) throws IOException { + return new GZIPOutputStream(fos); } } Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java?rev=1763943&r1=1763942&r2=1763943&view=diff ============================================================================== --- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java (original) +++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SXSSFWorkbook.java Sun Oct 9 04:43:14 2016 @@ -35,6 +35,8 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.util.ZipEntrySource; +import org.apache.poi.openxml4j.util.ZipFileZipEntrySource; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.formula.udf.UDFFinder; import org.apache.poi.ss.usermodel.CellStyle; @@ -46,6 +48,7 @@ import org.apache.poi.ss.usermodel.Pictu import org.apache.poi.ss.usermodel.Row.MissingCellPolicy; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.util.Internal; import org.apache.poi.util.NotImplemented; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -288,6 +291,14 @@ public class SXSSFWorkbook implements Wo } /** + * Get whether temp files should be compressed. + * + * @return whether to compress temp files + */ + public boolean isCompressTempFiles() { + return _compressTmpFiles; + } + /** * Set whether temp files should be compressed. *

* SXSSF writes sheet data in temporary files (a temp file per-sheet) @@ -300,11 +311,16 @@ public class SXSSFWorkbook implements Wo *

* @param compress whether to compress temp files */ - public void setCompressTempFiles(boolean compress){ + public void setCompressTempFiles(boolean compress) { _compressTmpFiles = compress; } + + @Internal + protected SharedStringsTable getSharedStringSource() { + return _sharedStringSource; + } - SheetDataWriter createSheetDataWriter() throws IOException { + protected SheetDataWriter createSheetDataWriter() throws IOException { if(_compressTmpFiles) { return new GZIPSheetDataWriter(_sharedStringSource); } @@ -353,21 +369,19 @@ public class SXSSFWorkbook implements Wo return null; } - private void injectData(File zipfile, OutputStream out) throws IOException + protected void injectData(ZipEntrySource zipEntrySource, OutputStream out) throws IOException { - // don't use ZipHelper.openZipFile here - see #59743 - ZipFile zip = new ZipFile(zipfile); try { ZipOutputStream zos = new ZipOutputStream(out); try { - Enumeration en = zip.entries(); + Enumeration en = zipEntrySource.getEntries(); while (en.hasMoreElements()) { ZipEntry ze = en.nextElement(); zos.putNextEntry(new ZipEntry(ze.getName())); - InputStream is = zip.getInputStream(ze); + InputStream is = zipEntrySource.getInputStream(ze); XSSFSheet xSheet=getSheetFromZipEntryName(ze.getName()); if(xSheet!=null) { @@ -396,7 +410,7 @@ public class SXSSFWorkbook implements Wo } finally { - zip.close(); + zipEntrySource.close(); } } private static void copyStream(InputStream in, OutputStream out) throws IOException { @@ -945,7 +959,8 @@ public class SXSSFWorkbook implements Wo } //Substitute the template entries with the generated sheet data files - injectData(tmplFile, stream); + final ZipEntrySource source = new ZipFileZipEntrySource(new ZipFile(tmplFile)); + injectData(source, stream); } finally { Modified: poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java?rev=1763943&r1=1763942&r2=1763943&view=diff ============================================================================== --- poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java (original) +++ poi/trunk/src/ooxml/java/org/apache/poi/xssf/streaming/SheetDataWriter.java Sun Oct 9 04:43:14 2016 @@ -25,6 +25,7 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Iterator; @@ -69,7 +70,7 @@ public class SheetDataWriter { _out = createWriter(_fd); } - public SheetDataWriter(SharedStringsTable sharedStringsTable) throws IOException{ + public SheetDataWriter(SharedStringsTable sharedStringsTable) throws IOException { this(); this._sharedStringSource = sharedStringsTable; } @@ -90,8 +91,23 @@ public class SheetDataWriter { * * @param fd the file to write to */ - public Writer createWriter(File fd)throws IOException { - return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fd), "UTF-8")); + public Writer createWriter(File fd) throws IOException { + final OutputStream decorated = decorateOutputStream(new FileOutputStream(fd)); + return new BufferedWriter(new OutputStreamWriter(decorated, "UTF-8")); + } + + /** + * Override this to translate (such as encrypt or compress) the file output stream + * as it is being written to disk. + * The default behavior is to to pass the stream through unmodified. + * + * @param fos the stream to decorate + * @return a decorated stream + * @throws IOException + * @see #decorateInputStream(FileInputStream) + */ + protected OutputStream decorateOutputStream(FileOutputStream fos) throws IOException { + return fos; } /** @@ -112,7 +128,21 @@ public class SheetDataWriter { */ public InputStream getWorksheetXMLInputStream() throws IOException { File fd = getTempFile(); - return new FileInputStream(fd); + return decorateInputStream(new FileInputStream(fd)); + } + + /** + * Override this to translate (such as decrypt or expand) the file input stream + * as it is being read from disk. + * The default behavior is to to pass the stream through unmodified. + * + * @param fis the stream to decorate + * @return a decorated stream + * @throws IOException + * @see #decorateOutputStream(FileOutputStream) + */ + protected InputStream decorateInputStream(FileInputStream fis) throws IOException { + return fis; } public int getNumberOfFlushedRows() { Added: 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=1763943&view=auto ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java (added) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AesZipFileZipEntrySource.java Sun Oct 9 04:43:14 2016 @@ -0,0 +1,135 @@ +/* ==================================================================== + 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.poifs.crypt; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.poi.openxml4j.util.ZipEntrySource; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.TempFile; + +public class AesZipFileZipEntrySource implements ZipEntrySource { + final File tmpFile; + final ZipFile zipFile; + final Cipher ci; + boolean closed; + + public AesZipFileZipEntrySource(File tmpFile, Cipher ci) throws IOException { + this.tmpFile = tmpFile; + this.zipFile = new ZipFile(tmpFile); + this.ci = ci; + this.closed = false; + } + + /** + * Note: the file sizes are rounded up to the next cipher block size, + * so don't rely on file sizes of these custom encrypted zip file entries! + */ + public Enumeration getEntries() { + return zipFile.entries(); + } + + @SuppressWarnings("resource") + public InputStream getInputStream(ZipEntry entry) throws IOException { + InputStream is = zipFile.getInputStream(entry); + return new CipherInputStream(is, ci); + } + + @Override + public void close() throws IOException { + zipFile.close(); + tmpFile.delete(); + closed = true; + } + + @Override + public boolean isClosed() { + return closed; + } + + public static ZipEntrySource createZipEntrySource(InputStream is) throws IOException, GeneralSecurityException { + // generate session key + SecureRandom sr = new SecureRandom(); + byte[] ivBytes = new byte[16], keyBytes = new byte[16]; + sr.nextBytes(ivBytes); + sr.nextBytes(keyBytes); + final File tmpFile = TempFile.createTempFile("protectedXlsx", ".zip"); + copyToFile(is, tmpFile, CipherAlgorithm.aes128, keyBytes, ivBytes); + IOUtils.closeQuietly(is); + return fileToSource(tmpFile, CipherAlgorithm.aes128, keyBytes, ivBytes); + } + + private static void copyToFile(InputStream is, File tmpFile, CipherAlgorithm cipherAlgorithm, byte keyBytes[], byte ivBytes[]) throws IOException, GeneralSecurityException { + SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId); + Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, "PKCS5Padding"); + + ZipInputStream zis = new ZipInputStream(is); + FileOutputStream fos = new FileOutputStream(tmpFile); + ZipOutputStream zos = new ZipOutputStream(fos); + + ZipEntry ze; + while ((ze = zis.getNextEntry()) != null) { + // the cipher output stream pads the data, therefore we can't reuse the ZipEntry with set sizes + // as those will be validated upon close() + ZipEntry zeNew = new ZipEntry(ze.getName()); + zeNew.setComment(ze.getComment()); + zeNew.setExtra(ze.getExtra()); + zeNew.setTime(ze.getTime()); + // zeNew.setMethod(ze.getMethod()); + zos.putNextEntry(zeNew); + FilterOutputStream fos2 = new FilterOutputStream(zos){ + // don't close underlying ZipOutputStream + public void close() {} + }; + CipherOutputStream cos = new CipherOutputStream(fos2, ciEnc); + IOUtils.copy(zis, cos); + cos.close(); + fos2.close(); + zos.closeEntry(); + zis.closeEntry(); + } + zos.close(); + fos.close(); + zis.close(); + } + + private static ZipEntrySource fileToSource(File tmpFile, CipherAlgorithm cipherAlgorithm, byte keyBytes[], byte ivBytes[]) throws ZipException, IOException { + SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId); + Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, "PKCS5Padding"); + return new AesZipFileZipEntrySource(tmpFile, ciDec); + } + +} + Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSecureTempZip.java URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSecureTempZip.java?rev=1763943&r1=1763942&r2=1763943&view=diff ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSecureTempZip.java (original) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSecureTempZip.java Sun Oct 9 04:43:14 2016 @@ -22,30 +22,14 @@ import static org.junit.Assert.assertTru import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; -import java.security.SecureRandom; -import java.util.Enumeration; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.spec.SecretKeySpec; import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.util.ZipEntrySource; import org.apache.poi.poifs.filesystem.POIFSFileSystem; -import org.apache.poi.util.IOUtils; -import org.apache.poi.util.TempFile; import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor; import org.apache.poi.xssf.usermodel.XSSFWorkbook; @@ -59,7 +43,6 @@ public class TestSecureTempZip { */ @Test public void protectedTempZip() throws IOException, GeneralSecurityException, XmlException, OpenXML4JException { - final File tmpFile = TempFile.createTempFile("protectedXlsx", ".zip"); File tikaProt = XSSFTestDataSamples.getSampleFile("protected_passtika.xlsx"); FileInputStream fis = new FileInputStream(tikaProt); POIFSFileSystem poifs = new POIFSFileSystem(fis); @@ -68,19 +51,11 @@ public class TestSecureTempZip { boolean passOk = dec.verifyPassword("tika"); assertTrue(passOk); - // generate session key - SecureRandom sr = new SecureRandom(); - byte[] ivBytes = new byte[16], keyBytes = new byte[16]; - sr.nextBytes(ivBytes); - sr.nextBytes(keyBytes); - // extract encrypted ooxml file and write to custom encrypted zip file InputStream is = dec.getDataStream(poifs); - copyToFile(is, tmpFile, CipherAlgorithm.aes128, keyBytes, ivBytes); - is.close(); // provide ZipEntrySource to poi which decrypts on the fly - ZipEntrySource source = fileToSource(tmpFile, CipherAlgorithm.aes128, keyBytes, ivBytes); + ZipEntrySource source = AesZipFileZipEntrySource.createZipEntrySource(is); // test the source OPCPackage opc = OPCPackage.open(source); @@ -102,84 +77,5 @@ public class TestSecureTempZip { source.close(); poifs.close(); fis.close(); - tmpFile.delete(); - } - - private void copyToFile(InputStream is, File tmpFile, CipherAlgorithm cipherAlgorithm, byte keyBytes[], byte ivBytes[]) throws IOException, GeneralSecurityException { - SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId); - Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, "PKCS5Padding"); - - ZipInputStream zis = new ZipInputStream(is); - FileOutputStream fos = new FileOutputStream(tmpFile); - ZipOutputStream zos = new ZipOutputStream(fos); - - ZipEntry ze; - while ((ze = zis.getNextEntry()) != null) { - // the cipher output stream pads the data, therefore we can't reuse the ZipEntry with set sizes - // as those will be validated upon close() - ZipEntry zeNew = new ZipEntry(ze.getName()); - zeNew.setComment(ze.getComment()); - zeNew.setExtra(ze.getExtra()); - zeNew.setTime(ze.getTime()); - // zeNew.setMethod(ze.getMethod()); - zos.putNextEntry(zeNew); - FilterOutputStream fos2 = new FilterOutputStream(zos){ - // don't close underlying ZipOutputStream - public void close() {} - }; - CipherOutputStream cos = new CipherOutputStream(fos2, ciEnc); - IOUtils.copy(zis, cos); - cos.close(); - fos2.close(); - zos.closeEntry(); - zis.closeEntry(); - } - zos.close(); - fos.close(); - zis.close(); - } - - private ZipEntrySource fileToSource(File tmpFile, CipherAlgorithm cipherAlgorithm, byte keyBytes[], byte ivBytes[]) throws ZipException, IOException { - SecretKeySpec skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId); - Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, "PKCS5Padding"); - ZipFile zf = new ZipFile(tmpFile); - return new AesZipFileZipEntrySource(zf, ciDec); - } - - static class AesZipFileZipEntrySource implements ZipEntrySource { - final ZipFile zipFile; - final Cipher ci; - boolean closed; - - AesZipFileZipEntrySource(ZipFile zipFile, Cipher ci) { - this.zipFile = zipFile; - this.ci = ci; - this.closed = false; - } - - /** - * Note: the file sizes are rounded up to the next cipher block size, - * so don't rely on file sizes of these custom encrypted zip file entries! - */ - public Enumeration getEntries() { - return zipFile.entries(); - } - - @SuppressWarnings("resource") - public InputStream getInputStream(ZipEntry entry) throws IOException { - InputStream is = zipFile.getInputStream(entry); - return new CipherInputStream(is, ci); - } - - @Override - public void close() throws IOException { - zipFile.close(); - closed = true; - } - - @Override - public boolean isClosed() { - return closed; - } } } Added: 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=1763943&view=auto ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java (added) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/xssf/streaming/TestSXSSFWorkbookWithCustomZipEntrySource.java Sun Oct 9 04:43:14 2016 @@ -0,0 +1,144 @@ +/* + * ==================================================================== + * 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 static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.SecretKeySpec; + +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.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.poi.xssf.usermodel.XSSFCell; +import org.apache.poi.xssf.usermodel.XSSFRow; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.junit.Test; + +/** + * This class tests that an SXSSFWorkbook can be written and read where all temporary disk I/O + * is encrypted, but the final saved workbook is not encrypted + */ +public final class TestSXSSFWorkbookWithCustomZipEntrySource { + + @Test + public void customZipEntrySource() throws IOException, GeneralSecurityException { + final String sheetName = "TestSheet1"; + final String cellValue = "customZipEntrySource"; + SXSSFWorkbookWithCustomZipEntrySource workbook = new SXSSFWorkbookWithCustomZipEntrySource(); + 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(); + workbook.dispose(); + XSSFWorkbook xwb = new XSSFWorkbook(new ByteArrayInputStream(os.toByteArray())); + 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(); + } + + static class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook { + + private static final POILogger logger = POILogFactory.getLogger(SXSSFWorkbookWithCustomZipEntrySource.class); + + @Override + public void write(OutputStream stream) throws IOException { + flushSheets(); + ByteArrayOutputStream os = new ByteArrayOutputStream(); + getXSSFWorkbook().write(os); + ZipEntrySource source = null; + try { + // provide ZipEntrySource to poi which decrypts on the fly + source = AesZipFileZipEntrySource.createZipEntrySource(new ByteArrayInputStream(os.toByteArray())); + injectData(source, stream); + } catch (GeneralSecurityException e) { + throw new IOException(e); + } finally { + source.close(); + } + } + + @Override + protected SheetDataWriter createSheetDataWriter() throws IOException { + //log values to ensure these values are accessible to subclasses + logger.log(POILogger.INFO, "isCompressTempFiles: " + isCompressTempFiles()); + logger.log(POILogger.INFO, "SharedStringSource: " + getSharedStringSource()); + return new SheetDataWriterWithDecorator(); + } + } + + static class SheetDataWriterWithDecorator extends SheetDataWriter { + final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128; + SecretKeySpec skeySpec; + byte[] ivBytes; + + public SheetDataWriterWithDecorator() throws IOException { + super(); + } + + void init() { + if(skeySpec == null) { + 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); + } + } + + @Override + protected OutputStream decorateOutputStream(FileOutputStream fos) { + init(); + Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, "PKCS5Padding"); + return new CipherOutputStream(fos, ciEnc); + } + + @Override + protected InputStream decorateInputStream(FileInputStream fis) { + Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, "PKCS5Padding"); + return new CipherInputStream(fis, ciDec); + } + + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org For additional commands, e-mail: commits-help@poi.apache.org