- 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-ffa450edef68tags/REL_3_16_BETA1
@@ -27,6 +27,9 @@ import org.apache.poi.poifs.filesystem.POIFSFileSystem; | |||
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); |
@@ -25,14 +25,17 @@ import java.io.IOException; | |||
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); | |||
} | |||
} |
@@ -20,12 +20,13 @@ | |||
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; | |||
@@ -40,29 +41,25 @@ 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 { |
@@ -24,13 +24,14 @@ import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.security.GeneralSecurityException; | |||
import org.apache.poi.crypt.examples.EncryptedTempData; | |||
import org.apache.poi.examples.util.TempFileUtils; | |||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.poifs.crypt.EncryptionInfo; | |||
import org.apache.poi.poifs.crypt.EncryptionMode; | |||
import org.apache.poi.poifs.crypt.Encryptor; | |||
import org.apache.poi.poifs.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; | |||
@@ -47,42 +48,38 @@ import org.apache.poi.xssf.streaming.SXSSFSheet; | |||
*/ | |||
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) |
@@ -22,10 +22,10 @@ package org.apache.poi.xssf.usermodel.examples; | |||
import java.io.FileInputStream; | |||
import java.io.InputStream; | |||
import org.apache.poi.crypt.examples.AesZipFileZipEntrySource; | |||
import org.apache.poi.crypt.examples.EncryptionUtils; | |||
import org.apache.poi.examples.util.TempFileUtils; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.poifs.crypt.temp.AesZipFileZipEntrySource; | |||
import org.apache.poi.util.IOUtils; | |||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | |||
@@ -38,29 +38,25 @@ 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 { |
@@ -17,7 +17,12 @@ | |||
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); |
@@ -110,7 +110,7 @@ public final class FilePassRecord extends StandardRecord implements Cloneable { | |||
((CryptoAPIEncryptionVerifier)encryptionInfo.getVerifier()).write(bos); | |||
break; | |||
default: | |||
throw new RuntimeException("not supported"); | |||
throw new EncryptedDocumentException("not supported"); | |||
} | |||
out.write(data, 0, bos.getWriteIndex()); | |||
@@ -140,16 +140,16 @@ public final class FilePassRecord extends StandardRecord implements Cloneable { | |||
@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(); | |||
} |
@@ -32,16 +32,16 @@ import org.apache.poi.util.LittleEndianInputStream; | |||
@Internal | |||
public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { | |||
private final int _chunkSize; | |||
private final int _chunkBits; | |||
private final int chunkSize; | |||
private final int chunkBits; | |||
private final long _size; | |||
private final byte[] _chunk, _plain; | |||
private final Cipher _cipher; | |||
private final long size; | |||
private final byte[] chunk, plain; | |||
private final Cipher cipher; | |||
private int _lastIndex; | |||
private long _pos; | |||
private boolean _chunkIsValid = false; | |||
private int lastIndex; | |||
private long pos; | |||
private boolean chunkIsValid = false; | |||
public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize) | |||
throws GeneralSecurityException { | |||
@@ -51,24 +51,24 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { | |||
public ChunkedCipherInputStream(InputStream stream, long size, int chunkSize, int initialPos) | |||
throws GeneralSecurityException { | |||
super(stream); | |||
_size = size; | |||
_pos = initialPos; | |||
this._chunkSize = chunkSize; | |||
this.size = size; | |||
this.pos = initialPos; | |||
this.chunkSize = chunkSize; | |||
int cs = chunkSize == -1 ? 4096 : chunkSize; | |||
_chunk = new byte[cs]; | |||
_plain = new byte[cs]; | |||
_chunkBits = Integer.bitCount(_chunk.length-1); | |||
_lastIndex = (int)(_pos >> _chunkBits); | |||
_cipher = initCipherForBlock(null, _lastIndex); | |||
this.chunk = new byte[cs]; | |||
this.plain = new byte[cs]; | |||
this.chunkBits = Integer.bitCount(chunk.length-1); | |||
this.lastIndex = (int)(pos >> chunkBits); | |||
this.cipher = initCipherForBlock(null, lastIndex); | |||
} | |||
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..."); | |||
} | |||
_chunkIsValid = false; | |||
return initCipherForBlock(_cipher, block); | |||
chunkIsValid = false; | |||
return initCipherForBlock(cipher, block); | |||
} | |||
protected abstract Cipher initCipherForBlock(Cipher existing, int block) | |||
@@ -100,28 +100,28 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { | |||
final int chunkMask = getChunkMask(); | |||
while (len > 0) { | |||
if (!_chunkIsValid) { | |||
if (!chunkIsValid) { | |||
try { | |||
nextChunk(); | |||
_chunkIsValid = true; | |||
chunkIsValid = true; | |||
} catch (GeneralSecurityException e) { | |||
throw new EncryptedDocumentException(e.getMessage(), e); | |||
} | |||
} | |||
int count = (int)(_chunk.length - (_pos & chunkMask)); | |||
int count = (int)(chunk.length - (pos & chunkMask)); | |||
int avail = available(); | |||
if (avail == 0) { | |||
return total; | |||
} | |||
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; | |||
len -= count; | |||
_pos += count; | |||
if ((_pos & chunkMask) == 0) { | |||
_chunkIsValid = false; | |||
pos += count; | |||
if ((pos & chunkMask) == 0) { | |||
chunkIsValid = false; | |||
} | |||
total += count; | |||
} | |||
@@ -131,13 +131,13 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { | |||
@Override | |||
public long skip(final long n) throws IOException { | |||
long start = _pos; | |||
long start = pos; | |||
long skip = Math.min(remainingBytes(), n); | |||
if ((((_pos + skip) ^ start) & ~getChunkMask()) != 0) { | |||
_chunkIsValid = false; | |||
if ((((pos + skip) ^ start) & ~getChunkMask()) != 0) { | |||
chunkIsValid = false; | |||
} | |||
_pos += skip; | |||
pos += skip; | |||
return skip; | |||
} | |||
@@ -152,7 +152,7 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { | |||
* @return the remaining byte until EOF | |||
*/ | |||
private int remainingBytes() { | |||
return (int)(_size - _pos); | |||
return (int)(size - pos); | |||
} | |||
@Override | |||
@@ -171,35 +171,35 @@ public abstract class ChunkedCipherInputStream extends LittleEndianInputStream { | |||
} | |||
protected int getChunkMask() { | |||
return _chunk.length-1; | |||
return chunk.length-1; | |||
} | |||
private void nextChunk() throws GeneralSecurityException, IOException { | |||
if (_chunkSize != -1) { | |||
int index = (int)(_pos >> _chunkBits); | |||
initCipherForBlock(_cipher, index); | |||
if (chunkSize != -1) { | |||
int index = (int)(pos >> chunkBits); | |||
initCipherForBlock(cipher, index); | |||
if (_lastIndex != index) { | |||
super.skip((index - _lastIndex) << _chunkBits); | |||
if (lastIndex != index) { | |||
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; | |||
do { | |||
readBytes = super.read(_plain, totalBytes, todo-totalBytes); | |||
readBytes = super.read(plain, totalBytes, todo-totalBytes); | |||
totalBytes += Math.max(0, readBytes); | |||
} 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"); | |||
} | |||
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 { | |||
if (doFinal) { | |||
return _cipher.doFinal(_chunk, 0, totalBytes, _chunk); | |||
return cipher.doFinal(chunk, 0, totalBytes, chunk); | |||
} 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 | |||
*/ | |||
protected byte[] getChunk() { | |||
return _chunk; | |||
return chunk; | |||
} | |||
/** | |||
* @return the plain bytes | |||
*/ | |||
protected byte[] getPlain() { | |||
return _plain; | |||
return plain; | |||
} | |||
/** | |||
* @return the absolute position in the stream | |||
*/ | |||
public long getPos() { | |||
return _pos; | |||
return pos; | |||
} | |||
} |
@@ -49,47 +49,50 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
private static final POILogger LOG = POILogFactory.getLogger(ChunkedCipherOutputStream.class); | |||
private static final int STREAMING = -1; | |||
private final int _chunkSize; | |||
private final int _chunkBits; | |||
private final int chunkSize; | |||
private final int chunkBits; | |||
private final byte[] _chunk; | |||
private final BitSet _plainByteFlags; | |||
private final File _fileOut; | |||
private final DirectoryNode _dir; | |||
private final byte[] chunk; | |||
private final BitSet plainByteFlags; | |||
private final File fileOut; | |||
private final DirectoryNode dir; | |||
private long _pos = 0; | |||
private long _totalPos = 0; | |||
private long _written = 0; | |||
private Cipher _cipher; | |||
private long pos = 0; | |||
private long totalPos = 0; | |||
private long written = 0; | |||
// 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 { | |||
super(null); | |||
this._chunkSize = chunkSize; | |||
this.chunkSize = chunkSize; | |||
int cs = chunkSize == STREAMING ? 4096 : chunkSize; | |||
_chunk = new byte[cs]; | |||
_plainByteFlags = new BitSet(cs); | |||
_chunkBits = Integer.bitCount(cs-1); | |||
_fileOut = TempFile.createTempFile("encrypted_package", "crypt"); | |||
_fileOut.deleteOnExit(); | |||
this.out = new FileOutputStream(_fileOut); | |||
this._dir = dir; | |||
_cipher = initCipherForBlock(null, 0, false); | |||
this.chunk = new byte[cs]; | |||
this.plainByteFlags = new BitSet(cs); | |||
this.chunkBits = Integer.bitCount(cs-1); | |||
this.fileOut = TempFile.createTempFile("encrypted_package", "crypt"); | |||
this.fileOut.deleteOnExit(); | |||
this.out = new FileOutputStream(fileOut); | |||
this.dir = dir; | |||
this.cipher = initCipherForBlock(null, 0, false); | |||
} | |||
public ChunkedCipherOutputStream(OutputStream stream, int chunkSize) throws IOException, GeneralSecurityException { | |||
super(stream); | |||
this._chunkSize = chunkSize; | |||
this.chunkSize = chunkSize; | |||
int cs = chunkSize == STREAMING ? 4096 : chunkSize; | |||
_chunk = new byte[cs]; | |||
_plainByteFlags = new BitSet(cs); | |||
_chunkBits = Integer.bitCount(cs-1); | |||
_fileOut = null; | |||
_dir = null; | |||
_cipher = initCipherForBlock(null, 0, false); | |||
this.chunk = new byte[cs]; | |||
this.plainByteFlags = new BitSet(cs); | |||
this.chunkBits = Integer.bitCount(cs-1); | |||
this.fileOut = null; | |||
this.dir = null; | |||
this.cipher = initCipherForBlock(null, 0, false); | |||
} | |||
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) | |||
@@ -131,40 +134,40 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
final int chunkMask = getChunkMask(); | |||
while (len > 0) { | |||
int posInChunk = (int)(_pos & chunkMask); | |||
int nextLen = Math.min(_chunk.length-posInChunk, len); | |||
System.arraycopy(b, off, _chunk, posInChunk, nextLen); | |||
int posInChunk = (int)(pos & chunkMask); | |||
int nextLen = Math.min(chunk.length-posInChunk, len); | |||
System.arraycopy(b, off, chunk, posInChunk, nextLen); | |||
if (writePlain) { | |||
_plainByteFlags.set(posInChunk, posInChunk+nextLen); | |||
plainByteFlags.set(posInChunk, posInChunk+nextLen); | |||
} | |||
_pos += nextLen; | |||
_totalPos += nextLen; | |||
pos += nextLen; | |||
totalPos += nextLen; | |||
off += nextLen; | |||
len -= nextLen; | |||
if ((_pos & chunkMask) == 0) { | |||
if ((pos & chunkMask) == 0) { | |||
writeChunk(len > 0); | |||
} | |||
} | |||
} | |||
protected int getChunkMask() { | |||
return _chunk.length-1; | |||
return chunk.length-1; | |||
} | |||
protected void writeChunk(boolean continued) throws IOException { | |||
if (_pos == 0 || _totalPos == _written) { | |||
if (pos == 0 || totalPos == written) { | |||
return; | |||
} | |||
int posInChunk = (int)(_pos & getChunkMask()); | |||
int posInChunk = (int)(pos & getChunkMask()); | |||
// normally posInChunk is 0, i.e. on the next chunk (-> index-1) | |||
// but if called on close(), posInChunk is somewhere within the chunk data | |||
int index = (int)(_pos >> _chunkBits); | |||
int index = (int)(pos >> chunkBits); | |||
boolean lastChunk; | |||
if (posInChunk==0) { | |||
index--; | |||
posInChunk = _chunk.length; | |||
posInChunk = chunk.length; | |||
lastChunk = false; | |||
} else { | |||
// pad the last chunk | |||
@@ -174,27 +177,27 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
int ciLen; | |||
try { | |||
boolean doFinal = true; | |||
long oldPos = _pos; | |||
long oldPos = pos; | |||
// reset stream (not only) in case we were interrupted by plain stream parts | |||
// this also needs to be set to prevent an endless loop | |||
_pos = 0; | |||
if (_chunkSize == STREAMING) { | |||
pos = 0; | |||
if (chunkSize == STREAMING) { | |||
if (continued) { | |||
doFinal = false; | |||
} | |||
} else { | |||
_cipher = initCipherForBlock(_cipher, index, lastChunk); | |||
cipher = initCipherForBlock(cipher, index, lastChunk); | |||
// restore pos - only streaming chunks will be reset | |||
_pos = oldPos; | |||
pos = oldPos; | |||
} | |||
ciLen = invokeCipher(posInChunk, doFinal); | |||
} catch (GeneralSecurityException e) { | |||
throw new IOException("can't re-/initialize cipher", e); | |||
} | |||
out.write(_chunk, 0, ciLen); | |||
_plainByteFlags.clear(); | |||
_written += ciLen; | |||
out.write(chunk, 0, ciLen); | |||
plainByteFlags.clear(); | |||
written += ciLen; | |||
} | |||
/** | |||
@@ -206,14 +209,14 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
* @throws ShortBufferException | |||
*/ | |||
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) | |||
? _cipher.doFinal(_chunk, 0, posInChunk, _chunk) | |||
: _cipher.update(_chunk, 0, posInChunk, _chunk); | |||
? cipher.doFinal(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)) { | |||
_chunk[i] = plain[i]; | |||
for (int i = plainByteFlags.nextSetBit(0); i >= 0 && i < posInChunk; i = plainByteFlags.nextSetBit(i+1)) { | |||
chunk[i] = plain[i]; | |||
} | |||
return ciLen; | |||
@@ -226,11 +229,11 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
super.close(); | |||
if (_fileOut != null) { | |||
int oleStreamSize = (int)(_fileOut.length()+LittleEndianConsts.LONG_SIZE); | |||
calculateChecksum(_fileOut, (int)_pos); | |||
_dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter()); | |||
createEncryptionInfoEntry(_dir, _fileOut); | |||
if (fileOut != null) { | |||
int oleStreamSize = (int)(fileOut.length()+LittleEndianConsts.LONG_SIZE); | |||
calculateChecksum(fileOut, (int)pos); | |||
dir.createDocument(DEFAULT_POIFS_ENTRY, oleStreamSize, new EncryptedPackageWriter()); | |||
createEncryptionInfoEntry(dir, fileOut); | |||
} | |||
} catch (GeneralSecurityException e) { | |||
throw new IOException(e); | |||
@@ -238,19 +241,19 @@ public abstract class ChunkedCipherOutputStream extends FilterOutputStream { | |||
} | |||
protected byte[] getChunk() { | |||
return _chunk; | |||
return chunk; | |||
} | |||
protected BitSet getPlainByteFlags() { | |||
return _plainByteFlags; | |||
return plainByteFlags; | |||
} | |||
protected long getPos() { | |||
return _pos; | |||
return pos; | |||
} | |||
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 | |||
// value, depending on the block size of the chosen encryption algorithm | |||
byte buf[] = new byte[LittleEndianConsts.LONG_SIZE]; | |||
LittleEndian.putLong(buf, 0, _pos); | |||
LittleEndian.putLong(buf, 0, pos); | |||
os.write(buf); | |||
FileInputStream fis = new FileInputStream(_fileOut); | |||
FileInputStream fis = new FileInputStream(fileOut); | |||
IOUtils.copy(fis, os); | |||
fis.close(); | |||
os.close(); | |||
if (!_fileOut.delete()) { | |||
LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+_fileOut); | |||
if (!fileOut.delete()) { | |||
LOG.log(POILogger.ERROR, "Can't delete temporary encryption file: "+fileOut); | |||
} | |||
} catch (IOException e) { | |||
throw new EncryptedDocumentException(e); |
@@ -67,7 +67,7 @@ public abstract class Decryptor implements Cloneable { | |||
*/ | |||
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"); | |||
} | |||
/** | |||
@@ -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 | |||
*/ | |||
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) | |||
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) |
@@ -23,6 +23,7 @@ import java.security.GeneralSecurityException; | |||
import javax.crypto.SecretKey; | |||
import javax.crypto.spec.SecretKeySpec; | |||
import org.apache.poi.EncryptedDocumentException; | |||
import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; | |||
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem; | |||
@@ -63,7 +64,7 @@ public abstract class Encryptor implements Cloneable { | |||
public ChunkedCipherOutputStream getDataStream(OutputStream stream, int initialOffset) | |||
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() { | |||
@@ -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 | |||
*/ | |||
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 |
@@ -35,8 +35,8 @@ import org.apache.poi.util.LittleEndian; | |||
import org.apache.poi.util.StringUtil; | |||
public class BinaryRC4Decryptor extends Decryptor implements Cloneable { | |||
private long _length = -1L; | |||
private int _chunkSize = 512; | |||
private long length = -1L; | |||
private int chunkSize = 512; | |||
private class BinaryRC4CipherInputStream extends ChunkedCipherInputStream { | |||
@@ -48,12 +48,12 @@ public class BinaryRC4Decryptor extends Decryptor implements Cloneable { | |||
public BinaryRC4CipherInputStream(DocumentInputStream stream, long size) | |||
throws GeneralSecurityException { | |||
super(stream, size, _chunkSize); | |||
super(stream, size, chunkSize); | |||
} | |||
public BinaryRC4CipherInputStream(InputStream stream) | |||
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, | |||
GeneralSecurityException { | |||
DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY); | |||
_length = dis.readLong(); | |||
return new BinaryRC4CipherInputStream(dis, _length); | |||
length = dis.readLong(); | |||
return new BinaryRC4CipherInputStream(dis, length); | |||
} | |||
@Override | |||
@@ -147,16 +147,16 @@ public class BinaryRC4Decryptor extends Decryptor implements Cloneable { | |||
@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 |
@@ -41,7 +41,7 @@ import org.apache.poi.util.LittleEndianByteArrayOutputStream; | |||
public class BinaryRC4Encryptor extends Encryptor implements Cloneable { | |||
private int _chunkSize = 512; | |||
private int chunkSize = 512; | |||
protected BinaryRC4Encryptor() { | |||
} | |||
@@ -115,7 +115,7 @@ public class BinaryRC4Encryptor extends Encryptor implements Cloneable { | |||
@Override | |||
public void setChunkSize(int chunkSize) { | |||
_chunkSize = chunkSize; | |||
this.chunkSize = chunkSize; | |||
} | |||
@Override | |||
@@ -127,12 +127,12 @@ public class BinaryRC4Encryptor extends Encryptor implements Cloneable { | |||
public BinaryRC4CipherOutputStream(OutputStream stream) | |||
throws IOException, GeneralSecurityException { | |||
super(stream, BinaryRC4Encryptor.this._chunkSize); | |||
super(stream, BinaryRC4Encryptor.this.chunkSize); | |||
} | |||
public BinaryRC4CipherOutputStream(DirectoryNode dir) | |||
throws IOException, GeneralSecurityException { | |||
super(dir, BinaryRC4Encryptor.this._chunkSize); | |||
super(dir, BinaryRC4Encryptor.this.chunkSize); | |||
} | |||
@Override |
@@ -50,8 +50,8 @@ import org.apache.poi.util.StringUtil; | |||
public class CryptoAPIDecryptor extends Decryptor implements Cloneable { | |||
private long _length; | |||
private int _chunkSize = -1; | |||
private long length = -1L; | |||
private int chunkSize = -1; | |||
static class StreamDescriptorEntry { | |||
static BitField flagStream = BitFieldFactory.getInstance(1); | |||
@@ -65,7 +65,6 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable { | |||
} | |||
protected CryptoAPIDecryptor() { | |||
_length = -1L; | |||
} | |||
@Override | |||
@@ -209,14 +208,14 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable { | |||
*/ | |||
@Override | |||
public long getLength() { | |||
if (_length == -1L) { | |||
if (length == -1L) { | |||
throw new IllegalStateException("Decryptor.getDataStream() was not called"); | |||
} | |||
return _length; | |||
return length; | |||
} | |||
public void setChunkSize(int chunkSize) { | |||
_chunkSize = chunkSize; | |||
this.chunkSize = chunkSize; | |||
} | |||
@Override | |||
@@ -234,7 +233,7 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable { | |||
public CryptoAPICipherInputStream(InputStream stream, long size, int initialPos) | |||
throws GeneralSecurityException { | |||
super(stream, size, _chunkSize, initialPos); | |||
super(stream, size, chunkSize, initialPos); | |||
} | |||
} | |||
} |
@@ -29,13 +29,13 @@ import org.apache.poi.util.Internal; | |||
*/ | |||
@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() { | |||
@@ -47,7 +47,7 @@ import org.apache.poi.util.Internal; | |||
} | |||
public void setBlock(int block) throws GeneralSecurityException { | |||
cipher = encryptor.initCipherForBlock(cipher, block); | |||
encryptor.initCipherForBlock(cipher, block); | |||
} | |||
@Override |
@@ -53,7 +53,7 @@ import org.apache.poi.util.StringUtil; | |||
public class CryptoAPIEncryptor extends Encryptor implements Cloneable { | |||
private int _chunkSize = 512; | |||
private int chunkSize = 512; | |||
protected CryptoAPIEncryptor() { | |||
} | |||
@@ -215,7 +215,7 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable { | |||
@Override | |||
public void setChunkSize(int chunkSize) { | |||
_chunkSize = chunkSize; | |||
this.chunkSize = chunkSize; | |||
} | |||
protected void createEncryptionInfoEntry(DirectoryNode dir) throws IOException { | |||
@@ -259,12 +259,12 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable { | |||
@Override | |||
protected void createEncryptionInfoEntry(DirectoryNode dir, File tmpFile) | |||
throws IOException, GeneralSecurityException { | |||
throw new RuntimeException("createEncryptionInfoEntry not supported"); | |||
throw new EncryptedDocumentException("createEncryptionInfoEntry not supported"); | |||
} | |||
public CryptoAPICipherOutputStream(OutputStream stream) | |||
throws IOException, GeneralSecurityException { | |||
super(stream, CryptoAPIEncryptor.this._chunkSize); | |||
super(stream, CryptoAPIEncryptor.this.chunkSize); | |||
} | |||
@Override |
@@ -25,6 +25,7 @@ import javax.crypto.Cipher; | |||
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; | |||
@@ -33,8 +34,8 @@ import org.apache.poi.poifs.filesystem.DirectoryNode; | |||
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() { | |||
} | |||
@@ -69,7 +70,7 @@ public class XORDecryptor extends Decryptor implements Cloneable { | |||
@Override | |||
public ChunkedCipherInputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException { | |||
throw new RuntimeException("not supported"); | |||
throw new EncryptedDocumentException("not supported"); | |||
} | |||
@Override | |||
@@ -81,16 +82,16 @@ public class XORDecryptor extends Decryptor implements Cloneable { | |||
@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 | |||
@@ -99,14 +100,14 @@ public class XORDecryptor extends Decryptor implements Cloneable { | |||
} | |||
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 | |||
@@ -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. | |||
* 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 | |||
@@ -165,8 +166,8 @@ public class XORDecryptor extends Decryptor implements Cloneable { | |||
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); | |||
} |
@@ -60,12 +60,12 @@ public class XOREncryptionVerifier extends EncryptionVerifier implements Encrypt | |||
} | |||
@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); | |||
} | |||
} |
@@ -61,8 +61,7 @@ public class XOREncryptor extends Encryptor implements Cloneable { | |||
@Override | |||
public OutputStream getDataStream(DirectoryNode dir) | |||
throws IOException, GeneralSecurityException { | |||
OutputStream countStream = new XORCipherOutputStream(dir); | |||
return countStream; | |||
return new XORCipherOutputStream(dir); | |||
} | |||
@Override | |||
@@ -89,19 +88,15 @@ public class XOREncryptor extends Encryptor implements Cloneable { | |||
} | |||
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 | |||
@@ -122,13 +117,12 @@ public class XOREncryptor extends Encryptor implements Cloneable { | |||
@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 | |||
@@ -143,7 +137,7 @@ public class XOREncryptor extends Encryptor implements Cloneable { | |||
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(); | |||
@@ -161,7 +155,7 @@ public class XOREncryptor extends Encryptor implements Cloneable { | |||
* 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]; |
@@ -573,8 +573,7 @@ public class POIXMLDocumentPart { | |||
} | |||
// 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); |
@@ -312,7 +312,7 @@ public class AgileDecryptor extends Decryptor implements Cloneable { | |||
} else { | |||
aps = new IvParameterSpec(iv); | |||
} | |||
existing.init(encryptionMode, skey, aps); | |||
return existing; |
@@ -21,7 +21,6 @@ import java.security.GeneralSecurityException; | |||
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; | |||
@@ -68,11 +67,11 @@ public class AgileEncryptionVerifier extends EncryptionVerifier implements Clone | |||
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); | |||
@@ -230,7 +229,7 @@ public class AgileEncryptionVerifier extends EncryptionVerifier implements Clone | |||
} | |||
@Override | |||
protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) { | |||
protected final void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) { | |||
super.setCipherAlgorithm(cipherAlgorithm); | |||
if (cipherAlgorithm.allowedKeySize.length == 1) { | |||
setKeySize(cipherAlgorithm.defaultKeySize); |
@@ -17,7 +17,7 @@ | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.crypt.examples; | |||
package org.apache.poi.poifs.crypt.temp; | |||
import java.io.File; | |||
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.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 { | |||
final File tmpFile; | |||
final ZipFile zipFile; | |||
final Cipher ci; | |||
boolean closed; | |||
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; | |||
@@ -81,7 +87,9 @@ public class AesZipFileZipEntrySource implements ZipEntrySource { | |||
public void close() throws IOException { | |||
if(!closed) { | |||
zipFile.close(); | |||
tmpFile.delete(); | |||
if (!tmpFile.delete()) { | |||
LOG.log(POILogger.WARN, tmpFile.getAbsolutePath()+" can't be removed (or was already removed."); | |||
}; | |||
} | |||
closed = true; | |||
} |
@@ -17,7 +17,7 @@ | |||
* ==================================================================== | |||
*/ | |||
package org.apache.poi.crypt.examples; | |||
package org.apache.poi.poifs.crypt.temp; | |||
import java.io.File; | |||
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.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 { | |||
final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128; | |||
final SecretKeySpec skeySpec; | |||
final byte[] ivBytes; | |||
final File tempFile; | |||
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(); | |||
@@ -67,6 +74,8 @@ public class EncryptedTempData { | |||
} | |||
public void dispose() { | |||
tempFile.delete(); | |||
if (!tempFile.delete()) { | |||
LOG.log(POILogger.WARN, tempFile.getAbsolutePath()+" can't be removed (or was already removed."); | |||
} | |||
} | |||
} |
@@ -17,33 +17,24 @@ | |||
* ==================================================================== | |||
*/ | |||
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.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.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); | |||
@@ -74,40 +65,9 @@ public class SXSSFWorkbookWithCustomZipEntrySource extends SXSSFWorkbook { | |||
@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(); | |||
} | |||
static class SheetDataWriterWithDecorator extends SheetDataWriter { | |||
final static CipherAlgorithm cipherAlgorithm = CipherAlgorithm.aes128; | |||
SecretKeySpec skeySpec; | |||
byte[] ivBytes; | |||
public SheetDataWriterWithDecorator() throws IOException { | |||
super(); | |||
} | |||
void init() { | |||
if(skeySpec == null) { | |||
SecureRandom sr = new SecureRandom(); | |||
ivBytes = new byte[16]; | |||
byte[] keyBytes = new byte[16]; | |||
sr.nextBytes(ivBytes); | |||
sr.nextBytes(keyBytes); | |||
skeySpec = new SecretKeySpec(keyBytes, cipherAlgorithm.jceId); | |||
} | |||
} | |||
@Override | |||
protected OutputStream decorateOutputStream(FileOutputStream fos) { | |||
init(); | |||
Cipher ciEnc = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.ENCRYPT_MODE, "PKCS5Padding"); | |||
return new CipherOutputStream(fos, ciEnc); | |||
} | |||
@Override | |||
protected InputStream decorateInputStream(FileInputStream fis) { | |||
Cipher ciDec = CryptoFunctions.getCipher(skeySpec, cipherAlgorithm, ChainingMode.cbc, ivBytes, Cipher.DECRYPT_MODE, "PKCS5Padding"); | |||
return new CipherInputStream(fis, ciDec); | |||
} | |||
} | |||
} |
@@ -0,0 +1,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); | |||
} | |||
} |
@@ -230,9 +230,8 @@ public class XSSFReader { | |||
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); | |||
} | |||
} |
@@ -127,7 +127,7 @@ public class SheetDataWriter { | |||
_out.close(); | |||
} | |||
File getTempFile(){ | |||
protected File getTempFile(){ | |||
return _fd; | |||
} | |||
@@ -16,8 +16,6 @@ | |||
==================================================================== */ | |||
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; | |||
@@ -65,8 +63,6 @@ import com.microsoft.schemas.vml.STTrueFalse; | |||
* 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; | |||
@@ -282,7 +278,7 @@ public class XWPFHeaderFooterPolicy { | |||
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) { |
@@ -449,7 +449,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
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())); | |||
@@ -471,7 +471,7 @@ public class XWPFDocument extends POIXMLDocument implements Document, IBody { | |||
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())); |
@@ -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); | |||
} | |||
} | |||
@@ -29,6 +29,7 @@ import java.security.GeneralSecurityException; | |||
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; |
@@ -21,8 +21,8 @@ import java.io.IOException; | |||
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 |
@@ -27,31 +27,19 @@ import java.io.ByteArrayInputStream; | |||
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; | |||
@@ -145,104 +133,4 @@ public final class TestSXSSFWorkbookWithCustomZipEntrySource { | |||
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()); | |||
} | |||
} | |||
} | |||
} |
@@ -28,14 +28,24 @@ public final class PointerV5 extends Pointer { | |||
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); | |||
} | |||
@@ -33,7 +33,7 @@ import org.apache.poi.util.Units; | |||
* 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 { | |||
/** |
@@ -346,7 +346,9 @@ public final class HSLFFreeformShape extends HSLFAutoShape implements FreeformSh | |||
} | |||
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; | |||
} |