diff --git a/src/java/org/apache/poi/poifs/crypt/Decryptor.java b/src/java/org/apache/poi/poifs/crypt/Decryptor.java new file mode 100644 index 000000000..a47100d69 --- /dev/null +++ b/src/java/org/apache/poi/poifs/crypt/Decryptor.java @@ -0,0 +1,133 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.poifs.crypt; + +import org.apache.poi.poifs.filesystem.DocumentInputStream; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.LittleEndian; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.security.GeneralSecurityException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +/** + * @author Maxim Valyanskiy + */ +public class Decryptor { + public static final String DEFAULT_PASSWORD="VelvetSweatshop"; + + private final EncryptionInfo info; + private byte[] passwordHash; + + public Decryptor(EncryptionInfo info) { + this.info = info; + } + + private void generatePasswordHash(String password) throws NoSuchAlgorithmException { + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + + sha1.update(info.getVerifier().getSalt()); + byte[] hash = sha1.digest(password.getBytes(Charset.forName("UTF-16LE"))); + + byte[] iterator = new byte[4]; + for (int i = 0; i<50000; i++) { + sha1.reset(); + + LittleEndian.putInt(iterator, i); + sha1.update(iterator); + hash = sha1.digest(hash); + } + + passwordHash = hash; + } + + private byte[] generateKey(int block) throws NoSuchAlgorithmException { + MessageDigest sha1 = MessageDigest.getInstance("SHA-1"); + + sha1.update(passwordHash); + byte[] blockValue = new byte[4]; + LittleEndian.putInt(blockValue, block); + byte[] finalHash = sha1.digest(blockValue); + + int requiredKeyLength = info.getHeader().getKeySize()/8; + + byte[] buff = new byte[64]; + + Arrays.fill(buff, (byte) 0x36); + + for (int i=0; i0) { + zin.skip(zin.available()); + } + } + } +} diff --git a/src/testcases/org/apache/poi/poifs/crypt/EncryptionInfoTest.java b/src/testcases/org/apache/poi/poifs/crypt/EncryptionInfoTest.java new file mode 100644 index 000000000..eb84727e3 --- /dev/null +++ b/src/testcases/org/apache/poi/poifs/crypt/EncryptionInfoTest.java @@ -0,0 +1,45 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.poifs.crypt; + +import junit.framework.TestCase; +import org.apache.poi.POIDataSamples; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +import java.io.IOException; + +/** + * @author Maxim Valyanskiy + */ +public class EncryptionInfoTest extends TestCase { + public void testEncryptionInfo() throws IOException { + POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx")); + + EncryptionInfo info = new EncryptionInfo(fs); + + assertEquals(3, info.getVersionMajor()); + assertEquals(2, info.getVersionMinor()); + + assertEquals(EncryptionHeader.ALGORITHM_AES_128, info.getHeader().getAlgorithm()); + assertEquals(EncryptionHeader.HASH_SHA1, info.getHeader().getHashAlgorithm()); + assertEquals(128, info.getHeader().getKeySize()); + assertEquals(EncryptionHeader.PROVIDER_AES, info.getHeader().getProviderType()); + assertEquals("Microsoft Enhanced RSA and AES Cryptographic Provider", info.getHeader().getCspName()); + + assertEquals(32, info.getVerifier().getVerifierHash().length); + } +} diff --git a/test-data/poifs/protect.xlsx b/test-data/poifs/protect.xlsx new file mode 100644 index 000000000..1767b1437 Binary files /dev/null and b/test-data/poifs/protect.xlsx differ