View | Details | Raw Unified | Return to bug 60320
Collapse All | Expand All

(-)src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java (+10 lines)
Lines 16-21 Link Here
16
==================================================================== */
16
==================================================================== */
17
package org.apache.poi.poifs.crypt;
17
package org.apache.poi.poifs.crypt;
18
18
19
import org.apache.poi.util.Removal;
20
19
/**
21
/**
20
 * Reads and processes OOXML Encryption Headers
22
 * Reads and processes OOXML Encryption Headers
21
 * The constants are largely based on ZIP constants.
23
 * The constants are largely based on ZIP constants.
Lines 83-89 Link Here
83
    protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
85
    protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
84
        this.cipherAlgorithm = cipherAlgorithm;
86
        this.cipherAlgorithm = cipherAlgorithm;
85
    }
87
    }
88
89
    public HashAlgorithm getHashAlgorithm() {
90
        return hashAlgorithm;
91
    }
86
    
92
    
93
    /**
94
     * @deprecated POI 3.16 beta 1. use {@link #getHashAlgorithm()}
95
     */
96
    @Removal(version="3.18")
87
    public HashAlgorithm getHashAlgorithmEx() {
97
    public HashAlgorithm getHashAlgorithmEx() {
88
        return hashAlgorithm;
98
        return hashAlgorithm;
89
    }
99
    }
(-)src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java (-19 / +21 lines)
Lines 40-45 Link Here
40
import javax.crypto.spec.SecretKeySpec;
40
import javax.crypto.spec.SecretKeySpec;
41
41
42
import org.apache.poi.EncryptedDocumentException;
42
import org.apache.poi.EncryptedDocumentException;
43
import org.apache.poi.poifs.crypt.ChainingMode;
43
import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
44
import org.apache.poi.poifs.crypt.ChunkedCipherInputStream;
44
import org.apache.poi.poifs.crypt.CipherAlgorithm;
45
import org.apache.poi.poifs.crypt.CipherAlgorithm;
45
import org.apache.poi.poifs.crypt.CryptoFunctions;
46
import org.apache.poi.poifs.crypt.CryptoFunctions;
Lines 93-103 Link Here
93
    public boolean verifyPassword(String password) throws GeneralSecurityException {
94
    public boolean verifyPassword(String password) throws GeneralSecurityException {
94
        AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
95
        AgileEncryptionVerifier ver = (AgileEncryptionVerifier)getEncryptionInfo().getVerifier();
95
        AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader(); 
96
        AgileEncryptionHeader header = (AgileEncryptionHeader)getEncryptionInfo().getHeader(); 
96
        HashAlgorithm hashAlgo = header.getHashAlgorithmEx();
97
        HashAlgorithm hashAlgo = ver.getHashAlgorithm();
97
        CipherAlgorithm cipherAlgo = header.getCipherAlgorithm();
98
        int blockSize = header.getBlockSize();
99
        int keySize = header.getKeySize()/8;
100
98
99
        int keySize = (ver.getKeySize() == -1 ? header.getKeySize() : ver.getKeySize())/8;
100
        int blockSize = (ver.getBlockSize() == -1 ? header.getBlockSize() : ver.getBlockSize());
101
101
        byte[] pwHash = hashPassword(password, ver.getHashAlgorithm(), ver.getSalt(), ver.getSpinCount());
102
        byte[] pwHash = hashPassword(password, ver.getHashAlgorithm(), ver.getSalt(), ver.getSpinCount());
102
103
103
        /**
104
        /**
Lines 113-119 Link Here
113
         *    blockSize bytes.
114
         *    blockSize bytes.
114
         * 4. Use base64 to encode the result of step 3.
115
         * 4. Use base64 to encode the result of step 3.
115
         */
116
         */
116
        byte verfierInputEnc[] = hashInput(getEncryptionInfo(), pwHash, kVerifierInputBlock, ver.getEncryptedVerifier(), Cipher.DECRYPT_MODE);
117
        byte verfierInputEnc[] = hashInput(ver, pwHash, kVerifierInputBlock, ver.getEncryptedVerifier(), Cipher.DECRYPT_MODE);
117
        setVerifier(verfierInputEnc);
118
        setVerifier(verfierInputEnc);
118
        MessageDigest hashMD = getMessageDigest(hashAlgo);
119
        MessageDigest hashMD = getMessageDigest(hashAlgo);
119
        byte[] verifierHash = hashMD.digest(verfierInputEnc);
120
        byte[] verifierHash = hashMD.digest(verfierInputEnc);
Lines 130-136 Link Here
130
         *    blockSize bytes, pad the hash value with 0x00 to an integral multiple of blockSize bytes.
131
         *    blockSize bytes, pad the hash value with 0x00 to an integral multiple of blockSize bytes.
131
         * 4. Use base64 to encode the result of step 3.
132
         * 4. Use base64 to encode the result of step 3.
132
         */
133
         */
133
        byte verifierHashDec[] = hashInput(getEncryptionInfo(), pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
134
        byte verifierHashDec[] = hashInput(ver, pwHash, kHashedVerifierBlock, ver.getEncryptedVerifierHash(), Cipher.DECRYPT_MODE);
134
        verifierHashDec = getBlock0(verifierHashDec, hashAlgo.hashSize);
135
        verifierHashDec = getBlock0(verifierHashDec, hashAlgo.hashSize);
135
        
136
        
136
        /**
137
        /**
Lines 146-152 Link Here
146
         *    blockSize bytes.
147
         *    blockSize bytes.
147
         * 4. Use base64 to encode the result of step 3.
148
         * 4. Use base64 to encode the result of step 3.
148
         */
149
         */
149
        byte keyspec[] = hashInput(getEncryptionInfo(), pwHash, kCryptoKeyBlock, ver.getEncryptedKey(), Cipher.DECRYPT_MODE);
150
        byte keyspec[] = hashInput(header, pwHash, kCryptoKeyBlock, ver.getEncryptedKey(), Cipher.DECRYPT_MODE);
150
        keyspec = getBlock0(keyspec, keySize);
151
        keyspec = getBlock0(keyspec, keySize);
151
        SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.getCipherAlgorithm().jceId);
152
        SecretKeySpec secretKey = new SecretKeySpec(keyspec, ver.getCipherAlgorithm().jceId);
152
153
Lines 163-170 Link Here
163
         *    array with 0x00 to the next integral multiple of blockSize bytes.
164
         *    array with 0x00 to the next integral multiple of blockSize bytes.
164
         * 4. Assign the encryptedHmacKey attribute to the base64-encoded form of the result of step 3.
165
         * 4. Assign the encryptedHmacKey attribute to the base64-encoded form of the result of step 3.
165
         */
166
         */
166
        byte vec[] = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityKeyBlock, blockSize); 
167
        byte vec[] = CryptoFunctions.generateIv(hashAlgo, header.getKeySalt(), kIntegrityKeyBlock, blockSize);
167
        Cipher cipher = getCipher(secretKey, cipherAlgo, ver.getChainingMode(), vec, Cipher.DECRYPT_MODE);
168
        CipherAlgorithm cipherAlgo = ver.getCipherAlgorithm();
169
        Cipher cipher = getCipher(secretKey, cipherAlgo, header.getChainingMode(), vec, Cipher.DECRYPT_MODE);
168
        byte hmacKey[] = cipher.doFinal(header.getEncryptedHmacKey());
170
        byte hmacKey[] = cipher.doFinal(header.getEncryptedHmacKey());
169
        hmacKey = getBlock0(hmacKey, hashAlgo.hashSize);
171
        hmacKey = getBlock0(hmacKey, hashAlgo.hashSize);
170
172
Lines 257-274 Link Here
257
        return fillSize;
259
        return fillSize;
258
    }
260
    }
259
261
260
    protected static byte[] hashInput(EncryptionInfo encryptionInfo, byte pwHash[], byte blockKey[], byte inputKey[], int cipherMode) {
262
    /* package */ static byte[] hashInput(AgileEncryptionConfig aec, byte pwHash[], byte blockKey[], byte inputKey[], int cipherMode) {
261
        EncryptionVerifier ver = encryptionInfo.getVerifier();
263
        int keySize = aec.getKeySize()/8;
262
        AgileDecryptor dec = (AgileDecryptor)encryptionInfo.getDecryptor();
264
        int blockSize = aec.getBlockSize();
263
        int keySize = dec.getKeySizeInBytes();
265
        byte salt[] = aec.getSalt();
264
        int blockSize = dec.getBlockSizeInBytes();
266
        HashAlgorithm hashAlgo = aec.getHashAlgorithm();
265
        HashAlgorithm hashAlgo = ver.getHashAlgorithm();
267
        CipherAlgorithm cipherAlgo = aec.getCipherAlgorithm();
266
        byte[] salt = ver.getSalt();
268
        ChainingMode chainMode = aec.getChainingMode();
267
269
        
268
        byte intermedKey[] = generateKey(pwHash, hashAlgo, blockKey, keySize);
270
        byte intermedKey[] = generateKey(pwHash, hashAlgo, blockKey, keySize);
269
        SecretKey skey = new SecretKeySpec(intermedKey, ver.getCipherAlgorithm().jceId);
271
        SecretKey skey = new SecretKeySpec(intermedKey, cipherAlgo.jceId);
270
        byte[] iv = generateIv(hashAlgo, salt, null, blockSize);
272
        byte[] iv = generateIv(hashAlgo, salt, null, blockSize);
271
        Cipher cipher = getCipher(skey, ver.getCipherAlgorithm(), ver.getChainingMode(), iv, cipherMode);
273
        Cipher cipher = getCipher(skey, cipherAlgo, chainMode, iv, cipherMode);
272
        byte[] hashFinal;
274
        byte[] hashFinal;
273
        
275
        
274
        try {
276
        try {
(-)src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionConfig.java (+20 lines)
Line 0 Link Here
1
package org.apache.poi.poifs.crypt.agile;
2
3
import org.apache.poi.poifs.crypt.ChainingMode;
4
import org.apache.poi.poifs.crypt.CipherAlgorithm;
5
import org.apache.poi.poifs.crypt.HashAlgorithm;
6
import org.apache.poi.util.Internal;
7
8
/**
9
 * A internal interface to provide the common config of header and verifier
10
 */
11
@Internal
12
/* package */ interface AgileEncryptionConfig {
13
    int getKeySize();
14
    int getBlockSize();
15
    byte[] getSalt();
16
    CipherAlgorithm getCipherAlgorithm();
17
    HashAlgorithm getHashAlgorithm();
18
    ChainingMode getChainingMode();
19
}
20
native
(-)src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionHeader.java (-1 / +5 lines)
Lines 27-33 Link Here
27
import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
27
import com.microsoft.schemas.office.x2006.encryption.EncryptionDocument;
28
import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
28
import com.microsoft.schemas.office.x2006.encryption.STCipherChaining;
29
29
30
public class AgileEncryptionHeader extends EncryptionHeader implements Cloneable {
30
public class AgileEncryptionHeader extends EncryptionHeader implements Cloneable, AgileEncryptionConfig {
31
    private byte encryptedHmacKey[], encryptedHmacValue[];
31
    private byte encryptedHmacKey[], encryptedHmacValue[];
32
    
32
    
33
    public AgileEncryptionHeader(String descriptor) {
33
    public AgileEncryptionHeader(String descriptor) {
Lines 98-103 Link Here
98
        setChainingMode(chainingMode);
98
        setChainingMode(chainingMode);
99
    }
99
    }
100
100
101
    public byte[] getSalt() {
102
        return getKeySalt();
103
    }
104
    
101
    // make method visible for this package
105
    // make method visible for this package
102
    @Override
106
    @Override
103
    protected void setKeySalt(byte salt[]) {
107
    protected void setKeySalt(byte salt[]) {
(-)src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java (-1 / +52 lines)
Lines 39-45 Link Here
39
/**
39
/**
40
 * Used when checking if a key is valid for a document 
40
 * Used when checking if a key is valid for a document 
41
 */
41
 */
42
public class AgileEncryptionVerifier extends EncryptionVerifier implements Cloneable {
42
public class AgileEncryptionVerifier extends EncryptionVerifier implements Cloneable, AgileEncryptionConfig {
43
43
44
    public static class AgileCertificateEntry {
44
    public static class AgileCertificateEntry {
45
        X509Certificate x509;
45
        X509Certificate x509;
Lines 48-53 Link Here
48
    }
48
    }
49
    
49
    
50
    private List<AgileCertificateEntry> certList = new ArrayList<AgileCertificateEntry>();
50
    private List<AgileCertificateEntry> certList = new ArrayList<AgileCertificateEntry>();
51
    private int keyBits = -1;
52
    private int blockSize = -1;
51
53
52
    public AgileEncryptionVerifier(String descriptor) {
54
    public AgileEncryptionVerifier(String descriptor) {
53
        this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor));
55
        this(AgileEncryptionInfoBuilder.parseDescriptor(descriptor));
Lines 66-72 Link Here
66
        }
68
        }
67
        
69
        
68
        int keyBits = (int)keyData.getKeyBits();
70
        int keyBits = (int)keyData.getKeyBits();
71
        setKeySize(keyBits);
69
        
72
        
73
        int blockSize = keyData.getBlockSize();
74
        setBlockSize(blockSize);
75
        
70
        CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), keyBits);
76
        CipherAlgorithm ca = CipherAlgorithm.fromXmlId(keyData.getCipherAlgorithm().toString(), keyBits);
71
        setCipherAlgorithm(ca);
77
        setCipherAlgorithm(ca);
72
78
Lines 125-130 Link Here
125
        setCipherAlgorithm(cipherAlgorithm);
131
        setCipherAlgorithm(cipherAlgorithm);
126
        setHashAlgorithm(hashAlgorithm);
132
        setHashAlgorithm(hashAlgorithm);
127
        setChainingMode(chainingMode);
133
        setChainingMode(chainingMode);
134
        setKeySize(keyBits);
135
        setBlockSize(blockSize);
128
        setSpinCount(100000); // TODO: use parameter
136
        setSpinCount(100000); // TODO: use parameter
129
    }
137
    }
130
    
138
    
Lines 171-174 Link Here
171
        other.certList = new ArrayList<AgileCertificateEntry>(certList);
179
        other.certList = new ArrayList<AgileCertificateEntry>(certList);
172
        return other;
180
        return other;
173
    }
181
    }
182
    
183
    
184
    /**
185
     * The keysize (in bits) of the verifier data. This usually equals the keysize of the header,
186
     * but only on a few exceptions, like files generated by Office for Mac, can be
187
     * different.
188
     *
189
     * @return the keysize (in bits) of the verifier. if {@code -1}, the keysize hasn't been set,
190
     * i.e. the keysize of the header has to be used
191
     */
192
    public int getKeySize() {
193
        return keyBits;
194
    }
195
196
    
197
    /**
198
     * The blockSize (in bytes) of the verifier data. This usually equals the blocksize of the header.
199
     *
200
     * @return the blockSize (in bytes) of the verifier, if {@code -1}, the blockSize hasn't been set,
201
     * i.e. the blockSize of the header has to be used
202
     */
203
    public int getBlockSize() {
204
        return blockSize;
205
    }
206
    
207
    /**
208
     * Sets the keysize (in bits) of the verifier
209
     *
210
     * @param keyBits the keysize (in bits), use {@code -1} to unset it
211
     */
212
    protected void setKeySize(int keyBits) {
213
        this.keyBits = keyBits;
214
    }
215
216
    
217
    /**
218
     * Sets the blockSize (in bytes) of the verifier
219
     *
220
     * @param blockSize the blockSize (in bytes), use {@code -1} to unset it
221
     */
222
    protected void setBlockSize(int blockSize) {
223
        this.blockSize = blockSize;
224
    }
174
}
225
}
(-)src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java (-3 / +3 lines)
Lines 129-135 Link Here
129
         *    blockSize bytes.
129
         *    blockSize bytes.
130
         * 4. Use base64 to encode the result of step 3.
130
         * 4. Use base64 to encode the result of step 3.
131
         */
131
         */
132
        byte encryptedVerifier[] = hashInput(getEncryptionInfo(), pwHash, kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE);
132
        byte encryptedVerifier[] = hashInput(ver, pwHash, kVerifierInputBlock, verifier, Cipher.ENCRYPT_MODE);
133
        ver.setEncryptedVerifier(encryptedVerifier);
133
        ver.setEncryptedVerifier(encryptedVerifier);
134
	    
134
	    
135
135
Lines 147-153 Link Here
147
         */
147
         */
148
        MessageDigest hashMD = getMessageDigest(hashAlgo);
148
        MessageDigest hashMD = getMessageDigest(hashAlgo);
149
        byte[] hashedVerifier = hashMD.digest(verifier);
149
        byte[] hashedVerifier = hashMD.digest(verifier);
150
        byte encryptedVerifierHash[] = hashInput(getEncryptionInfo(), pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
150
        byte encryptedVerifierHash[] = hashInput(ver, pwHash, kHashedVerifierBlock, hashedVerifier, Cipher.ENCRYPT_MODE);
151
        ver.setEncryptedVerifierHash(encryptedVerifierHash);
151
        ver.setEncryptedVerifierHash(encryptedVerifierHash);
152
        
152
        
153
        /**
153
        /**
Lines 163-169 Link Here
163
         *    blockSize bytes.
163
         *    blockSize bytes.
164
         * 4. Use base64 to encode the result of step 3.
164
         * 4. Use base64 to encode the result of step 3.
165
         */
165
         */
166
        byte encryptedKey[] = hashInput(getEncryptionInfo(), pwHash, kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE);
166
        byte encryptedKey[] = hashInput(header, pwHash, kCryptoKeyBlock, keySpec, Cipher.ENCRYPT_MODE);
167
        ver.setEncryptedKey(encryptedKey);
167
        ver.setEncryptedKey(encryptedKey);
168
        
168
        
169
        SecretKey secretKey = new SecretKeySpec(keySpec, ver.getCipherAlgorithm().jceId);
169
        SecretKey secretKey = new SecretKeySpec(keySpec, ver.getCipherAlgorithm().jceId);
(-)src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java (-4 / +14 lines)
Lines 37-48 Link Here
37
import org.apache.poi.xssf.XSSFTestDataSamples;
37
import org.apache.poi.xssf.XSSFTestDataSamples;
38
import org.junit.Test;
38
import org.junit.Test;
39
39
40
/**
41
 *  @author Maxim Valyanskiy
42
 *  @author Gary King
43
 */
44
public class TestDecryptor {
40
public class TestDecryptor {
45
    @Test
41
    @Test
42
    public void decrypt2() throws IOException, GeneralSecurityException {
43
        POIFSFileSystem fs = new POIFSFileSystem(new FileInputStream("TestThisEncryption.xlsx"));
44
45
        EncryptionInfo info = new EncryptionInfo(fs);
46
47
        Decryptor d = Decryptor.getInstance(info);
48
49
        boolean b = d.verifyPassword("Test001!!");
50
        assertTrue(b);
51
52
        zipOk(fs.getRoot(), d);
53
    }    
54
    
55
    @Test
46
    public void passwordVerification() throws IOException, GeneralSecurityException {
56
    public void passwordVerification() throws IOException, GeneralSecurityException {
47
        POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx"));
57
        POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx"));
48
58

Return to bug 60320