git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1763943 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_16_BETA1
@@ -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 SheetDataWriter { | |||
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); | |||
} | |||
} |
@@ -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.PictureData; | |||
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; | |||
@@ -287,6 +290,14 @@ public class SXSSFWorkbook implements Workbook { | |||
_randomAccessWindowSize = rowAccessWindowSize; | |||
} | |||
/** | |||
* 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. | |||
* <p> | |||
@@ -300,11 +311,16 @@ public class SXSSFWorkbook implements Workbook { | |||
* </p> | |||
* @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 Workbook { | |||
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<? extends ZipEntry> en = zip.entries(); | |||
Enumeration<? extends ZipEntry> 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 Workbook { | |||
} | |||
finally | |||
{ | |||
zip.close(); | |||
zipEntrySource.close(); | |||
} | |||
} | |||
private static void copyStream(InputStream in, OutputStream out) throws IOException { | |||
@@ -945,7 +959,8 @@ public class SXSSFWorkbook implements Workbook { | |||
} | |||
//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 | |||
{ |
@@ -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() { |
@@ -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<? extends ZipEntry> 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); | |||
} | |||
} | |||
@@ -22,30 +22,14 @@ import static org.junit.Assert.assertTrue; | |||
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<? extends ZipEntry> 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; | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
} |