- 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:
parent
3820215393
commit
fc86b15f13
@ -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) {
|
||||
try {
|
||||
if(args.length != 2) {
|
||||
throw new Exception("Expected 2 params: filename and password");
|
||||
}
|
||||
TempFileUtils.checkTempFiles();
|
||||
String filename = args[0];
|
||||
String password = args[1];
|
||||
FileInputStream fis = new FileInputStream(filename);
|
||||
try {
|
||||
InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
|
||||
try {
|
||||
printSheetCount(unencryptedStream);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(unencryptedStream);
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fis);
|
||||
}
|
||||
TempFileUtils.checkTempFiles();
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
public static void 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 {
|
||||
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 {
|
||||
|
@ -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();
|
||||
}
|
||||
} finally {
|
||||
wb.close();
|
||||
wb.dispose();
|
||||
}
|
||||
TempFileUtils.checkTempFiles();
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
try {
|
||||
if(args.length != 2) {
|
||||
throw new Exception("Expected 2 params: filename and password");
|
||||
}
|
||||
TempFileUtils.checkTempFiles();
|
||||
String filename = args[0];
|
||||
String password = args[1];
|
||||
FileInputStream fis = new FileInputStream(filename);
|
||||
try {
|
||||
InputStream unencryptedStream = EncryptionUtils.decrypt(fis, password);
|
||||
try {
|
||||
printSheetCount(unencryptedStream);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(unencryptedStream);
|
||||
}
|
||||
} finally {
|
||||
IOUtils.closeQuietly(fis);
|
||||
}
|
||||
TempFileUtils.checkTempFiles();
|
||||
} catch(Throwable t) {
|
||||
t.printStackTrace();
|
||||
public static void 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 {
|
||||
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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user