aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorYegor Kozlov <yegor@apache.org>2012-02-26 08:49:36 +0000
committerYegor Kozlov <yegor@apache.org>2012-02-26 08:49:36 +0000
commita3ee22079cad3065349c57276dcfa31d731ad0a8 (patch)
tree3c542084b61f20dfe16d9bec4bbeaa6052c770fd /src
parenta5375147a107d4df0d26ca997093c51d86ff3cff (diff)
downloadpoi-a3ee22079cad3065349c57276dcfa31d731ad0a8.tar.gz
poi-a3ee22079cad3065349c57276dcfa31d731ad0a8.zip
Bugzilla 52690 - added a getter for length of encrypted data in Ecma and Agile decryptors
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1293784 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
-rw-r--r--src/documentation/content/xdocs/status.xml1
-rw-r--r--src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java18
-rw-r--r--src/java/org/apache/poi/poifs/crypt/Decryptor.java30
-rw-r--r--src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java12
-rw-r--r--src/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java34
5 files changed, 81 insertions, 14 deletions
diff --git a/src/documentation/content/xdocs/status.xml b/src/documentation/content/xdocs/status.xml
index 721c503758..c0dda11212 100644
--- a/src/documentation/content/xdocs/status.xml
+++ b/src/documentation/content/xdocs/status.xml
@@ -34,6 +34,7 @@
<changes>
<release version="3.8-beta6" date="2012-??-??">
+ <action dev="poi-developers" type="fix">52690 - added a getter for length of encrypted data in Ecma and Agile decryptors</action>
<action dev="poi-developers" type="fix">52255 - support adding TIFF,EPS and WPG pictures in OOXML documents </action>
<action dev="poi-developers" type="fix">52078 - avoid OutOfMemoryError when rendering groupped pictures in HSLF </action>
<action dev="poi-developers" type="fix">52745 - fixed XSSFRichtextString.append to preserve leading / trailing spaces </action>
diff --git a/src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java b/src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java
index 17ef47bb83..88e5f887f4 100644
--- a/src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java
@@ -19,24 +19,18 @@ package org.apache.poi.poifs.crypt;
import java.util.Arrays;
import java.io.IOException;
import java.io.InputStream;
-import java.io.FilterInputStream;
-import java.io.ByteArrayInputStream;
import java.security.MessageDigest;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.EncryptedDocumentException;
import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
/**
@@ -46,6 +40,7 @@ public class AgileDecryptor extends Decryptor {
private final EncryptionInfo _info;
private SecretKey _secretKey;
+ private long _length = -1;
private static final byte[] kVerifierInputBlock;
private static final byte[] kHashedVerifierBlock;
@@ -104,8 +99,13 @@ public class AgileDecryptor extends Decryptor {
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
- long size = dis.readLong();
- return new ChunkedCipherInputStream(dis, size);
+ _length = dis.readLong();
+ return new ChunkedCipherInputStream(dis, _length);
+ }
+
+ public long getLength(){
+ if(_length == -1) throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
+ return _length;
}
protected AgileDecryptor(EncryptionInfo info) {
@@ -182,7 +182,7 @@ public class AgileDecryptor extends Decryptor {
private byte[] nextChunk() throws GeneralSecurityException, IOException {
int index = (int)(_pos >> 12);
byte[] blockKey = new byte[4];
- LittleEndian.putInt(blockKey, index);
+ LittleEndian.putInt(blockKey, 0, index);
byte[] iv = generateIv(_info.getHeader().getAlgorithm(),
_info.getHeader().getKeySalt(), blockKey);
_cipher.init(Cipher.DECRYPT_MODE, _secretKey, new IvParameterSpec(iv));
diff --git a/src/java/org/apache/poi/poifs/crypt/Decryptor.java b/src/java/org/apache/poi/poifs/crypt/Decryptor.java
index fb5b11f7b3..f7ed16f875 100644
--- a/src/java/org/apache/poi/poifs/crypt/Decryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/Decryptor.java
@@ -31,12 +31,40 @@ import org.apache.poi.util.LittleEndian;
public abstract class Decryptor {
public static final String DEFAULT_PASSWORD="VelvetSweatshop";
+ /**
+ * Return a stream with decrypted data.
+ * <p>
+ * Use {@link #getLength()} to get the size of that data that can be safely read from the stream.
+ * Just reading to the end of the input stream is not sufficient because there are
+ * normally padding bytes that must be discarded
+ * </p>
+ *
+ * @param dir the node to read from
+ * @return decrypted stream
+ */
public abstract InputStream getDataStream(DirectoryNode dir)
throws IOException, GeneralSecurityException;
public abstract boolean verifyPassword(String password)
throws GeneralSecurityException;
+ /**
+ * Returns the length of the encytpted data that can be safely read with
+ * {@link #getDataStream(org.apache.poi.poifs.filesystem.DirectoryNode)}.
+ * Just reading to the end of the input stream is not sufficient because there are
+ * normally padding bytes that must be discarded
+ *
+ * <p>
+ * The length variable is initialized in {@link #getDataStream(org.apache.poi.poifs.filesystem.DirectoryNode)},
+ * an attempt to call getLength() prior to getDataStream() will result in IllegalStateException.
+ * </p>
+ *
+ * @return length of the encrypted data
+ * @throws IllegalStateException if {@link #getDataStream(org.apache.poi.poifs.filesystem.DirectoryNode)}
+ * was not called
+ */
+ public abstract long getLength();
+
public static Decryptor getInstance(EncryptionInfo info) {
int major = info.getVersionMajor();
int minor = info.getVersionMinor();
@@ -82,7 +110,7 @@ public abstract class Decryptor {
for (int i = 0; i < info.getVerifier().getSpinCount(); i++) {
sha1.reset();
- LittleEndian.putInt(iterator, i);
+ LittleEndian.putInt(iterator, 0, i);
sha1.update(iterator);
hash = sha1.digest(hash);
}
diff --git a/src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java b/src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
index 441a4335fe..641ee8cb47 100644
--- a/src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
+++ b/src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
@@ -18,7 +18,6 @@ package org.apache.poi.poifs.crypt;
import java.io.IOException;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -31,7 +30,6 @@ import javax.crypto.spec.SecretKeySpec;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.util.LittleEndian;
/**
@@ -41,6 +39,7 @@ import org.apache.poi.util.LittleEndian;
public class EcmaDecryptor extends Decryptor {
private final EncryptionInfo info;
private byte[] passwordHash;
+ private long _length = -1;
public EcmaDecryptor(EncryptionInfo info) {
this.info = info;
@@ -51,7 +50,7 @@ public class EcmaDecryptor extends Decryptor {
sha1.update(passwordHash);
byte[] blockValue = new byte[4];
- LittleEndian.putInt(blockValue, block);
+ LittleEndian.putInt(blockValue, 0, block);
byte[] finalHash = sha1.digest(blockValue);
int requiredKeyLength = info.getHeader().getKeySize()/8;
@@ -125,8 +124,13 @@ public class EcmaDecryptor extends Decryptor {
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
- long size = dis.readLong();
+ _length = dis.readLong();
return new CipherInputStream(dis, getCipher());
}
+
+ public long getLength(){
+ if(_length == -1) throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
+ return _length;
+ }
}
diff --git a/src/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java b/src/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java
index 27385719c8..dcef8d31cd 100644
--- a/src/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java
+++ b/src/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java
@@ -20,7 +20,9 @@ import junit.framework.TestCase;
import org.apache.poi.POIDataSamples;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import java.io.ByteArrayInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -80,4 +82,36 @@ public class TestDecryptor extends TestCase {
}
}
}
+ public void testDataLength() throws Exception {
+ POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx"));
+
+ EncryptionInfo info = new EncryptionInfo(fs);
+
+ Decryptor d = Decryptor.getInstance(info);
+
+ d.verifyPassword(Decryptor.DEFAULT_PASSWORD);
+
+ InputStream is = d.getDataStream(fs);
+
+ long len = d.getLength();
+ assertEquals(12810, len);
+
+ byte[] buf = new byte[(int)len];
+
+ is.read(buf);
+
+ ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(buf));
+
+ while (true) {
+ ZipEntry entry = zin.getNextEntry();
+ if (entry==null) {
+ break;
+ }
+
+ while (zin.available()>0) {
+ zin.skip(zin.available());
+ }
+ }
+ }
+
}