Added: 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=1553336&view=auto ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java (added) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java Tue Dec 24 23:13:21 2013 @@ -0,0 +1,118 @@ +/* ==================================================================== + 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 java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import junit.framework.TestCase; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; + +/** + * @author Maxim Valyanskiy + * @author Gary King + */ +public class TestDecryptor extends TestCase { + public void testPasswordVerification() throws IOException, GeneralSecurityException { + POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx")); + + EncryptionInfo info = new EncryptionInfo(fs); + + Decryptor d = Decryptor.getInstance(info); + + assertTrue(d.verifyPassword(Decryptor.DEFAULT_PASSWORD)); + } + + public void testDecrypt() throws IOException, GeneralSecurityException { + POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protect.xlsx")); + + EncryptionInfo info = new EncryptionInfo(fs); + + Decryptor d = Decryptor.getInstance(info); + + d.verifyPassword(Decryptor.DEFAULT_PASSWORD); + + zipOk(fs, d); + } + + public void testAgile() throws IOException, GeneralSecurityException { + POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx")); + + EncryptionInfo info = new EncryptionInfo(fs); + + assertTrue(info.getVersionMajor() == 4 && info.getVersionMinor() == 4); + + Decryptor d = Decryptor.getInstance(info); + + assertTrue(d.verifyPassword(Decryptor.DEFAULT_PASSWORD)); + + zipOk(fs, d); + } + + private void zipOk(POIFSFileSystem fs, Decryptor d) throws IOException, GeneralSecurityException { + ZipInputStream zin = new ZipInputStream(d.getDataStream(fs)); + + while (true) { + ZipEntry entry = zin.getNextEntry(); + if (entry==null) { + break; + } + + while (zin.available()>0) { + zin.skip(zin.available()); + } + } + } + public void testDataLength() throws Exception { + POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx")); + + EncryptionInfo info = new EncryptionInfo(fs); + + Decryptor d = Decryptor.getInstance(info); + + d.verifyPassword(Decryptor.DEFAULT_PASSWORD); + + InputStream is = d.getDataStream(fs); + + long len = d.getLength(); + assertEquals(12810, len); + + byte[] buf = new byte[(int)len]; + + is.read(buf); + + ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(buf)); + + while (true) { + ZipEntry entry = zin.getNextEntry(); + if (entry==null) { + break; + } + + while (zin.available()>0) { + zin.skip(zin.available()); + } + } + } + +} \ No newline at end of file
Added: 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=1553336&view=auto ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java (added) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptionInfo.java Tue Dec 24 23:13:21 2013 @@ -0,0 +1,61 @@ +/* ==================================================================== + 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 static org.junit.Assert.assertEquals; + +import java.io.IOException; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.junit.Test; + +public class TestEncryptionInfo { + @Test + 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(CipherAlgorithm.aes128, info.getHeader().getCipherAlgorithm()); + assertEquals(HashAlgorithm.sha1, info.getHeader().getHashAlgorithmEx()); + assertEquals(128, info.getHeader().getKeySize()); + assertEquals(32, info.getVerifier().getEncryptedVerifierHash().length); + assertEquals(CipherProvider.aes, info.getHeader().getCipherProvider()); + assertEquals("Microsoft Enhanced RSA and AES Cryptographic Provider", info.getHeader().getCspName()); + } + + @Test + public void testEncryptionInfoSHA512() throws Exception { + POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_sha512.xlsx")); + + EncryptionInfo info = new EncryptionInfo(fs); + + assertEquals(4, info.getVersionMajor()); + assertEquals(4, info.getVersionMinor()); + + assertEquals(CipherAlgorithm.aes256, info.getHeader().getCipherAlgorithm()); + assertEquals(HashAlgorithm.sha512, info.getHeader().getHashAlgorithmEx()); + assertEquals(256, info.getHeader().getKeySize()); + assertEquals(64, info.getVerifier().getEncryptedVerifierHash().length); + assertEquals(CipherProvider.aes, info.getHeader().getCipherProvider()); +// assertEquals("Microsoft Enhanced RSA and AES Cryptographic Provider", info.getHeader().getCspName()); + } +} Added: 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=1553336&view=auto ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java (added) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestEncryptor.java Tue Dec 24 23:13:21 2013 @@ -0,0 +1,253 @@ +/* ==================================================================== + 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 static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Iterator; + +import org.apache.poi.POIDataSamples; +import org.apache.poi.poifs.crypt.agile.AgileEncryptionHeader; +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.DocumentNode; +import org.apache.poi.poifs.filesystem.Entry; +import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.util.BoundedInputStream; +import org.apache.poi.util.IOUtils; +import org.junit.Test; + +public class TestEncryptor { + @Test + public void testAgileEncryption() throws Exception { + File file = POIDataSamples.getDocumentInstance().getFile("bug53475-password-is-pass.docx"); + String pass = "pass"; + NPOIFSFileSystem nfs = new NPOIFSFileSystem(file); + + // Check the encryption details + EncryptionInfo infoExpected = new EncryptionInfo(nfs); + Decryptor decExpected = Decryptor.getInstance(infoExpected); + boolean passed = decExpected.verifyPassword(pass); + assertTrue("Unable to process: document is encrypted", passed); + + // extract the payload + InputStream is = decExpected.getDataStream(nfs); + byte payloadExpected[] = IOUtils.toByteArray(is); + is.close(); + + long decPackLenExpected = decExpected.getLength(); + assertEquals(decPackLenExpected, payloadExpected.length); + + is = nfs.getRoot().createDocumentInputStream("EncryptedPackage"); + is = new BoundedInputStream(is, is.available()-16); // ignore padding block + byte encPackExpected[] = IOUtils.toByteArray(is); + is.close(); + + // listDir(nfs.getRoot(), "orig", ""); + + nfs.close(); + + // check that same verifier/salt lead to same hashes + byte verifierSaltExpected[] = infoExpected.getVerifier().getSalt(); + byte verifierExpected[] = decExpected.getVerifier(); + byte keySalt[] = infoExpected.getHeader().getKeySalt(); + byte keySpec[] = decExpected.getSecretKey().getEncoded(); + byte integritySalt[] = decExpected.getIntegrityHmacKey(); + // the hmacs of the file always differ, as we use PKCS5-padding to pad the bytes + // whereas office just uses random bytes + // byte integrityHash[] = d.getIntegrityHmacValue(); + + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo infoActual = new EncryptionInfo( + fs, EncryptionMode.agile + , infoExpected.getVerifier().getCipherAlgorithm() + , infoExpected.getVerifier().getHashAlgorithm() + , infoExpected.getHeader().getKeySize() + , infoExpected.getHeader().getBlockSize() + , infoExpected.getVerifier().getChainingMode() + ); + + Encryptor e = Encryptor.getInstance(infoActual); + e.confirmPassword(pass, keySpec, keySalt, verifierExpected, verifierSaltExpected, integritySalt); + + OutputStream os = e.getDataStream(fs); + IOUtils.copy(new ByteArrayInputStream(payloadExpected), os); + os.close(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + fs.writeFilesystem(bos); + + nfs = new NPOIFSFileSystem(new ByteArrayInputStream(bos.toByteArray())); + infoActual = new EncryptionInfo(nfs.getRoot()); + Decryptor decActual = Decryptor.getInstance(infoActual); + passed = decActual.verifyPassword(pass); + assertTrue("Unable to process: document is encrypted", passed); + + // extract the payload + is = decActual.getDataStream(nfs); + byte payloadActual[] = IOUtils.toByteArray(is); + is.close(); + + long decPackLenActual = decActual.getLength(); + + is = nfs.getRoot().createDocumentInputStream("EncryptedPackage"); + is = new BoundedInputStream(is, is.available()-16); // ignore padding block + byte encPackActual[] = IOUtils.toByteArray(is); + is.close(); + + // listDir(nfs.getRoot(), "copy", ""); + + nfs.close(); + + AgileEncryptionHeader aehExpected = (AgileEncryptionHeader)infoExpected.getHeader(); + AgileEncryptionHeader aehActual = (AgileEncryptionHeader)infoActual.getHeader(); + assertThat(aehExpected.getEncryptedHmacKey(), equalTo(aehActual.getEncryptedHmacKey())); + assertEquals(decPackLenExpected, decPackLenActual); + assertThat(payloadExpected, equalTo(payloadActual)); + assertThat(encPackExpected, equalTo(encPackActual)); + } + + @Test + public void testStandardEncryption() throws Exception { + File file = POIDataSamples.getDocumentInstance().getFile("bug53475-password-is-solrcell.docx"); + String pass = "solrcell"; + + NPOIFSFileSystem nfs = new NPOIFSFileSystem(file); + + // Check the encryption details + EncryptionInfo infoExpected = new EncryptionInfo(nfs); + Decryptor d = Decryptor.getInstance(infoExpected); + boolean passed = d.verifyPassword(pass); + assertTrue("Unable to process: document is encrypted", passed); + + // extract the payload + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + InputStream is = d.getDataStream(nfs); + IOUtils.copy(is, bos); + is.close(); + nfs.close(); + byte payloadExpected[] = bos.toByteArray(); + + // check that same verifier/salt lead to same hashes + byte verifierSaltExpected[] = infoExpected.getVerifier().getSalt(); + byte verifierExpected[] = d.getVerifier(); + byte keySpec[] = d.getSecretKey().getEncoded(); + byte keySalt[] = infoExpected.getHeader().getKeySalt(); + + + POIFSFileSystem fs = new POIFSFileSystem(); + EncryptionInfo infoActual = new EncryptionInfo( + fs, EncryptionMode.standard + , infoExpected.getVerifier().getCipherAlgorithm() + , infoExpected.getVerifier().getHashAlgorithm() + , infoExpected.getHeader().getKeySize() + , infoExpected.getHeader().getBlockSize() + , infoExpected.getVerifier().getChainingMode() + ); + + Encryptor e = Encryptor.getInstance(infoActual); + e.confirmPassword(pass, keySpec, keySalt, verifierExpected, verifierSaltExpected, null); + + assertThat(infoExpected.getVerifier().getEncryptedVerifier(), equalTo(infoActual.getVerifier().getEncryptedVerifier())); + assertThat(infoExpected.getVerifier().getEncryptedVerifierHash(), equalTo(infoActual.getVerifier().getEncryptedVerifierHash())); + + // now we use a newly generated salt/verifier and check + // if the file content is still the same + + fs = new POIFSFileSystem(); + infoActual = new EncryptionInfo( + fs, EncryptionMode.standard + , infoExpected.getVerifier().getCipherAlgorithm() + , infoExpected.getVerifier().getHashAlgorithm() + , infoExpected.getHeader().getKeySize() + , infoExpected.getHeader().getBlockSize() + , infoExpected.getVerifier().getChainingMode() + ); + + e = Encryptor.getInstance(infoActual); + e.confirmPassword(pass); + + OutputStream os = e.getDataStream(fs); + IOUtils.copy(new ByteArrayInputStream(payloadExpected), os); + os.close(); + + bos.reset(); + fs.writeFilesystem(bos); + + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + + // FileOutputStream fos = new FileOutputStream("encrypted.docx"); + // IOUtils.copy(bis, fos); + // fos.close(); + // bis.reset(); + + nfs = new NPOIFSFileSystem(bis); + infoExpected = new EncryptionInfo(nfs); + d = Decryptor.getInstance(infoExpected); + passed = d.verifyPassword(pass); + assertTrue("Unable to process: document is encrypted", passed); + + bos.reset(); + is = d.getDataStream(nfs); + IOUtils.copy(is, bos); + is.close(); + nfs.close(); + byte payloadActual[] = bos.toByteArray(); + + assertThat(payloadExpected, equalTo(payloadActual)); + } + + + private void listEntry(DocumentNode de, String ext, String path) throws IOException { + path += "\\" + de.getName().replace('\u0006', '_'); + System.out.println(ext+": "+path+" ("+de.getSize()+" bytes)"); + + String name = de.getName().replace('\u0006', '_'); + + InputStream is = ((DirectoryNode)de.getParent()).createDocumentInputStream(de); + FileOutputStream fos = new FileOutputStream("solr."+name+"."+ext); + IOUtils.copy(is, fos); + fos.close(); + is.close(); + } + + @SuppressWarnings("unused") + private void listDir(DirectoryNode dn, String ext, String path) throws IOException { + path += "\\" + dn.getName().replace('\u0006', '_'); + System.out.println(ext+": "+path+" ("+dn.getStorageClsid()+")"); + + Iterator<Entry> iter = dn.getEntries(); + while (iter.hasNext()) { + Entry ent = iter.next(); + if (ent instanceof DirectoryNode) { + listDir((DirectoryNode)ent, ext, path); + } else { + listEntry((DocumentNode)ent, ext, path); + } + } + } +} Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFBugs.java URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFBugs.java?rev=1553336&r1=1553335&r2=1553336&view=diff ============================================================================== --- poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFBugs.java (original) +++ poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFBugs.java Tue Dec 24 23:13:21 2013 @@ -11,9 +11,10 @@ import javax.crypto.Cipher; import org.apache.poi.POIDataSamples; import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.poifs.crypt.CipherAlgorithm; 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.HashAlgorithm; import org.apache.poi.poifs.filesystem.NPOIFSFileSystem; import org.apache.poi.xwpf.extractor.XWPFWordExtractor; import org.apache.poi.xwpf.usermodel.XWPFDocument; @@ -33,8 +34,8 @@ public class TestXWPFBugs { // Check the encryption details EncryptionInfo info = new EncryptionInfo(filesystem); assertEquals(128, info.getHeader().getKeySize()); - assertEquals(EncryptionHeader.ALGORITHM_AES_128, info.getHeader().getAlgorithm()); - assertEquals(EncryptionHeader.HASH_SHA1, info.getHeader().getHashAlgorithm()); + assertEquals(CipherAlgorithm.aes128, info.getHeader().getCipherAlgorithm()); + assertEquals(HashAlgorithm.sha1, info.getHeader().getHashAlgorithmEx()); // Check it can be decoded Decryptor d = Decryptor.getInstance(info); @@ -67,8 +68,8 @@ public class TestXWPFBugs { EncryptionInfo info = new EncryptionInfo(filesystem); assertEquals(16, info.getHeader().getBlockSize()); assertEquals(256, info.getHeader().getKeySize()); - assertEquals(EncryptionHeader.ALGORITHM_AES_256, info.getHeader().getAlgorithm()); - assertEquals(EncryptionHeader.HASH_SHA1, info.getHeader().getHashAlgorithm()); + assertEquals(CipherAlgorithm.aes256, info.getHeader().getCipherAlgorithm()); + assertEquals(HashAlgorithm.sha1, info.getHeader().getHashAlgorithmEx()); // Check it can be decoded Decryptor d = Decryptor.getInstance(info); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
