- SonarCube fixes

- moved SecureTempFile classes to OOXML, because of duplicated code in test and examples packages

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1769363 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2016-11-11 23:22:43 +00:00
parent 3820215393
commit fc86b15f13
37 changed files with 415 additions and 612 deletions

View File

@ -27,6 +27,9 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
public class EncryptionUtils { public class EncryptionUtils {
private EncryptionUtils() {
}
public static InputStream decrypt(final InputStream inputStream, final String pwd) throws Exception { public static InputStream decrypt(final InputStream inputStream, final String pwd) throws Exception {
try { try {
POIFSFileSystem fs = new POIFSFileSystem(inputStream); POIFSFileSystem fs = new POIFSFileSystem(inputStream);

View File

@ -25,14 +25,17 @@ import java.io.IOException;
import org.apache.poi.util.TempFile; import org.apache.poi.util.TempFile;
public class TempFileUtils { public class TempFileUtils {
private TempFileUtils() {
}
public static void checkTempFiles() throws IOException { public static void checkTempFiles() throws IOException {
String tmpDir = System.getProperty(TempFile.JAVA_IO_TMPDIR) + "/poifiles"; String tmpDir = System.getProperty(TempFile.JAVA_IO_TMPDIR) + "/poifiles";
File tempDir = new File(tmpDir); File tempDir = new File(tmpDir);
if(tempDir.exists()) { if(tempDir.exists()) {
String[] tempFiles = tempDir.list(); 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()); 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); System.out.println("file: " + filename);
} }
} }

View File

@ -20,12 +20,13 @@
package org.apache.poi.xssf.eventusermodel.examples; package org.apache.poi.xssf.eventusermodel.examples;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.apache.poi.crypt.examples.AesZipFileZipEntrySource;
import org.apache.poi.crypt.examples.EncryptionUtils; import org.apache.poi.crypt.examples.EncryptionUtils;
import org.apache.poi.examples.util.TempFileUtils; import org.apache.poi.examples.util.TempFileUtils;
import org.apache.poi.openxml4j.opc.OPCPackage; 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.util.IOUtils;
import org.apache.poi.xssf.eventusermodel.XSSFReader; import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator; import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator;
@ -40,29 +41,25 @@ import org.apache.poi.xssf.eventusermodel.XSSFReader.SheetIterator;
*/ */
public class LoadPasswordProtectedXlsxStreaming { public class LoadPasswordProtectedXlsxStreaming {
public static void main(String[] args) { public static void main(String[] args) throws Exception {
try { if(args.length != 2) {
if(args.length != 2) { throw new IllegalArgumentException("Expected 2 params: filename and password");
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();
} }
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();
} }
public static void printSheetCount(final InputStream inputStream) throws Exception { public static void printSheetCount(final InputStream inputStream) throws Exception {

View File

@ -24,13 +24,14 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import org.apache.poi.crypt.examples.EncryptedTempData;
import org.apache.poi.examples.util.TempFileUtils; import org.apache.poi.examples.util.TempFileUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.crypt.EncryptionInfo; import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode; import org.apache.poi.poifs.crypt.EncryptionMode;
import org.apache.poi.poifs.crypt.Encryptor; 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.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.IOUtils; import org.apache.poi.util.IOUtils;
import org.apache.poi.xssf.streaming.SXSSFCell; import org.apache.poi.xssf.streaming.SXSSFCell;
@ -47,42 +48,38 @@ import org.apache.poi.xssf.streaming.SXSSFSheet;
*/ */
public class SavePasswordProtectedXlsx { 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 { try {
if(args.length != 2) { for(int i = 0; i < 10; i++) {
throw new Exception("Expected 2 params: filename and password"); SXSSFSheet sheet = wb.createSheet("Sheet" + i);
} for(int r = 0; r < 1000; r++) {
TempFileUtils.checkTempFiles(); SXSSFRow row = sheet.createRow(r);
String filename = args[0]; for(int c = 0; c < 100; c++) {
String password = args[1]; SXSSFCell cell = row.createCell(c);
SXSSFWorkbookWithCustomZipEntrySource wb = new SXSSFWorkbookWithCustomZipEntrySource(); cell.setCellValue("abcd");
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(); EncryptedTempData tempData = new EncryptedTempData();
} catch(Throwable t) { try {
t.printStackTrace(); wb.write(tempData.getOutputStream());
save(tempData.getInputStream(), filename, password);
System.out.println("Saved " + filename);
} finally {
tempData.dispose();
}
} finally {
wb.close();
wb.dispose();
} }
TempFileUtils.checkTempFiles();
} }
public static void save(final InputStream inputStream, final String filename, final String pwd) public static void save(final InputStream inputStream, final String filename, final String pwd)

View File

@ -22,10 +22,10 @@ package org.apache.poi.xssf.usermodel.examples;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.InputStream; import java.io.InputStream;
import org.apache.poi.crypt.examples.AesZipFileZipEntrySource;
import org.apache.poi.crypt.examples.EncryptionUtils; import org.apache.poi.crypt.examples.EncryptionUtils;
import org.apache.poi.examples.util.TempFileUtils; import org.apache.poi.examples.util.TempFileUtils;
import org.apache.poi.openxml4j.opc.OPCPackage; 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.util.IOUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook;
@ -38,29 +38,25 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook;
*/ */
public class LoadPasswordProtectedXlsx { public class LoadPasswordProtectedXlsx {
public static void main(String[] args) { public static void main(String[] args) throws Exception {
try { if(args.length != 2) {
if(args.length != 2) { throw new IllegalArgumentException("Expected 2 params: filename and password");
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();
} }
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();
} }
public static void printSheetCount(final InputStream inputStream) throws Exception { public static void printSheetCount(final InputStream inputStream) throws Exception {

View File

@ -17,7 +17,12 @@
package org.apache.poi.hssf; package org.apache.poi.hssf;
import org.apache.poi.OldFileFormatException; 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 class OldExcelFormatException extends OldFileFormatException {
public OldExcelFormatException(String s) { public OldExcelFormatException(String s) {
super(s); super(s);

View File

@ -110,7 +110,7 @@ public final class FilePassRecord extends StandardRecord implements Cloneable {
((CryptoAPIEncryptionVerifier)encryptionInfo.getVerifier()).write(bos); ((CryptoAPIEncryptionVerifier)encryptionInfo.getVerifier()).write(bos);
break; break;
default: default:
throw new RuntimeException("not supported"); throw new EncryptedDocumentException("not supported");
} }
out.write(data, 0, bos.getWriteIndex()); out.write(data, 0, bos.getWriteIndex());
@ -140,16 +140,16 @@ public final class FilePassRecord extends StandardRecord implements Cloneable {
@Override @Override
public String toString() { public String toString() {
StringBuffer buffer = new StringBuffer(); StringBuilder buffer = new StringBuilder();
buffer.append("[FILEPASS]\n"); 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(); String prefix = " ."+encryptionInfo.getEncryptionMode();
buffer.append(prefix+".info = ").append(HexDump.shortToHex(encryptionInfo.getVersionMajor())).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+".ver = ").append(HexDump.shortToHex(encryptionInfo.getVersionMinor())).append('\n');
buffer.append(prefix+".salt = ").append(HexDump.toHex(encryptionInfo.getVerifier().getSalt())).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+".verifier = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifier())).append('\n');
buffer.append(prefix+".verifierHash = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifierHash())).append("\n"); buffer.append(prefix+".verifierHash = ").append(HexDump.toHex(encryptionInfo.getVerifier().getEncryptedVerifierHash())).append('\n');
buffer.append("[/FILEPASS]\n"); buffer.append("[/FILEPASS]\n");
return buffer.toString(); return buffer.toString();
} }

View File

@ -32,16 +32,16 @@ import org.apache.poi.util.LittleEndianInputStream;
@Internal @Internal
public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
private final int _chunkSize; private final int chunkSize;
private final int _chunkBits; private final int chunkBits;
private final long _size; private final long size;
private final byte[] _chunk, _plain; private final byte[] chunk, plain;
private final Cipher _cipher; private final Cipher cipher;
private int _lastIndex; private int lastIndex;
private long _pos; private long pos;
private boolean _chunkIsValid = false; private boolean chunkIsValid = false;
public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize) public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize)
throws GeneralSecurityException { throws GeneralSecurityException {
@ -51,24 +51,24 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize, int initialPos) public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize, int initialPos)
throws GeneralSecurityException { throws GeneralSecurityException {
super(stream); super(stream);
_size = size; this.size = size;
_pos = initialPos; this.pos = initialPos;
this._chunkSize = chunkSize; this.chunkSize = chunkSize;
int cs = chunkSize == -1 ? 4096 : chunkSize; int cs = chunkSize == -1 ? 4096 : chunkSize;
_chunk = new byte[cs]; this.chunk = new byte[cs];
_plain = new byte[cs]; this.plain = new byte[cs];
_chunkBits = Integer.bitCount(_chunk.length-1); this.chunkBits = Integer.bitCount(chunk.length-1);
_lastIndex = (int)(_pos >> _chunkBits); this.lastIndex = (int)(pos >> chunkBits);
_cipher = initCipherForBlock(null, _lastIndex); this.cipher = initCipherForBlock(null, lastIndex);
} }
public final Cipher initCipherForBlock(int block) throws IOException, GeneralSecurityException { public final Cipher initCipherForBlock(int block) throws IOException, GeneralSecurityException {
if (_chunkSize != -1) { if (chunkSize != -1) {
throw new GeneralSecurityException("the cipher block can only be set for streaming encryption, e.g. CryptoAPI..."); throw new GeneralSecurityException("the cipher block can only be set for streaming encryption, e.g. CryptoAPI...");
} }
_chunkIsValid = false; chunkIsValid = false;
return initCipherForBlock(_cipher, block); return initCipherForBlock(cipher, block);
} }
protected abstract Cipher initCipherForBlock(Cipher existing, int block) protected abstract Cipher initCipherForBlock(Cipher existing, int block)
@ -100,28 +100,28 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
final int chunkMask = getChunkMask(); final int chunkMask = getChunkMask();
while (len > 0) { while (len > 0) {
if (!_chunkIsValid) { if (!chunkIsValid) {
try { try {
nextChunk(); nextChunk();
_chunkIsValid = true; chunkIsValid = true;
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new EncryptedDocumentException(e.getMessage(), e); throw new EncryptedDocumentException(e.getMessage(), e);
} }
} }
int count = (int)(_chunk.length - (_pos & chunkMask)); int count = (int)(chunk.length - (pos & chunkMask));
int avail = available(); int avail = available();
if (avail == 0) { if (avail == 0) {
return total; return total;
} }
count = Math.min(avail, Math.min(count, len)); count = Math.min(avail, Math.min(count, len));
System.arraycopy(readPlain ? _plain : _chunk, (int)(_pos & chunkMask), b, off, count); System.arraycopy(readPlain ? plain : chunk, (int)(pos & chunkMask), b, off, count);
off += count; off += count;
len -= count; len -= count;
_pos += count; pos += count;
if ((_pos & chunkMask) == 0) { if ((pos & chunkMask) == 0) {
_chunkIsValid = false; chunkIsValid = false;
} }
total += count; total += count;
} }
@ -131,13 +131,13 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
@Override @Override
public long skip(final long n) throws IOException { public long skip(final long n) throws IOException {
long start = _pos; long start = pos;
long skip = Math.min(remainingBytes(), n); long skip = Math.min(remainingBytes(), n);
if ((((_pos + skip) ^ start) & ~getChunkMask()) != 0) { if ((((pos + skip) ^ start) & ~getChunkMask()) != 0) {
_chunkIsValid = false; chunkIsValid = false;
} }
_pos += skip; pos += skip;
return skip; return skip;
} }
@ -152,7 +152,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
* @return the remaining byte until EOF * @return the remaining byte until EOF
*/ */
private int remainingBytes() { private int remainingBytes() {
return (int)(_size - _pos); return (int)(size - pos);
} }
@Override @Override
@ -171,35 +171,35 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
} }
protected int getChunkMask() { protected int getChunkMask() {
return _chunk.length-1; return chunk.length-1;
} }
private void nextChunk() throws GeneralSecurityException, IOException { private void nextChunk() throws GeneralSecurityException, IOException {
if (_chunkSize != -1) { if (chunkSize != -1) {
int index = (int)(_pos >> _chunkBits); int index = (int)(pos >> chunkBits);
initCipherForBlock(_cipher, index); initCipherForBlock(cipher, index);
if (_lastIndex != index) { if (lastIndex != index) {
super.skip((index - _lastIndex) << _chunkBits); super.skip((index - lastIndex) << chunkBits);
} }
_lastIndex = index + 1; lastIndex = index + 1;
} }
final int todo = (int)Math.min(_size, _chunk.length); final int todo = (int)Math.min(size, chunk.length);
int readBytes = 0, totalBytes = 0; int readBytes = 0, totalBytes = 0;
do { do {
readBytes = super.read(_plain, totalBytes, todo-totalBytes); readBytes = super.read(plain, totalBytes, todo-totalBytes);
totalBytes += Math.max(0, readBytes); totalBytes += Math.max(0, readBytes);
} while (readBytes != -1 && totalBytes < todo); } while (readBytes != -1 && totalBytes < todo);
if (readBytes == -1 && _pos+totalBytes < _size && _size < Integer.MAX_VALUE) { if (readBytes == -1 && pos+totalBytes < size && size < Integer.MAX_VALUE) {
throw new EOFException("buffer underrun"); throw new EOFException("buffer underrun");
} }
System.arraycopy(_plain, 0, _chunk, 0, totalBytes); System.arraycopy(plain, 0, chunk, 0, totalBytes);
invokeCipher(totalBytes, totalBytes == _chunkSize); invokeCipher(totalBytes, totalBytes == chunkSize);
} }
/** /**
@ -212,9 +212,9 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
*/ */
protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException { protected int invokeCipher(int totalBytes, boolean doFinal) throws GeneralSecurityException {
if (doFinal) { if (doFinal) {
return _cipher.doFinal(_chunk, 0, totalBytes, _chunk); return cipher.doFinal(chunk, 0, totalBytes, chunk);
} else { } else {
return _cipher.update(_chunk, 0, totalBytes, _chunk); return cipher.update(chunk, 0, totalBytes, chunk);
} }
} }
@ -258,20 +258,20 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream {
* @return the chunk bytes * @return the chunk bytes
*/ */
protected byte[] getChunk() { protected byte[] getChunk() {
return _chunk; return chunk;
} }
/** /**
* @return the plain bytes * @return the plain bytes
*/ */
protected byte[] getPlain() { protected byte[] getPlain() {
return _plain; return plain;
} }
/** /**
* @return the absolute position in the stream * @return the absolute position in the stream
*/ */
public long getPos() { public long getPos() {
return _pos; return pos;
} }
} }

View File

@ -49,47 +49,50 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class); private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class);
private static final int STREAMING = -1; private static final int STREAMING = -1;
private final int _chunkSize; private final int chunkSize;
private final int _chunkBits; private final int chunkBits;
private final byte[] _chunk; private final byte[] chunk;
private final BitSet _plainByteFlags; private final BitSet plainByteFlags;
private final File _fileOut; private final File fileOut;
private final DirectoryNode _dir; private final DirectoryNode dir;
private long _pos = 0; private long pos = 0;
private long _totalPos = 0; private long totalPos = 0;
private long _written = 0; private long written = 0;
private Cipher _cipher;
// the cipher can't be final, because for the last chunk we change the padding
// and therefore need to change the cipher too
private Cipher cipher;
public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException { public ChunkedCipherOutputStream(DirectoryNode dir, int chunkSize) throws IOException, GeneralSecurityException {
super(null); super(null);
this._chunkSize = chunkSize; this.chunkSize = chunkSize;
int cs = chunkSize == STREAMING ? 4096 : chunkSize; int cs = chunkSize == STREAMING ? 4096 : chunkSize;
_chunk = new byte[cs]; this.chunk = new byte[cs];
_plainByteFlags = new BitSet(cs); this.plainByteFlags = new BitSet(cs);
_chunkBits = Integer.bitCount(cs-1); this.chunkBits = Integer.bitCount(cs-1);
_fileOut = TempFile.createTempFile("encrypted_package", "crypt"); this.fileOut = TempFile.createTempFile("encrypted_package", "crypt");
_fileOut.deleteOnExit(); this.fileOut.deleteOnExit();
this.out = new FileOutputStream(_fileOut); this.out = new FileOutputStream(fileOut);
this._dir = dir; this.dir = dir;
_cipher = initCipherForBlock(null, 0, false); this.cipher = initCipherForBlock(null, 0, false);
} }
public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException { public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException {
super(stream); super(stream);
this._chunkSize = chunkSize; this.chunkSize = chunkSize;
int cs = chunkSize == STREAMING ? 4096 : chunkSize; int cs = chunkSize == STREAMING ? 4096 : chunkSize;
_chunk = new byte[cs]; this.chunk = new byte[cs];
_plainByteFlags = new BitSet(cs); this.plainByteFlags = new BitSet(cs);
_chunkBits = Integer.bitCount(cs-1); this.chunkBits = Integer.bitCount(cs-1);
_fileOut = null; this.fileOut = null;
_dir = null; this.dir = null;
_cipher = initCipherForBlock(null, 0, false); this.cipher = initCipherForBlock(null, 0, false);
} }
public final Cipher initCipherForBlock(int block, boolean lastChunk) throws IOException, GeneralSecurityException { public final Cipher initCipherForBlock(int block, boolean lastChunk) throws IOException, GeneralSecurityException {
return initCipherForBlock(_cipher, block, lastChunk); return initCipherForBlock(cipher, block, lastChunk);
} }
protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk) protected abstract Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk)
@ -131,40 +134,40 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
final int chunkMask = getChunkMask(); final int chunkMask = getChunkMask();
while (len > 0) { while (len > 0) {
int posInChunk = (int)(_pos & chunkMask); int posInChunk = (int)(pos & chunkMask);
int nextLen = Math.min(_chunk.length-posInChunk, len); int nextLen = Math.min(chunk.length-posInChunk, len);
System.arraycopy(b, off, _chunk, posInChunk, nextLen); System.arraycopy(b, off, chunk, posInChunk, nextLen);
if (writePlain) { if (writePlain) {
_plainByteFlags.set(posInChunk, posInChunk+nextLen); plainByteFlags.set(posInChunk, posInChunk+nextLen);
} }
_pos += nextLen; pos += nextLen;
_totalPos += nextLen; totalPos += nextLen;
off += nextLen; off += nextLen;
len -= nextLen; len -= nextLen;
if ((_pos & chunkMask) == 0) { if ((pos & chunkMask) == 0) {
writeChunk(len > 0); writeChunk(len > 0);
} }
} }
} }
protected int getChunkMask() { protected int getChunkMask() {
return _chunk.length-1; return chunk.length-1;
} }
protected void writeChunk(boolean continued) throws IOException { protected void writeChunk(boolean continued) throws IOException {
if (_pos == 0 || _totalPos == _written) { if (pos == 0 || totalPos == written) {
return; return;
} }
int posInChunk = (int)(_pos & getChunkMask()); int posInChunk = (int)(pos & getChunkMask());
// normally posInChunk is 0, i.e. on the next chunk (-> index-1) // normally posInChunk is 0, i.e. on the next chunk (-> index-1)
// but if called on close(), posInChunk is somewhere within the chunk data // but if called on close(), posInChunk is somewhere within the chunk data
int index = (int)(_pos >> _chunkBits); int index = (int)(pos >> chunkBits);
boolean lastChunk; boolean lastChunk;
if (posInChunk==0) { if (posInChunk==0) {
index--; index--;
posInChunk = _chunk.length; posInChunk = chunk.length;
lastChunk = false; lastChunk = false;
} else { } else {
// pad the last chunk // pad the last chunk
@ -174,27 +177,27 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
int ciLen; int ciLen;
try { try {
boolean doFinal = true; boolean doFinal = true;
long oldPos = _pos; long oldPos = pos;
// reset stream (not only) in case we were interrupted by plain stream parts // reset stream (not only) in case we were interrupted by plain stream parts
// this also needs to be set to prevent an endless loop // this also needs to be set to prevent an endless loop
_pos = 0; pos = 0;
if (_chunkSize == STREAMING) { if (chunkSize == STREAMING) {
if (continued) { if (continued) {
doFinal = false; doFinal = false;
} }
} else { } else {
_cipher = initCipherForBlock(_cipher, index, lastChunk); cipher = initCipherForBlock(cipher, index, lastChunk);
// restore pos - only streaming chunks will be reset // restore pos - only streaming chunks will be reset
_pos = oldPos; pos = oldPos;
} }
ciLen = invokeCipher(posInChunk, doFinal); ciLen = invokeCipher(posInChunk, doFinal);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new IOException("can't re-/initialize cipher", e); throw new IOException("can't re-/initialize cipher", e);
} }
out.write(_chunk, 0, ciLen); out.write(chunk, 0, ciLen);
_plainByteFlags.clear(); plainByteFlags.clear();
_written += ciLen; written += ciLen;
} }
/** /**
@ -206,14 +209,14 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
* @throws ShortBufferException * @throws ShortBufferException
*/ */
protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException { protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException {
byte plain[] = (_plainByteFlags.isEmpty()) ? null : _chunk.clone(); byte plain[] = (plainByteFlags.isEmpty()) ? null : chunk.clone();
int ciLen = (doFinal) int ciLen = (doFinal)
? _cipher.doFinal(_chunk, 0, posInChunk, _chunk) ? cipher.doFinal(chunk, 0, posInChunk, chunk)
: _cipher.update(_chunk, 0, posInChunk, _chunk); : cipher.update(chunk, 0, posInChunk, chunk);
for (int i = _plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = _plainByteFlags.nextSetBit(i+1)) { for (int i = plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = plainByteFlags.nextSetBit(i+1)) {
_chunk[i] = plain[i]; chunk[i] = plain[i];
} }
return ciLen; return ciLen;
@ -226,11 +229,11 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
super.close(); super.close();
if (_fileOut != null) { if (fileOut != null) {
int oleStreamSize = (int)(_fileOut.length()+LittleEndianConsts.LONG_SIZE); int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE);
calculateChecksum(_fileOut, (int)_pos); calculateChecksum(fileOut, (int)pos);
_dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter()); dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter());
createEncryptionInfoEntry(_dir, _fileOut); createEncryptionInfoEntry(dir, fileOut);
} }
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new IOException(e); throw new IOException(e);
@ -238,19 +241,19 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
} }
protected byte[] getChunk() { protected byte[] getChunk() {
return _chunk; return chunk;
} }
protected BitSet getPlainByteFlags() { protected BitSet getPlainByteFlags() {
return _plainByteFlags; return plainByteFlags;
} }
protected long getPos() { protected long getPos() {
return _pos; return pos;
} }
protected long getTotalPos() { protected long getTotalPos() {
return _totalPos; return totalPos;
} }
/** /**
@ -274,17 +277,17 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream {
// Note that the actual size of the \EncryptedPackage stream (1) can be larger than this // Note that the actual size of the \EncryptedPackage stream (1) can be larger than this
// value, depending on the block size of the chosen encryption algorithm // value, depending on the block size of the chosen encryption algorithm
byte buf[] = new byte[LittleEndianConsts.LONG_SIZE]; byte buf[] = new byte[LittleEndianConsts.LONG_SIZE];
LittleEndian.putLong(buf, 0, _pos); LittleEndian.putLong(buf, 0, pos);
os.write(buf); os.write(buf);
FileInputStream fis = new FileInputStream(_fileOut); FileInputStream fis = new FileInputStream(fileOut);
IOUtils.copy(fis, os); IOUtils.copy(fis, os);
fis.close(); fis.close();
os.close(); os.close();
if (!_fileOut.delete()) { if (!fileOut.delete()) {
LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+_fileOut); LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut);
} }
} catch (IOException e) { } catch (IOException e) {
throw new EncryptedDocumentException(e); throw new EncryptedDocumentException(e);

View File

@ -67,7 +67,7 @@ public abstract class Decryptor implements Cloneable {
*/ */
public InputStream getDataStream(InputStream stream, int size, int initialPos) public InputStream getDataStream(InputStream stream, int size, int initialPos)
throws IOException, GeneralSecurityException { 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");
} }
/** /**
@ -78,7 +78,7 @@ public abstract class Decryptor implements Cloneable {
* @param chunkSize the chunk size, i.e. the block size with the same encryption key * @param chunkSize the chunk size, i.e. the block size with the same encryption key
*/ */
public void setChunkSize(int chunkSize) { 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");
} }
/** /**
@ -91,7 +91,7 @@ public abstract class Decryptor implements Cloneable {
*/ */
public Cipher initCipherForBlock(Cipher cipher, int block) public Cipher initCipherForBlock(Cipher cipher, int block)
throws GeneralSecurityException { 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) public abstract boolean verifyPassword(String password)

View File

@ -23,6 +23,7 @@ import java.security.GeneralSecurityException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.filesystem.DirectoryNode; import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;
@ -63,7 +64,7 @@ public abstract class Encryptor implements Cloneable {
public ChunkedCipherOutputStream getDataStream(OutputStream stream, int initialOffset) public ChunkedCipherOutputStream getDataStream(OutputStream stream, int initialOffset)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
throw new RuntimeException("this decryptor doesn't support writing directly to a stream"); throw new EncryptedDocumentException("this decryptor doesn't support writing directly to a stream");
} }
public SecretKey getSecretKey() { public SecretKey getSecretKey() {
@ -90,7 +91,7 @@ public abstract class Encryptor implements Cloneable {
* @param chunkSize the chunk size, i.e. the block size with the same encryption key * @param chunkSize the chunk size, i.e. the block size with the same encryption key
*/ */
public void setChunkSize(int chunkSize) { 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");
} }
@Override @Override

View File

@ -35,8 +35,8 @@ import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.StringUtil; import org.apache.poi.util.StringUtil;
public class BinaryRC4Decryptor extends Decryptor implements Cloneable { public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
private long _length = -1L; private long length = -1L;
private int _chunkSize = 512; private int chunkSize = 512;
private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream { private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream {
@ -48,12 +48,12 @@ public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
public BinaryRC4CipherInputStream(DocumentInputStream stream, long size) public BinaryRC4CipherInputStream(DocumentInputStream stream, long size)
throws GeneralSecurityException { throws GeneralSecurityException {
super(stream, size, _chunkSize); super(stream, size, chunkSize);
} }
public BinaryRC4CipherInputStream(InputStream stream) public BinaryRC4CipherInputStream(InputStream stream)
throws GeneralSecurityException { throws GeneralSecurityException {
super(stream, Integer.MAX_VALUE, _chunkSize); super(stream, Integer.MAX_VALUE, chunkSize);
} }
} }
@ -134,8 +134,8 @@ public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException, public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException,
GeneralSecurityException { GeneralSecurityException {
DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY); DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);
_length = dis.readLong(); length = dis.readLong();
return new BinaryRC4CipherInputStream(dis, _length); return new BinaryRC4CipherInputStream(dis, length);
} }
@Override @Override
@ -147,16 +147,16 @@ public class BinaryRC4Decryptor extends Decryptor implements Cloneable {
@Override @Override
public long getLength() { public long getLength() {
if (_length == -1L) { if (length == -1L) {
throw new IllegalStateException("Decryptor.getDataStream() was not called"); throw new IllegalStateException("Decryptor.getDataStream() was not called");
} }
return _length; return length;
} }
@Override @Override
public void setChunkSize(int chunkSize) { public void setChunkSize(int chunkSize) {
_chunkSize = chunkSize; this.chunkSize = chunkSize;
} }
@Override @Override

View File

@ -41,7 +41,7 @@ import org.apache.poi.util.LittleEndianByteArrayOutputStream;
public class BinaryRC4Encryptor extends Encryptor implements Cloneable { public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
private int _chunkSize = 512; private int chunkSize = 512;
protected BinaryRC4Encryptor() { protected BinaryRC4Encryptor() {
} }
@ -115,7 +115,7 @@ public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
@Override @Override
public void setChunkSize(int chunkSize) { public void setChunkSize(int chunkSize) {
_chunkSize = chunkSize; this.chunkSize = chunkSize;
} }
@Override @Override
@ -127,12 +127,12 @@ public class BinaryRC4Encryptor extends Encryptor implements Cloneable {
public BinaryRC4CipherOutputStream(OutputStream stream) public BinaryRC4CipherOutputStream(OutputStream stream)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
super(stream, BinaryRC4Encryptor.this._chunkSize); super(stream, BinaryRC4Encryptor.this.chunkSize);
} }
public BinaryRC4CipherOutputStream(DirectoryNode dir) public BinaryRC4CipherOutputStream(DirectoryNode dir)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
super(dir, BinaryRC4Encryptor.this._chunkSize); super(dir, BinaryRC4Encryptor.this.chunkSize);
} }
@Override @Override

View File

@ -50,8 +50,8 @@ import org.apache.poi.util.StringUtil;
public class CryptoAPIDecryptor extends Decryptor implements Cloneable { public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
private long _length; private long length = -1L;
private int _chunkSize = -1; private int chunkSize = -1;
static class StreamDescriptorEntry { static class StreamDescriptorEntry {
static BitField flagStream = BitFieldFactory.getInstance(1); static BitField flagStream = BitFieldFactory.getInstance(1);
@ -65,7 +65,6 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
} }
protected CryptoAPIDecryptor() { protected CryptoAPIDecryptor() {
_length = -1L;
} }
@Override @Override
@ -209,14 +208,14 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
*/ */
@Override @Override
public long getLength() { public long getLength() {
if (_length == -1L) { if (length == -1L) {
throw new IllegalStateException("Decryptor.getDataStream() was not called"); throw new IllegalStateException("Decryptor.getDataStream() was not called");
} }
return _length; return length;
} }
public void setChunkSize(int chunkSize) { public void setChunkSize(int chunkSize) {
_chunkSize = chunkSize; this.chunkSize = chunkSize;
} }
@Override @Override
@ -234,7 +233,7 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
public CryptoAPICipherInputStream(InputStream stream, long size, int initialPos) public CryptoAPICipherInputStream(InputStream stream, long size, int initialPos)
throws GeneralSecurityException { throws GeneralSecurityException {
super(stream, size, _chunkSize, initialPos); super(stream, size, chunkSize, initialPos);
} }
} }
} }

View File

@ -29,13 +29,13 @@ import org.apache.poi.util.Internal;
*/ */
@Internal @Internal
/* package */ class CryptoAPIDocumentOutputStream extends ByteArrayOutputStream { /* package */ class CryptoAPIDocumentOutputStream extends ByteArrayOutputStream {
private Cipher cipher; private final Cipher cipher;
private CryptoAPIEncryptor encryptor; private final CryptoAPIEncryptor encryptor;
private byte oneByte[] = { 0 }; private final byte oneByte[] = { 0 };
public CryptoAPIDocumentOutputStream(CryptoAPIEncryptor encryptor) throws GeneralSecurityException { public CryptoAPIDocumentOutputStream(CryptoAPIEncryptor encryptor) throws GeneralSecurityException {
this.encryptor = encryptor; this.encryptor = encryptor;
setBlock(0); cipher = encryptor.initCipherForBlock(null, 0);
} }
public byte[] getBuf() { public byte[] getBuf() {
@ -47,7 +47,7 @@ import org.apache.poi.util.Internal;
} }
public void setBlock(int block) throws GeneralSecurityException { public void setBlock(int block) throws GeneralSecurityException {
cipher = encryptor.initCipherForBlock(cipher, block); encryptor.initCipherForBlock(cipher, block);
} }
@Override @Override

View File

@ -53,7 +53,7 @@ import org.apache.poi.util.StringUtil;
public class CryptoAPIEncryptor extends Encryptor implements Cloneable { public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
private int _chunkSize = 512; private int chunkSize = 512;
protected CryptoAPIEncryptor() { protected CryptoAPIEncryptor() {
} }
@ -215,7 +215,7 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
@Override @Override
public void setChunkSize(int chunkSize) { public void setChunkSize(int chunkSize) {
_chunkSize = chunkSize; this.chunkSize = chunkSize;
} }
protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException { protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException {
@ -259,12 +259,12 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
@Override @Override
protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile) protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
throw new RuntimeException("createEncryptionInfoEntry not supported"); throw new EncryptedDocumentException("createEncryptionInfoEntry not supported");
} }
public CryptoAPICipherOutputStream(OutputStream stream) public CryptoAPICipherOutputStream(OutputStream stream)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
super(stream, CryptoAPIEncryptor.this._chunkSize); super(stream, CryptoAPIEncryptor.this.chunkSize);
} }
@Override @Override

View File

@ -25,6 +25,7 @@ import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.crypt.ChunkedCipherInputStream; import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.Decryptor; import org.apache.poi.poifs.crypt.Decryptor;
@ -33,8 +34,8 @@ import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.util.LittleEndian; import org.apache.poi.util.LittleEndian;
public class XORDecryptor extends Decryptor implements Cloneable { public class XORDecryptor extends Decryptor implements Cloneable {
private long _length = -1L; private long length = -1L;
private int _chunkSize = 512; private int chunkSize = 512;
protected XORDecryptor() { protected XORDecryptor() {
} }
@ -69,7 +70,7 @@ public class XORDecryptor extends Decryptor implements Cloneable {
@Override @Override
public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException { public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
throw new RuntimeException("not supported"); throw new EncryptedDocumentException("not supported");
} }
@Override @Override
@ -81,16 +82,16 @@ public class XORDecryptor extends Decryptor implements Cloneable {
@Override @Override
public long getLength() { public long getLength() {
if (_length == -1L) { if (length == -1L) {
throw new IllegalStateException("Decryptor.getDataStream() was not called"); throw new IllegalStateException("Decryptor.getDataStream() was not called");
} }
return _length; return length;
} }
@Override @Override
public void setChunkSize(int chunkSize) { public void setChunkSize(int chunkSize) {
_chunkSize = chunkSize; this.chunkSize = chunkSize;
} }
@Override @Override
@ -99,14 +100,14 @@ public class XORDecryptor extends Decryptor implements Cloneable {
} }
private class XORCipherInputStream extends ChunkedCipherInputStream { private class XORCipherInputStream extends ChunkedCipherInputStream {
private final int _initialOffset; private final int initialOffset;
private int _recordStart = 0; private int recordStart = 0;
private int _recordEnd = 0; private int recordEnd = 0;
public XORCipherInputStream(InputStream stream, int initialPos) public XORCipherInputStream(InputStream stream, int initialPos)
throws GeneralSecurityException { throws GeneralSecurityException {
super(stream, Integer.MAX_VALUE, _chunkSize); super(stream, Integer.MAX_VALUE, chunkSize);
_initialOffset = initialPos; this.initialOffset = initialPos;
} }
@Override @Override
@ -133,9 +134,9 @@ public class XORDecryptor extends Decryptor implements Cloneable {
* the time we are about to write each of the bytes of the record data. * 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. * 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 // The following is taken from the Libre Office implementation
// It seems that the encrypt and decrypt method is mixed up // It seems that the encrypt and decrypt method is mixed up
// in the MS-OFFCRYPTO docs // in the MS-OFFCRYPTO docs
@ -165,8 +166,8 @@ public class XORDecryptor extends Decryptor implements Cloneable {
final int pos = (int)getPos(); final int pos = (int)getPos();
final byte chunk[] = getChunk(); final byte chunk[] = getChunk();
final int chunkMask = getChunkMask(); final int chunkMask = getChunkMask();
_recordStart = pos; recordStart = pos;
_recordEnd = _recordStart+recordSize; recordEnd = recordStart+recordSize;
int nextBytes = Math.min(recordSize, chunk.length-(pos & chunkMask)); int nextBytes = Math.min(recordSize, chunk.length-(pos & chunkMask));
invokeCipher(nextBytes, true); invokeCipher(nextBytes, true);
} }

View File

@ -60,12 +60,12 @@ public class XOREncryptionVerifier extends EncryptionVerifier implements Encrypt
} }
@Override @Override
protected void setEncryptedVerifier(byte[] encryptedVerifier) { protected final void setEncryptedVerifier(byte[] encryptedVerifier) {
super.setEncryptedVerifier(encryptedVerifier); super.setEncryptedVerifier(encryptedVerifier);
} }
@Override @Override
protected void setEncryptedKey(byte[] encryptedKey) { protected final void setEncryptedKey(byte[] encryptedKey) {
super.setEncryptedKey(encryptedKey); super.setEncryptedKey(encryptedKey);
} }
} }

View File

@ -61,8 +61,7 @@ public class XOREncryptor extends Encryptor implements Cloneable {
@Override @Override
public OutputStream getDataStream(DirectoryNode dir) public OutputStream getDataStream(DirectoryNode dir)
throws IOException, GeneralSecurityException { throws IOException, GeneralSecurityException {
OutputStream countStream = new XORCipherOutputStream(dir); return new XORCipherOutputStream(dir);
return countStream;
} }
@Override @Override
@ -89,19 +88,15 @@ public class XOREncryptor extends Encryptor implements Cloneable {
} }
private class XORCipherOutputStream extends ChunkedCipherOutputStream { private class XORCipherOutputStream extends ChunkedCipherOutputStream {
private final int _initialOffset; private int recordStart = 0;
private int _recordStart = 0; private int recordEnd = 0;
private int _recordEnd = 0;
private boolean _isPlain = false;
public XORCipherOutputStream(OutputStream stream, int initialPos) throws IOException, GeneralSecurityException { public XORCipherOutputStream(OutputStream stream, int initialPos) throws IOException, GeneralSecurityException {
super(stream, -1); super(stream, -1);
_initialOffset = initialPos;
} }
public XORCipherOutputStream(DirectoryNode dir) throws IOException, GeneralSecurityException { public XORCipherOutputStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
super(dir, -1); super(dir, -1);
_initialOffset = 0;
} }
@Override @Override
@ -122,13 +117,12 @@ public class XOREncryptor extends Encryptor implements Cloneable {
@Override @Override
public void setNextRecordSize(int recordSize, boolean isPlain) { public void setNextRecordSize(int recordSize, boolean isPlain) {
if (_recordEnd > 0 && !_isPlain) { if (recordEnd > 0 && !isPlain) {
// encrypt last record // encrypt last record
invokeCipher((int)getPos(), true); invokeCipher((int)getPos(), true);
} }
_recordStart = (int)getTotalPos()+4; recordStart = (int)getTotalPos()+4;
_recordEnd = _recordStart+recordSize; recordEnd = recordStart+recordSize;
_isPlain = isPlain;
} }
@Override @Override
@ -143,7 +137,7 @@ public class XOREncryptor extends Encryptor implements Cloneable {
return 0; 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 BitSet plainBytes = getPlainByteFlags();
final byte xorArray[] = getEncryptionInfo().getEncryptor().getSecretKey().getEncoded(); final byte xorArray[] = getEncryptionInfo().getEncryptor().getSecretKey().getEncoded();
@ -161,7 +155,7 @@ public class XOREncryptor extends Encryptor implements Cloneable {
* This (the value) is then incremented after each byte is written. * This (the value) is then incremented after each byte is written.
*/ */
// ... also need to handle invocation in case of a filled chunk // ... 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++) { for (int i=start; i < posInChunk; i++) {
byte value = chunk[i]; byte value = chunk[i];

View File

@ -573,8 +573,7 @@ public class POIXMLDocumentPart {
} }
// Default to searching from 1, unless they asked for 0+ // Default to searching from 1, unless they asked for 0+
int idx = minIdx; int idx = (minIdx < 0) ? 1 : minIdx;
if (minIdx < 0) idx = 1;
int maxIdx = minIdx + pkg.getParts().size(); int maxIdx = minIdx + pkg.getParts().size();
while (idx <= maxIdx) { while (idx <= maxIdx) {
name = descriptor.getFileName(idx); name = descriptor.getFileName(idx);

View File

@ -21,7 +21,6 @@ import java.security.GeneralSecurityException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -68,11 +67,11 @@ public class AgileEncryptionVerifier extends EncryptionVerifier implements Clone
throw new EncryptedDocumentException("Unable to parse keyData", e); throw new EncryptedDocumentException("Unable to parse keyData", e);
} }
int keyBits = (int)keyData.getKeyBits(); int kb = (int)keyData.getKeyBits();
CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), keyBits); CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), kb);
setCipherAlgorithm(ca); setCipherAlgorithm(ca);
setKeySize(keyBits); setKeySize(kb);
int blockSize = keyData.getBlockSize(); int blockSize = keyData.getBlockSize();
setBlockSize(blockSize); setBlockSize(blockSize);
@ -230,7 +229,7 @@ public class AgileEncryptionVerifier extends EncryptionVerifier implements Clone
} }
@Override @Override
protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) { protected final void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
super.setCipherAlgorithm(cipherAlgorithm); super.setCipherAlgorithm(cipherAlgorithm);
if (cipherAlgorithm.allowedKeySize.length == 1) { if (cipherAlgorithm.allowedKeySize.length == 1) {
setKeySize(cipherAlgorithm.defaultKeySize); setKeySize(cipherAlgorithm.defaultKeySize);

View File

@ -17,7 +17,7 @@
* ==================================================================== * ====================================================================
*/ */
package org.apache.poi.crypt.examples; package org.apache.poi.poifs.crypt.temp;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -42,18 +42,24 @@ import org.apache.poi.openxml4j.util.ZipEntrySource;
import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.ChainingMode;
import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.CipherAlgorithm;
import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.util.Beta;
import org.apache.poi.util.IOUtils; 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.util.TempFile;
/** /**
* An example <code>ZipEntrySource</code> that has encrypted temp files to ensure that * An example <code>ZipEntrySource</code> that has encrypted temp files to ensure that
* sensitive data is not stored in raw format on disk. * sensitive data is not stored in raw format on disk.
*/ */
@Beta
public class AesZipFileZipEntrySource implements ZipEntrySource { public class AesZipFileZipEntrySource implements ZipEntrySource {
final File tmpFile; private static POILogger LOG = POILogFactory.getLogger(AesZipFileZipEntrySource.class);
final ZipFile zipFile;
final Cipher ci; private final File tmpFile;
boolean closed; private final ZipFile zipFile;
private final Cipher ci;
private boolean closed;
public AesZipFileZipEntrySource(File tmpFile, Cipher ci) throws IOException { public AesZipFileZipEntrySource(File tmpFile, Cipher ci) throws IOException {
this.tmpFile = tmpFile; this.tmpFile = tmpFile;
@ -81,7 +87,9 @@ public class AesZipFileZipEntrySource implements ZipEntrySource {
public void close() throws IOException { public void close() throws IOException {
if(!closed) { if(!closed) {
zipFile.close(); zipFile.close();
tmpFile.delete(); if (!tmpFile.delete()) {
LOG.log(POILogger.WARN, tmpFile.getAbsolutePath()+" can't be removed (or was already removed.");
};
} }
closed = true; closed = true;
} }

View File

@ -17,7 +17,7 @@
* ==================================================================== * ====================================================================
*/ */
package org.apache.poi.crypt.examples; package org.apache.poi.poifs.crypt.temp;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -35,16 +35,23 @@ import javax.crypto.spec.SecretKeySpec;
import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.ChainingMode;
import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.CipherAlgorithm;
import org.apache.poi.poifs.crypt.CryptoFunctions; 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; import org.apache.poi.util.TempFile;
/** /**
* EncryptedTempData can be used to buffer binary data in a secure way, by using encrypted temp files. * EncryptedTempData can be used to buffer binary data in a secure way, by using encrypted temp files.
*/ */
@Beta
public class EncryptedTempData { public class EncryptedTempData {
final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128; private static POILogger LOG = POILogFactory.getLogger(EncryptedTempData.class);
final SecretKeySpec skeySpec;
final byte[] ivBytes; private final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128;
final File tempFile; private final SecretKeySpec skeySpec;
private final byte[] ivBytes;
private final File tempFile;
public EncryptedTempData() throws IOException { public EncryptedTempData() throws IOException {
SecureRandom sr = new SecureRandom(); SecureRandom sr = new SecureRandom();
@ -67,6 +74,8 @@ public class EncryptedTempData {
} }
public void dispose() { public void dispose() {
tempFile.delete(); if (!tempFile.delete()) {
LOG.log(POILogger.WARN, tempFile.getAbsolutePath()+" can't be removed (or was already removed.");
}
} }
} }

View File

@ -17,32 +17,23 @@
* ==================================================================== * ====================================================================
*/ */
package org.apache.poi.xssf.streaming.examples; package org.apache.poi.poifs.crypt.temp;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.security.GeneralSecurityException; 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.openxml4j.util.ZipEntrySource;
import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.util.Beta;
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.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.SXSSFWorkbook;
import org.apache.poi.xssf.streaming.SheetDataWriter; import org.apache.poi.xssf.streaming.SheetDataWriter;
@Beta
public class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook { public class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook {
private static final POILogger LOG = POILogFactory.getLogger(SXSSFWorkbookWithCustomZipEntrySource.class);
public SXSSFWorkbookWithCustomZipEntrySource() { public SXSSFWorkbookWithCustomZipEntrySource() {
super(20); super(20);
@ -74,40 +65,9 @@ public class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook {
@Override @Override
protected SheetDataWriter createSheetDataWriter() throws IOException { 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(); 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);
}
}
} }

View File

@ -0,0 +1,73 @@
/*
* ====================================================================
* 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);
}
}

View File

@ -230,9 +230,8 @@ public class XSSFReader {
List<CTSheet> validSheets = new ArrayList<CTSheet>(); List<CTSheet> validSheets = new ArrayList<CTSheet>();
for (CTSheet ctSheet : wbBean.getSheets().getSheetList()) { for (CTSheet ctSheet : wbBean.getSheets().getSheetList()) {
//if there's no relationship id, silently skip the sheet //if there's no relationship id, silently skip the sheet
if ("".equals(ctSheet.getId())) { String sheetId = ctSheet.getId();
//skip it if (sheetId != null && sheetId.length() > 0) {
} else {
validSheets.add(ctSheet); validSheets.add(ctSheet);
} }
} }

View File

@ -127,7 +127,7 @@ public class SheetDataWriter {
_out.close(); _out.close();
} }
File getTempFile(){ protected File getTempFile(){
return _fd; return _fd;
} }

View File

@ -16,8 +16,6 @@
==================================================================== */ ==================================================================== */
package org.apache.poi.xwpf.model; 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;
import org.apache.poi.POIXMLDocumentPart.RelationPart; import org.apache.poi.POIXMLDocumentPart.RelationPart;
import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogFactory;
@ -65,8 +63,6 @@ import com.microsoft.schemas.vml.STTrueFalse;
* the right headers and footers for the document. * the right headers and footers for the document.
*/ */
public class XWPFHeaderFooterPolicy { public class XWPFHeaderFooterPolicy {
private static final POILogger LOG = POILogFactory.getLogger(XWPFHeaderFooterPolicy.class);
public static final Enum DEFAULT = STHdrFtr.DEFAULT; public static final Enum DEFAULT = STHdrFtr.DEFAULT;
public static final Enum EVEN = STHdrFtr.EVEN; public static final Enum EVEN = STHdrFtr.EVEN;
public static final Enum FIRST = STHdrFtr.FIRST; public static final Enum FIRST = STHdrFtr.FIRST;
@ -282,7 +278,7 @@ public class XWPFHeaderFooterPolicy {
CTP p = ftr.addNewP(); CTP p = ftr.addNewP();
ftr.setPArray(i, paragraphs[i].getCTP()); ftr.setPArray(i, paragraphs[i].getCTP());
} }
} else { // } else {
// CTP p = ftr.addNewP(); // CTP p = ftr.addNewP();
// CTBody body = doc.getDocument().getBody(); // CTBody body = doc.getDocument().getBody();
// if (body.sizeOfPArray() > 0) { // if (body.sizeOfPArray() > 0) {

View File

@ -449,7 +449,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
CTOnOff titlePg = ctSectPr.addNewTitlePg(); CTOnOff titlePg = ctSectPr.addNewTitlePg();
titlePg.setVal(STOnOff.ON); titlePg.setVal(STOnOff.ON);
} }
} else if (type == HeaderFooterType.EVEN) { // } else if (type == HeaderFooterType.EVEN) {
// TODO Add support for Even/Odd headings and footers // TODO Add support for Even/Odd headings and footers
} }
return hfPolicy.createHeader(STHdrFtr.Enum.forInt(type.toInt())); return hfPolicy.createHeader(STHdrFtr.Enum.forInt(type.toInt()));
@ -471,7 +471,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody {
CTOnOff titlePg = ctSectPr.addNewTitlePg(); CTOnOff titlePg = ctSectPr.addNewTitlePg();
titlePg.setVal(STOnOff.ON); titlePg.setVal(STOnOff.ON);
} }
} else if (type == HeaderFooterType.EVEN) { // } else if (type == HeaderFooterType.EVEN) {
// TODO Add support for Even/Odd headings and footers // TODO Add support for Even/Odd headings and footers
} }
return hfPolicy.createFooter(STHdrFtr.Enum.forInt(type.toInt())); return hfPolicy.createFooter(STHdrFtr.Enum.forInt(type.toInt()));

View File

@ -1,140 +0,0 @@
/* ====================================================================
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);
}
}

View File

@ -29,6 +29,7 @@ import java.security.GeneralSecurityException;
import org.apache.poi.openxml4j.exceptions.OpenXML4JException; import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.util.ZipEntrySource; 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.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.xssf.XSSFTestDataSamples; import org.apache.poi.xssf.XSSFTestDataSamples;
import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor; import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor;

View File

@ -21,8 +21,8 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.poi.xssf.streaming.TestSXSSFWorkbookWithCustomZipEntrySource.SXSSFWorkbookWithCustomZipEntrySource; import org.apache.poi.poifs.crypt.temp.SXSSFWorkbookWithCustomZipEntrySource;
import org.apache.poi.xssf.streaming.TestSXSSFWorkbookWithCustomZipEntrySource.SheetDataWriterWithDecorator; import org.apache.poi.poifs.crypt.temp.SheetDataWriterWithDecorator;
// a class to record a list of temporary files that are written to disk // 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 // afterwards, a test function can check whether these files were encrypted or not

View File

@ -27,31 +27,19 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.List; 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.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.util.ZipEntrySource; import org.apache.poi.openxml4j.util.ZipEntrySource;
import org.apache.poi.poifs.crypt.AesZipFileZipEntrySource; import org.apache.poi.poifs.crypt.temp.AesZipFileZipEntrySource;
import org.apache.poi.poifs.crypt.ChainingMode; import org.apache.poi.poifs.crypt.temp.EncryptedTempData;
import org.apache.poi.poifs.crypt.CipherAlgorithm; import org.apache.poi.poifs.crypt.temp.SXSSFWorkbookWithCustomZipEntrySource;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.util.IOUtils; 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.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet;
@ -145,104 +133,4 @@ public final class TestSXSSFWorkbookWithCustomZipEntrySource {
workbook.dispose(); workbook.dispose();
assertFalse("tempFile deleted after dispose?", tempFile.exists()); 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());
}
}
} }

View File

@ -28,14 +28,24 @@ public final class PointerV5 extends Pointer {
return (0x40 <= format && format < 0x50); return (0x40 <= format && format < 0x50);
} }
public boolean destinationHasPointers() { public boolean destinationHasPointers() {
if(type == 20) return true; if(type == 20) {
if(type == 22) return false; return true;
if(format == 0x1d || format == 0x1e) return true; }
if(type == 22) {
return false;
}
if(format == 0x1d || format == 0x1e) {
return true;
}
return (0x50 <= format && format < 0x60); return (0x50 <= format && format < 0x60);
} }
public boolean destinationHasChunks() { public boolean destinationHasChunks() {
if (type == 21) return true; if (type == 21) {
if (type == 24) return true; return true;
}
if (type == 24) {
return true;
}
return (0xd0 <= format && format < 0xdf); return (0xd0 <= format && format < 0xdf);
} }

View File

@ -33,7 +33,7 @@ import org.apache.poi.util.Units;
* Represents Macintosh PICT picture data. * Represents Macintosh PICT picture data.
*/ */
public final class PICT extends Metafile { 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 { public static class NativeHeader {
/** /**

View File

@ -346,7 +346,9 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh
} }
private void fillPoint(byte xyMaster[], double xyPoints[]) { 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"); logger.log(POILogger.WARN, "Invalid number of master bytes for a single point - ignore point");
return; return;
} }