mirror of https://github.com/apache/poi.git
bug 60321: add examples with encrypted temp data. Patch from PJ Fanning.
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1768744 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
0a7a454082
commit
776621c3bb
|
@ -0,0 +1,147 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.crypt.examples;
|
||||||
|
|
||||||
|
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.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.TempFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example <code>ZipEntrySource</code> that has encrypted temp files to ensure that
|
||||||
|
* sensitive data is not stored in raw format on disk.
|
||||||
|
*/
|
||||||
|
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!
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Enumeration<? extends ZipEntry> getEntries() {
|
||||||
|
return zipFile.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream(ZipEntry entry) throws IOException {
|
||||||
|
InputStream is = zipFile.getInputStream(entry);
|
||||||
|
return new CipherInputStream(is, ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if(!closed) {
|
||||||
|
zipFile.close();
|
||||||
|
tmpFile.delete();
|
||||||
|
}
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isClosed() {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AesZipFileZipEntrySource 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
|
||||||
|
@Override
|
||||||
|
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 AesZipFileZipEntrySource 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.crypt.examples;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
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.poifs.crypt.ChainingMode;
|
||||||
|
import org.apache.poi.poifs.crypt.CipherAlgorithm;
|
||||||
|
import org.apache.poi.poifs.crypt.CryptoFunctions;
|
||||||
|
import org.apache.poi.util.TempFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EncryptedTempData can be used to buffer binary data in a secure way, by using encrypted temp files.
|
||||||
|
*/
|
||||||
|
public class EncryptedTempData {
|
||||||
|
final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
|
||||||
|
final SecretKeySpec skeySpec;
|
||||||
|
final byte[] ivBytes;
|
||||||
|
final File tempFile;
|
||||||
|
|
||||||
|
public 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream getOutputStream() throws IOException {
|
||||||
|
Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, null);
|
||||||
|
return new CipherOutputStream(new FileOutputStream(tempFile), ciEnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, null);
|
||||||
|
return new CipherInputStream(new FileInputStream(tempFile), ciDec);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.crypt.examples;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.poi.poifs.crypt.Decryptor;
|
||||||
|
import org.apache.poi.poifs.crypt.EncryptionInfo;
|
||||||
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
|
||||||
|
public class EncryptionUtils {
|
||||||
|
public static InputStream decrypt(final InputStream inputStream, final String pwd) throws Exception {
|
||||||
|
try {
|
||||||
|
POIFSFileSystem fs = new POIFSFileSystem(inputStream);
|
||||||
|
EncryptionInfo info = new EncryptionInfo(fs);
|
||||||
|
Decryptor d = Decryptor.getInstance(info);
|
||||||
|
if (!d.verifyPassword(pwd)) {
|
||||||
|
throw new RuntimeException("incorrect password");
|
||||||
|
}
|
||||||
|
return d.getDataStream(fs);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.examples.util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.apache.poi.util.TempFile;
|
||||||
|
|
||||||
|
public class TempFileUtils {
|
||||||
|
public static void checkTempFiles() throws IOException {
|
||||||
|
String tmpDir = System.getProperty(TempFile.JAVA_IO_TMPDIR) + "/poifiles";
|
||||||
|
File tempDir = new File(tmpDir);
|
||||||
|
if(tempDir.exists()) {
|
||||||
|
String[] tempFiles = tempDir.list();
|
||||||
|
if(tempFiles.length > 0) {
|
||||||
|
System.out.println("found files in poi temp dir " + tempDir.getAbsolutePath());
|
||||||
|
for(String filename : tempDir.list()) {
|
||||||
|
System.out.println("file: " + filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("unable to find poi temp dir");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.eventusermodel.examples;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.poi.crypt.examples.AesZipFileZipEntrySource;
|
||||||
|
import org.apache.poi.crypt.examples.EncryptionUtils;
|
||||||
|
import org.apache.poi.examples.util.TempFileUtils;
|
||||||
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.apache.poi.xssf.eventusermodel.XSSFReader;
|
||||||
|
import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example that loads a password protected workbook and counts the sheets.
|
||||||
|
* The example highlights how to do this in streaming way.
|
||||||
|
* <p><ul>
|
||||||
|
* <li>The example demonstrates that all temp files are removed.
|
||||||
|
* <li><code>AesZipFileZipEntrySource</code> is used to ensure that temp files are encrypted.
|
||||||
|
* </ul><p>
|
||||||
|
*/
|
||||||
|
public class LoadPasswordProtectedXlsxStreaming {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
if(args.length != 2) {
|
||||||
|
throw new Exception("Expected 2 params: filename and password");
|
||||||
|
}
|
||||||
|
TempFileUtils.checkTempFiles();
|
||||||
|
String filename = args[0];
|
||||||
|
String password = args[1];
|
||||||
|
FileInputStream fis = new FileInputStream(filename);
|
||||||
|
try {
|
||||||
|
InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
|
||||||
|
try {
|
||||||
|
printSheetCount(unencryptedStream);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(unencryptedStream);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(fis);
|
||||||
|
}
|
||||||
|
TempFileUtils.checkTempFiles();
|
||||||
|
} catch(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printSheetCount(final InputStream inputStream) throws Exception {
|
||||||
|
AesZipFileZipEntrySource source = AesZipFileZipEntrySource.createZipEntrySource(inputStream);
|
||||||
|
try {
|
||||||
|
OPCPackage pkg = OPCPackage.open(source);
|
||||||
|
try {
|
||||||
|
XSSFReader reader = new XSSFReader(pkg);
|
||||||
|
SheetIterator iter = (SheetIterator)reader.getSheetsData();
|
||||||
|
int count = 0;
|
||||||
|
while(iter.hasNext()) {
|
||||||
|
iter.next();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
System.out.println("sheet count: " + count);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(pkg);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.examples;
|
||||||
|
|
||||||
|
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.crypt.examples.AesZipFileZipEntrySource;
|
||||||
|
import org.apache.poi.crypt.examples.EncryptedTempData;
|
||||||
|
import org.apache.poi.openxml4j.util.ZipEntrySource;
|
||||||
|
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.xssf.streaming.SXSSFWorkbook;
|
||||||
|
import org.apache.poi.xssf.streaming.SheetDataWriter;
|
||||||
|
|
||||||
|
public class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook {
|
||||||
|
|
||||||
|
public SXSSFWorkbookWithCustomZipEntrySource() {
|
||||||
|
super(20);
|
||||||
|
setCompressTempFiles(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(OutputStream stream) throws IOException {
|
||||||
|
flushSheets();
|
||||||
|
EncryptedTempData tempData = new EncryptedTempData();
|
||||||
|
ZipEntrySource source = null;
|
||||||
|
try {
|
||||||
|
OutputStream os = tempData.getOutputStream();
|
||||||
|
try {
|
||||||
|
getXSSFWorkbook().write(os);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(os);
|
||||||
|
}
|
||||||
|
// provide ZipEntrySource to poi which decrypts on the fly
|
||||||
|
source = AesZipFileZipEntrySource.createZipEntrySource(tempData.getInputStream());
|
||||||
|
injectData(source, stream);
|
||||||
|
} catch (GeneralSecurityException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
} finally {
|
||||||
|
tempData.dispose();
|
||||||
|
IOUtils.closeQuietly(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SheetDataWriter createSheetDataWriter() throws IOException {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.examples;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
|
||||||
|
import org.apache.poi.crypt.examples.EncryptedTempData;
|
||||||
|
import org.apache.poi.examples.util.TempFileUtils;
|
||||||
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||||
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
|
import org.apache.poi.poifs.crypt.EncryptionInfo;
|
||||||
|
import org.apache.poi.poifs.crypt.EncryptionMode;
|
||||||
|
import org.apache.poi.poifs.crypt.Encryptor;
|
||||||
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.apache.poi.xssf.streaming.SXSSFCell;
|
||||||
|
import org.apache.poi.xssf.streaming.SXSSFRow;
|
||||||
|
import org.apache.poi.xssf.streaming.SXSSFSheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example that outputs a simple generated workbook that is password protected.
|
||||||
|
* The example highlights how to do this in streaming way.
|
||||||
|
* <p><ul>
|
||||||
|
* <li>The example demonstrates that all temp files are removed.
|
||||||
|
* <li><code>SXSSFWorkbookWithCustomZipEntrySource</code> extends SXSSFWorkbook to ensure temp files are encrypted.
|
||||||
|
* </ul><p>
|
||||||
|
*/
|
||||||
|
public class SavePasswordProtectedXlsx {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
if(args.length != 2) {
|
||||||
|
throw new Exception("Expected 2 params: filename and password");
|
||||||
|
}
|
||||||
|
TempFileUtils.checkTempFiles();
|
||||||
|
String filename = args[0];
|
||||||
|
String password = args[1];
|
||||||
|
SXSSFWorkbookWithCustomZipEntrySource wb = new SXSSFWorkbookWithCustomZipEntrySource();
|
||||||
|
try {
|
||||||
|
for(int i = 0; i < 10; i++) {
|
||||||
|
SXSSFSheet sheet = wb.createSheet("Sheet" + i);
|
||||||
|
for(int r = 0; r < 1000; r++) {
|
||||||
|
SXSSFRow row = sheet.createRow(r);
|
||||||
|
for(int c = 0; c < 100; c++) {
|
||||||
|
SXSSFCell cell = row.createCell(c);
|
||||||
|
cell.setCellValue("abcd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EncryptedTempData tempData = new EncryptedTempData();
|
||||||
|
try {
|
||||||
|
wb.write(tempData.getOutputStream());
|
||||||
|
save(tempData.getInputStream(), filename, password);
|
||||||
|
System.out.println("Saved " + filename);
|
||||||
|
} finally {
|
||||||
|
tempData.dispose();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
wb.close();
|
||||||
|
wb.dispose();
|
||||||
|
}
|
||||||
|
TempFileUtils.checkTempFiles();
|
||||||
|
} catch(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void save(final InputStream inputStream, final String filename, final String pwd)
|
||||||
|
throws InvalidFormatException, IOException, GeneralSecurityException {
|
||||||
|
try {
|
||||||
|
POIFSFileSystem fs = new POIFSFileSystem();
|
||||||
|
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
|
||||||
|
Encryptor enc = Encryptor.getInstance(info);
|
||||||
|
enc.confirmPassword(pwd);
|
||||||
|
OPCPackage opc = OPCPackage.open(inputStream);
|
||||||
|
try {
|
||||||
|
FileOutputStream fos = new FileOutputStream(filename);
|
||||||
|
try {
|
||||||
|
opc.save(enc.getDataStream(fs));
|
||||||
|
fs.writeFilesystem(fos);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(fos);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(opc);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.usermodel.examples;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.apache.poi.crypt.examples.AesZipFileZipEntrySource;
|
||||||
|
import org.apache.poi.crypt.examples.EncryptionUtils;
|
||||||
|
import org.apache.poi.examples.util.TempFileUtils;
|
||||||
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
|
import org.apache.poi.util.IOUtils;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An example that loads a password protected workbook and counts the sheets.
|
||||||
|
* <p><ul>
|
||||||
|
* <li>The example demonstrates that all temp files are removed.
|
||||||
|
* <li><code>AesZipFileZipEntrySource</code> is used to ensure that temp files are encrypted.
|
||||||
|
* </ul><p>
|
||||||
|
*/
|
||||||
|
public class LoadPasswordProtectedXlsx {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
try {
|
||||||
|
if(args.length != 2) {
|
||||||
|
throw new Exception("Expected 2 params: filename and password");
|
||||||
|
}
|
||||||
|
TempFileUtils.checkTempFiles();
|
||||||
|
String filename = args[0];
|
||||||
|
String password = args[1];
|
||||||
|
FileInputStream fis = new FileInputStream(filename);
|
||||||
|
try {
|
||||||
|
InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
|
||||||
|
try {
|
||||||
|
printSheetCount(unencryptedStream);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(unencryptedStream);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(fis);
|
||||||
|
}
|
||||||
|
TempFileUtils.checkTempFiles();
|
||||||
|
} catch(Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printSheetCount(final InputStream inputStream) throws Exception {
|
||||||
|
AesZipFileZipEntrySource source = AesZipFileZipEntrySource.createZipEntrySource(inputStream);
|
||||||
|
try {
|
||||||
|
OPCPackage pkg = OPCPackage.open(source);
|
||||||
|
try {
|
||||||
|
XSSFWorkbook workbook = new XSSFWorkbook(pkg);
|
||||||
|
try {
|
||||||
|
System.out.println("sheet count: " + workbook.getNumberOfSheets());
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(workbook);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(pkg);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue