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/11/01 01:30:48 UTC

svn commit: r1767399 - in /poi: site/src/documentation/content/xdocs/ trunk/src/integrationtest/org/apache/poi/ trunk/src/java/org/apache/poi/poifs/crypt/ trunk/src/java/org/apache/poi/poifs/crypt/standard/ trunk/src/ooxml/java/org/apache/poi/poifs/cry...

Author: kiwiwings
Date: Tue Nov  1 01:30:48 2016
New Revision: 1767399

URL: http://svn.apache.org/viewvc?rev=1767399&view=rev
Log:
Bug 60320 - issue opening password protected xlsx

Added:
    poi/trunk/test-data/poifs/60320-protected.xlsx   (with props)
Modified:
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/integrationtest/org/apache/poi/TestAllFiles.java
    poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
    poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
    poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
    poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Tue Nov  1 01:30:48 2016
@@ -61,6 +61,7 @@
         <summary-item>Document each major change in the release notes in a summary item</summary-item>
       </summary -->
       <actions>
+        <action dev="PD" type="fix" fixes-bug="60320" module="POIFS">issue opening password protected xlsx</action>
       	<action dev="PD" type="fix" fixes-bug="57366" breaks-compatibility=true module="XWPF">Refactor Header/Footer creation to allow table in header/footer. No longer creates empty paragraph in header/footer.</action>
         <action dev="PD" type="fix" fixes-bug="53611" module="XSSF">Populate dimension of XSSF Worksheet when writing the document</action>
         <action dev="PD" type="fix" fixes-bug="60315" module="OPC">Add 'yyyy-MM-dd' as a possible format for date metadata items in OPC</action>

Modified: poi/trunk/src/integrationtest/org/apache/poi/TestAllFiles.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/integrationtest/org/apache/poi/TestAllFiles.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/integrationtest/org/apache/poi/TestAllFiles.java (original)
+++ poi/trunk/src/integrationtest/org/apache/poi/TestAllFiles.java Tue Nov  1 01:30:48 2016
@@ -210,6 +210,7 @@ public class TestAllFiles {
         //EXPECTED_FAILURES.add("poifs/extenxls_pwd123.xlsx");
         //EXPECTED_FAILURES.add("poifs/protected_agile.docx");
         EXPECTED_FAILURES.add("spreadsheet/58616.xlsx");
+        EXPECTED_FAILURES.add("poifs/60320-protected.xlsx");
 
         // TODO: fails XMLExportTest, is this ok?
         EXPECTED_FAILURES.add("spreadsheet/CustomXMLMapping-singleattributenamespace.xlsx");

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java Tue Nov  1 01:30:48 2016
@@ -16,6 +16,9 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt;
 
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.util.Removal;
+
 /**
  * Reads and processes OOXML Encryption Headers
  * The constants are largely based on ZIP constants.
@@ -82,8 +85,19 @@ public abstract class EncryptionHeader i
     
     protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
         this.cipherAlgorithm = cipherAlgorithm;
+        if (cipherAlgorithm.allowedKeySize.length == 1) {
+            setKeySize(cipherAlgorithm.defaultKeySize);
+        }
+    }
+
+    public HashAlgorithm getHashAlgorithm() {
+        return hashAlgorithm;
     }
     
+    /**
+     * @deprecated POI 3.16 beta 1. use {@link #getHashAlgorithm()}
+     */
+    @Removal(version="3.18")
     public HashAlgorithm getHashAlgorithmEx() {
         return hashAlgorithm;
     }
@@ -96,8 +110,21 @@ public abstract class EncryptionHeader i
         return keyBits;
     }
     
+    /**
+     * Sets the keySize (in bits). Before calling this method, make sure
+     * to set the cipherAlgorithm, as the amount of keyBits gets validated against
+     * the list of allowed keyBits of the corresponding cipherAlgorithm
+     *
+     * @param keyBits
+     */
     protected void setKeySize(int keyBits) {
         this.keyBits = keyBits;
+        for (int allowedBits : getCipherAlgorithm().allowedKeySize) {
+            if (allowedBits == keyBits) {
+                return;
+            }
+        }
+        throw new EncryptedDocumentException("KeySize "+keyBits+" not allowed for cipher "+getCipherAlgorithm());
     }
 
     public int getBlockSize() {

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java Tue Nov  1 01:30:48 2016
@@ -16,6 +16,8 @@
 ==================================================================== */
 package org.apache.poi.poifs.crypt;
 
+import org.apache.poi.util.Removal;
+
 /**
  * Used when checking if a key is valid for a document 
  */
@@ -48,10 +50,17 @@ public abstract class EncryptionVerifier
         return spinCount;
     }
 
+    /**
+     * @deprecated POI 3.16 beta 1. use {@link #getChainingMode()}
+     */
+    @Removal(version="3.18")
     public int getCipherMode() {
         return chainingMode.ecmaId;
     }
 
+    /**
+     * @deprecated POI 3.16 beta 1. use {@link #getCipherAlgorithm()}
+     */
     public int getAlgorithm() {
         return cipherAlgorithm.ecmaId;
     }

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java Tue Nov  1 01:30:48 2016
@@ -108,7 +108,7 @@ public class StandardEncryptionHeader ex
         bos.writeInt(getFlags());
         bos.writeInt(0); // size extra
         bos.writeInt(getCipherAlgorithm().ecmaId);
-        bos.writeInt(getHashAlgorithmEx().ecmaId);
+        bos.writeInt(getHashAlgorithm().ecmaId);
         bos.writeInt(getKeySize());
         bos.writeInt(getCipherProvider().ecmaId);
         bos.writeInt(0); // reserved1

Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java Tue Nov  1 01:30:48 2016
@@ -56,7 +56,7 @@ public class StandardEncryptionVerifier
         setCipherAlgorithm(header.getCipherAlgorithm());
         setChainingMode(header.getChainingMode());
         setEncryptedKey(null);
-        setHashAlgorithm(header.getHashAlgorithmEx()); 
+        setHashAlgorithm(header.getHashAlgorithm()); 
     }
     
     protected StandardEncryptionVerifier(CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode) {

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=1767399&r1=1767398&r2=1767399&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 Tue Nov  1 01:30:48 2016
@@ -40,13 +40,13 @@ import javax.crypto.spec.RC2ParameterSpe
 import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.ChainingMode;
 import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
 import org.apache.poi.poifs.crypt.CipherAlgorithm;
 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.EncryptionInfo;
-import org.apache.poi.poifs.crypt.EncryptionVerifier;
 import org.apache.poi.poifs.crypt.HashAlgorithm;
 import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier.AgileCertificateEntry;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
@@ -93,10 +93,8 @@ public class AgileDecryptor extends Decr
     public boolean verifyPassword(String password) throws GeneralSecurityException {
         AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
         AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader(); 
-        HashAlgorithm hashAlgo = header.getHashAlgorithmEx();
-        CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
+
         int blockSize = header.getBlockSize();
-        int keySize = header.getKeySize()/8;
 
         byte[] pwHash = hashPassword(password, ver.getHashAlgorithm(), ver.getSalt(), ver.getSpinCount());
 
@@ -113,9 +111,9 @@ public class AgileDecryptor extends Decr
          *    blockSize bytes.
          * 4. Use base64 to encode the result of step 3.
          */
-        byte verfierInputEnc[] = hashInput(getEncryptionInfo(), pwHash, kVerifierInputBlock, ver.getEncryptedVerifier(), Cipher.DECRYPT_MODE);
+        byte verfierInputEnc[] = hashInput(ver, pwHash, kVerifierInputBlock, ver.getEncryptedVerifier(), Cipher.DECRYPT_MODE);
         setVerifier(verfierInputEnc);
-        MessageDigest hashMD = getMessageDigest(hashAlgo);
+        MessageDigest hashMD = getMessageDigest(ver.getHashAlgorithm());
         byte[] verifierHash = hashMD.digest(verfierInputEnc);
 
         /**
@@ -130,8 +128,8 @@ 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(getEncryptionInfo(), pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
-        verifierHashDec = getBlock0(verifierHashDec, hashAlgo.hashSize);
+        byte verifierHashDec[] = hashInput(ver, pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
+        verifierHashDec = getBlock0(verifierHashDec, ver.getHashAlgorithm().hashSize);
         
         /**
          * encryptedKeyValue: This attribute MUST be generated by using the following steps:
@@ -146,9 +144,9 @@ public class AgileDecryptor extends Decr
          *    blockSize bytes.
          * 4. Use base64 to encode the result of step 3.
          */
-        byte keyspec[] = hashInput(getEncryptionInfo(), pwHash, kCryptoKeyBlock, ver.getEncryptedKey(), Cipher.DECRYPT_MODE);
-        keyspec = getBlock0(keyspec, keySize);
-        SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.getCipherAlgorithm().jceId);
+        byte keyspec[] = hashInput(ver, pwHash, kCryptoKeyBlock, ver.getEncryptedKey(), Cipher.DECRYPT_MODE);
+        keyspec = getBlock0(keyspec, header.getKeySize()/8);
+        SecretKeySpec secretKey = new SecretKeySpec(keyspec, header.getCipherAlgorithm().jceId);
 
         /**
          * 1. Obtain the intermediate key by decrypting the encryptedKeyValue from a KeyEncryptor
@@ -163,10 +161,11 @@ public class AgileDecryptor extends Decr
          *    array with 0x00 to the next integral multiple of blockSize bytes.
          * 4. Assign the encryptedHmacKey attribute to the base64-encoded form of the result of step 3.
          */
-        byte vec[] = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityKeyBlock, blockSize); 
-        Cipher cipher = getCipher(secretKey, cipherAlgo, ver.getChainingMode(), vec, Cipher.DECRYPT_MODE);
+        byte vec[] = CryptoFunctions.generateIv(header.getHashAlgorithm(), header.getKeySalt(), kIntegrityKeyBlock, blockSize);
+        CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
+        Cipher cipher = getCipher(secretKey, cipherAlgo, header.getChainingMode(), vec, Cipher.DECRYPT_MODE);
         byte hmacKey[] = cipher.doFinal(header.getEncryptedHmacKey());
-        hmacKey = getBlock0(hmacKey, hashAlgo.hashSize);
+        hmacKey = getBlock0(hmacKey, header.getHashAlgorithm().hashSize);
 
         /**
          * 5. Generate an HMAC, as specified in [RFC2104], of the encrypted form of the data (message),
@@ -177,10 +176,10 @@ public class AgileDecryptor extends Decr
          *    0xa0, 0x67, 0x7f, 0x02, 0xb2, 0x2c, 0x84, and 0x33.
          * 7. Assign the encryptedHmacValue attribute to the base64-encoded form of the result of step 6.
          */
-        vec = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityValueBlock, blockSize);
+        vec = CryptoFunctions.generateIv(header.getHashAlgorithm(), header.getKeySalt(), kIntegrityValueBlock, blockSize);
         cipher = getCipher(secretKey, cipherAlgo, ver.getChainingMode(), vec, Cipher.DECRYPT_MODE);
         byte hmacValue[] = cipher.doFinal(header.getEncryptedHmacValue());
-        hmacValue = getBlock0(hmacValue, hashAlgo.hashSize);
+        hmacValue = getBlock0(hmacValue, header.getHashAlgorithm().hashSize);
         
         if (Arrays.equals(verifierHashDec, verifierHash)) {
             setSecretKey(secretKey);
@@ -206,7 +205,7 @@ public class AgileDecryptor extends Decr
     public boolean verifyPassword(KeyPair keyPair, X509Certificate x509) throws GeneralSecurityException {
         AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
         AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
-        HashAlgorithm hashAlgo = header.getHashAlgorithmEx();
+        HashAlgorithm hashAlgo = header.getHashAlgorithm();
         CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
         int blockSize = header.getBlockSize();
         
@@ -231,12 +230,12 @@ public class AgileDecryptor extends Decr
         byte certVerifier[] = x509Hmac.doFinal(ace.x509.getEncoded());
 
         byte vec[] = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityKeyBlock, blockSize); 
-        cipher = getCipher(secretKey, cipherAlgo, ver.getChainingMode(), vec, Cipher.DECRYPT_MODE);
+        cipher = getCipher(secretKey, cipherAlgo, header.getChainingMode(), vec, Cipher.DECRYPT_MODE);
         byte hmacKey[] = cipher.doFinal(header.getEncryptedHmacKey());
         hmacKey = getBlock0(hmacKey, hashAlgo.hashSize);
 
         vec = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityValueBlock, blockSize);
-        cipher = getCipher(secretKey, cipherAlgo, ver.getChainingMode(), vec, Cipher.DECRYPT_MODE);
+        cipher = getCipher(secretKey, cipherAlgo, header.getChainingMode(), vec, Cipher.DECRYPT_MODE);
         byte hmacValue[] = cipher.doFinal(header.getEncryptedHmacValue());
         hmacValue = getBlock0(hmacValue, hashAlgo.hashSize);
         
@@ -257,18 +256,17 @@ public class AgileDecryptor extends Decr
         return fillSize;
     }
 
-    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();
+    /* package */ static byte[] hashInput(AgileEncryptionVerifier ver, byte pwHash[], byte blockKey[], byte inputKey[], int cipherMode) {
+        CipherAlgorithm cipherAlgo = ver.getCipherAlgorithm();
+        ChainingMode chainMode = ver.getChainingMode();
+        int keySize = ver.getKeySize()/8;
+        int blockSize = ver.getBlockSize();
         HashAlgorithm hashAlgo = ver.getHashAlgorithm();
-        byte[] salt = ver.getSalt();
-
+        
         byte intermedKey[] = generateKey(pwHash, hashAlgo, blockKey, keySize);
-        SecretKey skey = new SecretKeySpec(intermedKey, ver.getCipherAlgorithm().jceId);
-        byte[] iv = generateIv(hashAlgo, salt, null, blockSize);
-        Cipher cipher = getCipher(skey, ver.getCipherAlgorithm(), ver.getChainingMode(), iv, cipherMode);
+        SecretKey skey = new SecretKeySpec(intermedKey, cipherAlgo.jceId);
+        byte[] iv = generateIv(hashAlgo, ver.getSalt(), null, blockSize);
+        Cipher cipher = getCipher(skey, cipherAlgo, chainMode, iv, cipherMode);
         byte[] hashFinal;
         
         try {
@@ -281,7 +279,6 @@ public class AgileDecryptor extends Decr
     }
 
     @Override
-    @SuppressWarnings("resource")
     public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
         DocumentInputStream dis = dir.createDocumentInputStream(DEFAULT_POIFS_ENTRY);
         _length = dis.readLong();
@@ -307,7 +304,7 @@ public class AgileDecryptor extends Decr
 
         byte[] blockKey = new byte[4];
         LittleEndian.putInt(blockKey, 0, block);
-        byte[] iv = generateIv(header.getHashAlgorithmEx(), header.getKeySalt(), blockKey, header.getBlockSize());
+        byte[] iv = generateIv(header.getHashAlgorithm(), header.getKeySalt(), blockKey, header.getBlockSize());
 
         AlgorithmParameterSpec aps;
         if (header.getCipherAlgorithm() == CipherAlgorithm.rc2) {

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=1767399&r1=1767398&r2=1767399&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 Tue Nov  1 01:30:48 2016
@@ -45,18 +45,18 @@ public class AgileEncryptionHeader exten
             throw new EncryptedDocumentException("Unable to parse keyData");
         }
 
-        setKeySize((int)keyData.getKeyBits());
-        setFlags(0);
-        setSizeExtra(0);
-        setCspName(null);
-        setBlockSize(keyData.getBlockSize());
-
         int keyBits = (int)keyData.getKeyBits();
         
         CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), keyBits);
         setCipherAlgorithm(ca);
         setCipherProvider(ca.provider);
 
+        setKeySize(keyBits);
+        setFlags(0);
+        setSizeExtra(0);
+        setCspName(null);
+        setBlockSize(keyData.getBlockSize());
+
         switch (keyData.getCipherChaining().intValue()) {
         case STCipherChaining.INT_CHAINING_MODE_CBC:
             setChainingMode(ChainingMode.cbc);
@@ -73,7 +73,7 @@ public class AgileEncryptionHeader exten
         HashAlgorithm ha = HashAlgorithm.fromEcmaId(keyData.getHashAlgorithm().toString());
         setHashAlgorithm(ha);
 
-        if (getHashAlgorithmEx().hashSize != hashSize) {
+        if (getHashAlgorithm().hashSize != hashSize) {
             throw new EncryptedDocumentException("Unsupported hash algorithm: " + 
                     keyData.getHashAlgorithm() + " @ " + hashSize + " bytes");
         }

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=1767399&r1=1767398&r2=1767399&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 Tue Nov  1 01:30:48 2016
@@ -21,6 +21,7 @@ import java.security.GeneralSecurityExce
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 
@@ -48,6 +49,8 @@ public class AgileEncryptionVerifier ext
     }
     
     private List<AgileCertificateEntry> certList = new ArrayList<AgileCertificateEntry>();
+    private int keyBits = -1;
+    private int blockSize = -1;
 
     public AgileEncryptionVerifier(String descriptor) {
         this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor));
@@ -66,10 +69,14 @@ public class AgileEncryptionVerifier ext
         }
         
         int keyBits = (int)keyData.getKeyBits();
-        
         CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), keyBits);
         setCipherAlgorithm(ca);
 
+        setKeySize(keyBits);
+        
+        int blockSize = keyData.getBlockSize();
+        setBlockSize(blockSize);
+        
         int hashSize = keyData.getHashSize();
 
         HashAlgorithm ha = HashAlgorithm.fromEcmaId(keyData.getHashAlgorithm().toString());
@@ -125,6 +132,8 @@ public class AgileEncryptionVerifier ext
         setCipherAlgorithm(cipherAlgorithm);
         setHashAlgorithm(hashAlgorithm);
         setChainingMode(chainingMode);
+        setKeySize(keyBits);
+        setBlockSize(blockSize);
         setSpinCount(100000); // TODO: use parameter
     }
     
@@ -171,4 +180,60 @@ public class AgileEncryptionVerifier ext
         other.certList = new ArrayList<AgileCertificateEntry>(certList);
         return other;
     }
+    
+    
+    /**
+     * The keysize (in bits) of the verifier data. This usually equals the keysize of the header,
+     * but only on a few exceptions, like files generated by Office for Mac, can be
+     * different.
+     *
+     * @return the keysize (in bits) of the verifier.
+     */
+    public int getKeySize() {
+        return keyBits;
+    }
+
+    
+    /**
+     * The blockSize (in bytes) of the verifier data.
+     * This usually equals the blocksize of the header.
+     *
+     * @return the blockSize (in bytes) of the verifier,
+     */
+    public int getBlockSize() {
+        return blockSize;
+    }
+    
+    /**
+     * Sets the keysize (in bits) of the verifier
+     *
+     * @param keyBits the keysize (in bits)
+     */
+    protected void setKeySize(int keyBits) {
+        this.keyBits = keyBits;
+        for (int allowedBits : getCipherAlgorithm().allowedKeySize) {
+            if (allowedBits == keyBits) {
+                return;
+            }
+        }
+        throw new EncryptedDocumentException("KeySize "+keyBits+" not allowed for cipher "+getCipherAlgorithm());
+    }
+
+    
+    /**
+     * Sets the blockSize (in bytes) of the verifier
+     *
+     * @param blockSize the blockSize (in bytes)
+     */
+    protected void setBlockSize(int blockSize) {
+        this.blockSize = blockSize;
+    }
+
+    @Override
+    protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
+        super.setCipherAlgorithm(cipherAlgorithm);
+        if (cipherAlgorithm.allowedKeySize.length == 1) {
+            setKeySize(cipherAlgorithm.defaultKeySize);
+        }
+    }
 }

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=1767399&r1=1767398&r2=1767399&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 Tue Nov  1 01:30:48 2016
@@ -86,9 +86,10 @@ public class AgileEncryptor extends Encr
     public void confirmPassword(String password) {
         // see [MS-OFFCRYPTO] - 2.3.3 EncryptionVerifier
         Random r = new SecureRandom();
-        int blockSize = getEncryptionInfo().getHeader().getBlockSize();
-        int keySize = getEncryptionInfo().getHeader().getKeySize()/8;
-        int hashSize = getEncryptionInfo().getHeader().getHashAlgorithmEx().hashSize;
+        AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
+        int blockSize = header.getBlockSize();
+        int keySize = header.getKeySize()/8;
+        int hashSize = header.getHashAlgorithm().hashSize;
         
         byte[] newVerifierSalt = new byte[blockSize]
              , newVerifier = new byte[blockSize]
@@ -107,14 +108,14 @@ public class AgileEncryptor extends Encr
 	@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 = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
+
+        ver.setSalt(verifierSalt);
         header.setKeySalt(keySalt);
-        HashAlgorithm hashAlgo = ver.getHashAlgorithm();
 
         int blockSize = header.getBlockSize();
 	    
-        pwHash = hashPassword(password, hashAlgo, verifierSalt, ver.getSpinCount());
+        pwHash = hashPassword(password, ver.getHashAlgorithm(), verifierSalt, ver.getSpinCount());
         
         /**
          * encryptedVerifierHashInput: This attribute MUST be generated by using the following steps:
@@ -129,7 +130,7 @@ public class AgileEncryptor extends Encr
          *    blockSize bytes.
          * 4. Use base64 to encode the result of step 3.
          */
-        byte encryptedVerifier[] = hashInput(getEncryptionInfo(), pwHash, kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE);
+        byte encryptedVerifier[] = hashInput(ver, pwHash, kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE);
         ver.setEncryptedVerifier(encryptedVerifier);
 	    
 
@@ -145,9 +146,9 @@ public class AgileEncryptor extends Encr
          *    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.
          */
-        MessageDigest hashMD = getMessageDigest(hashAlgo);
+        MessageDigest hashMD = getMessageDigest(ver.getHashAlgorithm());
         byte[] hashedVerifier = hashMD.digest(verifier);
-        byte encryptedVerifierHash[] = hashInput(getEncryptionInfo(), pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
+        byte encryptedVerifierHash[] = hashInput(ver, pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
         ver.setEncryptedVerifierHash(encryptedVerifierHash);
         
         /**
@@ -163,10 +164,10 @@ public class AgileEncryptor extends Encr
          *    blockSize bytes.
          * 4. Use base64 to encode the result of step 3.
          */
-        byte encryptedKey[] = hashInput(getEncryptionInfo(), pwHash, kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE);
+        byte encryptedKey[] = hashInput(ver, pwHash, kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE);
         ver.setEncryptedKey(encryptedKey);
         
-        SecretKey secretKey = new SecretKeySpec(keySpec, ver.getCipherAlgorithm().jceId);
+        SecretKey secretKey = new SecretKeySpec(keySpec, header.getCipherAlgorithm().jceId);
         setSecretKey(secretKey);
         
         /*
@@ -196,17 +197,17 @@ public class AgileEncryptor extends Encr
         this.integritySalt = integritySalt.clone();
 
         try {
-            byte vec[] = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityKeyBlock, header.getBlockSize());
-            Cipher cipher = getCipher(secretKey, ver.getCipherAlgorithm(), ver.getChainingMode(), vec, Cipher.ENCRYPT_MODE);
-            byte filledSalt[] = getBlock0(this.integritySalt, getNextBlockSize(this.integritySalt.length, blockSize));
-            byte encryptedHmacKey[] = cipher.doFinal(filledSalt);
+            byte vec[] = CryptoFunctions.generateIv(header.getHashAlgorithm(), header.getKeySalt(), kIntegrityKeyBlock, header.getBlockSize());
+            Cipher cipher = getCipher(secretKey, header.getCipherAlgorithm(), header.getChainingMode(), vec, Cipher.ENCRYPT_MODE);
+            byte hmacKey[] = getBlock0(this.integritySalt, getNextBlockSize(this.integritySalt.length, blockSize));
+            byte encryptedHmacKey[] = cipher.doFinal(hmacKey);
             header.setEncryptedHmacKey(encryptedHmacKey);
 
             cipher = Cipher.getInstance("RSA");
             for (AgileCertificateEntry ace : ver.getCertificates()) {
                 cipher.init(Cipher.ENCRYPT_MODE, ace.x509.getPublicKey());
                 ace.encryptedKey = cipher.doFinal(getSecretKey().getEncoded());
-                Mac x509Hmac = CryptoFunctions.getMac(hashAlgo);
+                Mac x509Hmac = CryptoFunctions.getMac(header.getHashAlgorithm());
                 x509Hmac.init(getSecretKey());
                 ace.certVerifier = x509Hmac.doFinal(ace.x509.getEncoded());
             }
@@ -236,10 +237,12 @@ 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 = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
-        HashAlgorithm hashAlgo = ver.getHashAlgorithm();
+        AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
+        int blockSize = header.getBlockSize();
+        HashAlgorithm hashAlgo = header.getHashAlgorithm();
         Mac integrityMD = CryptoFunctions.getMac(hashAlgo);
-        integrityMD.init(new SecretKeySpec(integritySalt, hashAlgo.jceHmacId));
+        byte hmacKey[] = getBlock0(this.integritySalt, getNextBlockSize(this.integritySalt.length, blockSize));
+        integrityMD.init(new SecretKeySpec(hmacKey, hashAlgo.jceHmacId));
 
         byte buf[] = new byte[1024];
         LittleEndian.putLong(buf, 0, oleStreamSize);
@@ -256,12 +259,10 @@ public class AgileEncryptor extends Encr
         }
         
         byte hmacValue[] = integrityMD.doFinal();
+        byte hmacValueFilled[] = getBlock0(hmacValue, getNextBlockSize(hmacValue.length, blockSize));
         
-        AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader();
-        int blockSize = header.getBlockSize();
-        byte iv[] = CryptoFunctions.generateIv(header.getHashAlgorithmEx(), header.getKeySalt(), kIntegrityValueBlock, blockSize);
+        byte iv[] = CryptoFunctions.generateIv(header.getHashAlgorithm(), header.getKeySalt(), kIntegrityValueBlock, blockSize);
         Cipher cipher = CryptoFunctions.getCipher(getSecretKey(), header.getCipherAlgorithm(), header.getChainingMode(), iv, Cipher.ENCRYPT_MODE);
-        byte hmacValueFilled[] = getBlock0(hmacValue, getNextBlockSize(hmacValue.length, blockSize));
         byte encryptedHmacValue[] = cipher.doFinal(hmacValueFilled);
         
         header.setEncryptedHmacValue(encryptedHmacValue);
@@ -288,18 +289,21 @@ public class AgileEncryptor extends Encr
         keyPass.setSpinCount(ver.getSpinCount());
         
         keyData.setSaltSize(header.getBlockSize());
-        keyPass.setSaltSize(header.getBlockSize());
+        keyPass.setSaltSize(ver.getBlockSize());
         
         keyData.setBlockSize(header.getBlockSize());
-        keyPass.setBlockSize(header.getBlockSize());
+        keyPass.setBlockSize(ver.getBlockSize());
 
         keyData.setKeyBits(header.getKeySize());
-        keyPass.setKeyBits(header.getKeySize());
+        keyPass.setKeyBits(ver.getKeySize());
 
-        HashAlgorithm hashAlgo = header.getHashAlgorithmEx();
-        keyData.setHashSize(hashAlgo.hashSize);
-        keyPass.setHashSize(hashAlgo.hashSize);
+        keyData.setHashSize(header.getHashAlgorithm().hashSize);
+        keyPass.setHashSize(ver.getHashAlgorithm().hashSize);
 
+        // header and verifier have to have the same cipher algorithm
+        if (!header.getCipherAlgorithm().xmlId.equals(ver.getCipherAlgorithm().xmlId)) {
+            throw new EncryptedDocumentException("Cipher algorithm of header and verifier have to match");
+        }
         STCipherAlgorithm.Enum xmlCipherAlgo = STCipherAlgorithm.Enum.forString(header.getCipherAlgorithm().xmlId);
         if (xmlCipherAlgo == null) {
             throw new EncryptedDocumentException("CipherAlgorithm "+header.getCipherAlgorithm()+" not supported.");
@@ -320,12 +324,8 @@ public class AgileEncryptor extends Encr
             throw new EncryptedDocumentException("ChainingMode "+header.getChainingMode()+" not supported.");
         }
         
-        STHashAlgorithm.Enum xmlHashAlgo = STHashAlgorithm.Enum.forString(hashAlgo.ecmaString);
-        if (xmlHashAlgo == null) {
-            throw new EncryptedDocumentException("HashAlgorithm "+hashAlgo+" not supported.");
-        }
-        keyData.setHashAlgorithm(xmlHashAlgo);
-        keyPass.setHashAlgorithm(xmlHashAlgo);
+        keyData.setHashAlgorithm(mapHashAlgorithm(header.getHashAlgorithm()));
+        keyPass.setHashAlgorithm(mapHashAlgorithm(ver.getHashAlgorithm()));
 
         keyData.setSaltValue(header.getKeySalt());
         keyPass.setSaltValue(ver.getSalt());
@@ -352,6 +352,14 @@ public class AgileEncryptor extends Encr
         
         return ed;
     }
+
+    private static STHashAlgorithm.Enum mapHashAlgorithm(HashAlgorithm hashAlgo) {
+        STHashAlgorithm.Enum xmlHashAlgo = STHashAlgorithm.Enum.forString(hashAlgo.ecmaString);
+        if (xmlHashAlgo == null) {
+            throw new EncryptedDocumentException("HashAlgorithm "+hashAlgo+" not supported.");
+        }
+        return xmlHashAlgo;
+    }
     
     protected void marshallEncryptionDocument(EncryptionDocument ed, LittleEndianByteArrayOutputStream os) {
         XmlOptions xo = new XmlOptions();
@@ -371,7 +379,7 @@ public class AgileEncryptor extends Encr
         try {
             bos.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n".getBytes("UTF-8"));
             ed.save(bos, xo);
-            os.write(bos.toByteArray());
+            bos.writeTo(os);
         } catch (IOException e) {
             throw new EncryptedDocumentException("error marshalling encryption info document", e);
         }

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java Tue Nov  1 01:30:48 2016
@@ -37,10 +37,6 @@ import org.apache.poi.util.IOUtils;
 import org.apache.poi.xssf.XSSFTestDataSamples;
 import org.junit.Test;
 
-/**
- *  @author Maxim Valyanskiy
- *  @author Gary King
- */
 public class TestDecryptor {
     @Test
     public void passwordVerification() throws IOException, GeneralSecurityException {
@@ -162,4 +158,22 @@ public class TestDecryptor {
         //dec.verifyPassword(null);
         dec.getDataStream(pfs);
     }
+
+    @Test
+    public void bug60320() throws IOException, GeneralSecurityException {
+        InputStream is = POIDataSamples.getPOIFSInstance().openResourceAsStream("60320-protected.xlsx");
+        POIFSFileSystem fs = new POIFSFileSystem(is);
+        is.close();
+
+        EncryptionInfo info = new EncryptionInfo(fs);
+
+        Decryptor d = Decryptor.getInstance(info);
+
+        boolean b = d.verifyPassword("Test001!!");
+        assertTrue(b);
+
+        zipOk(fs.getRoot(), d);
+        
+        fs.close();
+    }    
 }
\ No newline at end of file

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java Tue Nov  1 01:30:48 2016
@@ -35,7 +35,7 @@ public class TestEncryptionInfo {
         assertEquals(2, info.getVersionMinor());
 
         assertEquals(CipherAlgorithm.aes128, info.getHeader().getCipherAlgorithm());
-        assertEquals(HashAlgorithm.sha1, info.getHeader().getHashAlgorithmEx());
+        assertEquals(HashAlgorithm.sha1, info.getHeader().getHashAlgorithm());
         assertEquals(128, info.getHeader().getKeySize());
         assertEquals(32, info.getVerifier().getEncryptedVerifierHash().length);
         assertEquals(CipherProvider.aes, info.getHeader().getCipherProvider());                
@@ -54,7 +54,7 @@ public class TestEncryptionInfo {
         assertEquals(4, info.getVersionMinor());
 
         assertEquals(CipherAlgorithm.aes256, info.getHeader().getCipherAlgorithm());
-        assertEquals(HashAlgorithm.sha512, info.getHeader().getHashAlgorithmEx());
+        assertEquals(HashAlgorithm.sha512, info.getHeader().getHashAlgorithm());
         assertEquals(256, info.getHeader().getKeySize());
         assertEquals(64, info.getVerifier().getEncryptedVerifierHash().length);
         assertEquals(CipherProvider.aes, info.getHeader().getCipherProvider());                

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java?rev=1767399&r1=1767398&r2=1767399&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java Tue Nov  1 01:30:48 2016
@@ -36,7 +36,9 @@ import javax.crypto.Cipher;
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.openxml4j.opc.ContentTypes;
 import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.poifs.crypt.agile.AgileDecryptor;
 import org.apache.poi.poifs.crypt.agile.AgileEncryptionHeader;
+import org.apache.poi.poifs.crypt.agile.AgileEncryptionVerifier;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentNode;
 import org.apache.poi.poifs.filesystem.Entry;
@@ -87,7 +89,7 @@ public class TestEncryptor {
         
         assertArrayEquals(payloadExpected.toByteArray(), payloadActual.toByteArray());
     }
-    
+
     @Test
     public void agileEncryption() throws Exception {
         int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
@@ -379,4 +381,142 @@ public class TestEncryptor {
             }
         }
     }
+
+    /*
+     * this test simulates the generation of bugs 60320 sample file
+     * as the padding bytes of the EncryptedPackage stream are random or in POIs case PKCS5-padded
+     * one would need to mock those bytes to get the same hmacValues - see diff below
+     *
+     * this use-case is experimental - for the time being the setters of the encryption classes
+     * are spreaded between two packages and are protected - so you would need to violate
+     * the packages rules and provide a helper class in the *poifs.crypt package-namespace.
+     * the default way of defining the encryption settings is via the EncryptionInfo class
+     */
+    @Test
+    public void bug60320CustomEncrypt() throws Exception {
+        // --- src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java  (revision 1766745)
+        // +++ src/java/org/apache/poi/poifs/crypt/ChunkedCipherOutputStream.java  (working copy)
+        // @@ -208,6 +208,13 @@
+        //      protected int invokeCipher(int posInChunk, boolean doFinal) throws GeneralSecurityException {
+        //          byte plain[] = (_plainByteFlags.isEmpty()) ? null : _chunk.clone();
+        //  
+        // +        if (posInChunk < 4096) {
+        // +            _cipher.update(_chunk, 0, posInChunk, _chunk);
+        // +            byte bla[] = { (byte)0x7A,(byte)0x0F,(byte)0x27,(byte)0xF0,(byte)0x17,(byte)0x6E,(byte)0x77,(byte)0x05,(byte)0xB9,(byte)0xDA,(byte)0x49,(byte)0xF9,(byte)0xD7,(byte)0x8E,(byte)0x03,(byte)0x1D };
+        // +            System.arraycopy(bla, 0, _chunk, posInChunk-2, bla.length);
+        // +            return posInChunk-2+bla.length;
+        // +        }
+        // +        
+        //          int ciLen = (doFinal)
+        //              ? _cipher.doFinal(_chunk, 0, posInChunk, _chunk)
+        //              : _cipher.update(_chunk, 0, posInChunk, _chunk);
+        //
+        //      --- src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java (revision 1766745)
+        //      +++ src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java (working copy)
+        //      
+        //      @@ -300,7 +297,7 @@
+        //      protected static Cipher initCipherForBlock(Cipher existing, int block, boolean lastChunk, EncryptionInfo encryptionInfo, SecretKey skey, int encryptionMode)
+        //      throws GeneralSecurityException {
+        //          EncryptionHeader header = encryptionInfo.getHeader();
+        // -        String padding = (lastChunk ? "PKCS5Padding" : "NoPadding");
+        // +        String padding = "NoPadding"; // (lastChunk ? "PKCS5Padding" : "NoPadding");
+        //          if (existing == null || !existing.getAlgorithm().endsWith(padding)) {
+        //              existing = getCipher(skey, header.getCipherAlgorithm(), header.getChainingMode(), header.getKeySalt(), encryptionMode, padding);
+        //          }
+
+        InputStream is = POIDataSamples.getPOIFSInstance().openResourceAsStream("60320-protected.xlsx");
+        POIFSFileSystem fsOrig = new POIFSFileSystem(is);
+        is.close();
+        EncryptionInfo infoOrig = new EncryptionInfo(fsOrig);
+        Decryptor decOrig = infoOrig.getDecryptor();
+        boolean b = decOrig.verifyPassword("Test001!!");
+        assertTrue(b);
+        InputStream decIn = decOrig.getDataStream(fsOrig);
+        byte[] zipInput = IOUtils.toByteArray(decIn);
+        decIn.close();
+
+        InputStream epOrig = fsOrig.getRoot().createDocumentInputStream("EncryptedPackage");
+        // ignore the 16 padding bytes
+        byte[] epOrigBytes = IOUtils.toByteArray(epOrig, 9400);
+        epOrig.close();
+        
+        EncryptionInfo eiNew = new EncryptionInfo(EncryptionMode.agile);
+        AgileEncryptionHeader aehHeader = (AgileEncryptionHeader)eiNew.getHeader();
+        aehHeader.setCipherAlgorithm(CipherAlgorithm.aes128);
+        aehHeader.setHashAlgorithm(HashAlgorithm.sha1);
+        AgileEncryptionVerifier aehVerifier = (AgileEncryptionVerifier)eiNew.getVerifier();
+        
+        // this cast might look strange - if the setters would be public, it will become obsolete
+        // see http://stackoverflow.com/questions/5637650/overriding-protected-methods-in-java
+        ((EncryptionVerifier)aehVerifier).setCipherAlgorithm(CipherAlgorithm.aes256);
+        aehVerifier.setHashAlgorithm(HashAlgorithm.sha512);
+        
+        Encryptor enc = eiNew.getEncryptor();
+        enc.confirmPassword("Test001!!",
+            infoOrig.getDecryptor().getSecretKey().getEncoded(),
+            infoOrig.getHeader().getKeySalt(),
+            infoOrig.getDecryptor().getVerifier(),
+            infoOrig.getVerifier().getSalt(),
+            infoOrig.getDecryptor().getIntegrityHmacKey()
+        );
+        NPOIFSFileSystem fsNew = new NPOIFSFileSystem();
+        OutputStream os = enc.getDataStream(fsNew);
+        os.write(zipInput);
+        os.close();
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        fsNew.writeFilesystem(bos);
+        fsNew.close();
+        
+        NPOIFSFileSystem fsReload = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
+        InputStream epReload = fsReload.getRoot().createDocumentInputStream("EncryptedPackage");
+        byte[] epNewBytes = IOUtils.toByteArray(epReload, 9400);
+        epReload.close();
+        
+        assertArrayEquals(epOrigBytes, epNewBytes);
+        
+        EncryptionInfo infoReload = new EncryptionInfo(fsOrig);
+        Decryptor decReload = infoReload.getDecryptor();
+        b = decReload.verifyPassword("Test001!!");
+        assertTrue(b);
+        
+        AgileEncryptionHeader aehOrig = (AgileEncryptionHeader)infoOrig.getHeader();
+        AgileEncryptionHeader aehReload = (AgileEncryptionHeader)infoReload.getHeader();
+        assertEquals(aehOrig.getBlockSize(), aehReload.getBlockSize());
+        assertEquals(aehOrig.getChainingMode(), aehReload.getChainingMode());
+        assertEquals(aehOrig.getCipherAlgorithm(), aehReload.getCipherAlgorithm());
+        assertEquals(aehOrig.getCipherProvider(), aehReload.getCipherProvider());
+        assertEquals(aehOrig.getCspName(), aehReload.getCspName());
+        assertArrayEquals(aehOrig.getEncryptedHmacKey(), aehReload.getEncryptedHmacKey());
+        // this only works, when the paddings are mocked to be the same ...
+        // assertArrayEquals(aehOrig.getEncryptedHmacValue(), aehReload.getEncryptedHmacValue());
+        assertEquals(aehOrig.getFlags(), aehReload.getFlags());
+        assertEquals(aehOrig.getHashAlgorithm(), aehReload.getHashAlgorithm());
+        assertArrayEquals(aehOrig.getKeySalt(), aehReload.getKeySalt());
+        assertEquals(aehOrig.getKeySize(), aehReload.getKeySize());
+        
+        AgileEncryptionVerifier aevOrig = (AgileEncryptionVerifier)infoOrig.getVerifier();
+        AgileEncryptionVerifier aevReload = (AgileEncryptionVerifier)infoReload.getVerifier();
+        assertEquals(aevOrig.getBlockSize(), aevReload.getBlockSize());
+        assertEquals(aevOrig.getChainingMode(), aevReload.getChainingMode());
+        assertEquals(aevOrig.getCipherAlgorithm(), aevReload.getCipherAlgorithm());
+        assertArrayEquals(aevOrig.getEncryptedKey(), aevReload.getEncryptedKey());
+        assertArrayEquals(aevOrig.getEncryptedVerifier(), aevReload.getEncryptedVerifier());
+        assertArrayEquals(aevOrig.getEncryptedVerifierHash(), aevReload.getEncryptedVerifierHash());
+        assertEquals(aevOrig.getHashAlgorithm(), aevReload.getHashAlgorithm());
+        assertEquals(aevOrig.getKeySize(), aevReload.getKeySize());
+        assertArrayEquals(aevOrig.getSalt(), aevReload.getSalt());
+        assertEquals(aevOrig.getSpinCount(), aevReload.getSpinCount());
+
+        AgileDecryptor adOrig = (AgileDecryptor)infoOrig.getDecryptor();
+        AgileDecryptor adReload = (AgileDecryptor)infoReload.getDecryptor();
+        
+        assertArrayEquals(adOrig.getIntegrityHmacKey(), adReload.getIntegrityHmacKey());
+        // doesn't work without mocking ... see above
+        // assertArrayEquals(adOrig.getIntegrityHmacValue(), adReload.getIntegrityHmacValue());
+        assertArrayEquals(adOrig.getSecretKey().getEncoded(), adReload.getSecretKey().getEncoded());
+        assertArrayEquals(adOrig.getVerifier(), adReload.getVerifier());
+
+        fsReload.close();
+    }
 }

Added: poi/trunk/test-data/poifs/60320-protected.xlsx
URL: http://svn.apache.org/viewvc/poi/trunk/test-data/poifs/60320-protected.xlsx?rev=1767399&view=auto
==============================================================================
Binary file - no diff available.

Propchange: poi/trunk/test-data/poifs/60320-protected.xlsx
------------------------------------------------------------------------------
--- svn:mime-type (added)
+++ svn:mime-type Tue Nov  1 01:30:48 2016
@@ -0,0 +1 @@
+application/vnd.openxmlformats-officedocument.spreadsheetml.sheet



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org