+++ /dev/null
-/*
- * ====================================================================
- * 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
+++ /dev/null
-/*
- * ====================================================================
- * 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();
- }
-}
import org.apache.poi.util.IOUtils;
public class EncryptionUtils {
+ private EncryptionUtils() {
+ }
+
public static InputStream decrypt(final InputStream inputStream, final String pwd) throws Exception {
try {
POIFSFileSystem fs = new POIFSFileSystem(inputStream);
import org.apache.poi.util.TempFile;
public class TempFileUtils {
+ private 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) {
+ if(tempFiles != null && tempFiles.length > 0) {
System.out.println("found files in poi temp dir " + tempDir.getAbsolutePath());
- for(String filename : tempDir.list()) {
+ for(String filename : tempFiles) {
System.out.println("file: " + filename);
}
}
package org.apache.poi.xssf.eventusermodel.examples;
import java.io.FileInputStream;
+import java.io.IOException;
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.poifs.crypt.temp.AesZipFileZipEntrySource;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator;
*/
public class LoadPasswordProtectedXlsxStreaming {
- public static void main(String[] args) {
+ public static void main(String[] args) throws Exception {
+ if(args.length != 2) {
+ throw new IllegalArgumentException("Expected 2 params: filename and password");
+ }
+ TempFileUtils.checkTempFiles();
+ String filename = args[0];
+ String password = args[1];
+ FileInputStream fis = new FileInputStream(filename);
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);
+ InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
try {
- InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
- try {
- printSheetCount(unencryptedStream);
- } finally {
- IOUtils.closeQuietly(unencryptedStream);
- }
+ printSheetCount(unencryptedStream);
} finally {
- IOUtils.closeQuietly(fis);
+ IOUtils.closeQuietly(unencryptedStream);
}
- TempFileUtils.checkTempFiles();
- } catch(Throwable t) {
- t.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(fis);
}
+ TempFileUtils.checkTempFiles();
}
public static void printSheetCount(final InputStream inputStream) throws Exception {
+++ /dev/null
-/*
- * ====================================================================
- * 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);
- }
- }
-}
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.crypt.temp.EncryptedTempData;
+import org.apache.poi.poifs.crypt.temp.SXSSFWorkbookWithCustomZipEntrySource;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFCell;
*/
public class SavePasswordProtectedXlsx {
- public static void main(String[] args) {
+ public static void main(String[] args) throws Exception {
+ if(args.length != 2) {
+ throw new IllegalArgumentException("Expected 2 params: filename and password");
+ }
+ TempFileUtils.checkTempFiles();
+ String filename = args[0];
+ String password = args[1];
+ SXSSFWorkbookWithCustomZipEntrySource wb = new SXSSFWorkbookWithCustomZipEntrySource();
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");
- }
+ 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();
- }
+ }
+ EncryptedTempData tempData = new EncryptedTempData();
+ try {
+ wb.write(tempData.getOutputStream());
+ save(tempData.getInputStream(), filename, password);
+ System.out.println("Saved " + filename);
} finally {
- wb.close();
- wb.dispose();
+ tempData.dispose();
}
- TempFileUtils.checkTempFiles();
- } catch(Throwable t) {
- t.printStackTrace();
+ } finally {
+ wb.close();
+ wb.dispose();
}
+ TempFileUtils.checkTempFiles();
}
public static void save(final InputStream inputStream, final String filename, final String pwd)
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.poifs.crypt.temp.AesZipFileZipEntrySource;
import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
*/
public class LoadPasswordProtectedXlsx {
- public static void main(String[] args) {
+ public static void main(String[] args) throws Exception {
+ if(args.length != 2) {
+ throw new IllegalArgumentException("Expected 2 params: filename and password");
+ }
+ TempFileUtils.checkTempFiles();
+ String filename = args[0];
+ String password = args[1];
+ FileInputStream fis = new FileInputStream(filename);
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);
+ InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
try {
- InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
- try {
- printSheetCount(unencryptedStream);
- } finally {
- IOUtils.closeQuietly(unencryptedStream);
- }
+ printSheetCount(unencryptedStream);
} finally {
- IOUtils.closeQuietly(fis);
+ IOUtils.closeQuietly(unencryptedStream);
}
- TempFileUtils.checkTempFiles();
- } catch(Throwable t) {
- t.printStackTrace();
+ } finally {
+ IOUtils.closeQuietly(fis);
}
+ TempFileUtils.checkTempFiles();
}
public static void printSheetCount(final InputStream inputStream) throws Exception {
package org.apache.poi.hssf;
import org.apache.poi.OldFileFormatException;
+import org.apache.poi.util.Removal;
+/**
+ * @deprecated POI 3.16 beta 1. Use {@link org.apache.poi.OldFileFormatException}
+ */
+@Removal(version="3.18")
public class OldExcelFormatException extends OldFileFormatException {
public OldExcelFormatException(String s) {
super(s);
((CryptoAPIEncryptionVerifier)encryptionInfo.getVerifier()).write(bos);
break;
default:
- throw new RuntimeException("not supported");
+ throw new EncryptedDocumentException("not supported");
}
out.write(data, 0, bos.getWriteIndex());
@Override
public String toString() {
- StringBuffer buffer = new StringBuffer();
+ StringBuilder buffer = new StringBuilder();
buffer.append("[FILEPASS]\n");
- buffer.append(" .type = ").append(HexDump.shortToHex(encryptionType)).append("\n");
+ buffer.append(" .type = ").append(HexDump.shortToHex(encryptionType)).append('\n');
String prefix = " ."+encryptionInfo.getEncryptionMode();
- buffer.append(prefix+".info = ").append(HexDump.shortToHex(encryptionInfo.getVersionMajor())).append("\n");
- buffer.append(prefix+".ver = ").append(HexDump.shortToHex(encryptionInfo.getVersionMinor())).append("\n");
- buffer.append(prefix+".salt = ").append(HexDump.toHex(encryptionInfo.getVerifier().getSalt())).append("\n");
- buffer.append(prefix+".verifier = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifier())).append("\n");
- buffer.append(prefix+".verifierHash = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifierHash())).append("\n");
+ buffer.append(prefix+".info = ").append(HexDump.shortToHex(encryptionInfo.getVersionMajor())).append('\n');
+ buffer.append(prefix+".ver = ").append(HexDump.shortToHex(encryptionInfo.getVersionMinor())).append('\n');
+ buffer.append(prefix+".salt = ").append(HexDump.toHex(encryptionInfo.getVerifier().getSalt())).append('\n');
+ buffer.append(prefix+".verifier = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifier())).append('\n');
+ buffer.append(prefix+".verifierHash = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifierHash())).append('\n');
buffer.append("[/FILEPASS]\n");
return buffer.toString();
}
\r
@Internal\r
public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {\r
- private final int _chunkSize;\r
- private final int _chunkBits;\r
+ private final int chunkSize;\r
+ private final int chunkBits;\r
\r
- private final long _size;\r
- private final byte[] _chunk, _plain;\r
- private final Cipher _cipher;\r
+ private final long size;\r
+ private final byte[] chunk, plain;\r
+ private final Cipher cipher;\r
\r
- private int _lastIndex;\r
- private long _pos;\r
- private boolean _chunkIsValid = false;\r
+ private int lastIndex;\r
+ private long pos;\r
+ private boolean chunkIsValid = false;\r
\r
public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize)\r
throws GeneralSecurityException {\r
public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize, int initialPos)\r
throws GeneralSecurityException {\r
super(stream);\r
- _size = size;\r
- _pos = initialPos;\r
- this._chunkSize = chunkSize;\r
+ this.size = size;\r
+ this.pos = initialPos;\r
+ this.chunkSize = chunkSize;\r
int cs = chunkSize == -1 ? 4096 : chunkSize;\r
- _chunk = new byte[cs];\r
- _plain = new byte[cs];\r
- _chunkBits = Integer.bitCount(_chunk.length-1);\r
- _lastIndex = (int)(_pos >> _chunkBits);\r
- _cipher = initCipherForBlock(null, _lastIndex);\r
+ this.chunk = new byte[cs];\r
+ this.plain = new byte[cs];\r
+ this.chunkBits = Integer.bitCount(chunk.length-1);\r
+ this.lastIndex = (int)(pos >> chunkBits);\r
+ this.cipher = initCipherForBlock(null, lastIndex);\r
}\r
\r
public final Cipher initCipherForBlock(int block) throws IOException, GeneralSecurityException {\r
- if (_chunkSize != -1) {\r
+ if (chunkSize != -1) {\r
throw new GeneralSecurityException("the cipher block can only be set for streaming encryption, e.g. CryptoAPI...");\r
}\r
\r
- _chunkIsValid = false;\r
- return initCipherForBlock(_cipher, block);\r
+ chunkIsValid = false;\r
+ return initCipherForBlock(cipher, block);\r
}\r
\r
protected abstract Cipher initCipherForBlock(Cipher existing, int block)\r
\r
final int chunkMask = getChunkMask();\r
while (len > 0) {\r
- if (!_chunkIsValid) {\r
+ if (!chunkIsValid) {\r
try {\r
nextChunk();\r
- _chunkIsValid = true;\r
+ chunkIsValid = true;\r
} catch (GeneralSecurityException e) {\r
throw new EncryptedDocumentException(e.getMessage(), e);\r
}\r
}\r
- int count = (int)(_chunk.length - (_pos & chunkMask));\r
+ int count = (int)(chunk.length - (pos & chunkMask));\r
int avail = available();\r
if (avail == 0) {\r
return total;\r
}\r
count = Math.min(avail, Math.min(count, len));\r
\r
- System.arraycopy(readPlain ? _plain : _chunk, (int)(_pos & chunkMask), b, off, count);\r
+ System.arraycopy(readPlain ? plain : chunk, (int)(pos & chunkMask), b, off, count);\r
\r
off += count;\r
len -= count;\r
- _pos += count;\r
- if ((_pos & chunkMask) == 0) {\r
- _chunkIsValid = false;\r
+ pos += count;\r
+ if ((pos & chunkMask) == 0) {\r
+ chunkIsValid = false;\r
}\r
total += count;\r
}\r
\r
@Override\r
public long skip(final long n) throws IOException {\r
- long start = _pos;\r
+ long start = pos;\r
long skip = Math.min(remainingBytes(), n);\r
\r
- if ((((_pos + skip) ^ start) & ~getChunkMask()) != 0) {\r
- _chunkIsValid = false;\r
+ if ((((pos + skip) ^ start) & ~getChunkMask()) != 0) {\r
+ chunkIsValid = false;\r
}\r
- _pos += skip;\r
+ pos += skip;\r
return skip;\r
}\r
\r
* @return the remaining byte until EOF\r
*/\r
private int remainingBytes() {\r
- return (int)(_size - _pos);\r
+ return (int)(size - pos);\r
}\r
\r
@Override\r
}\r
\r
protected int getChunkMask() {\r
- return _chunk.length-1;\r
+ return chunk.length-1;\r
}\r
\r
private void nextChunk() throws GeneralSecurityException, IOException {\r
- if (_chunkSize != -1) {\r
- int index = (int)(_pos >> _chunkBits);\r
- initCipherForBlock(_cipher, index);\r
+ if (chunkSize != -1) {\r
+ int index = (int)(pos >> chunkBits);\r
+ initCipherForBlock(cipher, index);\r
\r
- if (_lastIndex != index) {\r
- super.skip((index - _lastIndex) << _chunkBits);\r
+ if (lastIndex != index) {\r
+ super.skip((index - lastIndex) << chunkBits);\r
}\r
\r
- _lastIndex = index + 1;\r
+ lastIndex = index + 1;\r
}\r
\r
- final int todo = (int)Math.min(_size, _chunk.length);\r
+ final int todo = (int)Math.min(size, chunk.length);\r
int readBytes = 0, totalBytes = 0;\r
do {\r
- readBytes = super.read(_plain, totalBytes, todo-totalBytes);\r
+ readBytes = super.read(plain, totalBytes, todo-totalBytes);\r
totalBytes += Math.max(0, readBytes);\r
} while (readBytes != -1 && totalBytes < todo);\r
\r
- if (readBytes == -1 && _pos+totalBytes < _size && _size < Integer.MAX_VALUE) {\r
+ if (readBytes == -1 && pos+totalBytes < size && size < Integer.MAX_VALUE) {\r
throw new EOFException("buffer underrun");\r
}\r
\r
- System.arraycopy(_plain, 0, _chunk, 0, totalBytes);\r
+ System.arraycopy(plain, 0, chunk, 0, totalBytes);\r
\r
- invokeCipher(totalBytes, totalBytes == _chunkSize);\r
+ invokeCipher(totalBytes, totalBytes == chunkSize);\r
}\r
\r
/**\r
*/\r
protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException {\r
if (doFinal) {\r
- return _cipher.doFinal(_chunk, 0, totalBytes, _chunk);\r
+ return cipher.doFinal(chunk, 0, totalBytes, chunk);\r
} else {\r
- return _cipher.update(_chunk, 0, totalBytes, _chunk);\r
+ return cipher.update(chunk, 0, totalBytes, chunk);\r
}\r
}\r
\r
* @return the chunk bytes\r
*/\r
protected byte[] getChunk() {\r
- return _chunk;\r
+ return chunk;\r
}\r
\r
/**\r
* @return the plain bytes\r
*/\r
protected byte[] getPlain() {\r
- return _plain;\r
+ return plain;\r
}\r
\r
/**\r
* @return the absolute position in the stream\r
*/\r
public long getPos() {\r
- return _pos;\r
+ return pos;\r
}\r
}\r
private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class);\r
private static final int STREAMING = -1;\r
\r
- private final int _chunkSize;\r
- private final int _chunkBits;\r
+ private final int chunkSize;\r
+ private final int chunkBits;\r
\r
- private final byte[] _chunk;\r
- private final BitSet _plainByteFlags;\r
- private final File _fileOut;\r
- private final DirectoryNode _dir;\r
+ private final byte[] chunk;\r
+ private final BitSet plainByteFlags;\r
+ private final File fileOut;\r
+ private final DirectoryNode dir;\r
\r
- private long _pos = 0;\r
- private long _totalPos = 0;\r
- private long _written = 0;\r
- private Cipher _cipher;\r
+ private long pos = 0;\r
+ private long totalPos = 0;\r
+ private long written = 0;\r
+ \r
+ // the cipher can't be final, because for the last chunk we change the padding\r
+ // and therefore need to change the cipher too\r
+ private Cipher cipher;\r
\r
public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException {\r
super(null);\r
- this._chunkSize = chunkSize;\r
+ this.chunkSize = chunkSize;\r
int cs = chunkSize == STREAMING ? 4096 : chunkSize;\r
- _chunk = new byte[cs];\r
- _plainByteFlags = new BitSet(cs);\r
- _chunkBits = Integer.bitCount(cs-1);\r
- _fileOut = TempFile.createTempFile("encrypted_package", "crypt");\r
- _fileOut.deleteOnExit();\r
- this.out = new FileOutputStream(_fileOut);\r
- this._dir = dir;\r
- _cipher = initCipherForBlock(null, 0, false);\r
+ this.chunk = new byte[cs];\r
+ this.plainByteFlags = new BitSet(cs);\r
+ this.chunkBits = Integer.bitCount(cs-1);\r
+ this.fileOut = TempFile.createTempFile("encrypted_package", "crypt");\r
+ this.fileOut.deleteOnExit();\r
+ this.out = new FileOutputStream(fileOut);\r
+ this.dir = dir;\r
+ this.cipher = initCipherForBlock(null, 0, false);\r
}\r
\r
public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException {\r
super(stream);\r
- this._chunkSize = chunkSize;\r
+ this.chunkSize = chunkSize;\r
int cs = chunkSize == STREAMING ? 4096 : chunkSize;\r
- _chunk = new byte[cs];\r
- _plainByteFlags = new BitSet(cs);\r
- _chunkBits = Integer.bitCount(cs-1);\r
- _fileOut = null;\r
- _dir = null;\r
- _cipher = initCipherForBlock(null, 0, false);\r
+ this.chunk = new byte[cs];\r
+ this.plainByteFlags = new BitSet(cs);\r
+ this.chunkBits = Integer.bitCount(cs-1);\r
+ this.fileOut = null;\r
+ this.dir = null;\r
+ this.cipher = initCipherForBlock(null, 0, false);\r
}\r
\r
public final Cipher initCipherForBlock(int block, boolean lastChunk) throws IOException, GeneralSecurityException {\r
- return initCipherForBlock(_cipher, block, lastChunk);\r
+ return initCipherForBlock(cipher, block, lastChunk);\r
}\r
\r
protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk)\r
\r
final int chunkMask = getChunkMask();\r
while (len > 0) {\r
- int posInChunk = (int)(_pos & chunkMask);\r
- int nextLen = Math.min(_chunk.length-posInChunk, len);\r
- System.arraycopy(b, off, _chunk, posInChunk, nextLen);\r
+ int posInChunk = (int)(pos & chunkMask);\r
+ int nextLen = Math.min(chunk.length-posInChunk, len);\r
+ System.arraycopy(b, off, chunk, posInChunk, nextLen);\r
if (writePlain) {\r
- _plainByteFlags.set(posInChunk, posInChunk+nextLen);\r
+ plainByteFlags.set(posInChunk, posInChunk+nextLen);\r
}\r
- _pos += nextLen;\r
- _totalPos += nextLen;\r
+ pos += nextLen;\r
+ totalPos += nextLen;\r
off += nextLen;\r
len -= nextLen;\r
- if ((_pos & chunkMask) == 0) {\r
+ if ((pos & chunkMask) == 0) {\r
writeChunk(len > 0);\r
}\r
}\r
}\r
\r
protected int getChunkMask() {\r
- return _chunk.length-1;\r
+ return chunk.length-1;\r
}\r
\r
protected void writeChunk(boolean continued) throws IOException {\r
- if (_pos == 0 || _totalPos == _written) {\r
+ if (pos == 0 || totalPos == written) {\r
return;\r
}\r
\r
- int posInChunk = (int)(_pos & getChunkMask());\r
+ int posInChunk = (int)(pos & getChunkMask());\r
\r
// normally posInChunk is 0, i.e. on the next chunk (-> index-1)\r
// but if called on close(), posInChunk is somewhere within the chunk data\r
- int index = (int)(_pos >> _chunkBits);\r
+ int index = (int)(pos >> chunkBits);\r
boolean lastChunk;\r
if (posInChunk==0) {\r
index--;\r
- posInChunk = _chunk.length;\r
+ posInChunk = chunk.length;\r
lastChunk = false;\r
} else {\r
// pad the last chunk\r
int ciLen;\r
try {\r
boolean doFinal = true;\r
- long oldPos = _pos;\r
+ long oldPos = pos;\r
// reset stream (not only) in case we were interrupted by plain stream parts\r
// this also needs to be set to prevent an endless loop\r
- _pos = 0;\r
- if (_chunkSize == STREAMING) {\r
+ pos = 0;\r
+ if (chunkSize == STREAMING) {\r
if (continued) {\r
doFinal = false;\r
}\r
} else {\r
- _cipher = initCipherForBlock(_cipher, index, lastChunk);\r
+ cipher = initCipherForBlock(cipher, index, lastChunk);\r
// restore pos - only streaming chunks will be reset\r
- _pos = oldPos;\r
+ pos = oldPos;\r
}\r
ciLen = invokeCipher(posInChunk, doFinal);\r
} catch (GeneralSecurityException e) {\r
throw new IOException("can't re-/initialize cipher", e);\r
}\r
\r
- out.write(_chunk, 0, ciLen);\r
- _plainByteFlags.clear();\r
- _written += ciLen;\r
+ out.write(chunk, 0, ciLen);\r
+ plainByteFlags.clear();\r
+ written += ciLen;\r
}\r
\r
/**\r
* @throws ShortBufferException \r
*/\r
protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException {\r
- byte plain[] = (_plainByteFlags.isEmpty()) ? null : _chunk.clone();\r
+ byte plain[] = (plainByteFlags.isEmpty()) ? null : chunk.clone();\r
\r
int ciLen = (doFinal)\r
- ? _cipher.doFinal(_chunk, 0, posInChunk, _chunk)\r
- : _cipher.update(_chunk, 0, posInChunk, _chunk);\r
+ ? cipher.doFinal(chunk, 0, posInChunk, chunk)\r
+ : cipher.update(chunk, 0, posInChunk, chunk);\r
\r
- for (int i = _plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = _plainByteFlags.nextSetBit(i+1)) {\r
- _chunk[i] = plain[i];\r
+ for (int i = plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = plainByteFlags.nextSetBit(i+1)) {\r
+ chunk[i] = plain[i];\r
}\r
\r
return ciLen;\r
\r
super.close();\r
\r
- if (_fileOut != null) {\r
- int oleStreamSize = (int)(_fileOut.length()+LittleEndianConsts.LONG_SIZE);\r
- calculateChecksum(_fileOut, (int)_pos);\r
- _dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());\r
- createEncryptionInfoEntry(_dir, _fileOut);\r
+ if (fileOut != null) {\r
+ int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);\r
+ calculateChecksum(fileOut, (int)pos);\r
+ dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());\r
+ createEncryptionInfoEntry(dir, fileOut);\r
}\r
} catch (GeneralSecurityException e) {\r
throw new IOException(e);\r
}\r
\r
protected byte[] getChunk() {\r
- return _chunk;\r
+ return chunk;\r
}\r
\r
protected BitSet getPlainByteFlags() {\r
- return _plainByteFlags;\r
+ return plainByteFlags;\r
}\r
\r
protected long getPos() {\r
- return _pos;\r
+ return pos;\r
}\r
\r
protected long getTotalPos() {\r
- return _totalPos;\r
+ return totalPos;\r
}\r
\r
/**\r
// Note that the actual size of the \EncryptedPackage stream (1) can be larger than this\r
// value, depending on the block size of the chosen encryption algorithm\r
byte buf[] = new byte[LittleEndianConsts.LONG_SIZE];\r
- LittleEndian.putLong(buf, 0, _pos);\r
+ LittleEndian.putLong(buf, 0, pos);\r
os.write(buf);\r
\r
- FileInputStream fis = new FileInputStream(_fileOut);\r
+ FileInputStream fis = new FileInputStream(fileOut);\r
IOUtils.copy(fis, os);\r
fis.close();\r
\r
os.close();\r
\r
- if (!_fileOut.delete()) {\r
- LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+_fileOut);\r
+ if (!fileOut.delete()) {\r
+ LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);\r
}\r
} catch (IOException e) {\r
throw new EncryptedDocumentException(e);\r
*/
public InputStream getDataStream(InputStream stream, int size, int initialPos)
throws IOException, GeneralSecurityException {
- throw new RuntimeException("this decryptor doesn't support reading from a stream");
+ throw new EncryptedDocumentException("this decryptor doesn't support reading from a stream");
}
/**
* @param chunkSize the chunk size, i.e. the block size with the same encryption key
*/
public void setChunkSize(int chunkSize) {
- throw new RuntimeException("this decryptor doesn't support changing the chunk size");
+ throw new EncryptedDocumentException("this decryptor doesn't support changing the chunk size");
}
/**
*/
public Cipher initCipherForBlock(Cipher cipher, int block)
throws GeneralSecurityException {
- throw new RuntimeException("this decryptor doesn't support initCipherForBlock");
+ throw new EncryptedDocumentException("this decryptor doesn't support initCipherForBlock");
}
public abstract boolean verifyPassword(String password)
import javax.crypto.SecretKey;\r
import javax.crypto.spec.SecretKeySpec;\r
\r
+import org.apache.poi.EncryptedDocumentException;\r
import org.apache.poi.poifs.filesystem.DirectoryNode;\r
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;\r
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;\r
\r
public ChunkedCipherOutputStream getDataStream(OutputStream stream, int initialOffset)\r
throws IOException, GeneralSecurityException {\r
- throw new RuntimeException("this decryptor doesn't support writing directly to a stream");\r
+ throw new EncryptedDocumentException("this decryptor doesn't support writing directly to a stream");\r
}\r
\r
public SecretKey getSecretKey() {\r
* @param chunkSize the chunk size, i.e. the block size with the same encryption key\r
*/\r
public void setChunkSize(int chunkSize) {\r
- throw new RuntimeException("this decryptor doesn't support changing the chunk size");\r
+ throw new EncryptedDocumentException("this decryptor doesn't support changing the chunk size");\r
}\r
\r
@Override\r
import org.apache.poi.util.StringUtil;\r
\r
public class BinaryRC4Decryptor extends Decryptor implements Cloneable {\r
- private long _length = -1L;\r
- private int _chunkSize = 512;\r
+ private long length = -1L;\r
+ private int chunkSize = 512;\r
\r
private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream {\r
\r
\r
public BinaryRC4CipherInputStream(DocumentInputStream stream, long size)\r
throws GeneralSecurityException {\r
- super(stream, size, _chunkSize);\r
+ super(stream, size, chunkSize);\r
}\r
\r
public BinaryRC4CipherInputStream(InputStream stream)\r
throws GeneralSecurityException {\r
- super(stream, Integer.MAX_VALUE, _chunkSize);\r
+ super(stream, Integer.MAX_VALUE, chunkSize);\r
} \r
}\r
\r
public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException,\r
GeneralSecurityException {\r
DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);\r
- _length = dis.readLong();\r
- return new BinaryRC4CipherInputStream(dis, _length);\r
+ length = dis.readLong();\r
+ return new BinaryRC4CipherInputStream(dis, length);\r
}\r
\r
@Override\r
\r
@Override\r
public long getLength() {\r
- if (_length == -1L) {\r
+ if (length == -1L) {\r
throw new IllegalStateException("Decryptor.getDataStream() was not called");\r
}\r
\r
- return _length;\r
+ return length;\r
}\r
\r
@Override\r
public void setChunkSize(int chunkSize) {\r
- _chunkSize = chunkSize;\r
+ this.chunkSize = chunkSize;\r
}\r
\r
@Override\r
\r
public class BinaryRC4Encryptor extends Encryptor implements Cloneable {\r
\r
- private int _chunkSize = 512;\r
+ private int chunkSize = 512;\r
\r
protected BinaryRC4Encryptor() {\r
}\r
\r
@Override\r
public void setChunkSize(int chunkSize) {\r
- _chunkSize = chunkSize;\r
+ this.chunkSize = chunkSize;\r
}\r
\r
@Override\r
\r
public BinaryRC4CipherOutputStream(OutputStream stream)\r
throws IOException, GeneralSecurityException {\r
- super(stream, BinaryRC4Encryptor.this._chunkSize);\r
+ super(stream, BinaryRC4Encryptor.this.chunkSize);\r
}\r
\r
public BinaryRC4CipherOutputStream(DirectoryNode dir)\r
throws IOException, GeneralSecurityException {\r
- super(dir, BinaryRC4Encryptor.this._chunkSize);\r
+ super(dir, BinaryRC4Encryptor.this.chunkSize);\r
}\r
\r
@Override\r
\r
public class CryptoAPIDecryptor extends Decryptor implements Cloneable {\r
\r
- private long _length;\r
- private int _chunkSize = -1;\r
+ private long length = -1L;\r
+ private int chunkSize = -1;\r
\r
static class StreamDescriptorEntry {\r
static BitField flagStream = BitFieldFactory.getInstance(1);\r
}\r
\r
protected CryptoAPIDecryptor() {\r
- _length = -1L;\r
}\r
\r
@Override\r
*/\r
@Override\r
public long getLength() {\r
- if (_length == -1L) {\r
+ if (length == -1L) {\r
throw new IllegalStateException("Decryptor.getDataStream() was not called");\r
}\r
- return _length;\r
+ return length;\r
}\r
\r
public void setChunkSize(int chunkSize) {\r
- _chunkSize = chunkSize;\r
+ this.chunkSize = chunkSize;\r
}\r
\r
@Override\r
\r
public CryptoAPICipherInputStream(InputStream stream, long size, int initialPos)\r
throws GeneralSecurityException {\r
- super(stream, size, _chunkSize, initialPos);\r
+ super(stream, size, chunkSize, initialPos);\r
} \r
}\r
}\r
*/
@Internal
/* package */ class CryptoAPIDocumentOutputStream extends ByteArrayOutputStream {
- private Cipher cipher;
- private CryptoAPIEncryptor encryptor;
- private byte oneByte[] = { 0 };
+ private final Cipher cipher;
+ private final CryptoAPIEncryptor encryptor;
+ private final byte oneByte[] = { 0 };
public CryptoAPIDocumentOutputStream(CryptoAPIEncryptor encryptor) throws GeneralSecurityException {
this.encryptor = encryptor;
- setBlock(0);
+ cipher = encryptor.initCipherForBlock(null, 0);
}
public byte[] getBuf() {
}
public void setBlock(int block) throws GeneralSecurityException {
- cipher = encryptor.initCipherForBlock(cipher, block);
+ encryptor.initCipherForBlock(cipher, block);
}
@Override
\r
public class CryptoAPIEncryptor extends Encryptor implements Cloneable {\r
\r
- private int _chunkSize = 512;\r
+ private int chunkSize = 512;\r
\r
protected CryptoAPIEncryptor() {\r
}\r
\r
@Override\r
public void setChunkSize(int chunkSize) {\r
- _chunkSize = chunkSize;\r
+ this.chunkSize = chunkSize;\r
}\r
\r
protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {\r
@Override\r
protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)\r
throws IOException, GeneralSecurityException {\r
- throw new RuntimeException("createEncryptionInfoEntry not supported");\r
+ throw new EncryptedDocumentException("createEncryptionInfoEntry not supported");\r
}\r
\r
public CryptoAPICipherOutputStream(OutputStream stream)\r
throws IOException, GeneralSecurityException {\r
- super(stream, CryptoAPIEncryptor.this._chunkSize);\r
+ super(stream, CryptoAPIEncryptor.this.chunkSize);\r
}\r
\r
@Override\r
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
+import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.util.LittleEndian;
public class XORDecryptor extends Decryptor implements Cloneable {
- private long _length = -1L;
- private int _chunkSize = 512;
+ private long length = -1L;
+ private int chunkSize = 512;
protected XORDecryptor() {
}
@Override
public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
- throw new RuntimeException("not supported");
+ throw new EncryptedDocumentException("not supported");
}
@Override
@Override
public long getLength() {
- if (_length == -1L) {
+ if (length == -1L) {
throw new IllegalStateException("Decryptor.getDataStream() was not called");
}
- return _length;
+ return length;
}
@Override
public void setChunkSize(int chunkSize) {
- _chunkSize = chunkSize;
+ this.chunkSize = chunkSize;
}
@Override
}
private class XORCipherInputStream extends ChunkedCipherInputStream {
- private final int _initialOffset;
- private int _recordStart = 0;
- private int _recordEnd = 0;
+ private final int initialOffset;
+ private int recordStart = 0;
+ private int recordEnd = 0;
public XORCipherInputStream(InputStream stream, int initialPos)
throws GeneralSecurityException {
- super(stream, Integer.MAX_VALUE, _chunkSize);
- _initialOffset = initialPos;
+ super(stream, Integer.MAX_VALUE, chunkSize);
+ this.initialOffset = initialPos;
}
@Override
* the time we are about to write each of the bytes of the record data.
* This (the value) is then incremented after each byte is written.
*/
- final int xorArrayIndex = _initialOffset+_recordEnd+(pos-_recordStart);
+ final int xorArrayIndex = initialOffset+recordEnd+(pos-recordStart);
- for (int i=0; pos+i < _recordEnd && i < totalBytes; i++) {
+ for (int i=0; pos+i < recordEnd && i < totalBytes; i++) {
// The following is taken from the Libre Office implementation
// It seems that the encrypt and decrypt method is mixed up
// in the MS-OFFCRYPTO docs
final int pos = (int)getPos();
final byte chunk[] = getChunk();
final int chunkMask = getChunkMask();
- _recordStart = pos;
- _recordEnd = _recordStart+recordSize;
+ recordStart = pos;
+ recordEnd = recordStart+recordSize;
int nextBytes = Math.min(recordSize, chunk.length-(pos & chunkMask));
invokeCipher(nextBytes, true);
}
}
@Override
- protected void setEncryptedVerifier(byte[] encryptedVerifier) {
+ protected final void setEncryptedVerifier(byte[] encryptedVerifier) {
super.setEncryptedVerifier(encryptedVerifier);
}
@Override
- protected void setEncryptedKey(byte[] encryptedKey) {
+ protected final void setEncryptedKey(byte[] encryptedKey) {
super.setEncryptedKey(encryptedKey);
}
}
@Override
public OutputStream getDataStream(DirectoryNode dir)
throws IOException, GeneralSecurityException {
- OutputStream countStream = new XORCipherOutputStream(dir);
- return countStream;
+ return new XORCipherOutputStream(dir);
}
@Override
}
private class XORCipherOutputStream extends ChunkedCipherOutputStream {
- private final int _initialOffset;
- private int _recordStart = 0;
- private int _recordEnd = 0;
- private boolean _isPlain = false;
+ private int recordStart = 0;
+ private int recordEnd = 0;
public XORCipherOutputStream(OutputStream stream, int initialPos) throws IOException, GeneralSecurityException {
super(stream, -1);
- _initialOffset = initialPos;
}
public XORCipherOutputStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
super(dir, -1);
- _initialOffset = 0;
}
@Override
@Override
public void setNextRecordSize(int recordSize, boolean isPlain) {
- if (_recordEnd > 0 && !_isPlain) {
+ if (recordEnd > 0 && !isPlain) {
// encrypt last record
invokeCipher((int)getPos(), true);
}
- _recordStart = (int)getTotalPos()+4;
- _recordEnd = _recordStart+recordSize;
- _isPlain = isPlain;
+ recordStart = (int)getTotalPos()+4;
+ recordEnd = recordStart+recordSize;
}
@Override
return 0;
}
- final int start = Math.max(posInChunk-(_recordEnd-_recordStart), 0);
+ final int start = Math.max(posInChunk-(recordEnd-recordStart), 0);
final BitSet plainBytes = getPlainByteFlags();
final byte xorArray[] = getEncryptionInfo().getEncryptor().getSecretKey().getEncoded();
* This (the value) is then incremented after each byte is written.
*/
// ... also need to handle invocation in case of a filled chunk
- int xorArrayIndex = _recordEnd+(start-_recordStart);
+ int xorArrayIndex = recordEnd+(start-recordStart);
for (int i=start; i < posInChunk; i++) {
byte value = chunk[i];
}
// Default to searching from 1, unless they asked for 0+
- int idx = minIdx;
- if (minIdx < 0) idx = 1;
+ int idx = (minIdx < 0) ? 1 : minIdx;
int maxIdx = minIdx + pkg.getParts().size();
while (idx <= maxIdx) {
name = descriptor.getFileName(idx);
} else {
aps = new IvParameterSpec(iv);
}
-
+
existing.init(encryptionMode, skey, aps);
return existing;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
throw new EncryptedDocumentException("Unable to parse keyData", e);
}
- int keyBits = (int)keyData.getKeyBits();
- CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), keyBits);
+ int kb = (int)keyData.getKeyBits();
+ CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), kb);
setCipherAlgorithm(ca);
- setKeySize(keyBits);
+ setKeySize(kb);
int blockSize = keyData.getBlockSize();
setBlockSize(blockSize);
}
@Override
- protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
+ protected final void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
super.setCipherAlgorithm(cipherAlgorithm);
if (cipherAlgorithm.allowedKeySize.length == 1) {
setKeySize(cipherAlgorithm.defaultKeySize);
--- /dev/null
+/*
+ * ====================================================================
+ * 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.temp;
+
+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.Beta;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+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.
+ */
+@Beta
+public class AesZipFileZipEntrySource implements ZipEntrySource {
+ private static POILogger LOG = POILogFactory.getLogger(AesZipFileZipEntrySource.class);
+
+ private final File tmpFile;
+ private final ZipFile zipFile;
+ private final Cipher ci;
+ private 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();
+ if (!tmpFile.delete()) {
+ LOG.log(POILogger.WARN, tmpFile.getAbsolutePath()+" can't be removed (or was already removed.");
+ };
+ }
+ 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
--- /dev/null
+/*
+ * ====================================================================
+ * 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.temp;
+
+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.Beta;
+import org.apache.poi.util.Internal;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.util.TempFile;
+
+/**
+ * EncryptedTempData can be used to buffer binary data in a secure way, by using encrypted temp files.
+ */
+@Beta
+public class EncryptedTempData {
+ private static POILogger LOG = POILogFactory.getLogger(EncryptedTempData.class);
+
+ private final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
+ private final SecretKeySpec skeySpec;
+ private final byte[] ivBytes;
+ private 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() {
+ if (!tempFile.delete()) {
+ LOG.log(POILogger.WARN, tempFile.getAbsolutePath()+" can't be removed (or was already removed.");
+ }
+ }
+}
--- /dev/null
+/*
+ * ====================================================================
+ * 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.temp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+
+import org.apache.poi.openxml4j.util.ZipEntrySource;
+import org.apache.poi.util.Beta;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.streaming.SheetDataWriter;
+
+@Beta
+public class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook {
+ private static final POILogger LOG = POILogFactory.getLogger(SXSSFWorkbookWithCustomZipEntrySource.class);
+
+ 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 {
+ //log values to ensure these values are accessible to subclasses
+ LOG.log(POILogger.INFO, "isCompressTempFiles: " + isCompressTempFiles());
+ LOG.log(POILogger.INFO, "SharedStringSource: " + getSharedStringSource());
+ return new SheetDataWriterWithDecorator();
+ }
+}
--- /dev/null
+/*
+ * ====================================================================
+ * 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.temp;
+
+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.Beta;
+import org.apache.poi.xssf.streaming.SheetDataWriter;
+
+@Beta
+public 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);
+ }
+}
List<CTSheet> validSheets = new ArrayList<CTSheet>();
for (CTSheet ctSheet : wbBean.getSheets().getSheetList()) {
//if there's no relationship id, silently skip the sheet
- if ("".equals(ctSheet.getId())) {
- //skip it
- } else {
+ String sheetId = ctSheet.getId();
+ if (sheetId != null && sheetId.length() > 0) {
validSheets.add(ctSheet);
}
}
_out.close();\r
}\r
\r
- File getTempFile(){\r
+ protected File getTempFile(){\r
return _fd;\r
}\r
\r
==================================================================== */
package org.apache.poi.xwpf.model;
-import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
-
import org.apache.poi.POIXMLDocumentPart;
import org.apache.poi.POIXMLDocumentPart.RelationPart;
import org.apache.poi.util.POILogFactory;
* the right headers and footers for the document.
*/
public class XWPFHeaderFooterPolicy {
- private static final POILogger LOG = POILogFactory.getLogger(XWPFHeaderFooterPolicy.class);
-
public static final Enum DEFAULT = STHdrFtr.DEFAULT;
public static final Enum EVEN = STHdrFtr.EVEN;
public static final Enum FIRST = STHdrFtr.FIRST;
CTP p = ftr.addNewP();
ftr.setPArray(i, paragraphs[i].getCTP());
}
- } else {
+// } else {
// CTP p = ftr.addNewP();
// CTBody body = doc.getDocument().getBody();
// if (body.sizeOfPArray() > 0) {
CTOnOff titlePg = ctSectPr.addNewTitlePg();
titlePg.setVal(STOnOff.ON);
}
- } else if (type == HeaderFooterType.EVEN) {
+ // } else if (type == HeaderFooterType.EVEN) {
// TODO Add support for Even/Odd headings and footers
}
return hfPolicy.createHeader(STHdrFtr.Enum.forInt(type.toInt()));
CTOnOff titlePg = ctSectPr.addNewTitlePg();
titlePg.setVal(STOnOff.ON);
}
- } else if (type == HeaderFooterType.EVEN) {
+ // } else if (type == HeaderFooterType.EVEN) {
// TODO Add support for Even/Odd headings and footers
}
return hfPolicy.createFooter(STHdrFtr.Enum.forInt(type.toInt()));
+++ /dev/null
-/* ====================================================================
- 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!
- */
- @Override
- public Enumeration<? extends ZipEntry> getEntries() {
- return zipFile.entries();
- }
-
- @Override
- @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 {
- if(!closed) {
- 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
- @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 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);
- }
-
-}
-
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.crypt.temp.AesZipFileZipEntrySource;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor;
import java.util.ArrayList;
import java.util.List;
-import org.apache.poi.xssf.streaming.TestSXSSFWorkbookWithCustomZipEntrySource.SXSSFWorkbookWithCustomZipEntrySource;
-import org.apache.poi.xssf.streaming.TestSXSSFWorkbookWithCustomZipEntrySource.SheetDataWriterWithDecorator;
+import org.apache.poi.poifs.crypt.temp.SXSSFWorkbookWithCustomZipEntrySource;
+import org.apache.poi.poifs.crypt.temp.SheetDataWriterWithDecorator;
// a class to record a list of temporary files that are written to disk
// afterwards, a test function can check whether these files were encrypted or not
import java.io.ByteArrayOutputStream;
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.nio.charset.Charset;
import java.security.GeneralSecurityException;
-import java.security.SecureRandom;
import java.util.List;
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.spec.SecretKeySpec;
-
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.util.ZipEntrySource;
-import org.apache.poi.poifs.crypt.AesZipFileZipEntrySource;
-import org.apache.poi.poifs.crypt.ChainingMode;
-import org.apache.poi.poifs.crypt.CipherAlgorithm;
-import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.temp.AesZipFileZipEntrySource;
+import org.apache.poi.poifs.crypt.temp.EncryptedTempData;
+import org.apache.poi.poifs.crypt.temp.SXSSFWorkbookWithCustomZipEntrySource;
import org.apache.poi.util.IOUtils;
-import org.apache.poi.util.POILogFactory;
-import org.apache.poi.util.POILogger;
-import org.apache.poi.util.TempFile;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
workbook.dispose();
assertFalse("tempFile deleted after dispose?", tempFile.exists());
}
-
- static class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook {
-
- private static final POILogger logger = POILogFactory.getLogger(SXSSFWorkbookWithCustomZipEntrySource.class);
-
- @Override
- public void write(OutputStream stream) throws IOException {
- flushSheets();
- EncryptedTempData tempData = new EncryptedTempData();
- OutputStream os = tempData.getOutputStream();
- getXSSFWorkbook().write(os);
- os.close();
- ZipEntrySource source = null;
- try {
- // provide ZipEntrySource to poi which decrypts on the fly
- source = AesZipFileZipEntrySource.createZipEntrySource(tempData.getInputStream());
- injectData(source, stream);
- } catch (GeneralSecurityException e) {
- throw new IOException(e);
- } finally {
- tempData.dispose();
- IOUtils.closeQuietly(source);
- }
- }
-
- @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);
- }
- }
-
- // a class to save and read an AES-encrypted workbook
- static class EncryptedTempData {
- final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
- final SecretKeySpec skeySpec;
- final byte[] ivBytes;
- final File tempFile;
-
- EncryptedTempData() throws IOException {
- SecureRandom sr = new SecureRandom();
- ivBytes = new byte[16];
- byte[] keyBytes = new byte[16];
- sr.nextBytes(ivBytes);
- sr.nextBytes(keyBytes);
- skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId);
- tempFile = TempFile.createTempFile("poi-temp-data", ".tmp");
- }
-
- OutputStream getOutputStream() throws IOException {
- Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, null);
- return new CipherOutputStream(new FileOutputStream(tempFile), ciEnc);
- }
-
- InputStream getInputStream() throws IOException {
- Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, null);
- return new CipherInputStream(new FileInputStream(tempFile), ciDec);
- }
-
- void dispose() {
- assertTrue("Could not delete tempfile " + tempFile + ": " + tempFile.exists(),
- !tempFile.exists() || tempFile.delete());
- }
- }
-}
+}
\ No newline at end of file
return (0x40 <= format && format < 0x50);
}
public boolean destinationHasPointers() {
- if(type == 20) return true;
- if(type == 22) return false;
- if(format == 0x1d || format == 0x1e) return true;
+ if(type == 20) {
+ return true;
+ }
+ if(type == 22) {
+ return false;
+ }
+ if(format == 0x1d || format == 0x1e) {
+ return true;
+ }
return (0x50 <= format && format < 0x60);
}
public boolean destinationHasChunks() {
- if (type == 21) return true;
- if (type == 24) return true;
+ if (type == 21) {
+ return true;
+ }
+ if (type == 24) {
+ return true;
+ }
return (0xd0 <= format && format < 0xdf);
}
* Represents Macintosh PICT picture data.
*/
public final class PICT extends Metafile {
- private static POILogger LOG = POILogFactory.getLogger(PICT.class);
+ private static final POILogger LOG = POILogFactory.getLogger(PICT.class);
public static class NativeHeader {
/**
}
private void fillPoint(byte xyMaster[], double xyPoints[]) {
- if (xyMaster == null || xyPoints == null || (xyMaster.length != 4 && xyMaster.length != 8) || xyPoints.length != 2) {
+ int masterCnt = (xyMaster == null) ? 0 : xyMaster.length;
+ int pointCnt = (xyPoints == null) ? 0 : xyPoints.length;
+ if ((masterCnt != 4 && masterCnt != 8) || pointCnt != 2) {
logger.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point");
return;
}