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