]> source.dussan.org Git - poi.git/commitdiff
bug 60321: add examples with encrypted temp data. Patch from PJ Fanning.
authorJaven O'Neal <onealj@apache.org>
Tue, 8 Nov 2016 17:32:38 +0000 (17:32 +0000)
committerJaven O'Neal <onealj@apache.org>
Tue, 8 Nov 2016 17:32:38 +0000 (17:32 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1768744 13f79535-47bb-0310-9956-ffa450edef68

src/examples/src/org/apache/poi/crypt/examples/AesZipFileZipEntrySource.java [new file with mode: 0644]
src/examples/src/org/apache/poi/crypt/examples/EncryptedTempData.java [new file with mode: 0644]
src/examples/src/org/apache/poi/crypt/examples/EncryptionUtils.java [new file with mode: 0644]
src/examples/src/org/apache/poi/examples/util/TempFileUtils.java [new file with mode: 0644]
src/examples/src/org/apache/poi/xssf/eventusermodel/examples/LoadPasswordProtectedXlsxStreaming.java [new file with mode: 0644]
src/examples/src/org/apache/poi/xssf/streaming/examples/SXSSFWorkbookWithCustomZipEntrySource.java [new file with mode: 0644]
src/examples/src/org/apache/poi/xssf/streaming/examples/SavePasswordProtectedXlsx.java [new file with mode: 0644]
src/examples/src/org/apache/poi/xssf/usermodel/examples/LoadPasswordProtectedXlsx.java [new file with mode: 0644]

diff --git a/src/examples/src/org/apache/poi/crypt/examples/AesZipFileZipEntrySource.java b/src/examples/src/org/apache/poi/crypt/examples/AesZipFileZipEntrySource.java
new file mode 100644 (file)
index 0000000..af281e2
--- /dev/null
@@ -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);
+    }
+    
+}
\ No newline at end of file
diff --git a/src/examples/src/org/apache/poi/crypt/examples/EncryptedTempData.java b/src/examples/src/org/apache/poi/crypt/examples/EncryptedTempData.java
new file mode 100644 (file)
index 0000000..e4c5796
--- /dev/null
@@ -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();
+    }
+}
diff --git a/src/examples/src/org/apache/poi/crypt/examples/EncryptionUtils.java b/src/examples/src/org/apache/poi/crypt/examples/EncryptionUtils.java
new file mode 100644 (file)
index 0000000..acbfdd1
--- /dev/null
@@ -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);
+        }
+    }
+}
diff --git a/src/examples/src/org/apache/poi/examples/util/TempFileUtils.java b/src/examples/src/org/apache/poi/examples/util/TempFileUtils.java
new file mode 100644 (file)
index 0000000..f34f35d
--- /dev/null
@@ -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");
+        }
+    }
+}
diff --git a/src/examples/src/org/apache/poi/xssf/eventusermodel/examples/LoadPasswordProtectedXlsxStreaming.java b/src/examples/src/org/apache/poi/xssf/eventusermodel/examples/LoadPasswordProtectedXlsxStreaming.java
new file mode 100644 (file)
index 0000000..aa7d0ce
--- /dev/null
@@ -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);
+        }
+    }
+}
diff --git a/src/examples/src/org/apache/poi/xssf/streaming/examples/SXSSFWorkbookWithCustomZipEntrySource.java b/src/examples/src/org/apache/poi/xssf/streaming/examples/SXSSFWorkbookWithCustomZipEntrySource.java
new file mode 100644 (file)
index 0000000..48f3f3a
--- /dev/null
@@ -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);
+        }
+    }
+}
diff --git a/src/examples/src/org/apache/poi/xssf/streaming/examples/SavePasswordProtectedXlsx.java b/src/examples/src/org/apache/poi/xssf/streaming/examples/SavePasswordProtectedXlsx.java
new file mode 100644 (file)
index 0000000..4c74187
--- /dev/null
@@ -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);
+        }
+    }
+
+}
diff --git a/src/examples/src/org/apache/poi/xssf/usermodel/examples/LoadPasswordProtectedXlsx.java b/src/examples/src/org/apache/poi/xssf/usermodel/examples/LoadPasswordProtectedXlsx.java
new file mode 100644 (file)
index 0000000..7bb677b
--- /dev/null
@@ -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);
+        }
+    }
+
+}