You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2016/09/28 23:36:09 UTC
svn commit: r1762726 [3/3] - in /poi: site/src/documentation/content/xdocs/
trunk/ trunk/src/java/org/apache/poi/
trunk/src/java/org/apache/poi/hssf/model/
trunk/src/java/org/apache/poi/hssf/record/
trunk/src/java/org/apache/poi/hssf/record/cont/ trunk...
Modified: poi/trunk/src/java/org/apache/poi/util/LittleEndianInput.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/util/LittleEndianInput.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/util/LittleEndianInput.java (original)
+++ poi/trunk/src/java/org/apache/poi/util/LittleEndianInput.java Wed Sep 28 23:36:09 2016
@@ -16,10 +16,7 @@
==================================================================== */
package org.apache.poi.util;
-/**
- *
- * @author Josh Micich
- */
+
public interface LittleEndianInput {
int available();
byte readByte();
@@ -31,4 +28,14 @@ public interface LittleEndianInput {
double readDouble();
void readFully(byte[] buf);
void readFully(byte[] buf, int off, int len);
+
+ /**
+ * Usually acts the same as {@link #readFully(byte[], int, int)}, but
+ * for an encrypted stream the raw (unencrypted) data is filled
+ *
+ * @param buf the byte array to receive the bytes
+ * @param off the start offset into the byte array
+ * @param len the amount of bytes to fill
+ */
+ void readPlain(byte[] buf, int off, int len);
}
Modified: poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java (original)
+++ poi/trunk/src/java/org/apache/poi/util/LittleEndianInputStream.java Wed Sep 28 23:36:09 2016
@@ -34,7 +34,8 @@ public class LittleEndianInputStream ext
super(is);
}
- public int available() {
+ @Override
+ public int available() {
try {
return super.available();
} catch (IOException e) {
@@ -42,11 +43,13 @@ public class LittleEndianInputStream ext
}
}
- public byte readByte() {
+ @Override
+ public byte readByte() {
return (byte)readUByte();
}
- public int readUByte() {
+ @Override
+ public int readUByte() {
byte buf[] = new byte[1];
try {
checkEOF(read(buf), 1);
@@ -56,11 +59,13 @@ public class LittleEndianInputStream ext
return LittleEndian.getUByte(buf);
}
- public double readDouble() {
+ @Override
+ public double readDouble() {
return Double.longBitsToDouble(readLong());
}
- public int readInt() {
+ @Override
+ public int readInt() {
byte buf[] = new byte[LittleEndianConsts.INT_SIZE];
try {
checkEOF(read(buf), buf.length);
@@ -82,7 +87,8 @@ public class LittleEndianInputStream ext
return retNum & 0x00FFFFFFFFL;
}
- public long readLong() {
+ @Override
+ public long readLong() {
byte buf[] = new byte[LittleEndianConsts.LONG_SIZE];
try {
checkEOF(read(buf), LittleEndianConsts.LONG_SIZE);
@@ -92,11 +98,13 @@ public class LittleEndianInputStream ext
return LittleEndian.getLong(buf);
}
- public short readShort() {
+ @Override
+ public short readShort() {
return (short)readUShort();
}
- public int readUShort() {
+ @Override
+ public int readUShort() {
byte buf[] = new byte[LittleEndianConsts.SHORT_SIZE];
try {
checkEOF(read(buf), LittleEndianConsts.SHORT_SIZE);
@@ -112,15 +120,22 @@ public class LittleEndianInputStream ext
}
}
- public void readFully(byte[] buf) {
+ @Override
+ public void readFully(byte[] buf) {
readFully(buf, 0, buf.length);
}
- public void readFully(byte[] buf, int off, int len) {
+ @Override
+ public void readFully(byte[] buf, int off, int len) {
try {
checkEOF(read(buf, off, len), len);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
+
+ @Override
+ public void readPlain(byte[] buf, int off, int len) {
+ readFully(buf, off, len);
+ }
}
Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java Wed Sep 28 23:36:09 2016
@@ -45,7 +45,7 @@ import org.apache.poi.poifs.crypt.Cipher
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.Decryptor;
import org.apache.poi.poifs.crypt.EncryptionHeader;
-import org.apache.poi.poifs.crypt.EncryptionInfoBuilder;
+import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.EncryptionVerifier;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier.AgileCertificateEntry;
@@ -56,14 +56,14 @@ import org.apache.poi.util.LittleEndian;
/**
* Decryptor implementation for Agile Encryption
*/
-public class AgileDecryptor extends Decryptor {
+public class AgileDecryptor extends Decryptor implements Cloneable {
private long _length = -1;
- protected static final byte[] kVerifierInputBlock;
- protected static final byte[] kHashedVerifierBlock;
- protected static final byte[] kCryptoKeyBlock;
- protected static final byte[] kIntegrityKeyBlock;
- protected static final byte[] kIntegrityValueBlock;
+ /* package */ static final byte[] kVerifierInputBlock;
+ /* package */ static final byte[] kHashedVerifierBlock;
+ /* package */ static final byte[] kCryptoKeyBlock;
+ /* package */ static final byte[] kIntegrityKeyBlock;
+ /* package */ static final byte[] kIntegrityValueBlock;
static {
kVerifierInputBlock =
@@ -83,16 +83,16 @@ public class AgileDecryptor extends Decr
(byte)0xb2, (byte)0x2c, (byte)0x84, (byte)0x33 };
}
- protected AgileDecryptor(AgileEncryptionInfoBuilder builder) {
- super(builder);
+ protected AgileDecryptor() {
}
/**
* set decryption password
*/
+ @Override
public boolean verifyPassword(String password) throws GeneralSecurityException {
- AgileEncryptionVerifier ver = (AgileEncryptionVerifier)builder.getVerifier();
- AgileEncryptionHeader header = (AgileEncryptionHeader)builder.getHeader();
+ AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
+ AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
HashAlgorithm hashAlgo = header.getHashAlgorithmEx();
CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
int blockSize = header.getBlockSize();
@@ -113,7 +113,7 @@ public class AgileDecryptor extends Decr
* blockSize bytes.
* 4. Use base64 to encode the result of step 3.
*/
- byte verfierInputEnc[] = hashInput(builder, pwHash, kVerifierInputBlock, ver.getEncryptedVerifier(), Cipher.DECRYPT_MODE);
+ byte verfierInputEnc[] = hashInput(getEncryptionInfo(), pwHash, kVerifierInputBlock, ver.getEncryptedVerifier(), Cipher.DECRYPT_MODE);
setVerifier(verfierInputEnc);
MessageDigest hashMD = getMessageDigest(hashAlgo);
byte[] verifierHash = hashMD.digest(verfierInputEnc);
@@ -130,7 +130,7 @@ public class AgileDecryptor extends Decr
* blockSize bytes, pad the hash value with 0x00 to an integral multiple of blockSize bytes.
* 4. Use base64 to encode the result of step 3.
*/
- byte verifierHashDec[] = hashInput(builder, pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
+ byte verifierHashDec[] = hashInput(getEncryptionInfo(), pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
verifierHashDec = getBlock0(verifierHashDec, hashAlgo.hashSize);
/**
@@ -146,7 +146,7 @@ public class AgileDecryptor extends Decr
* blockSize bytes.
* 4. Use base64 to encode the result of step 3.
*/
- byte keyspec[] = hashInput(builder, pwHash, kCryptoKeyBlock, ver.getEncryptedKey(), Cipher.DECRYPT_MODE);
+ byte keyspec[] = hashInput(getEncryptionInfo(), pwHash, kCryptoKeyBlock, ver.getEncryptedKey(), Cipher.DECRYPT_MODE);
keyspec = getBlock0(keyspec, keySize);
SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.getCipherAlgorithm().jceId);
@@ -204,8 +204,8 @@ public class AgileDecryptor extends Decr
* @throws GeneralSecurityException
*/
public boolean verifyPassword(KeyPair keyPair, X509Certificate x509) throws GeneralSecurityException {
- AgileEncryptionVerifier ver = (AgileEncryptionVerifier)builder.getVerifier();
- AgileEncryptionHeader header = (AgileEncryptionHeader)builder.getHeader();
+ AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
+ AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
HashAlgorithm hashAlgo = header.getHashAlgorithmEx();
CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
int blockSize = header.getBlockSize();
@@ -217,7 +217,9 @@ public class AgileDecryptor extends Decr
break;
}
}
- if (ace == null) return false;
+ if (ace == null) {
+ return false;
+ }
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
@@ -255,9 +257,9 @@ public class AgileDecryptor extends Decr
return fillSize;
}
- protected static byte[] hashInput(EncryptionInfoBuilder builder, byte pwHash[], byte blockKey[], byte inputKey[], int cipherMode) {
- EncryptionVerifier ver = builder.getVerifier();
- AgileDecryptor dec = (AgileDecryptor)builder.getDecryptor();
+ protected static byte[] hashInput(EncryptionInfo encryptionInfo, byte pwHash[], byte blockKey[], byte inputKey[], int cipherMode) {
+ EncryptionVerifier ver = encryptionInfo.getVerifier();
+ AgileDecryptor dec = (AgileDecryptor)encryptionInfo.getDecryptor();
int keySize = dec.getKeySizeInBytes();
int blockSize = dec.getBlockSizeInBytes();
HashAlgorithm hashAlgo = ver.getHashAlgorithm();
@@ -278,6 +280,7 @@ public class AgileDecryptor extends Decr
}
}
+ @Override
@SuppressWarnings("resource")
public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);
@@ -285,17 +288,20 @@ public class AgileDecryptor extends Decr
return new AgileCipherInputStream(dis, _length);
}
+ @Override
public long getLength(){
- if(_length == -1) throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
+ if(_length == -1) {
+ throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
+ }
return _length;
}
- protected static Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk, EncryptionInfoBuilder builder, SecretKey skey, int encryptionMode)
+ protected static Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk, EncryptionInfo encryptionInfo, SecretKey skey, int encryptionMode)
throws GeneralSecurityException {
- EncryptionHeader header = builder.getHeader();
- if (existing == null || lastChunk) {
- String padding = (lastChunk ? "PKCS5Padding" : "NoPadding");
+ EncryptionHeader header = encryptionInfo.getHeader();
+ String padding = (lastChunk ? "PKCS5Padding" : "NoPadding");
+ if (existing == null || !existing.getAlgorithm().endsWith(padding)) {
existing = getCipher(skey, header.getCipherAlgorithm(), header.getChainingMode(), header.getKeySalt(), encryptionMode, padding);
}
@@ -339,9 +345,15 @@ public class AgileDecryptor extends Decr
// TODO: calculate integrity hmac while reading the stream
// for a post-validation of the data
+ @Override
protected Cipher initCipherForBlock(Cipher cipher, int block)
throws GeneralSecurityException {
- return AgileDecryptor.initCipherForBlock(cipher, block, false, builder, getSecretKey(), Cipher.DECRYPT_MODE);
+ return AgileDecryptor.initCipherForBlock(cipher, block, false, getEncryptionInfo(), getSecretKey(), Cipher.DECRYPT_MODE);
}
}
+
+ @Override
+ public AgileDecryptor clone() throws CloneNotSupportedException {
+ return (AgileDecryptor)super.clone();
+ }
}
Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java Wed Sep 28 23:36:09 2016
@@ -27,7 +27,7 @@ import com.microsoft.schemas.office.x200
import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
-public class AgileEncryptionHeader extends EncryptionHeader {
+public class AgileEncryptionHeader extends EncryptionHeader implements Cloneable {
private byte encryptedHmacKey[], encryptedHmacValue[];
public AgileEncryptionHeader(String descriptor) {
@@ -99,6 +99,7 @@ public class AgileEncryptionHeader exten
}
// make method visible for this package
+ @Override
protected void setKeySalt(byte salt[]) {
if (salt == null || salt.length != getBlockSize()) {
throw new EncryptedDocumentException("invalid verifier salt");
@@ -121,4 +122,13 @@ public class AgileEncryptionHeader exten
protected void setEncryptedHmacValue(byte[] encryptedHmacValue) {
this.encryptedHmacValue = (encryptedHmacValue == null) ? null : encryptedHmacValue.clone();
}
+
+ @Override
+ public AgileEncryptionHeader clone() throws CloneNotSupportedException {
+ AgileEncryptionHeader other = (AgileEncryptionHeader)super.clone();
+ other.encryptedHmacKey = (encryptedHmacKey == null) ? null : encryptedHmacKey.clone();
+ other.encryptedHmacValue = (encryptedHmacValue == null) ? null : encryptedHmacValue.clone();
+ return other;
+ }
+
}
Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionInfoBuilder.java Wed Sep 28 23:36:09 2016
@@ -35,30 +35,24 @@ import com.microsoft.schemas.office.x200
public class AgileEncryptionInfoBuilder implements EncryptionInfoBuilder {
- EncryptionInfo info;
- AgileEncryptionHeader header;
- AgileEncryptionVerifier verifier;
- AgileDecryptor decryptor;
- AgileEncryptor encryptor;
-
@Override
- public void initialize(EncryptionInfo ei, LittleEndianInput dis) throws IOException {
- this.info = ei;
-
+ public void initialize(EncryptionInfo info, LittleEndianInput dis) throws IOException {
EncryptionDocument ed = parseDescriptor((InputStream)dis);
- header = new AgileEncryptionHeader(ed);
- verifier = new AgileEncryptionVerifier(ed);
- if (ei.getVersionMajor() == EncryptionMode.agile.versionMajor
- && ei.getVersionMinor() == EncryptionMode.agile.versionMinor) {
- decryptor = new AgileDecryptor(this);
- encryptor = new AgileEncryptor(this);
+ info.setHeader(new AgileEncryptionHeader(ed));
+ info.setVerifier(new AgileEncryptionVerifier(ed));
+ if (info.getVersionMajor() == EncryptionMode.agile.versionMajor
+ && info.getVersionMinor() == EncryptionMode.agile.versionMinor) {
+ AgileDecryptor dec = new AgileDecryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ AgileEncryptor enc = new AgileEncryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
}
}
@Override
- public void initialize(EncryptionInfo ei, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
- this.info = ei;
-
+ public void initialize(EncryptionInfo info, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {
if (cipherAlgorithm == null) {
cipherAlgorithm = CipherAlgorithm.aes128;
}
@@ -87,30 +81,14 @@ public class AgileEncryptionInfoBuilder
if (!found) {
throw new EncryptedDocumentException("KeySize "+keyBits+" not allowed for Cipher "+cipherAlgorithm.toString());
}
- header = new AgileEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
- verifier = new AgileEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
- decryptor = new AgileDecryptor(this);
- encryptor = new AgileEncryptor(this);
- }
-
- public AgileEncryptionHeader getHeader() {
- return header;
- }
-
- public AgileEncryptionVerifier getVerifier() {
- return verifier;
- }
-
- public AgileDecryptor getDecryptor() {
- return decryptor;
- }
-
- public AgileEncryptor getEncryptor() {
- return encryptor;
- }
-
- protected EncryptionInfo getInfo() {
- return info;
+ info.setHeader(new AgileEncryptionHeader(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+ info.setVerifier(new AgileEncryptionVerifier(cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode));
+ AgileDecryptor dec = new AgileDecryptor();
+ dec.setEncryptionInfo(info);
+ info.setDecryptor(dec);
+ AgileEncryptor enc = new AgileEncryptor();
+ enc.setEncryptionInfo(info);
+ info.setEncryptor(enc);
}
protected static EncryptionDocument parseDescriptor(String descriptor) {
Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java Wed Sep 28 23:36:09 2016
@@ -39,7 +39,7 @@ import com.microsoft.schemas.office.x200
/**
* Used when checking if a key is valid for a document
*/
-public class AgileEncryptionVerifier extends EncryptionVerifier {
+public class AgileEncryptionVerifier extends EncryptionVerifier implements Cloneable {
public static class AgileCertificateEntry {
X509Certificate x509;
@@ -87,8 +87,9 @@ public class AgileEncryptionVerifier ext
setEncryptedVerifierHash(keyData.getEncryptedVerifierHashValue());
int saltSize = keyData.getSaltSize();
- if (saltSize != getSalt().length)
+ if (saltSize != getSalt().length) {
throw new EncryptedDocumentException("Invalid salt size");
+ }
switch (keyData.getCipherChaining().intValue()) {
case STCipherChaining.INT_CHAINING_MODE_CBC:
@@ -101,7 +102,9 @@ public class AgileEncryptionVerifier ext
throw new EncryptedDocumentException("Unsupported chaining mode - "+keyData.getCipherChaining().toString());
}
- if (!encList.hasNext()) return;
+ if (!encList.hasNext()) {
+ return;
+ }
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
@@ -125,6 +128,7 @@ public class AgileEncryptionVerifier ext
setSpinCount(100000); // TODO: use parameter
}
+ @Override
protected void setSalt(byte salt[]) {
if (salt == null || salt.length != getCipherAlgorithm().blockSize) {
throw new EncryptedDocumentException("invalid verifier salt");
@@ -133,16 +137,19 @@ public class AgileEncryptionVerifier ext
}
// make method visible for this package
+ @Override
protected void setEncryptedVerifier(byte encryptedVerifier[]) {
super.setEncryptedVerifier(encryptedVerifier);
}
// make method visible for this package
+ @Override
protected void setEncryptedVerifierHash(byte encryptedVerifierHash[]) {
super.setEncryptedVerifierHash(encryptedVerifierHash);
}
// make method visible for this package
+ @Override
protected void setEncryptedKey(byte[] encryptedKey) {
super.setEncryptedKey(encryptedKey);
}
@@ -156,4 +163,12 @@ public class AgileEncryptionVerifier ext
public List<AgileCertificateEntry> getCertificates() {
return certList;
}
+
+ @Override
+ public AgileEncryptionVerifier clone() throws CloneNotSupportedException {
+ AgileEncryptionVerifier other = (AgileEncryptionVerifier)super.clone();
+ // TODO: deep copy of certList
+ other.certList = new ArrayList<AgileCertificateEntry>(certList);
+ return other;
+ }
}
Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java Wed Sep 28 23:36:09 2016
@@ -60,6 +60,7 @@ import org.apache.poi.poifs.crypt.standa
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianConsts;
import org.apache.xmlbeans.XmlOptions;
import com.microsoft.schemas.office.x2006.encryption.CTDataIntegrity;
@@ -74,21 +75,20 @@ import com.microsoft.schemas.office.x200
import com.microsoft.schemas.office.x2006.keyEncryptor.certificate.CTCertificateKeyEncryptor;
import com.microsoft.schemas.office.x2006.keyEncryptor.password.CTPasswordKeyEncryptor;
-public class AgileEncryptor extends Encryptor {
- private final AgileEncryptionInfoBuilder builder;
+public class AgileEncryptor extends Encryptor implements Cloneable {
private byte integritySalt[];
private byte pwHash[];
- protected AgileEncryptor(AgileEncryptionInfoBuilder builder) {
- this.builder = builder;
+ protected AgileEncryptor() {
}
+ @Override
public void confirmPassword(String password) {
// see [MS-OFFCRYPTO] - 2.3.3 EncryptionVerifier
Random r = new SecureRandom();
- int blockSize = builder.getHeader().getBlockSize();
- int keySize = builder.getHeader().getKeySize()/8;
- int hashSize = builder.getHeader().getHashAlgorithmEx().hashSize;
+ int blockSize = getEncryptionInfo().getHeader().getBlockSize();
+ int keySize = getEncryptionInfo().getHeader().getKeySize()/8;
+ int hashSize = getEncryptionInfo().getHeader().getHashAlgorithmEx().hashSize;
byte[] newVerifierSalt = new byte[blockSize]
, newVerifier = new byte[blockSize]
@@ -104,10 +104,11 @@ public class AgileEncryptor extends Encr
confirmPassword(password, newKeySpec, newKeySalt, newVerifierSalt, newVerifier, newIntegritySalt);
}
- public void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]) {
- AgileEncryptionVerifier ver = builder.getVerifier();
+ @Override
+ public void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]) {
+ AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
ver.setSalt(verifierSalt);
- AgileEncryptionHeader header = builder.getHeader();
+ AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
header.setKeySalt(keySalt);
HashAlgorithm hashAlgo = ver.getHashAlgorithm();
@@ -128,7 +129,7 @@ public class AgileEncryptor extends Encr
* blockSize bytes.
* 4. Use base64 to encode the result of step 3.
*/
- byte encryptedVerifier[] = hashInput(builder, pwHash, kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE);
+ byte encryptedVerifier[] = hashInput(getEncryptionInfo(), pwHash, kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE);
ver.setEncryptedVerifier(encryptedVerifier);
@@ -146,7 +147,7 @@ public class AgileEncryptor extends Encr
*/
MessageDigest hashMD = getMessageDigest(hashAlgo);
byte[] hashedVerifier = hashMD.digest(verifier);
- byte encryptedVerifierHash[] = hashInput(builder, pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
+ byte encryptedVerifierHash[] = hashInput(getEncryptionInfo(), pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
ver.setEncryptedVerifierHash(encryptedVerifierHash);
/**
@@ -162,7 +163,7 @@ public class AgileEncryptor extends Encr
* blockSize bytes.
* 4. Use base64 to encode the result of step 3.
*/
- byte encryptedKey[] = hashInput(builder, pwHash, kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE);
+ byte encryptedKey[] = hashInput(getEncryptionInfo(), pwHash, kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE);
ver.setEncryptedKey(encryptedKey);
SecretKey secretKey = new SecretKeySpec(keySpec, ver.getCipherAlgorithm().jceId);
@@ -214,6 +215,7 @@ public class AgileEncryptor extends Encr
}
}
+ @Override
public OutputStream getDataStream(DirectoryNode dir)
throws IOException, GeneralSecurityException {
// TODO: initialize headers
@@ -234,14 +236,14 @@ public class AgileEncryptor extends Encr
// as the integrity hmac needs to contain the StreamSize,
// it's not possible to calculate it on-the-fly while buffering
// TODO: add stream size parameter to getDataStream()
- AgileEncryptionVerifier ver = builder.getVerifier();
+ AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
HashAlgorithm hashAlgo = ver.getHashAlgorithm();
Mac integrityMD = CryptoFunctions.getMac(hashAlgo);
integrityMD.init(new SecretKeySpec(integritySalt, hashAlgo.jceHmacId));
byte buf[] = new byte[1024];
LittleEndian.putLong(buf, 0, oleStreamSize);
- integrityMD.update(buf, 0, LittleEndian.LONG_SIZE);
+ integrityMD.update(buf, 0, LittleEndianConsts.LONG_SIZE);
InputStream fis = new FileInputStream(tmpFile);
try {
@@ -255,7 +257,7 @@ public class AgileEncryptor extends Encr
byte hmacValue[] = integrityMD.doFinal();
- AgileEncryptionHeader header = builder.getHeader();
+ AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
int blockSize = header.getBlockSize();
byte iv[] = CryptoFunctions.generateIv(header.getHashAlgorithmEx(), header.getKeySalt(), kIntegrityValueBlock, blockSize);
Cipher cipher = CryptoFunctions.getCipher(getSecretKey(), header.getCipherAlgorithm(), header.getChainingMode(), iv, Cipher.ENCRYPT_MODE);
@@ -271,8 +273,8 @@ public class AgileEncryptor extends Encr
CTKeyEncryptor.Uri.HTTP_SCHEMAS_MICROSOFT_COM_OFFICE_2006_KEY_ENCRYPTOR_CERTIFICATE;
protected EncryptionDocument createEncryptionDocument() {
- AgileEncryptionVerifier ver = builder.getVerifier();
- AgileEncryptionHeader header = builder.getHeader();
+ AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
+ AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
EncryptionDocument ed = EncryptionDocument.Factory.newInstance();
CTEncryption edRoot = ed.addNewEncryption();
@@ -379,9 +381,10 @@ public class AgileEncryptor extends Encr
throws IOException, GeneralSecurityException {
DataSpaceMapUtils.addDefaultDataSpace(dir);
- final EncryptionInfo info = builder.getInfo();
+ final EncryptionInfo info = getEncryptionInfo();
EncryptionRecord er = new EncryptionRecord(){
+ @Override
public void write(LittleEndianByteArrayOutputStream bos) {
// EncryptionVersionInfo (4 bytes): A Version structure (section 2.1.4), where
// Version.vMajor MUST be 0x0004 and Version.vMinor MUST be 0x0004
@@ -422,7 +425,7 @@ public class AgileEncryptor extends Encr
@Override
protected Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk)
throws GeneralSecurityException {
- return AgileDecryptor.initCipherForBlock(existing, block, lastChunk, builder, getSecretKey(), Cipher.ENCRYPT_MODE);
+ return AgileDecryptor.initCipherForBlock(existing, block, lastChunk, getEncryptionInfo(), getSecretKey(), Cipher.ENCRYPT_MODE);
}
@Override
@@ -439,4 +442,11 @@ public class AgileEncryptor extends Encr
}
}
+ @Override
+ public AgileEncryptor clone() throws CloneNotSupportedException {
+ AgileEncryptor other = (AgileEncryptor)super.clone();
+ other.integritySalt = (integritySalt == null) ? null : integritySalt.clone();
+ other.pwHash = (pwHash == null) ? null : pwHash.clone();
+ return other;
+ }
}
Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestAgileEncryptionParameters.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestAgileEncryptionParameters.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestAgileEncryptionParameters.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestAgileEncryptionParameters.java Wed Sep 28 23:36:09 2016
@@ -16,8 +16,7 @@
==================================================================== */
package org.apache.poi.poifs.crypt;
-import static org.hamcrest.core.IsEqual.equalTo;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
@@ -94,6 +93,7 @@ public class TestAgileEncryptionParamete
os.close();
bos.reset();
fsEnc.writeFilesystem(bos);
+ fsEnc.close();
POIFSFileSystem fsDec = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
EncryptionInfo infoDec = new EncryptionInfo(fsDec);
@@ -103,6 +103,7 @@ public class TestAgileEncryptionParamete
InputStream is = dec.getDataStream(fsDec);
byte actualData[] = IOUtils.toByteArray(is);
is.close();
- assertThat("Failed roundtrip - "+ca+"-"+ha+"-"+cm, testData, equalTo(actualData));
+ fsDec.close();
+ assertArrayEquals("Failed roundtrip - "+ca+"-"+ha+"-"+cm, testData, actualData);
}
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/record/DocumentEncryptionAtom.java Wed Sep 28 23:36:09 2016
@@ -53,7 +53,8 @@ public final class DocumentEncryptionAto
ByteArrayInputStream bis = new ByteArrayInputStream(source, start+8, len-8);
LittleEndianInputStream leis = new LittleEndianInputStream(bis);
- ei = new EncryptionInfo(leis, true);
+ ei = new EncryptionInfo(leis, EncryptionMode.cryptoAPI);
+ leis.close();
}
public DocumentEncryptionAtom() {
@@ -121,6 +122,7 @@ public final class DocumentEncryptionAto
LittleEndian.putInt(_header, 4, bos.getWriteIndex());
out.write(_header);
out.write(data, 0, bos.getWriteIndex());
+ bos.close();
}
@Override
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowEncrypted.java Wed Sep 28 23:36:09 2016
@@ -17,6 +17,8 @@
package org.apache.poi.hslf.usermodel;
+import java.io.Closeable;
+import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
@@ -25,9 +27,6 @@ import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
-import javax.crypto.Cipher;
-import javax.crypto.CipherOutputStream;
-
import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException;
import org.apache.poi.hslf.exceptions.EncryptedPowerPointFileException;
import org.apache.poi.hslf.record.DocumentEncryptionAtom;
@@ -36,26 +35,45 @@ import org.apache.poi.hslf.record.Positi
import org.apache.poi.hslf.record.Record;
import org.apache.poi.hslf.record.UserEditAtom;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+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.util.BitField;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.Internal;
import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianByteArrayInputStream;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
/**
* This class provides helper functions for encrypted PowerPoint documents.
*/
@Internal
-public class HSLFSlideShowEncrypted {
+public class HSLFSlideShowEncrypted implements Closeable {
DocumentEncryptionAtom dea;
CryptoAPIEncryptor enc = null;
CryptoAPIDecryptor dec = null;
- Cipher cipher = null;
- CipherOutputStream cyos = null;
+// Cipher cipher = null;
+ ChunkedCipherOutputStream cyos = null;
private static final BitField fieldRecInst = new BitField(0xFFF0);
+
+ private static final int BLIB_STORE_ENTRY_PARTS[] = {
+ 1, // btWin32
+ 1, // btMacOS
+ 16, // rgbUid
+ 2, // tag
+ 4, // size
+ 4, // cRef
+ 4, // foDelay
+ 1, // unused1
+ 1, // cbName (@ index 33)
+ 1, // unused2
+ 1, // unused3
+ };
protected HSLFSlideShowEncrypted(DocumentEncryptionAtom dea) {
this.dea = dea;
@@ -67,7 +85,9 @@ public class HSLFSlideShowEncrypted {
UserEditAtom userEditAtomWithEncryption = null;
for (Map.Entry<Integer, Record> me : recordMap.descendingMap().entrySet()) {
Record r = me.getValue();
- if (!(r instanceof UserEditAtom)) continue;
+ if (!(r instanceof UserEditAtom)) {
+ continue;
+ }
UserEditAtom uea = (UserEditAtom)r;
if (uea.getEncryptSessionPersistIdRef() != -1) {
userEditAtomWithEncryption = uea;
@@ -83,7 +103,7 @@ public class HSLFSlideShowEncrypted {
Record r = recordMap.get(userEditAtomWithEncryption.getPersistPointersOffset());
assert(r instanceof PersistPtrHolder);
PersistPtrHolder ptr = (PersistPtrHolder)r;
-
+
Integer encOffset = ptr.getSlideLocationsLookup().get(userEditAtomWithEncryption.getEncryptSessionPersistIdRef());
if (encOffset == null) {
// encryption info doesn't exist anymore
@@ -91,7 +111,7 @@ public class HSLFSlideShowEncrypted {
dea = null;
return;
}
-
+
r = recordMap.get(encOffset);
if (r == null) {
r = Record.buildRecordAtOffset(docstream, encOffset);
@@ -100,7 +120,7 @@ public class HSLFSlideShowEncrypted {
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()");
@@ -110,119 +130,144 @@ public class HSLFSlideShowEncrypted {
public DocumentEncryptionAtom getDocumentEncryptionAtom() {
return dea;
}
-
- protected void setPersistId(int persistId) {
- if (enc != null && dec != null) {
- throw new EncryptedPowerPointFileException("Use instance either for en- or decryption");
- }
-
- try {
- if (enc != null) cipher = enc.initCipherForBlock(cipher, persistId);
- if (dec != null) cipher = dec.initCipherForBlock(cipher, persistId);
- } catch (GeneralSecurityException e) {
- throw new EncryptedPowerPointFileException(e);
- }
- }
-
+
protected void decryptInit() {
- if (dec != null) return;
+ if (dec != null) {
+ return;
+ }
EncryptionInfo ei = dea.getEncryptionInfo();
dec = (CryptoAPIDecryptor)ei.getDecryptor();
}
-
+
protected void encryptInit() {
- if (enc != null) return;
+ if (enc != null) {
+ return;
+ }
EncryptionInfo ei = dea.getEncryptionInfo();
enc = (CryptoAPIEncryptor)ei.getEncryptor();
}
-
-
+
+
protected OutputStream encryptRecord(OutputStream plainStream, int persistId, Record record) {
- boolean isPlain = (dea == null
+ boolean isPlain = (dea == null
|| record instanceof UserEditAtom
|| record instanceof PersistPtrHolder
|| record instanceof DocumentEncryptionAtom
);
- if (isPlain) return plainStream;
- encryptInit();
- setPersistId(persistId);
-
- if (cyos == null) {
- cyos = new CipherOutputStream(plainStream, cipher);
+ try {
+ if (isPlain) {
+ if (cyos != null) {
+ // write cached data to stream
+ cyos.flush();
+ }
+ return plainStream;
+ }
+
+ encryptInit();
+
+ if (cyos == null) {
+ enc.setChunkSize(-1);
+ cyos = enc.getDataStream(plainStream, 0);
+ }
+ cyos.initCipherForBlock(persistId, false);
+ } catch (Exception e) {
+ throw new EncryptedPowerPointFileException(e);
}
return cyos;
}
+ private static void readFully(ChunkedCipherInputStream ccis, byte[] docstream, int offset, int len) throws IOException {
+ if (IOUtils.readFully(ccis, docstream, offset, len) == -1) {
+ throw new EncryptedPowerPointFileException("unexpected EOF");
+ }
+ }
+
protected void decryptRecord(byte[] docstream, int persistId, int offset) {
- if (dea == null) return;
+ if (dea == null) {
+ return;
+ }
decryptInit();
- setPersistId(persistId);
-
+ dec.setChunkSize(-1);
+ LittleEndianByteArrayInputStream lei = new LittleEndianByteArrayInputStream(docstream, offset);
+ ChunkedCipherInputStream ccis = null;
try {
+ ccis = dec.getDataStream(lei, docstream.length-offset, 0);
+ ccis.initCipherForBlock(persistId);
+
// decrypt header and read length to be decrypted
- cipher.update(docstream, offset, 8, docstream, offset);
+ readFully(ccis, docstream, offset, 8);
// decrypt the rest of the record
int rlen = (int)LittleEndian.getUInt(docstream, offset+4);
- cipher.update(docstream, offset+8, rlen, docstream, offset+8);
- } catch (GeneralSecurityException e) {
- throw new CorruptPowerPointFileException(e);
- }
- }
+ readFully(ccis, docstream, offset+8, rlen);
+
+ } catch (Exception e) {
+ throw new EncryptedPowerPointFileException(e);
+ } finally {
+ try {
+ if (ccis != null) {
+ ccis.close();
+ }
+ lei.close();
+ } catch (IOException e) {
+ throw new EncryptedPowerPointFileException(e);
+ }
+ }
+ }
+
+ private void decryptPicBytes(byte[] pictstream, int offset, int len)
+ throws IOException, GeneralSecurityException {
+ // 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);
+ readFully(ccis, pictstream, offset, len);
+ ccis.close();
+ lei.close();
+ }
protected void decryptPicture(byte[] pictstream, int offset) {
- if (dea == null) return;
-
+ if (dea == null) {
+ return;
+ }
+
decryptInit();
- setPersistId(0);
-
+
try {
// decrypt header and read length to be decrypted
- cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ decryptPicBytes(pictstream, offset, 8);
int recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
int recType = LittleEndian.getUShort(pictstream, offset+2);
int rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
offset += 8;
- int endOffset = offset + rlen;
+ int endOffset = offset + rlen;
if (recType == 0xF007) {
// TOOD: get a real example file ... to actual test the FBSE entry
// not sure where the foDelay block is
-
+
// File BLIP Store Entry (FBSE)
- cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btWin32
- offset++;
- cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btMacOS
- offset++;
- cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid
- offset += 16;
- cipher.doFinal(pictstream, offset, 2, pictstream, offset); // tag
- offset += 2;
- cipher.doFinal(pictstream, offset, 4, pictstream, offset); // size
- offset += 4;
- cipher.doFinal(pictstream, offset, 4, pictstream, offset); // cRef
- offset += 4;
- cipher.doFinal(pictstream, offset, 4, pictstream, offset); // foDelay
- offset += 4;
- cipher.doFinal(pictstream, offset+0, 1, pictstream, offset+0); // unused1
- cipher.doFinal(pictstream, offset+1, 1, pictstream, offset+1); // cbName
- cipher.doFinal(pictstream, offset+2, 1, pictstream, offset+2); // unused2
- cipher.doFinal(pictstream, offset+3, 1, pictstream, offset+3); // unused3
- int cbName = LittleEndian.getUShort(pictstream, offset+1);
- offset += 4;
+ for (int part : BLIB_STORE_ENTRY_PARTS) {
+ decryptPicBytes(pictstream, offset, part);
+ }
+ offset += 36;
+
+ int cbName = LittleEndian.getUShort(pictstream, offset-3);
if (cbName > 0) {
- cipher.doFinal(pictstream, offset, cbName, pictstream, offset); // nameData
+ // read nameData
+ decryptPicBytes(pictstream, offset, cbName);
offset += cbName;
}
+
if (offset == endOffset) {
return; // no embedded blip
}
// fall through, read embedded blip now
// update header data
- cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ decryptPicBytes(pictstream, offset, 8);
recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
recType = LittleEndian.getUShort(pictstream, offset+2);
// rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
@@ -231,70 +276,73 @@ public class HSLFSlideShowEncrypted {
int rgbUidCnt = (recInst == 0x217 || recInst == 0x3D5 || recInst == 0x46B || recInst == 0x543 ||
recInst == 0x6E1 || recInst == 0x6E3 || recInst == 0x6E5 || recInst == 0x7A9) ? 2 : 1;
-
+
+ // rgbUid 1/2
for (int i=0; i<rgbUidCnt; i++) {
- cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid 1/2
+ decryptPicBytes(pictstream, offset, 16);
offset += 16;
}
-
+
+ int nextBytes;
if (recType == 0xF01A || recType == 0XF01B || recType == 0XF01C) {
- cipher.doFinal(pictstream, offset, 34, pictstream, offset); // metafileHeader
- offset += 34;
+ // metafileHeader
+ nextBytes = 34;
} else {
- cipher.doFinal(pictstream, offset, 1, pictstream, offset); // tag
- offset += 1;
+ // tag
+ nextBytes = 1;
}
+ decryptPicBytes(pictstream, offset, nextBytes);
+ offset += nextBytes;
+
int blipLen = endOffset - offset;
- cipher.doFinal(pictstream, offset, blipLen, pictstream, offset);
- } catch (GeneralSecurityException e) {
+ decryptPicBytes(pictstream, offset, blipLen);
+ } catch (Exception e) {
throw new CorruptPowerPointFileException(e);
- }
+ }
}
protected void encryptPicture(byte[] pictstream, int offset) {
- if (dea == null) return;
-
+ if (dea == null) {
+ return;
+ }
+
encryptInit();
- setPersistId(0);
+
+ LittleEndianByteArrayOutputStream los = new LittleEndianByteArrayOutputStream(pictstream, offset);
+ ChunkedCipherOutputStream ccos = null;
try {
+ enc.setChunkSize(-1);
+ ccos = enc.getDataStream(los, 0);
int recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
int recType = LittleEndian.getUShort(pictstream, offset+2);
- int rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
- cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ final int rlen = (int)LittleEndian.getUInt(pictstream, offset+4);
+
+ ccos.write(pictstream, offset, 8);
+ ccos.flush();
offset += 8;
- int endOffset = offset + rlen;
+ int endOffset = offset + rlen;
if (recType == 0xF007) {
// TOOD: get a real example file ... to actual test the FBSE entry
// not sure where the foDelay block is
-
+
// File BLIP Store Entry (FBSE)
- cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btWin32
- offset++;
- cipher.doFinal(pictstream, offset, 1, pictstream, offset); // btMacOS
- offset++;
- cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid
- offset += 16;
- cipher.doFinal(pictstream, offset, 2, pictstream, offset); // tag
- offset += 2;
- cipher.doFinal(pictstream, offset, 4, pictstream, offset); // size
- offset += 4;
- cipher.doFinal(pictstream, offset, 4, pictstream, offset); // cRef
- offset += 4;
- cipher.doFinal(pictstream, offset, 4, pictstream, offset); // foDelay
- offset += 4;
- int cbName = LittleEndian.getUShort(pictstream, offset+1);
- cipher.doFinal(pictstream, offset+0, 1, pictstream, offset+0); // unused1
- cipher.doFinal(pictstream, offset+1, 1, pictstream, offset+1); // cbName
- cipher.doFinal(pictstream, offset+2, 1, pictstream, offset+2); // unused2
- cipher.doFinal(pictstream, offset+3, 1, pictstream, offset+3); // unused3
- offset += 4;
+ int cbName = LittleEndian.getUShort(pictstream, offset+33);
+
+ for (int part : BLIB_STORE_ENTRY_PARTS) {
+ ccos.write(pictstream, offset, part);
+ ccos.flush();
+ offset += part;
+ }
+
if (cbName > 0) {
- cipher.doFinal(pictstream, offset, cbName, pictstream, offset); // nameData
+ ccos.write(pictstream, offset, cbName);
+ ccos.flush();
offset += cbName;
}
+
if (offset == endOffset) {
return; // no embedded blip
}
@@ -303,32 +351,45 @@ public class HSLFSlideShowEncrypted {
// update header data
recInst = fieldRecInst.getValue(LittleEndian.getUShort(pictstream, offset));
recType = LittleEndian.getUShort(pictstream, offset+2);
- // rlen = (int) LittleEndian.getUInt(pictstream, offset+4);
- cipher.doFinal(pictstream, offset, 8, pictstream, offset);
+ ccos.write(pictstream, offset, 8);
+ ccos.flush();
offset += 8;
}
-
+
int rgbUidCnt = (recInst == 0x217 || recInst == 0x3D5 || recInst == 0x46B || recInst == 0x543 ||
recInst == 0x6E1 || recInst == 0x6E3 || recInst == 0x6E5 || recInst == 0x7A9) ? 2 : 1;
-
+
for (int i=0; i<rgbUidCnt; i++) {
- cipher.doFinal(pictstream, offset, 16, pictstream, offset); // rgbUid 1/2
+ ccos.write(pictstream, offset, 16); // rgbUid 1/2
+ ccos.flush();
offset += 16;
}
-
+
if (recType == 0xF01A || recType == 0XF01B || recType == 0XF01C) {
- cipher.doFinal(pictstream, offset, 34, pictstream, offset); // metafileHeader
+ ccos.write(pictstream, offset, 34); // metafileHeader
offset += 34;
+ ccos.flush();
} else {
- cipher.doFinal(pictstream, offset, 1, pictstream, offset); // tag
+ ccos.write(pictstream, offset, 1); // tag
offset += 1;
+ ccos.flush();
}
-
+
int blipLen = endOffset - offset;
- cipher.doFinal(pictstream, offset, blipLen, pictstream, offset);
- } catch (GeneralSecurityException e) {
- throw new CorruptPowerPointFileException(e);
- }
+ ccos.write(pictstream, offset, blipLen);
+ ccos.flush();
+ } catch (Exception e) {
+ throw new EncryptedPowerPointFileException(e);
+ } finally {
+ try {
+ if (ccos != null) {
+ ccos.close();
+ }
+ los.close();
+ } catch (IOException e) {
+ throw new EncryptedPowerPointFileException(e);
+ }
+ }
}
protected Record[] updateEncryptionRecord(Record records[]) {
@@ -372,7 +433,7 @@ public class HSLFSlideShowEncrypted {
protected static Record[] normalizeRecords(Record records[]) {
// http://msdn.microsoft.com/en-us/library/office/gg615594(v=office.14).aspx
// repeated slideIds can be overwritten, i.e. ignored
-
+
UserEditAtom uea = null;
PersistPtrHolder pph = null;
TreeMap<Integer,Integer> slideLocations = new TreeMap<Integer,Integer>();
@@ -386,7 +447,7 @@ public class HSLFSlideShowEncrypted {
uea = (UserEditAtom)pdr;
continue;
}
-
+
if (pdr instanceof PersistPtrHolder) {
if (pph != null) {
duplicatedCount++;
@@ -394,16 +455,18 @@ public class HSLFSlideShowEncrypted {
pph = (PersistPtrHolder)pdr;
for (Map.Entry<Integer,Integer> me : pph.getSlideLocationsLookup().entrySet()) {
Integer oldOffset = slideLocations.put(me.getKey(), me.getValue());
- if (oldOffset != null) obsoleteOffsets.add(oldOffset);
+ if (oldOffset != null) {
+ obsoleteOffsets.add(oldOffset);
+ }
}
continue;
}
-
+
recordMap.put(pdr.getLastOnDiskOffset(), r);
}
-
+
assert(uea != null && pph != null && uea.getPersistPointersOffset() == pph.getLastOnDiskOffset());
-
+
recordMap.put(pph.getLastOnDiskOffset(), pph);
recordMap.put(uea.getLastOnDiskOffset(), uea);
@@ -416,15 +479,15 @@ public class HSLFSlideShowEncrypted {
for (Map.Entry<Integer,Integer> me : slideLocations.entrySet()) {
pph.addSlideLookup(me.getKey(), me.getValue());
}
-
+
for (Integer oldOffset : obsoleteOffsets) {
recordMap.remove(oldOffset);
}
-
+
return recordMap.values().toArray(new Record[recordMap.size()]);
}
-
-
+
+
protected static Record[] removeEncryptionRecord(Record records[]) {
int deaSlideId = -1;
int deaOffset = -1;
@@ -444,23 +507,27 @@ public class HSLFSlideShowEncrypted {
}
recordList.add(r);
}
-
+
assert(ptr != null);
- if (deaSlideId == -1 && deaOffset == -1) return records;
-
+ if (deaSlideId == -1 && deaOffset == -1) {
+ return records;
+ }
+
TreeMap<Integer,Integer> tm = new TreeMap<Integer,Integer>(ptr.getSlideLocationsLookup());
ptr.clear();
int maxSlideId = -1;
for (Map.Entry<Integer,Integer> me : tm.entrySet()) {
- if (me.getKey() == deaSlideId || me.getValue() == deaOffset) continue;
+ if (me.getKey() == deaSlideId || me.getValue() == deaOffset) {
+ continue;
+ }
ptr.addSlideLookup(me.getKey(), me.getValue());
maxSlideId = Math.max(me.getKey(), maxSlideId);
}
-
+
uea.setMaxPersistWritten(maxSlideId);
records = recordList.toArray(new Record[recordList.size()]);
-
+
return records;
}
@@ -470,9 +537,13 @@ public class HSLFSlideShowEncrypted {
int ueaIdx = -1, ptrIdx = -1, deaIdx = -1, idx = -1;
for (Record r : records) {
idx++;
- if (r instanceof UserEditAtom) ueaIdx = idx;
- else if (r instanceof PersistPtrHolder) ptrIdx = idx;
- else if (r instanceof DocumentEncryptionAtom) deaIdx = idx;
+ if (r instanceof UserEditAtom) {
+ ueaIdx = idx;
+ } else if (r instanceof PersistPtrHolder) {
+ ptrIdx = idx;
+ } else if (r instanceof DocumentEncryptionAtom) {
+ deaIdx = idx;
+ }
}
assert(ueaIdx != -1 && ptrIdx != -1 && ptrIdx < ueaIdx);
if (deaIdx != -1) {
@@ -488,13 +559,22 @@ public class HSLFSlideShowEncrypted {
ptr.addSlideLookup(nextSlideId, ptr.getLastOnDiskOffset()-1);
uea.setEncryptSessionPersistIdRef(nextSlideId);
uea.setMaxPersistWritten(nextSlideId);
-
+
Record newRecords[] = new Record[records.length+1];
- if (ptrIdx > 0) System.arraycopy(records, 0, newRecords, 0, ptrIdx);
- if (ptrIdx < records.length-1) System.arraycopy(records, ptrIdx, newRecords, ptrIdx+1, records.length-ptrIdx);
+ if (ptrIdx > 0) {
+ System.arraycopy(records, 0, newRecords, 0, ptrIdx);
+ }
+ if (ptrIdx < records.length-1) {
+ System.arraycopy(records, ptrIdx, newRecords, ptrIdx+1, records.length-ptrIdx);
+ }
newRecords[ptrIdx] = dea;
return newRecords;
}
}
+ public void close() throws IOException {
+ if (cyos != null) {
+ cyos.close();
+ }
+ }
}
Modified: poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java (original)
+++ poi/trunk/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideShowImpl.java Wed Sep 28 23:36:09 2016
@@ -54,6 +54,7 @@ import org.apache.poi.poifs.filesystem.E
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.sl.usermodel.PictureData.PictureType;
+import org.apache.poi.util.IOUtils;
import org.apache.poi.util.LittleEndian;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
@@ -205,14 +206,12 @@ public final class HSLFSlideShowImpl ext
// Grab the document stream
int len = docProps.getSize();
- _docstream = new byte[len];
- InputStream is = directory.createDocumentInputStream("PowerPoint Document");
- int readLen = is.read(_docstream);
- is.close();
-
- if (len != readLen) {
- throw new IOException("Document input stream ended prematurely - expected "+len+" bytes - received "+readLen+" bytes");
- }
+ InputStream is = directory.createDocumentInputStream("PowerPoint Document");
+ try {
+ _docstream = IOUtils.toByteArray(is, len);
+ } finally {
+ is.close();
+ }
}
/**
@@ -364,17 +363,10 @@ public final class HSLFSlideShowImpl ext
HSLFSlideShowEncrypted decryptData = new HSLFSlideShowEncrypted(getDocumentEncryptionAtom());
DocumentEntry entry = (DocumentEntry)directory.getEntry("Pictures");
- int len = entry.getSize();
- byte[] pictstream = new byte[len];
- DocumentInputStream is = directory.createDocumentInputStream(entry);
- int readLen = is.read(pictstream);
+ DocumentInputStream is = directory.createDocumentInputStream(entry);
+ byte[] pictstream = IOUtils.toByteArray(is, entry.getSize());
is.close();
- if (len != readLen) {
- throw new IOException("Picture stream ended prematurely - expected "+len+" bytes - received "+readLen+" bytes");
- }
-
-
int pos = 0;
// An empty picture record (length 0) will take up 8 bytes
while (pos <= (pictstream.length-8)) {
@@ -512,7 +504,7 @@ public final class HSLFSlideShowImpl ext
}
HSLFSlideShowEncrypted encData = new HSLFSlideShowEncrypted(getDocumentEncryptionAtom());
-
+
for (Record record : _records) {
assert(record instanceof PositionDependentRecord);
// We've already figured out their new location, and
@@ -533,6 +525,8 @@ public final class HSLFSlideShowImpl ext
record.writeOut(encData.encryptRecord(os, persistId, record));
}
}
+
+ encData.close();
// Update and write out the Current User atom
int oldLastUserEditAtomPos = (int)currentUser.getCurrentEditOffset();
@@ -733,7 +727,7 @@ public final class HSLFSlideShowImpl ext
if (dea != null) {
CryptoAPIEncryptor enc = (CryptoAPIEncryptor)dea.getEncryptionInfo().getEncryptor();
try {
- enc.getDataStream(outFS.getRoot()); // ignore OutputStream
+ enc.getSummaryEntries(outFS.getRoot()); // ignore OutputStream
} catch (IOException e) {
throw e;
} catch (GeneralSecurityException e) {
Modified: poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java (original)
+++ poi/trunk/src/scratchpad/testcases/org/apache/poi/hslf/record/TestDocumentEncryption.java Wed Sep 28 23:36:09 2016
@@ -44,6 +44,7 @@ import org.apache.poi.hssf.record.crypto
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
@@ -176,7 +177,7 @@ public class TestDocumentEncryption {
DocumentEncryptionAtom dea = hss.getDocumentEncryptionAtom();
- POIFSFileSystem fs2 = new POIFSFileSystem(dea.getEncryptionInfo().getDecryptor().getDataStream(fs));
+ POIFSFileSystem fs2 = ((CryptoAPIDecryptor)dea.getEncryptionInfo().getDecryptor()).getSummaryEntries(fs.getRoot(), "EncryptedSummary");
PropertySet ps = PropertySetFactory.create(fs2.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME);
assertTrue(ps.isSummaryInformation());
assertEquals("RC4 CryptoAPI Encryption", ps.getProperties()[1].getValue());
Modified: poi/trunk/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java Wed Sep 28 23:36:09 2016
@@ -21,8 +21,8 @@ import org.apache.poi.hssf.record.aggreg
import org.apache.poi.hssf.record.cf.TestCellRange;
import org.apache.poi.hssf.record.chart.AllChartRecordTests;
import org.apache.poi.hssf.record.common.TestUnicodeString;
-import org.apache.poi.hssf.record.crypto.AllHSSFEncryptionTests;
import org.apache.poi.hssf.record.pivot.AllPivotRecordTests;
+import org.apache.poi.poifs.crypt.AllEncryptionTests;
import org.apache.poi.ss.formula.constant.TestConstantValueParser;
import org.apache.poi.ss.formula.ptg.AllFormulaTests;
import org.junit.runner.RunWith;
@@ -34,7 +34,7 @@ import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
AllChartRecordTests.class,
- AllHSSFEncryptionTests.class,
+ AllEncryptionTests.class,
AllFormulaTests.class,
AllPivotRecordTests.class,
AllRecordAggregateTests.class,
Modified: poi/trunk/src/testcases/org/apache/poi/hssf/record/TestRecordFactoryInputStream.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/hssf/record/TestRecordFactoryInputStream.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/hssf/record/TestRecordFactoryInputStream.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/hssf/record/TestRecordFactoryInputStream.java Wed Sep 28 23:36:09 2016
@@ -151,11 +151,12 @@ public final class TestRecordFactoryInpu
/**
- * makes sure the record stream starts with {@link BOFRecord} and then {@link WindowOneRecord}
- * The second record is gets decrypted so this method also checks its content.
+ * makes sure the record stream starts with {@link BOFRecord}, {@link FilePassRecord} and then {@link WindowOneRecord}
+ * The third record is decrypted so this method also checks its content.
*/
private void confirmReadInitialRecords(RecordFactoryInputStream rfis) {
assertEquals(BOFRecord.class, rfis.nextRecord().getClass());
+ FilePassRecord recFP = (FilePassRecord) rfis.nextRecord();
WindowOneRecord rec1 = (WindowOneRecord) rfis.nextRecord();
assertArrayEquals(HexRead.readFromString(SAMPLE_WINDOW1),rec1.serialize());
}
Modified: poi/trunk/src/testcases/org/apache/poi/poifs/crypt/TestCipherAlgorithm.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/testcases/org/apache/poi/poifs/crypt/TestCipherAlgorithm.java?rev=1762726&r1=1762725&r2=1762726&view=diff
==============================================================================
--- poi/trunk/src/testcases/org/apache/poi/poifs/crypt/TestCipherAlgorithm.java (original)
+++ poi/trunk/src/testcases/org/apache/poi/poifs/crypt/TestCipherAlgorithm.java Wed Sep 28 23:36:09 2016
@@ -17,14 +17,14 @@
package org.apache.poi.poifs.crypt;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
import org.apache.poi.EncryptedDocumentException;
import org.junit.Test;
public class TestCipherAlgorithm {
@Test
- public void test() {
+ public void validInputs() {
assertEquals(128, CipherAlgorithm.aes128.defaultKeySize);
for(CipherAlgorithm alg : CipherAlgorithm.values()) {
@@ -33,27 +33,20 @@ public class TestCipherAlgorithm {
assertEquals(CipherAlgorithm.aes128, CipherAlgorithm.fromEcmaId(0x660E));
assertEquals(CipherAlgorithm.aes192, CipherAlgorithm.fromXmlId("AES", 192));
-
- try {
- CipherAlgorithm.fromEcmaId(0);
- fail("Should throw exception");
- } catch (EncryptedDocumentException e) {
- // expected
- }
-
- try {
- CipherAlgorithm.fromXmlId("AES", 1);
- fail("Should throw exception");
- } catch (EncryptedDocumentException e) {
- // expected
- }
-
- try {
- CipherAlgorithm.fromXmlId("RC1", 0x40);
- fail("Should throw exception");
- } catch (EncryptedDocumentException e) {
- // expected
- }
}
-
+
+ @Test(expected=EncryptedDocumentException.class)
+ public void invalidEcmaId() {
+ CipherAlgorithm.fromEcmaId(0);
+ }
+
+ @Test(expected=EncryptedDocumentException.class)
+ public void invalidXmlId1() {
+ CipherAlgorithm.fromXmlId("AES", 1);
+ }
+
+ @Test(expected=EncryptedDocumentException.class)
+ public void invalidXmlId2() {
+ CipherAlgorithm.fromXmlId("RC1", 0x40);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org