aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/java/org/apache/poi/POIDocument.java140
-rw-r--r--src/java/org/apache/poi/POIOLE2TextExtractor.java32
-rw-r--r--src/java/org/apache/poi/hssf/model/InternalWorkbook.java66
-rw-r--r--src/java/org/apache/poi/hssf/model/LinkTable.java12
-rw-r--r--src/java/org/apache/poi/hssf/model/WorkbookRecordList.java110
-rw-r--r--src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java81
-rw-r--r--src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java11
-rw-r--r--src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java34
-rw-r--r--src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java187
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java52
-rw-r--r--src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java47
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java232
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java200
-rw-r--r--src/scratchpad/src/org/apache/poi/hwpf/model/io/HWPFFileSystem.java6
-rw-r--r--src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java69
15 files changed, 738 insertions, 541 deletions
diff --git a/src/java/org/apache/poi/POIDocument.java b/src/java/org/apache/poi/POIDocument.java
index 774507722a..5c2c201512 100644
--- a/src/java/org/apache/poi/POIDocument.java
+++ b/src/java/org/apache/poi/POIDocument.java
@@ -24,6 +24,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.GeneralSecurityException;
import java.util.List;
import org.apache.poi.hpsf.DocumentSummaryInformation;
@@ -31,12 +32,15 @@ import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.OPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -60,8 +64,6 @@ public abstract class POIDocument implements Closeable {
/* Have the property streams been read yet? (Only done on-demand) */
private boolean initialized;
- private static final String[] encryptedStreamNames = { "EncryptedSummary" };
-
/**
* Constructs a POIDocument with the given directory node.
*
@@ -103,7 +105,9 @@ public abstract class POIDocument implements Closeable {
* if it could not be read for this document.
*/
public DocumentSummaryInformation getDocumentSummaryInformation() {
- if(!initialized) readProperties();
+ if(!initialized) {
+ readProperties();
+ }
return dsInf;
}
@@ -114,7 +118,9 @@ public abstract class POIDocument implements Closeable {
* if it could not be read for this document.
*/
public SummaryInformation getSummaryInformation() {
- if(!initialized) readProperties();
+ if(!initialized) {
+ readProperties();
+ }
return sInf;
}
@@ -128,7 +134,9 @@ public abstract class POIDocument implements Closeable {
* then nothing will happen.
*/
public void createInformationProperties() {
- if (!initialized) readProperties();
+ if (!initialized) {
+ readProperties();
+ }
if (sInf == null) {
sInf = PropertySetFactory.newSummaryInformation();
}
@@ -144,32 +152,40 @@ public abstract class POIDocument implements Closeable {
* it will remain null;
*/
protected void readProperties() {
- PropertySet ps;
-
- // DocumentSummaryInformation
- ps = getPropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
- if (ps instanceof DocumentSummaryInformation) {
- dsInf = (DocumentSummaryInformation)ps;
- } else if (ps != null) {
- logger.log(POILogger.WARN, "DocumentSummaryInformation property set came back with wrong class - ", ps.getClass());
- } else {
- logger.log(POILogger.WARN, "DocumentSummaryInformation property set came back as null");
+ if (initialized) {
+ return;
}
-
- // SummaryInformation
- ps = getPropertySet(SummaryInformation.DEFAULT_STREAM_NAME);
- if (ps instanceof SummaryInformation) {
- sInf = (SummaryInformation)ps;
- } else if (ps != null) {
- logger.log(POILogger.WARN, "SummaryInformation property set came back with wrong class - ", ps.getClass());
- } else {
- logger.log(POILogger.WARN, "SummaryInformation property set came back as null");
+ DocumentSummaryInformation dsi = readPropertySet(DocumentSummaryInformation.class, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
+ if (dsi != null) {
+ dsInf = dsi;
+ }
+ SummaryInformation si = readPropertySet(SummaryInformation.class, SummaryInformation.DEFAULT_STREAM_NAME);
+ if (si != null) {
+ sInf = si;
}
// Mark the fact that we've now loaded up the properties
initialized = true;
}
+ @SuppressWarnings("unchecked")
+ private <T> T readPropertySet(Class<T> clazz, String name) {
+ String localName = clazz.getName().substring(clazz.getName().lastIndexOf('.')+1);
+ try {
+ PropertySet ps = getPropertySet(name);
+ if (clazz.isInstance(ps)) {
+ return (T)ps;
+ } else if (ps != null) {
+ logger.log(POILogger.WARN, localName+" property set came back with wrong class - "+ps.getClass().getName());
+ } else {
+ logger.log(POILogger.WARN, localName+" property set came back as null");
+ }
+ } catch (IOException e) {
+ logger.log(POILogger.ERROR, "can't retrieve property set", e);
+ }
+ return null;
+ }
+
/**
* For a given named property entry, either return it or null if
* if it wasn't found
@@ -177,8 +193,8 @@ public abstract class POIDocument implements Closeable {
* @param setName The property to read
* @return The value of the given property or null if it wasn't found.
*/
- protected PropertySet getPropertySet(String setName) {
- return getPropertySet(setName, null);
+ protected PropertySet getPropertySet(String setName) throws IOException {
+ return getPropertySet(setName, getEncryptionInfo());
}
/**
@@ -189,7 +205,7 @@ public abstract class POIDocument implements Closeable {
* @param encryptionInfo the encryption descriptor in case of cryptoAPI encryption
* @return The value of the given property or null if it wasn't found.
*/
- protected PropertySet getPropertySet(String setName, EncryptionInfo encryptionInfo) {
+ protected PropertySet getPropertySet(String setName, EncryptionInfo encryptionInfo) throws IOException {
DirectoryNode dirNode = directory;
NPOIFSFileSystem encPoifs = null;
@@ -197,14 +213,9 @@ public abstract class POIDocument implements Closeable {
try {
if (encryptionInfo != null && encryptionInfo.isDocPropsEncrypted()) {
step = "getting encrypted";
- String encryptedStream = null;
- for (String s : encryptedStreamNames) {
- if (dirNode.hasEntry(s)) {
- encryptedStream = s;
- }
- }
- if (encryptedStream == null) {
- throw new EncryptedDocumentException("can't find matching encrypted property stream");
+ String encryptedStream = getEncryptedPropertyStreamName();
+ if (!dirNode.hasEntry(encryptedStream)) {
+ throw new EncryptedDocumentException("can't find encrypted property stream '"+encryptedStream+"'");
}
CryptoAPIDecryptor dec = (CryptoAPIDecryptor)encryptionInfo.getDecryptor();
encPoifs = dec.getSummaryEntries(dirNode, encryptedStream);
@@ -226,17 +237,12 @@ public abstract class POIDocument implements Closeable {
} finally {
dis.close();
}
+ } catch (IOException e) {
+ throw e;
} catch (Exception e) {
- logger.log(POILogger.WARN, "Error "+step+" property set with name " + setName, e);
- return null;
+ throw new IOException("Error "+step+" property set with name " + setName, e);
} finally {
- if (encPoifs != null) {
- try {
- encPoifs.close();
- } catch(IOException e) {
- logger.log(POILogger.WARN, "Error closing encrypted property poifs", e);
- }
- }
+ IOUtils.closeQuietly(encPoifs);
}
}
@@ -271,20 +277,48 @@ public abstract class POIDocument implements Closeable {
* {@link NPOIFSFileSystem} occurs
*/
protected void writeProperties(NPOIFSFileSystem outFS, List<String> writtenEntries) throws IOException {
+ EncryptionInfo ei = getEncryptionInfo();
+ final boolean encryptProps = (ei != null && ei.isDocPropsEncrypted());
+ NPOIFSFileSystem fs = (encryptProps) ? new NPOIFSFileSystem() : outFS;
+
SummaryInformation si = getSummaryInformation();
if (si != null) {
- writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, si, outFS);
+ writePropertySet(SummaryInformation.DEFAULT_STREAM_NAME, si, fs);
if(writtenEntries != null) {
writtenEntries.add(SummaryInformation.DEFAULT_STREAM_NAME);
}
}
DocumentSummaryInformation dsi = getDocumentSummaryInformation();
if (dsi != null) {
- writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, dsi, outFS);
+ writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, dsi, fs);
if(writtenEntries != null) {
writtenEntries.add(DocumentSummaryInformation.DEFAULT_STREAM_NAME);
}
}
+
+ if (!encryptProps) {
+ return;
+ }
+
+ // create empty document summary
+ dsi = PropertySetFactory.newDocumentSummaryInformation();
+ writePropertySet(DocumentSummaryInformation.DEFAULT_STREAM_NAME, dsi, outFS);
+ // remove summary, if previously available
+ if (outFS.getRoot().hasEntry(SummaryInformation.DEFAULT_STREAM_NAME)) {
+ outFS.getRoot().getEntry(SummaryInformation.DEFAULT_STREAM_NAME).delete();
+ }
+ Encryptor encGen = ei.getEncryptor();
+ if (!(encGen instanceof CryptoAPIEncryptor)) {
+ throw new EncryptedDocumentException("Using "+ei.getEncryptionMode()+" encryption. Only CryptoAPI encryption supports encrypted property sets!");
+ }
+ CryptoAPIEncryptor enc = (CryptoAPIEncryptor)encGen;
+ try {
+ enc.setSummaryEntries(outFS.getRoot(), getEncryptedPropertyStreamName(), fs);
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ } finally {
+ fs.close();
+ }
}
/**
@@ -443,4 +477,18 @@ public abstract class POIDocument implements Closeable {
directory = newDirectory;
return dn;
}
+
+ /**
+ * @return the stream name of the property set collection, if the document is encrypted
+ */
+ protected String getEncryptedPropertyStreamName() {
+ return "encryption";
+ }
+
+ /**
+ * @return the encryption info if the document is encrypted, otherwise {@code null}
+ */
+ public EncryptionInfo getEncryptionInfo() throws IOException {
+ return null;
+ }
}
diff --git a/src/java/org/apache/poi/POIOLE2TextExtractor.java b/src/java/org/apache/poi/POIOLE2TextExtractor.java
index 5884a9054e..05c7781057 100644
--- a/src/java/org/apache/poi/POIOLE2TextExtractor.java
+++ b/src/java/org/apache/poi/POIOLE2TextExtractor.java
@@ -27,7 +27,7 @@ import org.apache.poi.poifs.filesystem.DirectoryEntry;
* You will typically find the implementation of
* a given format's text extractor under
* org.apache.poi.[format].extractor .
- *
+ *
* @see org.apache.poi.hssf.extractor.ExcelExtractor
* @see org.apache.poi.hslf.extractor.PowerPointExtractor
* @see org.apache.poi.hdgf.extractor.VisioTextExtractor
@@ -39,12 +39,12 @@ public abstract class POIOLE2TextExtractor extends POITextExtractor {
/**
* Creates a new text extractor for the given document
- *
+ *
* @param document The POIDocument to use in this extractor.
*/
public POIOLE2TextExtractor(POIDocument document) {
this.document = document;
-
+
// Ensure any underlying resources, such as open files,
// will get cleaned up if the user calls #close()
setFilesystem(document);
@@ -54,17 +54,17 @@ public abstract class POIOLE2TextExtractor extends POITextExtractor {
* Creates a new text extractor, using the same
* document as another text extractor. Normally
* only used by properties extractors.
- *
+ *
* @param otherExtractor the extractor which document to be used
*/
protected POIOLE2TextExtractor(POIOLE2TextExtractor otherExtractor) {
this.document = otherExtractor.document;
}
-
+
/**
* Returns the document information metadata for the document
- *
- * @return The Document Summary Information or null
+ *
+ * @return The Document Summary Information or null
* if it could not be read for this document.
*/
public DocumentSummaryInformation getDocSummaryInformation() {
@@ -72,7 +72,7 @@ public abstract class POIOLE2TextExtractor extends POITextExtractor {
}
/**
* Returns the summary information metadata for the document.
- *
+ *
* @return The Summary information for the document or null
* if it could not be read for this document.
*/
@@ -83,7 +83,7 @@ public abstract class POIOLE2TextExtractor extends POITextExtractor {
/**
* Returns an HPSF powered text extractor for the
* document properties metadata, such as title and author.
- *
+ *
* @return an instance of POIExtractor that can extract meta-data.
*/
@Override
@@ -96,8 +96,16 @@ public abstract class POIOLE2TextExtractor extends POITextExtractor {
*
* @return the DirectoryEntry that is associated with the POIDocument of this extractor.
*/
- public DirectoryEntry getRoot()
- {
+ public DirectoryEntry getRoot() {
return document.getDirectory();
}
-}
+
+ /**
+ * Return the underlying POIDocument
+ *
+ * @return the underlying POIDocument
+ */
+ public POIDocument getDocument() {
+ return document;
+ }
+} \ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
index 7bddcdd670..1538ca5835 100644
--- a/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
+++ b/src/java/org/apache/poi/hssf/model/InternalWorkbook.java
@@ -20,16 +20,12 @@ package org.apache.poi.hssf.model;
import static org.apache.poi.util.POILogger.DEBUG;
import java.security.AccessControlException;
-import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import javax.crypto.SecretKey;
-
-import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.ddf.EscherBSERecord;
import org.apache.poi.ddf.EscherBoolProperty;
import org.apache.poi.ddf.EscherContainerRecord;
@@ -57,7 +53,6 @@ import org.apache.poi.hssf.record.EscherAggregate;
import org.apache.poi.hssf.record.ExtSSTRecord;
import org.apache.poi.hssf.record.ExtendedFormatRecord;
import org.apache.poi.hssf.record.ExternSheetRecord;
-import org.apache.poi.hssf.record.FilePassRecord;
import org.apache.poi.hssf.record.FileSharingRecord;
import org.apache.poi.hssf.record.FnGroupCountRecord;
import org.apache.poi.hssf.record.FontRecord;
@@ -88,13 +83,8 @@ import org.apache.poi.hssf.record.WindowProtectRecord;
import org.apache.poi.hssf.record.WriteAccessRecord;
import org.apache.poi.hssf.record.WriteProtectRecord;
import org.apache.poi.hssf.record.common.UnicodeString;
-import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.util.HSSFColor.HSSFColorPredefined;
import org.apache.poi.poifs.crypt.CryptoFunctions;
-import org.apache.poi.poifs.crypt.Decryptor;
-import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.EncryptionMode;
-import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalName;
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheet;
import org.apache.poi.ss.formula.EvaluationWorkbook.ExternalSheetRange;
@@ -1048,7 +1038,7 @@ public final class InternalWorkbook {
SSTRecord lSST = null;
int sstPos = 0;
boolean wroteBoundSheets = false;
- for ( Record record : records ) {
+ for ( Record record : records.getRecords() ) {
int len = 0;
if (record instanceof SSTRecord) {
lSST = (SSTRecord)record;
@@ -1080,8 +1070,6 @@ public final class InternalWorkbook {
* Include in it ant code that modifies the workbook record stream and affects its size.
*/
public void preSerialize(){
- updateEncryptionRecord();
-
// Ensure we have enough tab IDs
// Can be a few short if new sheets were added
if(records.getTabpos() > 0) {
@@ -1092,45 +1080,11 @@ public final class InternalWorkbook {
}
}
- private void updateEncryptionRecord() {
- FilePassRecord fpr = (FilePassRecord)findFirstRecordBySid(FilePassRecord.sid);
-
- String password = Biff8EncryptionKey.getCurrentUserPassword();
- if (password == null) {
- if (fpr != null) {
- // need to remove password data
- records.remove(fpr);
- }
- } else {
- // create password record
- if (fpr == null) {
- fpr = new FilePassRecord(EncryptionMode.binaryRC4);
- records.add(1, fpr);
- }
-
- // check if the password has been changed
- EncryptionInfo ei = fpr.getEncryptionInfo();
- byte encVer[] = ei.getVerifier().getEncryptedVerifier();
- try {
- Decryptor dec = ei.getDecryptor();
- Encryptor enc = ei.getEncryptor();
- if (encVer == null || !dec.verifyPassword(password)) {
- enc.confirmPassword(password);
- } else {
- SecretKey sk = dec.getSecretKey();
- ei.getEncryptor().setSecretKey(sk);
- }
- } catch (GeneralSecurityException e) {
- throw new EncryptedDocumentException("can't validate/update encryption setting", e);
- }
- }
- }
-
public int getSize() {
int retval = 0;
SSTRecord lSST = null;
- for ( Record record : records ) {
+ for ( Record record : records.getRecords() ) {
if (record instanceof SSTRecord) {
lSST = (SSTRecord)record;
}
@@ -1803,7 +1757,7 @@ public final class InternalWorkbook {
* @return the matching record or {@code null} if it wasn't found
*/
public Record findFirstRecordBySid(short sid) {
- for (Record record : records) {
+ for (Record record : records.getRecords() ) {
if (record.getSid() == sid) {
return record;
}
@@ -1818,7 +1772,7 @@ public final class InternalWorkbook {
*/
public int findFirstRecordLocBySid(short sid) {
int index = 0;
- for (Record record : records) {
+ for (Record record : records.getRecords() ) {
if (record.getSid() == sid) {
return index;
}
@@ -1837,7 +1791,7 @@ public final class InternalWorkbook {
*/
public Record findNextRecordBySid(short sid, int pos) {
int matches = 0;
- for (Record record : records) {
+ for (Record record : records.getRecords() ) {
if (record.getSid() == sid && matches++ == pos) {
return record;
}
@@ -1901,7 +1855,7 @@ public final class InternalWorkbook {
}
// Need to find a DrawingGroupRecord that contains a EscherDggRecord
- for(Record r : records) {
+ for(Record r : records.getRecords() ) {
if (!(r instanceof DrawingGroupRecord)) {
continue;
}
@@ -2301,4 +2255,12 @@ public final class InternalWorkbook {
public boolean changeExternalReference(String oldUrl, String newUrl) {
return linkTable.changeExternalReference(oldUrl, newUrl);
}
+
+ /**
+ * Only for internal calls - code based on this is not supported ...
+ */
+ @Internal
+ public WorkbookRecordList getWorkbookRecordList() {
+ return records;
+ }
} \ No newline at end of file
diff --git a/src/java/org/apache/poi/hssf/model/LinkTable.java b/src/java/org/apache/poi/hssf/model/LinkTable.java
index f6f75692ce..cc40258382 100644
--- a/src/java/org/apache/poi/hssf/model/LinkTable.java
+++ b/src/java/org/apache/poi/hssf/model/LinkTable.java
@@ -522,9 +522,7 @@ final class LinkTable {
*/
private int findFirstRecordLocBySid(short sid) {
int index = 0;
- for (Iterator<Record> iterator = _workbookRecordList.iterator(); iterator.hasNext(); ) {
- Record record = iterator.next();
-
+ for (Record record : _workbookRecordList.getRecords()) {
if (record.getSid() == sid) {
return index;
}
@@ -639,11 +637,11 @@ final class LinkTable {
int supLinkIndex = 0;
// find the posistion of the Add-In SupBookRecord in the workbook stream,
// the created ExternalNameRecord will be appended to it
- for (Iterator<Record> iterator = _workbookRecordList.iterator(); iterator.hasNext(); supLinkIndex++) {
- Record record = iterator.next();
- if (record instanceof SupBookRecord) {
- if (((SupBookRecord) record).isAddInFunctions()) break;
+ for (Record record : _workbookRecordList.getRecords()) {
+ if (record instanceof SupBookRecord && ((SupBookRecord) record).isAddInFunctions()) {
+ break;
}
+ supLinkIndex++;
}
int numberOfNames = extBlock.getNumberOfNames();
// a new name is inserted in the end of the SupBookRecord, after the last name
diff --git a/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java b/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
index e02f8ba84a..b29d7268e9 100644
--- a/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
+++ b/src/java/org/apache/poi/hssf/model/WorkbookRecordList.java
@@ -18,28 +18,37 @@
package org.apache.poi.hssf.model;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import org.apache.poi.hssf.record.Record;
-public final class WorkbookRecordList implements Iterable<Record> {
+public final class WorkbookRecordList {
private List<Record> records = new ArrayList<Record>();
- private int protpos = 0; // holds the position of the protect record.
- private int bspos = 0; // holds the position of the last bound sheet.
- private int tabpos = 0; // holds the position of the tabid record
- private int fontpos = 0; // hold the position of the last font record
- private int xfpos = 0; // hold the position of the last extended font record
- private int backuppos = 0; // holds the position of the backup record.
- private int namepos = 0; // holds the position of last name record
- private int supbookpos = 0; // holds the position of sup book
- private int externsheetPos = 0;// holds the position of the extern sheet
- private int palettepos = -1; // hold the position of the palette, if applicable
+ /** holds the position of the protect record */
+ private int protpos = 0;
+ /** holds the position of the last bound sheet */
+ private int bspos = 0;
+ /** holds the position of the tabid record */
+ private int tabpos = 0;
+ /** hold the position of the last font record */
+ private int fontpos = 0;
+ /** hold the position of the last extended font record */
+ private int xfpos = 0;
+ /** holds the position of the backup record */
+ private int backuppos = 0;
+ /** holds the position of last name record */
+ private int namepos = 0;
+ /** holds the position of sup book */
+ private int supbookpos = 0;
+ /** holds the position of the extern sheet */
+ private int externsheetPos = 0;
+ /** hold the position of the palette, if applicable */
+ private int palettepos = -1;
public void setRecords(List<Record> records) {
- this.records = records;
+ this.records = records;
}
public int size() {
@@ -52,26 +61,13 @@ public final class WorkbookRecordList implements Iterable<Record> {
public void add(int pos, Record r) {
records.add(pos, r);
- if (getProtpos() >= pos) setProtpos( protpos + 1 );
- if (getBspos() >= pos) setBspos( bspos + 1 );
- if (getTabpos() >= pos) setTabpos( tabpos + 1 );
- if (getFontpos() >= pos) setFontpos( fontpos + 1 );
- if (getXfpos() >= pos) setXfpos( xfpos + 1 );
- if (getBackuppos() >= pos) setBackuppos( backuppos + 1 );
- if (getNamepos() >= pos) setNamepos(namepos+1);
- if (getSupbookpos() >= pos) setSupbookpos(supbookpos+1);
- if ((getPalettepos() != -1) && (getPalettepos() >= pos)) setPalettepos( palettepos + 1 );
- if (getExternsheetPos() >= pos) setExternsheetPos(getExternsheetPos() + 1);
+ updateRecordPos(pos, true);
}
public List<Record> getRecords() {
return records;
}
- public Iterator<Record> iterator() {
- return records.iterator();
- }
-
/**
* Find the given record in the record list by identity and removes it
*
@@ -89,19 +85,9 @@ public final class WorkbookRecordList implements Iterable<Record> {
}
}
- public void remove( int pos )
- {
+ public void remove( int pos ) {
records.remove(pos);
- if (getProtpos() >= pos) setProtpos( protpos - 1 );
- if (getBspos() >= pos) setBspos( bspos - 1 );
- if (getTabpos() >= pos) setTabpos( tabpos - 1 );
- if (getFontpos() >= pos) setFontpos( fontpos - 1 );
- if (getXfpos() >= pos) setXfpos( xfpos - 1 );
- if (getBackuppos() >= pos) setBackuppos( backuppos - 1 );
- if (getNamepos() >= pos) setNamepos(getNamepos()-1);
- if (getSupbookpos() >= pos) setSupbookpos(getSupbookpos()-1);
- if ((getPalettepos() != -1) && (getPalettepos() >= pos)) setPalettepos( palettepos - 1 );
- if (getExternsheetPos() >= pos) setExternsheetPos( getExternsheetPos() -1);
+ updateRecordPos(pos, false);
}
public int getProtpos() {
@@ -160,7 +146,7 @@ public final class WorkbookRecordList implements Iterable<Record> {
this.palettepos = palettepos;
}
-
+
/**
* Returns the namepos.
* @return int
@@ -208,4 +194,48 @@ public final class WorkbookRecordList implements Iterable<Record> {
public void setExternsheetPos(int externsheetPos) {
this.externsheetPos = externsheetPos;
}
+
+ private void updateRecordPos(int pos, boolean add) {
+ int delta = (add) ? 1 : -1;
+ int p = getProtpos();
+ if (p >= pos) {
+ setProtpos( p + delta );
+ }
+ p = getBspos();
+ if (p >= pos) {
+ setBspos( p + delta );
+ }
+ p = getTabpos();
+ if (p >= pos) {
+ setTabpos( p + delta );
+ }
+ p = getFontpos();
+ if (p >= pos) {
+ setFontpos( p + delta );
+ }
+ p = getXfpos();
+ if (p >= pos) {
+ setXfpos( p + delta );
+ }
+ p = getBackuppos();
+ if (p >= pos) {
+ setBackuppos( p + delta );
+ }
+ p = getNamepos();
+ if (p >= pos) {
+ setNamepos(p + delta );
+ }
+ p = getSupbookpos();
+ if (p >= pos) {
+ setSupbookpos(p + delta);
+ }
+ p = getPalettepos();
+ if (p != -1 && p >= pos) {
+ setPalettepos( p + delta );
+ }
+ p = getExternsheetPos();
+ if (p >= pos) {
+ setExternsheetPos( p + delta );
+ }
+ }
}
diff --git a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
index 4cfe3b9fe8..6fe2fd309d 100644
--- a/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
+++ b/src/java/org/apache/poi/hssf/usermodel/HSSFWorkbook.java
@@ -30,6 +30,7 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.Charset;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -52,6 +53,8 @@ import org.apache.poi.ddf.EscherBlipRecord;
import org.apache.poi.ddf.EscherMetafileBlip;
import org.apache.poi.ddf.EscherRecord;
import org.apache.poi.hpsf.ClassID;
+import org.apache.poi.hpsf.DocumentSummaryInformation;
+import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hssf.OldExcelFormatException;
import org.apache.poi.hssf.model.DrawingManager2;
import org.apache.poi.hssf.model.HSSFFormulaParser;
@@ -59,6 +62,7 @@ import org.apache.poi.hssf.model.InternalSheet;
import org.apache.poi.hssf.model.InternalSheet.UnsupportedBOFType;
import org.apache.poi.hssf.model.InternalWorkbook;
import org.apache.poi.hssf.model.RecordStream;
+import org.apache.poi.hssf.model.WorkbookRecordList;
import org.apache.poi.hssf.record.AbstractEscherHolderRecord;
import org.apache.poi.hssf.record.BackupRecord;
import org.apache.poi.hssf.record.BoundSheetRecord;
@@ -77,9 +81,13 @@ import org.apache.poi.hssf.record.UnknownRecord;
import org.apache.poi.hssf.record.aggregates.RecordAggregate.RecordVisitor;
import org.apache.poi.hssf.record.common.UnicodeString;
import org.apache.poi.hssf.record.crypto.Biff8DecryptingStream;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.util.CellReference;
import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
import org.apache.poi.poifs.crypt.Decryptor;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.EncryptionVerifier;
import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DirectoryNode;
@@ -1454,13 +1462,21 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
// Write out our HPFS properties, if we have them
writeProperties(fs, excepts);
-
+
if (preserveNodes) {
// Don't write out the old Workbook, we'll be doing our new one
// If the file had an "incorrect" name for the workbook stream,
// don't write the old one as we'll use the correct name shortly
excepts.addAll(Arrays.asList(WORKBOOK_DIR_ENTRY_NAMES));
+ // summary information has been already written via writeProperties and might go in a
+ // different stream, if the file is cryptoapi encrypted
+ excepts.addAll(Arrays.asList(
+ DocumentSummaryInformation.DEFAULT_STREAM_NAME,
+ SummaryInformation.DEFAULT_STREAM_NAME,
+ getEncryptedPropertyStreamName()
+ ));
+
// Copy over all the other nodes to our new poifs
EntryUtils.copyNodes(
new FilteringDirectoryNode(getDirectory(), excepts)
@@ -1520,6 +1536,7 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
HSSFSheet[] sheets = getSheets();
int nSheets = sheets.length;
+ updateEncryptionInfo();
// before getting the workbook size we must tell the sheets that
// serialization is about to occur.
@@ -1566,22 +1583,14 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
@SuppressWarnings("resource")
protected void encryptBytes(byte buf[]) {
- int initialOffset = 0;
- FilePassRecord fpr = null;
- for (Record r : workbook.getRecords()) {
- initialOffset += r.getRecordSize();
- if (r instanceof FilePassRecord) {
- fpr = (FilePassRecord)r;
- break;
- }
- }
- if (fpr == null) {
+ EncryptionInfo ei = getEncryptionInfo();
+ if (ei == null) {
return;
}
-
+ Encryptor enc = ei.getEncryptor();
+ int initialOffset = 0;
LittleEndianByteArrayInputStream plain = new LittleEndianByteArrayInputStream(buf, 0); // NOSONAR
LittleEndianByteArrayOutputStream leos = new LittleEndianByteArrayOutputStream(buf, 0); // NOSONAR
- Encryptor enc = fpr.getEncryptionInfo().getEncryptor();
enc.setChunkSize(Biff8DecryptingStream.RC4_REKEYING_INTERVAL);
byte tmp[] = new byte[1024];
try {
@@ -2306,4 +2315,50 @@ public final class HSSFWorkbook extends POIDocument implements org.apache.poi.ss
public SpreadsheetVersion getSpreadsheetVersion() {
return SpreadsheetVersion.EXCEL97;
}
+
+ @Override
+ public EncryptionInfo getEncryptionInfo() {
+ FilePassRecord fpr = (FilePassRecord)workbook.findFirstRecordBySid(FilePassRecord.sid);
+ return (fpr != null) ? fpr.getEncryptionInfo() : null;
+ }
+
+
+ private void updateEncryptionInfo() {
+ // make sure, that we've read all the streams ...
+ readProperties();
+ FilePassRecord fpr = (FilePassRecord)workbook.findFirstRecordBySid(FilePassRecord.sid);
+
+ String password = Biff8EncryptionKey.getCurrentUserPassword();
+ WorkbookRecordList wrl = workbook.getWorkbookRecordList();
+ if (password == null) {
+ if (fpr != null) {
+ // need to remove password data
+ wrl.remove(fpr);
+ }
+ } else {
+ // create password record
+ if (fpr == null) {
+ fpr = new FilePassRecord(EncryptionMode.cryptoAPI);
+ wrl.add(1, fpr);
+ }
+
+ // check if the password has been changed
+ EncryptionInfo ei = fpr.getEncryptionInfo();
+ EncryptionVerifier ver = ei.getVerifier();
+ byte encVer[] = ver.getEncryptedVerifier();
+ Decryptor dec = ei.getDecryptor();
+ Encryptor enc = ei.getEncryptor();
+ try {
+ if (encVer == null || !dec.verifyPassword(password)) {
+ enc.confirmPassword(password);
+ } else {
+ byte verifier[] = dec.getVerifier();
+ byte salt[] = ver.getSalt();
+ enc.confirmPassword(password, null, null, verifier, salt, null);
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("can't validate/update encryption setting", e);
+ }
+ }
+ }
}
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
index 222b425178..f1b5e235ac 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIDecryptor.java
@@ -153,14 +153,19 @@ public class CryptoAPIDecryptor extends Decryptor implements Cloneable {
/**
* Decrypt the Document-/SummaryInformation and other optionally streams.
* Opposed to other crypto modes, cryptoapi is record based and can't be used
- * to stream-decrypt a whole file
+ * to stream-decrypt a whole file.<p>
+ *
+ * Summary entries are only encrypted within cryptoapi encrypted files.
+ * Binary RC4 encrypted files use non-encrypted/default property sets
+ *
+ * @param root root directory node of the OLE file containing the encrypted properties
+ * @param encryptedStream name of the encrypted stream -
+ * "encryption" for HSSF/HWPF, "encryptedStream" (or encryptedSummary?) for HSLF
*
* @see <a href="http://msdn.microsoft.com/en-us/library/dd943321(v=office.12).aspx">2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream</a>
*/
public POIFSFileSystem getSummaryEntries(DirectoryNode root, String encryptedStream)
throws IOException, GeneralSecurityException {
- // HSLF: encryptedStream
- // HSSF: encryption
DocumentNode es = (DocumentNode) root.getEntry(encryptedStream);
DocumentInputStream dis = root.createDocumentInputStream(es);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
diff --git a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
index 2a47922883..2dec416034 100644
--- a/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/cryptoapi/CryptoAPIEncryptor.java
@@ -32,10 +32,6 @@ import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.hpsf.DocumentSummaryInformation;
-import org.apache.poi.hpsf.PropertySetFactory;
-import org.apache.poi.hpsf.SummaryInformation;
-import org.apache.poi.hpsf.WritingNotSupportedException;
import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.DataSpaceMapUtils;
@@ -46,6 +42,8 @@ import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor.StreamDescriptorE
import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
+import org.apache.poi.poifs.filesystem.Entry;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayOutputStream;
@@ -124,41 +122,34 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
*
* @see <a href="http://msdn.microsoft.com/en-us/library/dd943321(v=office.12).aspx">2.3.5.4 RC4 CryptoAPI Encrypted Summary Stream</a>
*/
- public OutputStream getSummaryEntries(DirectoryNode dir)
+ public void setSummaryEntries(DirectoryNode dir, String encryptedStream, NPOIFSFileSystem entries)
throws IOException, GeneralSecurityException {
CryptoAPIDocumentOutputStream bos = new CryptoAPIDocumentOutputStream(this); // NOSONAR
byte buf[] = new byte[8];
bos.write(buf, 0, 8); // skip header
- String entryNames[] = {
- SummaryInformation.DEFAULT_STREAM_NAME,
- DocumentSummaryInformation.DEFAULT_STREAM_NAME
- };
-
List<StreamDescriptorEntry> descList = new ArrayList<StreamDescriptorEntry>();
int block = 0;
- for (String entryName : entryNames) {
- if (!dir.hasEntry(entryName)) {
+ for (Entry entry : entries.getRoot()) {
+ if (entry.isDirectoryEntry()) {
continue;
}
StreamDescriptorEntry descEntry = new StreamDescriptorEntry();
descEntry.block = block;
descEntry.streamOffset = bos.size();
- descEntry.streamName = entryName;
+ descEntry.streamName = entry.getName();
descEntry.flags = StreamDescriptorEntry.flagStream.setValue(0, 1);
descEntry.reserved2 = 0;
bos.setBlock(block);
- DocumentInputStream dis = dir.createDocumentInputStream(entryName);
+ DocumentInputStream dis = dir.createDocumentInputStream(entry);
IOUtils.copy(dis, bos);
dis.close();
descEntry.streamSize = bos.size() - descEntry.streamOffset;
descList.add(descEntry);
- dir.getEntry(entryName).delete();
-
block++;
}
@@ -197,16 +188,7 @@ public class CryptoAPIEncryptor extends Encryptor implements Cloneable {
bos.write(buf, 0, 8);
bos.setSize(savedSize);
- dir.createDocument("EncryptedSummary", new ByteArrayInputStream(bos.getBuf(), 0, savedSize));
- DocumentSummaryInformation dsi = PropertySetFactory.newDocumentSummaryInformation();
-
- try {
- dsi.write(dir, DocumentSummaryInformation.DEFAULT_STREAM_NAME);
- } catch (WritingNotSupportedException e) {
- throw new IOException(e);
- }
-
- return bos;
+ dir.createDocument(encryptedStream, new ByteArrayInputStream(bos.getBuf(), 0, savedSize));
}
protected int getKeySizeInBytes() {
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java
new file mode 100644
index 0000000000..c9be3d9ed2
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestHxxFEncryption.java
@@ -0,0 +1,187 @@
+/* ====================================================================
+ 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 static org.apache.poi.POIDataSamples.getDocumentInstance;
+import static org.apache.poi.POIDataSamples.getSlideShowInstance;
+import static org.apache.poi.POIDataSamples.getSpreadSheetInstance;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.poi.POIDataSamples;
+import org.apache.poi.POIDocument;
+import org.apache.poi.POIOLE2TextExtractor;
+import org.apache.poi.POITextExtractor;
+import org.apache.poi.extractor.ExtractorFactory;
+import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
+import org.apache.poi.poifs.storage.RawDataUtil;
+import org.apache.xmlbeans.XmlException;
+import org.junit.AfterClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TestHxxFEncryption {
+ @AfterClass
+ public static void clearPass() {
+ Biff8EncryptionKey.setCurrentUserPassword(null);
+ }
+
+ @Parameter(value = 0)
+ public POIDataSamples sampleDir;
+
+ @Parameter(value = 1)
+ public String file;
+
+ @Parameter(value = 2)
+ public String password;
+
+ @Parameter(value = 3)
+ public String expected;
+
+ @Parameters(name="{1}")
+ public static Collection<Object[]> data() throws IOException {
+ return Arrays.asList(
+ // binary rc4
+ new Object[]{ getDocumentInstance(), "password_tika_binaryrc4.doc", "tika", "This is an encrypted Word 2007 File." },
+ // cryptoapi
+ new Object[]{ getDocumentInstance(), "password_password_cryptoapi.doc", "password", "This is a test" },
+ // binary rc4
+ new Object[]{ getSpreadSheetInstance(), "password.xls", "password",
+ x("H4sIAAAAAAAAAF1Uu24bMRDs/RULVwkgCUhSpHaZwkDgpHJH8fZ0G/Nx4ZI6y13yG/mRfIb9R5mlZFlIpdPtcnZmdnjPf57/vvx6+f3h6obuv3"+
+ "ylbY5bEiVHe1fEpUp5pOgkrK0iabehm7FyoZi1ks8xcvHiQu8h5bLnorTlnUvkJ/YPOHKsLVInAqCs91KakuaxLq4w3g00SgCo9Xou1UnCmSBe"+
+ "MhpRY6qHmXVFteQfQJ5yUaaOw4qXwgPVjPGAqhNH5bBHAfTmwqqoSkLdFT/J3nC0eZBRk7yiu5s7yoU+r+9l3tDtm5A3jgt6AQxNOY2ya+U4sK"+
+ "XZ+YczbpfSVVuzFOuunKraqIVD2ND3yVXauT3TNthR/O3IJAM7gzTOGeIcXZvj14ahotW8wSognlMu0Yyp/Fi7O6s+CK6haUUjtPCji7MVcgqH"+
+ "jh+42tqeqPDMroJ/lBAE4AZbJbJu6Fu35ej42Tw9mYeTwVXoBKJiPeFV94q2rZJAyNEPo/qOdWYLBpq3B2JX8GDZeJ14mZf3tOQWBmpd9yQ7kI"+
+ "DCY/jmkj1oGOicFy62r9vutC5uJsVEMFgmAXXfYcC6BRBKNHCybALFJolnrDcPXNLl+K60Vctt09YZT7YgbeOICGJ/ZgC2JztOnm1JhX3eJXni"+
+ "U5Bqhezzlu334vD/Ajr3yDGXw5G9IZ6aLmLfQafY42N3J7cjj1LaXOHihSrcC5ThmuYIB5FX5AU8tKlnNG9Dn1EnsdD4KcnPhsSNPRiXtz461b"+
+ "VZw8Pm6vn0afh4fvr0D5P/+cMuBAAA") },
+ // cryptoapi
+ new Object[]{ getSpreadSheetInstance(), "35897-type4.xls", "freedom", "Sheet1\nhello there!" },
+ // cryptoapi (PPT only supports cryptoapi...)
+ new Object[]{ getSlideShowInstance(), "cryptoapi-proc2356.ppt", "crypto", "Dominic Salemno" }
+ );
+ }
+
+ private static String x(String base64) throws IOException {
+ return new String(RawDataUtil.decompress(base64), Charset.forName("UTF-8"));
+ }
+
+ @Test
+ public void extract() throws IOException, OpenXML4JException, XmlException {
+ Biff8EncryptionKey.setCurrentUserPassword(password);
+ File f = sampleDir.getFile(file);
+ POITextExtractor te = ExtractorFactory.createExtractor(f);
+ String actual = te.getText().trim();
+ assertEquals(expected, actual);
+ te.close();
+ }
+
+ @Test
+ public void changePassword() throws IOException, OpenXML4JException, XmlException {
+ newPassword("test");
+ }
+
+ @Test
+ public void removePassword() throws IOException, OpenXML4JException, XmlException {
+ newPassword(null);
+ }
+
+ public void newPassword(String newPass) throws IOException, OpenXML4JException, XmlException {
+ Biff8EncryptionKey.setCurrentUserPassword(password);
+ File f = sampleDir.getFile(file);
+ POIOLE2TextExtractor te1 = (POIOLE2TextExtractor)ExtractorFactory.createExtractor(f);
+ Biff8EncryptionKey.setCurrentUserPassword(newPass);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ POIDocument doc = te1.getDocument();
+ doc.write(bos);
+ doc.close();
+ te1.close();
+ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
+ POITextExtractor te2 = ExtractorFactory.createExtractor(bis);
+ String actual = te2.getText().trim();
+ assertEquals(expected, actual);
+ te2.close();
+ }
+
+ /** changing the encryption mode and key size in poor mans style - see comments below */
+ @Test
+ public void changeEncryption() throws IOException, OpenXML4JException, XmlException {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ Biff8EncryptionKey.setCurrentUserPassword(password);
+ File f = sampleDir.getFile(file);
+ POIOLE2TextExtractor te1 = (POIOLE2TextExtractor)ExtractorFactory.createExtractor(f);
+ // first remove encryption
+ Biff8EncryptionKey.setCurrentUserPassword(null);
+ POIDocument doc = te1.getDocument();
+ doc.write(bos);
+ doc.close();
+ te1.close();
+ // then use default setting, which is cryptoapi
+ String newPass = "newPass";
+ POIOLE2TextExtractor te2 = (POIOLE2TextExtractor)ExtractorFactory.createExtractor(new ByteArrayInputStream(bos.toByteArray()));
+ Biff8EncryptionKey.setCurrentUserPassword(newPass);
+ doc = te2.getDocument();
+ bos.reset();
+ doc.write(bos);
+ doc.close();
+ te2.close();
+ // and finally update cryptoapi setting
+ POIOLE2TextExtractor te3 = (POIOLE2TextExtractor)ExtractorFactory.createExtractor(new ByteArrayInputStream(bos.toByteArray()));
+ doc = te3.getDocument();
+ // need to cache data (i.e. read all data) before changing the key size
+ if (doc instanceof HSLFSlideShowImpl) {
+ HSLFSlideShowImpl hss = (HSLFSlideShowImpl)doc;
+ hss.getPictureData();
+ hss.getDocumentSummaryInformation();
+ }
+ EncryptionInfo ei = doc.getEncryptionInfo();
+ assertNotNull(ei);
+ assertTrue(ei.getHeader() instanceof CryptoAPIEncryptionHeader);
+ assertEquals(0x28, ((CryptoAPIEncryptionHeader)ei.getHeader()).getKeySize());
+ ((CryptoAPIEncryptionHeader)ei.getHeader()).setKeySize(0x78);
+ bos.reset();
+ doc.write(bos);
+ doc.close();
+ te3.close();
+ // check the setting
+ POIOLE2TextExtractor te4 = (POIOLE2TextExtractor)ExtractorFactory.createExtractor(new ByteArrayInputStream(bos.toByteArray()));
+ doc = te4.getDocument();
+ ei = doc.getEncryptionInfo();
+ assertNotNull(ei);
+ assertTrue(ei.getHeader() instanceof CryptoAPIEncryptionHeader);
+ assertEquals(0x78, ((CryptoAPIEncryptionHeader)ei.getHeader()).getKeySize());
+ doc.close();
+ te4.close();
+ }
+}
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
index 1251904b21..83925bc540 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
@@ -40,8 +40,7 @@ import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
+import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.util.BitField;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
@@ -55,8 +54,7 @@ import org.apache.poi.util.LittleEndianByteArrayOutputStream;
@Internal
public class HSLFSlideShowEncrypted implements Closeable {
DocumentEncryptionAtom dea;
- CryptoAPIEncryptor enc = null;
- CryptoAPIDecryptor dec = null;
+ EncryptionInfo _encryptionInfo;
// Cipher cipher = null;
ChunkedCipherOutputStream cyos = null;
@@ -120,11 +118,15 @@ public class HSLFSlideShowEncrypted implements Closeable {
}
assert(r instanceof DocumentEncryptionAtom);
this.dea = (DocumentEncryptionAtom)r;
- decryptInit();
String pass = Biff8EncryptionKey.getCurrentUserPassword();
- if(!dec.verifyPassword(pass != null ? pass : Decryptor.DEFAULT_PASSWORD)) {
- throw new EncryptedPowerPointFileException("PowerPoint file is encrypted. The correct password needs to be set via Biff8EncryptionKey.setCurrentUserPassword()");
+ EncryptionInfo ei = getEncryptionInfo();
+ try {
+ if(!ei.getDecryptor().verifyPassword(pass != null ? pass : Decryptor.DEFAULT_PASSWORD)) {
+ throw new EncryptedPowerPointFileException("PowerPoint file is encrypted. The correct password needs to be set via Biff8EncryptionKey.setCurrentUserPassword()");
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedPowerPointFileException(e);
}
}
@@ -132,24 +134,10 @@ public class HSLFSlideShowEncrypted implements Closeable {
return dea;
}
- protected void decryptInit() {
- if (dec != null) {
- return;
- }
- EncryptionInfo ei = dea.getEncryptionInfo();
- dec = (CryptoAPIDecryptor)ei.getDecryptor();
- }
-
- protected void encryptInit() {
- if (enc != null) {
- return;
- }
- EncryptionInfo ei = dea.getEncryptionInfo();
- enc = (CryptoAPIEncryptor)ei.getEncryptor();
+ protected EncryptionInfo getEncryptionInfo() {
+ return (dea != null) ? dea.getEncryptionInfo() : null;
}
-
-
protected OutputStream encryptRecord(OutputStream plainStream, int persistId, Record record) {
boolean isPlain = (dea == null
|| record instanceof UserEditAtom
@@ -166,9 +154,8 @@ public class HSLFSlideShowEncrypted implements Closeable {
return plainStream;
}
- encryptInit();
-
if (cyos == null) {
+ Encryptor enc = getEncryptionInfo().getEncryptor();
enc.setChunkSize(-1);
cyos = enc.getDataStream(plainStream, 0);
}
@@ -190,12 +177,12 @@ public class HSLFSlideShowEncrypted implements Closeable {
return;
}
- decryptInit();
+ Decryptor dec = getEncryptionInfo().getDecryptor();
dec.setChunkSize(-1);
LittleEndianByteArrayInputStream lei = new LittleEndianByteArrayInputStream(docstream, offset); // NOSONAR
ChunkedCipherInputStream ccis = null;
try {
- ccis = dec.getDataStream(lei, docstream.length-offset, 0);
+ ccis = (ChunkedCipherInputStream)dec.getDataStream(lei, docstream.length-offset, 0);
ccis.initCipherForBlock(persistId);
// decrypt header and read length to be decrypted
@@ -217,7 +204,8 @@ public class HSLFSlideShowEncrypted implements Closeable {
// when reading the picture elements, each time a segment is read, the cipher needs
// to be reset (usually done when calling Cipher.doFinal)
LittleEndianByteArrayInputStream lei = new LittleEndianByteArrayInputStream(pictstream, offset);
- ChunkedCipherInputStream ccis = dec.getDataStream(lei, len, 0);
+ Decryptor dec = getEncryptionInfo().getDecryptor();
+ ChunkedCipherInputStream ccis = (ChunkedCipherInputStream)dec.getDataStream(lei, len, 0);
readFully(ccis, pictstream, offset, len);
ccis.close();
lei.close();
@@ -228,8 +216,6 @@ public class HSLFSlideShowEncrypted implements Closeable {
return;
}
- decryptInit();
-
try {
// decrypt header and read length to be decrypted
decryptPicBytes(pictstream, offset, 8);
@@ -302,12 +288,11 @@ public class HSLFSlideShowEncrypted implements Closeable {
return;
}
- encryptInit();
-
LittleEndianByteArrayOutputStream los = new LittleEndianByteArrayOutputStream(pictstream, offset); // NOSONAR
ChunkedCipherOutputStream ccos = null;
try {
+ Encryptor enc = getEncryptionInfo().getEncryptor();
enc.setChunkSize(-1);
ccos = enc.getDataStream(los, 0);
int recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
@@ -396,11 +381,10 @@ public class HSLFSlideShowEncrypted implements Closeable {
// create password record
if (dea == null) {
dea = new DocumentEncryptionAtom();
- enc = null;
}
- encryptInit();
EncryptionInfo ei = dea.getEncryptionInfo();
byte salt[] = ei.getVerifier().getSalt();
+ Encryptor enc = getEncryptionInfo().getEncryptor();
if (salt == null) {
enc.confirmPassword(password);
} else {
diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
index 02e3d3e0cc..2b758cff3d 100644
--- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
+++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
@@ -24,7 +24,6 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -34,7 +33,6 @@ import java.util.NavigableMap;
import java.util.TreeMap;
import org.apache.poi.POIDocument;
-import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.HSLFException;
import org.apache.poi.hslf.record.CurrentUserAtom;
@@ -46,7 +44,7 @@ import org.apache.poi.hslf.record.PositionDependentRecord;
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.RecordTypes;
import org.apache.poi.hslf.record.UserEditAtom;
-import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptor;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
@@ -731,44 +729,15 @@ public final class HSLFSlideShowImpl extends POIDocument implements Closeable {
}
}
- /**
- * For a given named property entry, either return it or null if
- * if it wasn't found
- *
- * @param setName The property to read
- * @return The value of the given property or null if it wasn't found.
- */
- @Override
- protected PropertySet getPropertySet(String setName) {
- DocumentEncryptionAtom dea = getDocumentEncryptionAtom();
- return (dea == null)
- ? super.getPropertySet(setName)
- : super.getPropertySet(setName, dea.getEncryptionInfo());
- }
- /**
- * Writes out the standard Documment Information Properties (HPSF)
- *
- * @param outFS the POIFSFileSystem to write the properties into
- * @param writtenEntries a list of POIFS entries to add the property names too
- * @throws IOException if an error when writing to the
- * {@link POIFSFileSystem} occurs
- */
@Override
- protected void writeProperties(NPOIFSFileSystem outFS, List<String> writtenEntries) throws IOException {
- super.writeProperties(outFS, writtenEntries);
+ public EncryptionInfo getEncryptionInfo() throws IOException {
DocumentEncryptionAtom dea = getDocumentEncryptionAtom();
- if (dea != null) {
- CryptoAPIEncryptor enc = (CryptoAPIEncryptor) dea.getEncryptionInfo().getEncryptor();
- try {
- enc.getSummaryEntries(outFS.getRoot()); // ignore OutputStream
- } catch (IOException e) {
- throw e;
- } catch (GeneralSecurityException e) {
- throw new IOException(e);
- }
- }
+ return (dea != null) ? dea.getEncryptionInfo() : null;
}
+
+
+
/* ******************* adding methods follow ********************* */
@@ -893,6 +862,10 @@ public final class HSLFSlideShowImpl extends POIDocument implements Closeable {
}
}
+ @Override
+ protected String getEncryptedPropertyStreamName() {
+ return "EncryptedSummary";
+ }
private static class BufAccessBAOS extends ByteArrayOutputStream {
public byte[] getBuf() {
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
index 25e83b95ef..1369841acf 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocument.java
@@ -23,6 +23,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.security.GeneralSecurityException;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.SummaryInformation;
@@ -61,13 +62,18 @@ import org.apache.poi.hwpf.usermodel.OfficeDrawings;
import org.apache.poi.hwpf.usermodel.OfficeDrawingsImpl;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.poifs.common.POIFSConstants;
+import org.apache.poi.poifs.crypt.ChunkedCipherOutputStream;
import org.apache.poi.poifs.crypt.EncryptionInfo;
+import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.Encryptor;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.EntryUtils;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.Internal;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
/**
*
@@ -322,6 +328,7 @@ public final class HWPFDocument extends HWPFDocumentCore {
_fields = new FieldsImpl(_fieldsTables);
}
+ @Override
@Internal
public TextPieceTable getTextTable()
{
@@ -340,6 +347,7 @@ public final class HWPFDocument extends HWPFDocumentCore {
return _dop;
}
+ @Override
public Range getOverallRange() {
return new Range(0, _text.length(), this);
}
@@ -348,6 +356,7 @@ public final class HWPFDocument extends HWPFDocumentCore {
* Returns the range which covers the whole of the document, but excludes
* any headers and footers.
*/
+ @Override
public Range getRange()
{
// // First up, trigger a full-recalculate
@@ -394,8 +403,9 @@ public final class HWPFDocument extends HWPFDocumentCore {
{
int length = getFileInformationBlock()
.getSubdocumentTextStreamLength( previos );
- if ( subdocument == previos )
+ if ( subdocument == previos ) {
return new Range( startCp, startCp + length, this );
+ }
startCp += length;
}
throw new UnsupportedOperationException(
@@ -603,34 +613,52 @@ public final class HWPFDocument extends HWPFDocumentCore {
* @throws IOException If there is an unexpected IOException from the passed
* in OutputStream.
*/
+ @Override
public void write(OutputStream out) throws IOException {
NPOIFSFileSystem pfs = new NPOIFSFileSystem();
write(pfs, true);
pfs.writeFilesystem( out );
}
- private void write(NPOIFSFileSystem pfs, boolean copyOtherEntries) throws IOException {
- // initialize our streams for writing.
- HWPFFileSystem docSys = new HWPFFileSystem();
- ByteArrayOutputStream wordDocumentStream = docSys.getStream(STREAM_WORD_DOCUMENT);
- ByteArrayOutputStream tableStream = docSys.getStream(STREAM_TABLE_1);
- //HWPFOutputStream dataStream = docSys.getStream("Data");
- int tableOffset = 0;
- // FileInformationBlock fib = (FileInformationBlock)_fib.clone();
+ private void write(NPOIFSFileSystem pfs, boolean copyOtherEntries) throws IOException {
// clear the offsets and sizes in our FileInformationBlock.
_fib.clearOffsetsSizes();
// determine the FileInformationBLock size
int fibSize = _fib.getSize();
- fibSize += POIFSConstants.SMALLER_BIG_BLOCK_SIZE -
- (fibSize % POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
+ fibSize += POIFSConstants.SMALLER_BIG_BLOCK_SIZE - (fibSize % POIFSConstants.SMALLER_BIG_BLOCK_SIZE);
+
+ // initialize our streams for writing.
+ HWPFFileSystem docSys = new HWPFFileSystem();
+ ByteArrayOutputStream wordDocumentStream = docSys.getStream(STREAM_WORD_DOCUMENT);
+ ByteArrayOutputStream tableStream = docSys.getStream(STREAM_TABLE_1);
// preserve space for the FileInformationBlock because we will be writing
// it after we write everything else.
byte[] placeHolder = new byte[fibSize];
wordDocumentStream.write(placeHolder);
int mainOffset = wordDocumentStream.size();
+ int tableOffset = 0;
+ // write out EncryptionInfo
+ updateEncryptionInfo();
+ EncryptionInfo ei = getEncryptionInfo();
+ if (ei != null) {
+ byte buf[] = new byte[1000];
+ LittleEndianByteArrayOutputStream leos = new LittleEndianByteArrayOutputStream(buf, 0);
+ leos.writeShort(ei.getVersionMajor());
+ leos.writeShort(ei.getVersionMinor());
+ if (ei.getEncryptionMode() == EncryptionMode.cryptoAPI) {
+ leos.writeInt(ei.getEncryptionFlags());
+ }
+
+ ((EncryptionRecord)ei.getHeader()).write(leos);
+ ((EncryptionRecord)ei.getVerifier()).write(leos);
+ tableStream.write(buf, 0, leos.getWriteIndex());
+ tableOffset += leos.getWriteIndex();
+ _fib.getFibBase().setLKey(tableOffset);
+ }
+
// write out the StyleSheet.
_fib.setFcStshf(tableOffset);
_ss.writeTo(tableStream);
@@ -869,13 +897,7 @@ public final class HWPFDocument extends HWPFDocumentCore {
_fib.setCbMac(wordDocumentStream.size());
// make sure that the table, doc and data streams use big blocks.
- byte[] mainBuf = wordDocumentStream.toByteArray();
- if (mainBuf.length < 4096)
- {
- byte[] tempBuf = new byte[4096];
- System.arraycopy(mainBuf, 0, tempBuf, 0, mainBuf.length);
- mainBuf = tempBuf;
- }
+ byte[] mainBuf = fillUp4096(wordDocumentStream);
// Table1 stream will be used
_fib.getFibBase().setFWhichTblStm( true );
@@ -884,98 +906,52 @@ public final class HWPFDocument extends HWPFDocumentCore {
//_fib.serialize(mainBuf, 0);
_fib.writeTo(mainBuf, tableStream);
- byte[] tableBuf = tableStream.toByteArray();
- if (tableBuf.length < 4096)
- {
- byte[] tempBuf = new byte[4096];
- System.arraycopy(tableBuf, 0, tempBuf, 0, tableBuf.length);
- tableBuf = tempBuf;
- }
-
- byte[] dataBuf = _dataStream;
- if (dataBuf == null)
- {
- dataBuf = new byte[4096];
- }
- if (dataBuf.length < 4096)
- {
- byte[] tempBuf = new byte[4096];
- System.arraycopy(dataBuf, 0, tempBuf, 0, dataBuf.length);
- dataBuf = tempBuf;
+ byte[] tableBuf = fillUp4096(tableStream);
+ byte[] dataBuf = fillUp4096(_dataStream);
+
+ // Create a new document - ignoring the order of the old entries
+ if (ei == null) {
+ write(pfs, mainBuf, STREAM_WORD_DOCUMENT);
+ write(pfs, tableBuf, STREAM_TABLE_1);
+ write(pfs, dataBuf, STREAM_DATA);
+ } else {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+ encryptBytes(mainBuf, FIB_BASE_LEN, bos);
+ write(pfs, bos.toByteArray(), STREAM_WORD_DOCUMENT);
+ bos.reset();
+ encryptBytes(tableBuf, _fib.getFibBase().getLKey(), bos);
+ write(pfs, bos.toByteArray(), STREAM_TABLE_1);
+ bos.reset();
+ encryptBytes(dataBuf, 0, bos);
+ write(pfs, bos.toByteArray(), STREAM_DATA);
+ bos.reset();
}
- // Create a new document preserving order of entries / Update existing
- boolean docWritten = false;
- boolean dataWritten = false;
- boolean objectPoolWritten = false;
- boolean tableWritten = false;
- boolean propertiesWritten = false;
- for (Entry entry : getDirectory()) {
- if ( entry.getName().equals( STREAM_WORD_DOCUMENT ) )
- {
- if ( !docWritten )
- {
- write(pfs, mainBuf, STREAM_WORD_DOCUMENT);
- docWritten = true;
- }
- }
- else if ( entry.getName().equals( STREAM_OBJECT_POOL ) )
- {
- if ( !objectPoolWritten )
- {
- if ( copyOtherEntries ) {
- _objectPool.writeTo( pfs.getRoot() );
- } else {
- // Object pool is already there, no need to change/copy
- }
- objectPoolWritten = true;
- }
- }
- else if ( entry.getName().equals( STREAM_TABLE_0 )
- || entry.getName().equals( STREAM_TABLE_1 ) )
- {
- if ( !tableWritten )
- {
- write(pfs, tableBuf, STREAM_TABLE_1);
- tableWritten = true;
- }
- }
- else if ( entry.getName().equals(
- SummaryInformation.DEFAULT_STREAM_NAME )
- || entry.getName().equals(
- DocumentSummaryInformation.DEFAULT_STREAM_NAME ) )
- {
- if ( !propertiesWritten )
- {
- writeProperties( pfs );
- propertiesWritten = true;
- }
- }
- else if ( entry.getName().equals( STREAM_DATA ) )
- {
- if ( !dataWritten )
- {
- write(pfs, dataBuf, STREAM_DATA);
- dataWritten = true;
+ writeProperties( pfs );
+
+ if ( copyOtherEntries && ei == null ) {
+ // For encrypted files:
+ // The ObjectPool storage MUST NOT be present and if the file contains OLE objects, the storage
+ // objects for the OLE objects MUST be stored in the Data stream as specified in sprmCPicLocation.
+ DirectoryNode newRoot = pfs.getRoot();
+ _objectPool.writeTo( newRoot );
+
+ for (Entry entry : getDirectory()) {
+ String entryName = entry.getName();
+ if ( !(
+ STREAM_WORD_DOCUMENT.equals(entryName) ||
+ STREAM_TABLE_0.equals(entryName) ||
+ STREAM_TABLE_1.equals(entryName) ||
+ STREAM_DATA.equals(entryName) ||
+ STREAM_OBJECT_POOL.equals(entryName) ||
+ SummaryInformation.DEFAULT_STREAM_NAME.equals(entryName) ||
+ DocumentSummaryInformation.DEFAULT_STREAM_NAME.equals(entryName)
+ ) ) {
+ EntryUtils.copyNodeRecursively( entry, newRoot );
}
}
- else if ( copyOtherEntries )
- {
- EntryUtils.copyNodeRecursively( entry, pfs.getRoot() );
- }
}
- if ( !docWritten )
- write(pfs, mainBuf, STREAM_WORD_DOCUMENT);
- if ( !tableWritten )
- write(pfs, tableBuf, STREAM_TABLE_1);
- if ( !propertiesWritten )
- writeProperties( pfs );
- if ( !dataWritten )
- write(pfs, dataBuf, STREAM_DATA);
- if ( !objectPoolWritten && copyOtherEntries )
- _objectPool.writeTo( pfs.getRoot() );
-
/*
* since we updated all references in FIB and etc, using new arrays to
* access data
@@ -984,6 +960,43 @@ public final class HWPFDocument extends HWPFDocumentCore {
this._tableStream = tableStream.toByteArray();
this._dataStream = dataBuf;
}
+
+ private void encryptBytes(byte[] plain, int encryptOffset, OutputStream bos) throws IOException {
+ try {
+ EncryptionInfo ei = getEncryptionInfo();
+ Encryptor enc = ei.getEncryptor();
+ enc.setChunkSize(RC4_REKEYING_INTERVAL);
+ ChunkedCipherOutputStream os = enc.getDataStream(bos, 0);
+ if (encryptOffset > 0) {
+ os.writePlain(plain, 0, encryptOffset);
+ }
+ os.write(plain, encryptOffset, plain.length-encryptOffset);
+ os.close();
+ } catch (GeneralSecurityException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private static byte[] fillUp4096(byte[] buf) {
+ if (buf == null) {
+ return new byte[4096];
+ } else if (buf.length < 4096) {
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(4096);
+ bos.write(buf, 0, buf.length);
+ return fillUp4096(bos);
+ } else {
+ return buf;
+ }
+ }
+
+ private static byte[] fillUp4096(ByteArrayOutputStream bos) {
+ int fillSize = 4096 - bos.size();
+ if (fillSize > 0) {
+ bos.write(new byte[fillSize], 0, fillSize);
+ }
+ return bos.toByteArray();
+ }
+
private static void write(NPOIFSFileSystem pfs, byte[] data, String name) throws IOException {
pfs.createOrUpdateDocument(new ByteArrayInputStream(data), name);
}
@@ -1009,9 +1022,8 @@ public final class HWPFDocument extends HWPFDocumentCore {
list.getLFOData() );
}
- public void delete(int start, int length)
- {
- Range r = new Range(start, start + length, this);
- r.delete();
- }
-}
+ public void delete(int start, int length) {
+ Range r = new Range(start, start + length, this);
+ r.delete();
+ }
+} \ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java
index c52abc101e..42639decbb 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/HWPFDocumentCore.java
@@ -18,7 +18,6 @@
package org.apache.poi.hwpf;
import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
@@ -26,7 +25,6 @@ import java.security.GeneralSecurityException;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.POIDocument;
-import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hwpf.model.CHPBinTable;
import org.apache.poi.hwpf.model.FibBase;
@@ -44,6 +42,7 @@ import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionMode;
+import org.apache.poi.poifs.crypt.Encryptor;
import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentEntry;
@@ -67,7 +66,17 @@ public abstract class HWPFDocumentCore extends POIDocument {
protected static final String STREAM_TABLE_0 = "0Table";
protected static final String STREAM_TABLE_1 = "1Table";
- private static final int FIB_BASE_LEN = 68;
+ /**
+ * Size of the not encrypted part of the FIB
+ */
+ protected static final int FIB_BASE_LEN = 68;
+
+ /**
+ * [MS-DOC] 2.2.6.2/3 Office Binary Document ... Encryption:
+ * "... The block number MUST be set to zero at the beginning of the stream and
+ * MUST be incremented at each 512 byte boundary. ..."
+ */
+ protected static final int RC4_REKEYING_INTERVAL = 512;
/** Holds OLE2 objects */
protected ObjectPoolImpl _objectPool;
@@ -171,35 +180,85 @@ public abstract class HWPFDocumentCore extends POIDocument {
}
_objectPool = new ObjectPoolImpl(objectPoolEntry);
}
+ /**
+ * Returns the range which covers the whole of the document, but excludes
+ * any headers and footers.
+ */
+ public abstract Range getRange();
/**
- * For a given named property entry, either return it or null if
- * if it wasn't found
- *
- * @param setName The property to read
- * @return The value of the given property or null if it wasn't found.
+ * Returns the range that covers all text in the file, including main text,
+ * footnotes, headers and comments
*/
- @Override
- protected PropertySet getPropertySet(String setName) {
- EncryptionInfo ei;
- try {
- ei = getEncryptionInfo();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return (ei == null)
- ? super.getPropertySet(setName)
- : super.getPropertySet(setName, ei);
+ public abstract Range getOverallRange();
+
+ /**
+ * Returns document text, i.e. text information from all text pieces,
+ * including OLE descriptions and field codes
+ */
+ public String getDocumentText() {
+ return getText().toString();
+ }
+
+ /**
+ * Internal method to access document text
+ */
+ @Internal
+ public abstract StringBuilder getText();
+
+ public CHPBinTable getCharacterTable() {
+ return _cbt;
}
- protected EncryptionInfo getEncryptionInfo() throws IOException {
+ public PAPBinTable getParagraphTable() {
+ return _pbt;
+ }
+
+ public SectionTable getSectionTable() {
+ return _st;
+ }
+
+ public StyleSheet getStyleSheet() {
+ return _ss;
+ }
+
+ public ListTables getListTables() {
+ return _lt;
+ }
+
+ public FontTable getFontTable() {
+ return _ft;
+ }
+
+ public FileInformationBlock getFileInformationBlock() {
+ return _fib;
+ }
+
+ public ObjectsPool getObjectsPool() {
+ return _objectPool;
+ }
+
+ public abstract TextPieceTable getTextTable();
+
+ @Internal
+ public byte[] getMainStream() {
+ return _mainStream;
+ }
+
+ @Override
+ public EncryptionInfo getEncryptionInfo() throws IOException {
if (_encryptionInfo != null) {
return _encryptionInfo;
}
// Create our FIB, and check for the doc being encrypted
- byte[] fibBaseBytes = (_mainStream != null) ? _mainStream : getDocumentEntryBytes(STREAM_WORD_DOCUMENT, -1, FIB_BASE_LEN);
- FibBase fibBase = new FibBase( fibBaseBytes, 0 );
+ FibBase fibBase;
+ if (_fib != null && _fib.getFibBase() != null) {
+ fibBase = _fib.getFibBase();
+ } else {
+ byte[] fibBaseBytes = (_mainStream != null) ? _mainStream : getDocumentEntryBytes(STREAM_WORD_DOCUMENT, -1, FIB_BASE_LEN);
+ fibBase = new FibBase( fibBaseBytes, 0 );
+ }
if (!fibBase.isFEncrypted()) {
return null;
}
@@ -210,7 +269,7 @@ public abstract class HWPFDocumentCore extends POIDocument {
EncryptionMode em = fibBase.isFObfuscated() ? EncryptionMode.xor : null;
EncryptionInfo ei = new EncryptionInfo(leis, em);
Decryptor dec = ei.getDecryptor();
- dec.setChunkSize(512);
+ dec.setChunkSize(RC4_REKEYING_INTERVAL);
try {
String pass = Biff8EncryptionKey.getCurrentUserPassword();
if (pass == null) {
@@ -226,6 +285,35 @@ public abstract class HWPFDocumentCore extends POIDocument {
return ei;
}
+ protected void updateEncryptionInfo() {
+ // make sure, that we've read all the streams ...
+ readProperties();
+ // now check for the password
+ String password = Biff8EncryptionKey.getCurrentUserPassword();
+ FibBase fBase = _fib.getFibBase();
+ if (password == null) {
+ fBase.setLKey(0);
+ fBase.setFEncrypted(false);
+ fBase.setFObfuscated(false);
+ _encryptionInfo = null;
+ } else {
+ // create password record
+ if (_encryptionInfo == null) {
+ _encryptionInfo = new EncryptionInfo(EncryptionMode.cryptoAPI);
+ fBase.setFEncrypted(true);
+ fBase.setFObfuscated(false);
+ }
+ Encryptor enc = _encryptionInfo.getEncryptor();
+ byte salt[] = _encryptionInfo.getVerifier().getSalt();
+ if (salt == null) {
+ enc.confirmPassword(password);
+ } else {
+ byte verifier[] = _encryptionInfo.getDecryptor().getVerifier();
+ enc.confirmPassword(password, null, null, verifier, salt, null);
+ }
+ }
+ }
+
/**
* Reads OLE Stream into byte array - if an {@link EncryptionInfo} is available,
* decrypt the bytes starting at encryptionOffset. If encryptionOffset = -1, then do not try
@@ -273,70 +361,4 @@ public abstract class HWPFDocumentCore extends POIDocument {
IOUtils.closeQuietly(dis);
}
}
-
-
- /**
- * Returns the range which covers the whole of the document, but excludes
- * any headers and footers.
- */
- public abstract Range getRange();
-
- /**
- * Returns the range that covers all text in the file, including main text,
- * footnotes, headers and comments
- */
- public abstract Range getOverallRange();
-
- /**
- * Returns document text, i.e. text information from all text pieces,
- * including OLE descriptions and field codes
- */
- public String getDocumentText() {
- return getText().toString();
- }
-
- /**
- * Internal method to access document text
- */
- @Internal
- public abstract StringBuilder getText();
-
- public CHPBinTable getCharacterTable() {
- return _cbt;
- }
-
- public PAPBinTable getParagraphTable() {
- return _pbt;
- }
-
- public SectionTable getSectionTable() {
- return _st;
- }
-
- public StyleSheet getStyleSheet() {
- return _ss;
- }
-
- public ListTables getListTables() {
- return _lt;
- }
-
- public FontTable getFontTable() {
- return _ft;
- }
-
- public FileInformationBlock getFileInformationBlock() {
- return _fib;
- }
-
- public ObjectsPool getObjectsPool() {
- return _objectPool;
- }
-
- public abstract TextPieceTable getTextTable();
-
- @Internal
- public byte[] getMainStream() {
- return _mainStream;
- }
} \ No newline at end of file
diff --git a/src/scratchpad/src/org/apache/poi/hwpf/model/io/HWPFFileSystem.java b/src/scratchpad/src/org/apache/poi/hwpf/model/io/HWPFFileSystem.java
index 4ab383f9b3..f8aa03a1de 100644
--- a/src/scratchpad/src/org/apache/poi/hwpf/model/io/HWPFFileSystem.java
+++ b/src/scratchpad/src/org/apache/poi/hwpf/model/io/HWPFFileSystem.java
@@ -31,9 +31,9 @@ public final class HWPFFileSystem
public HWPFFileSystem()
{
- _streams.put("WordDocument", new ByteArrayOutputStream());
- _streams.put("1Table", new ByteArrayOutputStream());
- _streams.put("Data", new ByteArrayOutputStream());
+ _streams.put("WordDocument", new ByteArrayOutputStream(100000));
+ _streams.put("1Table", new ByteArrayOutputStream(100000));
+ _streams.put("Data", new ByteArrayOutputStream(100000));
}
public ByteArrayOutputStream getStream(String name)
diff --git a/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java b/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java
deleted file mode 100644
index 875fb9ec7b..0000000000
--- a/src/scratchpad/testcases/org/apache/poi/hwpf/HWPFTestEncryption.java
+++ /dev/null
@@ -1,69 +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.hwpf;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collection;
-
-import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
-import org.apache.poi.hwpf.extractor.WordExtractor;
-import org.junit.AfterClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class HWPFTestEncryption {
- @AfterClass
- public static void clearPass() {
- Biff8EncryptionKey.setCurrentUserPassword(null);
- }
-
- @Parameter(value = 0)
- public String file;
-
- @Parameter(value = 1)
- public String password;
-
- @Parameter(value = 2)
- public String expected;
-
- @Parameters(name="{0}")
- public static Collection<String[]> data() {
- return Arrays.asList(
- new String[]{ "password_tika_binaryrc4.doc", "tika", "This is an encrypted Word 2007 File." },
- new String[]{ "password_password_cryptoapi.doc", "password", "This is a test" }
- );
- }
-
- @Test
- public void extract() throws IOException {
- Biff8EncryptionKey.setCurrentUserPassword(password);
- HWPFDocument docD = HWPFTestDataSamples.openSampleFile(file);
- WordExtractor we = new WordExtractor(docD);
- String actual = we.getText().trim();
- assertEquals(expected, actual);
- we.close();
- docD.close();
- }
-}