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
This commit is contained in:
parent
a6aa1fd99e
commit
582ea1c54c
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
<changes>
|
<changes>
|
||||||
<release version="3.8-beta6" date="2012-??-??">
|
<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">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">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>
|
<action dev="poi-developers" type="fix">52745 - fixed XSSFRichtextString.append to preserve leading / trailing spaces </action>
|
||||||
|
@ -19,24 +19,18 @@ package org.apache.poi.poifs.crypt;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
import org.apache.poi.poifs.filesystem.DirectoryNode;
|
||||||
import org.apache.poi.EncryptedDocumentException;
|
import org.apache.poi.EncryptedDocumentException;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.CipherInputStream;
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
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.DocumentInputStream;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,6 +40,7 @@ public class AgileDecryptor extends Decryptor {
|
|||||||
|
|
||||||
private final EncryptionInfo _info;
|
private final EncryptionInfo _info;
|
||||||
private SecretKey _secretKey;
|
private SecretKey _secretKey;
|
||||||
|
private long _length = -1;
|
||||||
|
|
||||||
private static final byte[] kVerifierInputBlock;
|
private static final byte[] kVerifierInputBlock;
|
||||||
private static final byte[] kHashedVerifierBlock;
|
private static final byte[] kHashedVerifierBlock;
|
||||||
@ -104,8 +99,13 @@ public class AgileDecryptor extends Decryptor {
|
|||||||
|
|
||||||
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
|
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
|
||||||
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
|
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
|
||||||
long size = dis.readLong();
|
_length = dis.readLong();
|
||||||
return new ChunkedCipherInputStream(dis, size);
|
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) {
|
protected AgileDecryptor(EncryptionInfo info) {
|
||||||
@ -182,7 +182,7 @@ public class AgileDecryptor extends Decryptor {
|
|||||||
private byte[] nextChunk() throws GeneralSecurityException, IOException {
|
private byte[] nextChunk() throws GeneralSecurityException, IOException {
|
||||||
int index = (int)(_pos >> 12);
|
int index = (int)(_pos >> 12);
|
||||||
byte[] blockKey = new byte[4];
|
byte[] blockKey = new byte[4];
|
||||||
LittleEndian.putInt(blockKey, index);
|
LittleEndian.putInt(blockKey, 0, index);
|
||||||
byte[] iv = generateIv(_info.getHeader().getAlgorithm(),
|
byte[] iv = generateIv(_info.getHeader().getAlgorithm(),
|
||||||
_info.getHeader().getKeySalt(), blockKey);
|
_info.getHeader().getKeySalt(), blockKey);
|
||||||
_cipher.init(Cipher.DECRYPT_MODE, _secretKey, new IvParameterSpec(iv));
|
_cipher.init(Cipher.DECRYPT_MODE, _secretKey, new IvParameterSpec(iv));
|
||||||
|
@ -31,12 +31,40 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
public abstract class Decryptor {
|
public abstract class Decryptor {
|
||||||
public static final String DEFAULT_PASSWORD="VelvetSweatshop";
|
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)
|
public abstract InputStream getDataStream(DirectoryNode dir)
|
||||||
throws IOException, GeneralSecurityException;
|
throws IOException, GeneralSecurityException;
|
||||||
|
|
||||||
public abstract boolean verifyPassword(String password)
|
public abstract boolean verifyPassword(String password)
|
||||||
throws GeneralSecurityException;
|
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) {
|
public static Decryptor getInstance(EncryptionInfo info) {
|
||||||
int major = info.getVersionMajor();
|
int major = info.getVersionMajor();
|
||||||
int minor = info.getVersionMinor();
|
int minor = info.getVersionMinor();
|
||||||
@ -82,7 +110,7 @@ public abstract class Decryptor {
|
|||||||
|
|
||||||
for (int i = 0; i < info.getVerifier().getSpinCount(); i++) {
|
for (int i = 0; i < info.getVerifier().getSpinCount(); i++) {
|
||||||
sha1.reset();
|
sha1.reset();
|
||||||
LittleEndian.putInt(iterator, i);
|
LittleEndian.putInt(iterator, 0, i);
|
||||||
sha1.update(iterator);
|
sha1.update(iterator);
|
||||||
hash = sha1.digest(hash);
|
hash = sha1.digest(hash);
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package org.apache.poi.poifs.crypt;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
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.DirectoryNode;
|
||||||
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
import org.apache.poi.poifs.filesystem.DocumentInputStream;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
|
||||||
import org.apache.poi.util.LittleEndian;
|
import org.apache.poi.util.LittleEndian;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +39,7 @@ import org.apache.poi.util.LittleEndian;
|
|||||||
public class EcmaDecryptor extends Decryptor {
|
public class EcmaDecryptor extends Decryptor {
|
||||||
private final EncryptionInfo info;
|
private final EncryptionInfo info;
|
||||||
private byte[] passwordHash;
|
private byte[] passwordHash;
|
||||||
|
private long _length = -1;
|
||||||
|
|
||||||
public EcmaDecryptor(EncryptionInfo info) {
|
public EcmaDecryptor(EncryptionInfo info) {
|
||||||
this.info = info;
|
this.info = info;
|
||||||
@ -51,7 +50,7 @@ public class EcmaDecryptor extends Decryptor {
|
|||||||
|
|
||||||
sha1.update(passwordHash);
|
sha1.update(passwordHash);
|
||||||
byte[] blockValue = new byte[4];
|
byte[] blockValue = new byte[4];
|
||||||
LittleEndian.putInt(blockValue, block);
|
LittleEndian.putInt(blockValue, 0, block);
|
||||||
byte[] finalHash = sha1.digest(blockValue);
|
byte[] finalHash = sha1.digest(blockValue);
|
||||||
|
|
||||||
int requiredKeyLength = info.getHeader().getKeySize()/8;
|
int requiredKeyLength = info.getHeader().getKeySize()/8;
|
||||||
@ -125,8 +124,13 @@ public class EcmaDecryptor extends Decryptor {
|
|||||||
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
|
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
|
||||||
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
|
DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
|
||||||
|
|
||||||
long size = dis.readLong();
|
_length = dis.readLong();
|
||||||
|
|
||||||
return new CipherInputStream(dis, getCipher());
|
return new CipherInputStream(dis, getCipher());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getLength(){
|
||||||
|
if(_length == -1) throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
|
||||||
|
return _length;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ import junit.framework.TestCase;
|
|||||||
import org.apache.poi.POIDataSamples;
|
import org.apache.poi.POIDataSamples;
|
||||||
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.security.GeneralSecurityException;
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user