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 2013/12/25 00:13:22 UTC
svn commit: r1553336 [1/4] - in /poi: site/src/documentation/content/xdocs/
trunk/ trunk/src/java/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/ trunk/...
Author: kiwiwings
Date: Tue Dec 24 23:13:21 2013
New Revision: 1553336
URL: http://svn.apache.org/r1553336
Log:
Patch for Bug/Enhancement 55818 - add encryption support
Added:
poi/trunk/src/java/org/apache/poi/poifs/crypt/ChainingMode.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherProvider.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/HashAlgorithm.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/package.html
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/EncryptionRecord.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardDecryptor.java
- copied, changed from r1541009, poi/trunk/src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionHeader.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionInfoBuilder.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptionVerifier.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/standard/StandardEncryptor.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/
- copied from r1538068, poi/trunk/src/java/org/apache/poi/poifs/crypt/
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileDecryptor.java
- copied, changed from r1544367, poi/trunk/src/java/org/apache/poi/poifs/crypt/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/AgileEncryptionInfoBuilder.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptionVerifier.java
- copied, changed from r1541255, poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/agile/AgileEncryptor.java
poi/trunk/src/ooxml/resources/
poi/trunk/src/ooxml/resources/org/
poi/trunk/src/ooxml/resources/org/apache/
poi/trunk/src/ooxml/resources/org/apache/poi/
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/encryptionCertificate.xsd
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/encryptionCertificate.xsdconfig
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/encryptionInfo.xsd
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/encryptionInfo.xsdconfig
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/encryptionPassword.xsd
poi/trunk/src/ooxml/resources/org/apache/poi/poifs/crypt/encryptionPassword.xsdconfig
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/AllPOIFSCryptoTests.java
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestAgileEncryptionParameters.java
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestCertificateEncryption.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
Removed:
poi/trunk/src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/AgileDecryptor.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/Decryptor.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
poi/trunk/src/testcases/org/apache/poi/poifs/crypt/
Modified:
poi/site/src/documentation/content/xdocs/encryption.xml
poi/trunk/.classpath
poi/trunk/build.xml
poi/trunk/src/java/org/apache/poi/EncryptedDocumentException.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionHeader.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionVerifier.java
poi/trunk/src/ooxml/java/org/apache/poi/POIXMLException.java
poi/trunk/src/ooxml/java/org/apache/poi/util/OOXMLLite.java
poi/trunk/src/ooxml/testcases/org/apache/poi/xwpf/TestXWPFBugs.java
Modified: poi/site/src/documentation/content/xdocs/encryption.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/encryption.xml?rev=1553336&r1=1553335&r2=1553336&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/encryption.xml (original)
+++ poi/site/src/documentation/content/xdocs/encryption.xml Tue Dec 24 23:13:21 2013
@@ -46,7 +46,7 @@
</p>
</section>
- <section><title>XML-based formats</title>
+ <section><title>XML-based formats - Decryption</title>
<p>XML-based formats are stored in OLE-package stream "EncryptedPackage". Use org.apache.poi.poifs.crypt.Decryptor
to decode file:</p>
@@ -70,6 +70,31 @@ try {
<p>If you want to read file encrypted with build-in password, use Decryptor.DEFAULT_PASSWORD.</p>
</section>
+
+ <section><title>XML-based formats - Encryption</title>
+ <p>Encrypting a file is similar to the above decryption process. Basically you'll need to choose between
+ <link href="http://msdn.microsoft.com/en-us/library/dd952186(v=office.12).aspx">standard and agile encryption</link>.
+ Apart of the CipherMode, the EncryptionInfo class provides further parameters to specify the cipher and
+ hashing algorithm to be used.</p>
+
+ <source>
+POIFSFileSystem fs = new POIFSFileSystem();
+EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile);
+// EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile, CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null);
+
+Encryptor enc = info.getEncryptor();
+enc.confirmPassword("foobaa");
+
+OPCPackage opc = OPCPackage.open(new File("..."), PackageAccess.READ_WRITE);
+OutputStream os = enc.getDataStream(fs);
+opc.save(os);
+opc.close();
+
+FileOutputStream fos = new FileOutputStream("...");
+fs.writeFilesystem(fos);
+fos.close();
+ </source>
+ </section>
</body>
<footer>
Modified: poi/trunk/.classpath
URL: http://svn.apache.org/viewvc/poi/trunk/.classpath?rev=1553336&r1=1553335&r2=1553336&view=diff
==============================================================================
--- poi/trunk/.classpath (original)
+++ poi/trunk/.classpath Tue Dec 24 23:13:21 2013
@@ -24,6 +24,7 @@
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
<classpathentry kind="lib" path="lib/junit-4.11.jar"/>
<classpathentry kind="lib" path="ooxml-lib/ooxml-schemas-1.1.jar" sourcepath="ooxml-lib/ooxml-schemas-src-1.1.jar"/>
+ <classpathentry kind="lib" path="ooxml-lib/ooxml-encryption-1.1.jar" sourcepath="ooxml-lib/ooxml-encryption-src-1.1.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="build/eclipse"/>
</classpath>
Modified: poi/trunk/build.xml
URL: http://svn.apache.org/viewvc/poi/trunk/build.xml?rev=1553336&r1=1553335&r2=1553336&view=diff
==============================================================================
--- poi/trunk/build.xml (original)
+++ poi/trunk/build.xml Tue Dec 24 23:13:21 2013
@@ -116,6 +116,7 @@ under the License.
<property name="ooxml.output.test.dir" location="build/ooxml-test-classes"/>
<property name="ooxml.testokfile" location="build/ooxml-testokfile.txt"/>
<property name="ooxml.lite.output.dir" location="build/ooxml-lite-classes"/>
+ <property name="ooxml.encryption.xsd.dir" location="src/ooxml/resources/org/apache/poi/poifs/crypt"/>
<!-- Excelant: -->
<property name="excelant.resource.dir" value="src/excelant/resources"/>
@@ -167,6 +168,11 @@ under the License.
<property name="ooxml.xsds.src.jar" location="${ooxml.lib}/ooxml-schemas-src-1.1.jar"/>
<property name="ooxml.xsds.jar" location="${ooxml.lib}/ooxml-schemas-1.1.jar"/>
+ <property name="ooxml.encryption.src.dir" location="build/ooxml-encryption-src"/>
+ <property name="ooxml.encryption.src.jar" location="${ooxml.lib}/ooxml-encryption-src-1.1.jar"/>
+ <property name="ooxml.encryption.jar" location="${ooxml.lib}/ooxml-encryption-1.1.jar"/>
+
+
<property name="maven.ooxml.xsds.version.id" value="1.0"/>
<property name="maven.ooxml.xsds.jar" value="ooxml-schemas-${maven.ooxml.xsds.version.id}.jar"/>
@@ -210,6 +216,7 @@ under the License.
<path refid="main.classpath"/>
<pathelement location="${main.output.dir}"/>
<pathelement location="${scratchpad.output.dir}"/>
+ <pathelement location="${ooxml.encryption.jar}"/>
</path>
<path id="test.classpath">
@@ -436,9 +443,13 @@ under the License.
<target name="check-compiled-ooxml-xsds" depends="fetch-ooxml-xsds">
<condition property="ooxml-compiled-xsds.present">
<or>
- <and>
- <available file="${ooxml.xsds.jar}"/>
- </and>
+ <available file="${ooxml.xsds.jar}"/>
+ <isset property="disconnected"/>
+ </or>
+ </condition>
+ <condition property="ooxml-compiled-encryption-xsds.present">
+ <or>
+ <available file="${ooxml.encryption.jar}"/>
<isset property="disconnected"/>
</or>
</condition>
@@ -485,6 +496,40 @@ under the License.
/>
</target>
+ <target name="compile-ooxml-encryption-xsds" unless="ooxml-compiled-encryption-xsds.present"
+ depends="check-jars,fetch-jars,check-compiled-ooxml-xsds"
+ description="Compiles the OOXML encryption xsd files into XmlBeans">
+ <taskdef name="xmlbean"
+ classname="org.apache.xmlbeans.impl.tool.XMLBean"
+ classpath="${ooxml.xmlbeans.jar}:${ooxml.jsr173.jar}"/>
+
+ <!-- We need a fair amount of memory to compile the xml schema, -->
+ <!-- but limit it in case it goes wrong! -->
+ <!-- Pick the right amount based on 32 vs 64 bit jvm -->
+ <condition property="ooxml.memory" value="768m" else="512m">
+ <equals arg1="${sun.arch.data.model}" arg2="64" />
+ </condition>
+
+ <xmlbean
+ schema="${ooxml.encryption.xsd.dir}"
+ srcgendir="${ooxml.encryption.src.dir}"
+ optimize="yes"
+ destfile="${ooxml.encryption.jar}"
+ javasource="1.5"
+ failonerror="true"
+ fork="true"
+ memoryMaximumSize="${ooxml.memory}"
+ >
+ <classpath refid="ooxml.classpath"/>
+ </xmlbean>
+
+ <!-- Now make a jar of the schema sources -->
+ <jar
+ basedir="${ooxml.encryption.src.dir}"
+ destfile="${ooxml.encryption.src.jar}"
+ />
+ </target>
+
<target name="compile" depends="init, compile-main,
compile-scratchpad, compile-examples, compile-excelant"
description="Compiles the POI main classes, scratchpad and examples"/>
@@ -571,7 +616,7 @@ under the License.
</copy>
</target>
- <target name="compile-ooxml" depends="compile-main,compile-scratchpad,compile-ooxml-xsds">
+ <target name="compile-ooxml" depends="compile-main,compile-scratchpad,compile-ooxml-xsds,compile-ooxml-encryption-xsds">
<javac target="${jdk.version.class}"
source="${jdk.version.source}"
destdir="${ooxml.output.dir}"
@@ -853,12 +898,23 @@ under the License.
</target>
<target name="compile-ooxml-lite" depends="compile-ooxml">
+ <property name="ooxml.lite-merged.dir" location="build/ooxml-lite-merged"/>
+ <mkdir dir="${ooxml.lite-merged.dir}"/>
+
+ <jar destfile="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar">
+ <zipfileset includes="**/*" src="${ooxml.xsds.jar}"/>
+ <zipfileset includes="**/*" src="${ooxml.encryption.jar}"/>
+ </jar>
+
<java classname="org.apache.poi.util.OOXMLLite" fork="yes">
+ <classpath>
+ <pathelement path="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar"/>
+ </classpath>
<classpath refid="test.ooxml.classpath"/>
<syspropertyset refid="junit.properties"/>
<jvmarg value="${poi.test.locale}"/>
<arg value="-ooxml"/>
- <arg value="${ooxml.xsds.jar}"/>
+ <arg value="${ooxml.lite-merged.dir}/ooxml-lite-merged.jar"/>
<arg value="-test"/>
<arg value="${ooxml.output.test.dir}"/>
<arg value="-dest"/>
@@ -951,7 +1007,7 @@ under the License.
description="Generates the API documentation">
<javadoc verbose="false" author="true" destdir="${apidocs.report.dir}"
windowtitle="POI API Documentation" use="true" version="true"
- maxmemory="256M" additionalparam="-notimestamp">
+ maxmemory="384M" additionalparam="-notimestamp">
<packageset dir="${main.src}" defaultexcludes="yes">
<include name="org/apache/poi/**"/>
Modified: poi/trunk/src/java/org/apache/poi/EncryptedDocumentException.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/EncryptedDocumentException.java?rev=1553336&r1=1553335&r2=1553336&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/EncryptedDocumentException.java (original)
+++ poi/trunk/src/java/org/apache/poi/EncryptedDocumentException.java Tue Dec 24 23:13:21 2013
@@ -16,9 +16,18 @@
==================================================================== */
package org.apache.poi;
+@SuppressWarnings("serial")
public class EncryptedDocumentException extends IllegalStateException
{
public EncryptedDocumentException(String s) {
super(s);
}
+
+ public EncryptedDocumentException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public EncryptedDocumentException(Throwable cause) {
+ super(cause);
+ }
}
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/ChainingMode.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/ChainingMode.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/ChainingMode.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/ChainingMode.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,33 @@
+/* ====================================================================
+ 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;
+
+public enum ChainingMode {
+ // ecb - only for standard encryption
+ ecb("ECB", 1),
+ cbc("CBC", 2),
+ /* Cipher feedback chaining (CFB), with an 8-bit window */
+ cfb("CFB8", 3);
+
+ public final String jceId;
+ public final int ecmaId;
+ ChainingMode(String jceId, int ecmaId) {
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherAlgorithm.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,79 @@
+/* ====================================================================
+ 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.EncryptedDocumentException;
+
+public enum CipherAlgorithm {
+ // key size for rc4: 0x00000028 - 0x00000080 (inclusive) with 8-bit increments
+ // no block size, because its a streaming cipher
+ rc4(CipherProvider.rc4, "RC4", 0x6801, 0x40, new int[]{0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,0x80}, -1, 20, "RC4", false),
+ // aes has always a block size of 128 - only its keysize may vary
+ aes128(CipherProvider.aes, "AES", 0x660E, 128, new int[]{128}, 16, 32, "AES", false),
+ aes192(CipherProvider.aes, "AES", 0x660F, 192, new int[]{192}, 16, 32, "AES", false),
+ aes256(CipherProvider.aes, "AES", 0x6610, 256, new int[]{256}, 16, 32, "AES", false),
+ rc2(null, "RC2", -1, 0x80, new int[]{0x28,0x30,0x38,0x40,0x48,0x50,0x58,0x60,0x68,0x70,0x78,0x80}, 8, 20, "RC2", false),
+ des(null, "DES", -1, 64, new int[]{64}, 8/*for 56-bit*/, 32, "DES", false),
+ // desx is not supported. Not sure, if it can be simulated by des3 somehow
+ des3(null, "DESede", -1, 192, new int[]{192}, 8, 32, "3DES", false),
+ // need bouncycastle provider for this one ...
+ // see http://stackoverflow.com/questions/4436397/3des-des-encryption-using-the-jce-generating-an-acceptable-key
+ des3_112(null, "DESede", -1, 128, new int[]{128}, 8, 32, "3DES_112", true),
+ ;
+
+ public final CipherProvider provider;
+ public final String jceId;
+ public final int ecmaId;
+ public final int defaultKeySize;
+ public final int allowedKeySize[];
+ public final int blockSize;
+ public final int encryptedVerifierHashLength;
+ public final String xmlId;
+ public final boolean needsBouncyCastle;
+
+ CipherAlgorithm(CipherProvider provider, String jceId, int ecmaId, int defaultKeySize, int allowedKeySize[], int blockSize, int encryptedVerifierHashLength, String xmlId, boolean needsBouncyCastle) {
+ this.provider = provider;
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ this.defaultKeySize = defaultKeySize;
+ this.allowedKeySize = allowedKeySize;
+ this.blockSize = blockSize;
+ this.encryptedVerifierHashLength = encryptedVerifierHashLength;
+ this.xmlId = xmlId;
+ this.needsBouncyCastle = needsBouncyCastle;
+ }
+
+ public static CipherAlgorithm fromEcmaId(int ecmaId) {
+ for (CipherAlgorithm ca : CipherAlgorithm.values()) {
+ if (ca.ecmaId == ecmaId) return ca;
+ }
+ throw new EncryptedDocumentException("cipher algorithm not found");
+ }
+
+ public static CipherAlgorithm fromXmlId(String xmlId, int keySize) {
+ for (CipherAlgorithm ca : CipherAlgorithm.values()) {
+ if (!ca.xmlId.equals(xmlId)) continue;
+ for (int ks : ca.allowedKeySize) {
+ if (ks == keySize) return ca;
+ }
+ }
+ throw new EncryptedDocumentException("cipher algorithm not found");
+ }
+
+
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherProvider.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherProvider.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherProvider.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/CipherProvider.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,39 @@
+/* ====================================================================
+ 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.EncryptedDocumentException;
+
+public enum CipherProvider {
+ rc4("RC4", 1),
+ aes("AES", 0x18);
+
+ public static CipherProvider fromEcmaId(int ecmaId) {
+ for (CipherProvider cp : CipherProvider.values()) {
+ if (cp.ecmaId == ecmaId) return cp;
+ }
+ throw new EncryptedDocumentException("cipher provider not found");
+ }
+
+ public final String jceId;
+ public final int ecmaId;
+ CipherProvider(String jceId, int ecmaId) {
+ this.jceId = jceId;
+ this.ecmaId = ecmaId;
+ }
+}
\ No newline at end of file
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/CryptoFunctions.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,258 @@
+/* ====================================================================
+ 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.nio.charset.Charset;
+import java.security.DigestException;
+import java.security.GeneralSecurityException;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.util.LittleEndian;
+import org.apache.poi.util.LittleEndianConsts;
+
+/**
+ * Helper functions used for standard and agile encryption
+ */
+public class CryptoFunctions {
+ /**
+ * 2.3.4.7 ECMA-376 Document Encryption Key Generation (Standard Encryption)
+ * 2.3.4.11 Encryption Key Generation (Agile Encryption)
+ *
+ * The encryption key for ECMA-376 document encryption [ECMA-376] using agile encryption MUST be
+ * generated by using the following method, which is derived from PKCS #5: Password-Based
+ * Cryptography Version 2.0 [RFC2898].
+ *
+ * Let H() be a hashing algorithm as determined by the PasswordKeyEncryptor.hashAlgorithm
+ * element, H_n be the hash data of the n-th iteration, and a plus sign (+) represent concatenation. The
+ * password MUST be provided as an array of Unicode characters. Limitations on the length of the
+ * password and the characters used by the password are implementation-dependent. The initial
+ * password hash is generated as follows:
+ *
+ * - H_0 = H(salt + password)
+ *
+ * The salt used MUST be generated randomly. The salt MUST be stored in the
+ * PasswordKeyEncryptor.saltValue element contained within the \EncryptionInfo stream (1) as
+ * specified in section 2.3.4.10. The hash is then iterated by using the following approach:
+ *
+ * - H_n = H(iterator + H_n-1)
+ *
+ * where iterator is an unsigned 32-bit value that is initially set to 0x00000000 and then incremented
+ * monotonically on each iteration until PasswordKey.spinCount iterations have been performed.
+ * The value of iterator on the last iteration MUST be one less than PasswordKey.spinCount.
+ *
+ * For POI, H_final will be calculated by {@link generateKey()}
+ *
+ * @param password
+ * @param hashAlgorithm
+ * @param salt
+ * @param spinCount
+ * @return
+ */
+ public static byte[] hashPassword(String password, HashAlgorithm hashAlgorithm, byte salt[], int spinCount) {
+ // If no password was given, use the default
+ if (password == null) {
+ password = Decryptor.DEFAULT_PASSWORD;
+ }
+
+ MessageDigest hashAlg = getMessageDigest(hashAlgorithm);
+
+ hashAlg.update(salt);
+ byte[] hash = hashAlg.digest(getUtf16LeString(password));
+ byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
+
+ try {
+ for (int i = 0; i < spinCount; i++) {
+ LittleEndian.putInt(iterator, 0, i);
+ hashAlg.reset();
+ hashAlg.update(iterator);
+ hashAlg.update(hash);
+ hashAlg.digest(hash, 0, hash.length); // don't create hash buffer everytime new
+ }
+ } catch (DigestException e) {
+ throw new EncryptedDocumentException("error in password hashing");
+ }
+
+ return hash;
+ }
+
+ /**
+ * 2.3.4.12 Initialization Vector Generation (Agile Encryption)
+ *
+ * Initialization vectors are used in all cases for agile encryption. An initialization vector MUST be
+ * generated by using the following method, where H() is a hash function that MUST be the same as
+ * specified in section 2.3.4.11 and a plus sign (+) represents concatenation:
+ * 1. If a blockKey is provided, let IV be a hash of the KeySalt and the following value:
+ * blockKey: IV = H(KeySalt + blockKey)
+ * 2. If a blockKey is not provided, let IV be equal to the following value:
+ * KeySalt:IV = KeySalt.
+ * 3. If the number of bytes in the value of IV is less than the the value of the blockSize attribute
+ * corresponding to the cipherAlgorithm attribute, pad the array of bytes by appending 0x36 until
+ * the array is blockSize bytes. If the array of bytes is larger than blockSize bytes, truncate the
+ * array to blockSize bytes.
+ **/
+ public static byte[] generateIv(HashAlgorithm hashAlgorithm, byte[] salt, byte[] blockKey, int blockSize) {
+ byte iv[] = salt;
+ if (blockKey != null) {
+ MessageDigest hashAlgo = getMessageDigest(hashAlgorithm);
+ hashAlgo.update(salt);
+ iv = hashAlgo.digest(blockKey);
+ }
+ return getBlock36(iv, blockSize);
+ }
+
+ /**
+ * 2.3.4.11 Encryption Key Generation (Agile Encryption)
+ *
+ * ... continued ...
+ *
+ * The final hash data that is used for an encryption key is then generated by using the following
+ * method:
+ *
+ * - H_final = H(H_n + blockKey)
+ *
+ * where blockKey represents an array of bytes used to prevent two different blocks from encrypting
+ * to the same cipher text.
+ *
+ * If the size of the resulting H_final is smaller than that of PasswordKeyEncryptor.keyBits, the key
+ * MUST be padded by appending bytes with a value of 0x36. If the hash value is larger in size than
+ * PasswordKeyEncryptor.keyBits, the key is obtained by truncating the hash value.
+ *
+ * @param passwordHash
+ * @param hashAlgorithm
+ * @param blockKey
+ * @param keySize
+ * @return
+ */
+ public static byte[] generateKey(byte[] passwordHash, HashAlgorithm hashAlgorithm, byte[] blockKey, int keySize) {
+ MessageDigest hashAlgo = getMessageDigest(hashAlgorithm);
+ hashAlgo.update(passwordHash);
+ byte[] key = hashAlgo.digest(blockKey);
+ return getBlock36(key, keySize);
+ }
+
+ public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode) {
+ return getCipher(key, cipherAlgorithm, chain, vec, cipherMode, null);
+ }
+
+ /**
+ *
+ *
+ * @param key
+ * @param chain
+ * @param vec
+ * @param cipherMode Cipher.DECRYPT_MODE or Cipher.ENCRYPT_MODE
+ * @return
+ * @throws GeneralSecurityException
+ */
+ public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
+ int keySizeInBytes = key.getEncoded().length;
+ if (padding == null) padding = "NoPadding";
+
+ try {
+ // Ensure the JCE policies files allow for this sized key
+ if (Cipher.getMaxAllowedKeyLength(key.getAlgorithm()) < keySizeInBytes*8) {
+ throw new EncryptedDocumentException("Export Restrictions in place - please install JCE Unlimited Strength Jurisdiction Policy files");
+ }
+
+ Cipher cipher;
+ if (cipherAlgorithm.needsBouncyCastle) {
+ registerBouncyCastle();
+ cipher = Cipher.getInstance(key.getAlgorithm() + "/" + chain.jceId + "/" + padding, "BC");
+ } else {
+ cipher = Cipher.getInstance(key.getAlgorithm() + "/" + chain.jceId + "/" + padding);
+ }
+
+ if (vec == null) {
+ cipher.init(cipherMode, key);
+ } else {
+ IvParameterSpec iv = new IvParameterSpec(vec);
+ cipher.init(cipherMode, key, iv);
+ }
+ return cipher;
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ }
+
+ public static byte[] getBlock36(byte[] hash, int size) {
+ return getBlockX(hash, size, (byte)0x36);
+ }
+
+ public static byte[] getBlock0(byte[] hash, int size) {
+ return getBlockX(hash, size, (byte)0);
+ }
+
+ private static byte[] getBlockX(byte[] hash, int size, byte fill) {
+ if (hash.length == size) return hash;
+
+ byte[] result = new byte[size];
+ Arrays.fill(result, fill);
+ System.arraycopy(hash, 0, result, 0, Math.min(result.length, hash.length));
+ return result;
+ }
+
+ public static byte[] getUtf16LeString(String str) {
+ Charset cs = Charset.forName("UTF-16LE");
+ return str.getBytes(cs);
+ }
+
+ public static MessageDigest getMessageDigest(HashAlgorithm hashAlgorithm) {
+ try {
+ if (hashAlgorithm.needsBouncyCastle) {
+ registerBouncyCastle();
+ return MessageDigest.getInstance(hashAlgorithm.jceId, "BC");
+ } else {
+ return MessageDigest.getInstance(hashAlgorithm.jceId);
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("hash algo not supported", e);
+ }
+ }
+
+ public static Mac getMac(HashAlgorithm hashAlgorithm) {
+ try {
+ if (hashAlgorithm.needsBouncyCastle) {
+ registerBouncyCastle();
+ return Mac.getInstance(hashAlgorithm.jceHmacId, "BC");
+ } else {
+ return Mac.getInstance(hashAlgorithm.jceHmacId);
+ }
+ } catch (GeneralSecurityException e) {
+ throw new EncryptedDocumentException("hmac algo not supported", e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static void registerBouncyCastle() {
+ if (Security.getProvider("BC") != null) return;
+ try {
+ Class<Provider> clazz = (Class<Provider>)Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
+ Security.addProvider(clazz.newInstance());
+ } catch (Exception e) {
+ throw new EncryptedDocumentException("Only the BouncyCastle provider supports your encryption settings - please add it to the classpath.");
+ }
+ }
+}
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/DataSpaceMapUtils.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,365 @@
+/* ====================================================================
+ 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.IOException;
+import java.nio.charset.Charset;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.crypt.standard.EncryptionRecord;
+import org.apache.poi.poifs.filesystem.DirectoryEntry;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSWriterEvent;
+import org.apache.poi.poifs.filesystem.POIFSWriterListener;
+import org.apache.poi.util.LittleEndianByteArrayOutputStream;
+import org.apache.poi.util.LittleEndianConsts;
+import org.apache.poi.util.LittleEndianInput;
+import org.apache.poi.util.LittleEndianOutput;
+
+public class DataSpaceMapUtils {
+ public static void addDefaultDataSpace(DirectoryEntry dir) throws IOException {
+ DataSpaceMapEntry dsme = new DataSpaceMapEntry(
+ new int[]{ 0 }
+ , new String[]{ "EncryptedPackage" }
+ , "StrongEncryptionDataSpace"
+ );
+ DataSpaceMap dsm = new DataSpaceMap(new DataSpaceMapEntry[]{dsme});
+ createEncryptionEntry(dir, "\u0006DataSpaces/DataSpaceMap", dsm);
+
+ DataSpaceDefinition dsd = new DataSpaceDefinition(new String[]{ "StrongEncryptionTransform" });
+ createEncryptionEntry(dir, "\u0006DataSpaces/DataSpaceInfo/StrongEncryptionDataSpace", dsd);
+
+ TransformInfoHeader tih = new TransformInfoHeader(
+ 1
+ , "{FF9A3F03-56EF-4613-BDD5-5A41C1D07246}"
+ , "Microsoft.Container.EncryptionTransform"
+ , 1, 0, 1, 0, 1, 0
+ );
+ IRMDSTransformInfo irm = new IRMDSTransformInfo(tih, 0, null);
+ createEncryptionEntry(dir, "\u0006DataSpaces/TransformInfo/StrongEncryptionTransform/\u0006Primary", irm);
+
+ DataSpaceVersionInfo dsvi = new DataSpaceVersionInfo("Microsoft.Container.DataSpaces", 1, 0, 1, 0, 1, 0);
+ createEncryptionEntry(dir, "\u0006DataSpaces/Version", dsvi);
+ }
+
+ public static DocumentEntry createEncryptionEntry(DirectoryEntry dir, String path, EncryptionRecord out) throws IOException {
+ String parts[] = path.split("/");
+ for (int i=0; i<parts.length-1; i++) {
+ dir = dir.hasEntry(parts[i])
+ ? (DirectoryEntry)dir.getEntry(parts[i])
+ : dir.createDirectory(parts[i]);
+ }
+
+ final byte buf[] = new byte[5000];
+ LittleEndianByteArrayOutputStream bos = new LittleEndianByteArrayOutputStream(buf, 0);
+ out.write(bos);
+
+ return dir.createDocument(parts[parts.length-1], bos.getWriteIndex(), new POIFSWriterListener(){
+ public void processPOIFSWriterEvent(POIFSWriterEvent event) {
+ try {
+ event.getStream().write(buf, 0, event.getLimit());
+ } catch (IOException e) {
+ throw new EncryptedDocumentException(e);
+ }
+ }
+ });
+ }
+
+ public static class DataSpaceMap implements EncryptionRecord {
+ DataSpaceMapEntry entries[];
+
+ public DataSpaceMap(DataSpaceMapEntry entries[]) {
+ this.entries = entries;
+ }
+
+ public DataSpaceMap(LittleEndianInput is) {
+ @SuppressWarnings("unused")
+ int length = is.readInt();
+ int entryCount = is.readInt();
+ entries = new DataSpaceMapEntry[entryCount];
+ for (int i=0; i<entryCount; i++) {
+ entries[i] = new DataSpaceMapEntry(is);
+ }
+ }
+
+ public void write(LittleEndianByteArrayOutputStream os) {
+ os.writeInt(8);
+ os.writeInt(entries.length);
+ for (DataSpaceMapEntry dsme : entries) {
+ dsme.write(os);
+ }
+ }
+ }
+
+ public static class DataSpaceMapEntry implements EncryptionRecord {
+ int referenceComponentType[];
+ String referenceComponent[];
+ String dataSpaceName;
+
+ public DataSpaceMapEntry(int referenceComponentType[], String referenceComponent[], String dataSpaceName) {
+ this.referenceComponentType = referenceComponentType;
+ this.referenceComponent = referenceComponent;
+ this.dataSpaceName = dataSpaceName;
+ }
+
+ public DataSpaceMapEntry(LittleEndianInput is) {
+ @SuppressWarnings("unused")
+ int length = is.readInt();
+ int referenceComponentCount = is.readInt();
+ referenceComponentType = new int[referenceComponentCount];
+ referenceComponent = new String[referenceComponentCount];
+ for (int i=0; i<referenceComponentCount; i++) {
+ referenceComponentType[i] = is.readInt();
+ referenceComponent[i] = readUnicodeLPP4(is);
+ }
+ dataSpaceName = readUnicodeLPP4(is);
+ }
+
+ public void write(LittleEndianByteArrayOutputStream os) {
+ int start = os.getWriteIndex();
+ LittleEndianOutput sizeOut = os.createDelayedOutput(LittleEndianConsts.INT_SIZE);
+ os.writeInt(referenceComponent.length);
+ for (int i=0; i<referenceComponent.length; i++) {
+ os.writeInt(referenceComponentType[i]);
+ writeUnicodeLPP4(os, referenceComponent[i]);
+ }
+ writeUnicodeLPP4(os, dataSpaceName);
+ sizeOut.writeInt(os.getWriteIndex()-start);
+ }
+ }
+
+ public static class DataSpaceDefinition implements EncryptionRecord {
+ String transformer[];
+
+ public DataSpaceDefinition(String transformer[]) {
+ this.transformer = transformer;
+ }
+
+ public DataSpaceDefinition(LittleEndianInput is) {
+ @SuppressWarnings("unused")
+ int headerLength = is.readInt();
+ int transformReferenceCount = is.readInt();
+ transformer = new String[transformReferenceCount];
+ for (int i=0; i<transformReferenceCount; i++) {
+ transformer[i] = readUnicodeLPP4(is);
+ }
+ }
+
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ bos.writeInt(8);
+ bos.writeInt(transformer.length);
+ for (String str : transformer) {
+ writeUnicodeLPP4(bos, str);
+ }
+ }
+ }
+
+ public static class IRMDSTransformInfo implements EncryptionRecord {
+ TransformInfoHeader transformInfoHeader;
+ int extensibilityHeader;
+ String xrMLLicense;
+
+ public IRMDSTransformInfo(TransformInfoHeader transformInfoHeader, int extensibilityHeader, String xrMLLicense) {
+ this.transformInfoHeader = transformInfoHeader;
+ this.extensibilityHeader = extensibilityHeader;
+ this.xrMLLicense = xrMLLicense;
+ }
+
+ public IRMDSTransformInfo(LittleEndianInput is) {
+ transformInfoHeader = new TransformInfoHeader(is);
+ extensibilityHeader = is.readInt();
+ xrMLLicense = readUtf8LPP4(is);
+ // finish with 0x04 (int) ???
+ }
+
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ transformInfoHeader.write(bos);
+ bos.writeInt(extensibilityHeader);
+ writeUtf8LPP4(bos, xrMLLicense);
+ bos.writeInt(4); // where does this 4 come from???
+ }
+ }
+
+ public static class TransformInfoHeader implements EncryptionRecord {
+ int transformType;
+ String transformerId;
+ String transformerName;
+ int readerVersionMajor = 1, readerVersionMinor = 0;
+ int updaterVersionMajor = 1, updaterVersionMinor = 0;
+ int writerVersionMajor = 1, writerVersionMinor = 0;
+
+ public TransformInfoHeader(
+ int transformType,
+ String transformerId,
+ String transformerName,
+ int readerVersionMajor, int readerVersionMinor,
+ int updaterVersionMajor, int updaterVersionMinor,
+ int writerVersionMajor, int writerVersionMinor
+ ){
+ this.transformType = transformType;
+ this.transformerId = transformerId;
+ this.transformerName = transformerName;
+ this.readerVersionMajor = readerVersionMajor;
+ this.readerVersionMinor = readerVersionMinor;
+ this.updaterVersionMajor = updaterVersionMajor;
+ this.updaterVersionMinor = updaterVersionMinor;
+ this.writerVersionMajor = writerVersionMajor;
+ this.writerVersionMinor = writerVersionMinor;
+ }
+
+ public TransformInfoHeader(LittleEndianInput is) {
+ @SuppressWarnings("unused")
+ int length = is.readInt();
+ transformType = is.readInt();
+ transformerId = readUnicodeLPP4(is);
+ transformerName = readUnicodeLPP4(is);
+ readerVersionMajor = is.readShort();
+ readerVersionMinor = is.readShort();
+ updaterVersionMajor = is.readShort();
+ updaterVersionMinor = is.readShort();
+ writerVersionMajor = is.readShort();
+ writerVersionMinor = is.readShort();
+ }
+
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ int start = bos.getWriteIndex();
+ LittleEndianOutput sizeOut = bos.createDelayedOutput(LittleEndianConsts.INT_SIZE);
+ bos.writeInt(transformType);
+ writeUnicodeLPP4(bos, transformerId);
+ sizeOut.writeInt(bos.getWriteIndex()-start);
+ writeUnicodeLPP4(bos, transformerName);
+ bos.writeShort(readerVersionMajor);
+ bos.writeShort(readerVersionMinor);
+ bos.writeShort(updaterVersionMajor);
+ bos.writeShort(updaterVersionMinor);
+ bos.writeShort(writerVersionMajor);
+ bos.writeShort(writerVersionMinor);
+ }
+ }
+
+ public static class DataSpaceVersionInfo implements EncryptionRecord {
+ String featureIdentifier;
+ int readerVersionMajor = 1, readerVersionMinor = 0;
+ int updaterVersionMajor = 1, updaterVersionMinor = 0;
+ int writerVersionMajor = 1, writerVersionMinor = 0;
+
+ public DataSpaceVersionInfo(LittleEndianInput is) {
+ featureIdentifier = readUnicodeLPP4(is);
+ readerVersionMajor = is.readShort();
+ readerVersionMinor = is.readShort();
+ updaterVersionMajor = is.readShort();
+ updaterVersionMinor = is.readShort();
+ writerVersionMajor = is.readShort();
+ writerVersionMinor = is.readShort();
+ }
+
+ public DataSpaceVersionInfo(
+ String featureIdentifier,
+ int readerVersionMajor, int readerVersionMinor,
+ int updaterVersionMajor, int updaterVersionMinor,
+ int writerVersionMajor, int writerVersionMinor
+ ){
+ this.featureIdentifier = featureIdentifier;
+ this.readerVersionMajor = readerVersionMajor;
+ this.readerVersionMinor = readerVersionMinor;
+ this.updaterVersionMajor = updaterVersionMajor;
+ this.updaterVersionMinor = updaterVersionMinor;
+ this.writerVersionMajor = writerVersionMajor;
+ this.writerVersionMinor = writerVersionMinor;
+ }
+
+ public void write(LittleEndianByteArrayOutputStream bos) {
+ writeUnicodeLPP4(bos, featureIdentifier);
+ bos.writeShort(readerVersionMajor);
+ bos.writeShort(readerVersionMinor);
+ bos.writeShort(updaterVersionMajor);
+ bos.writeShort(updaterVersionMinor);
+ bos.writeShort(writerVersionMajor);
+ bos.writeShort(writerVersionMinor);
+ }
+ }
+
+ public static String readUnicodeLPP4(LittleEndianInput is) {
+ Charset cs = Charset.forName("UTF-16LE");
+ int length = is.readInt();
+ byte data[] = new byte[length];
+ is.readFully(data);
+ if (length%4==2) {
+ // Padding (variable): A set of bytes that MUST be of the correct size such that the size of the
+ // UNICODE-LP-P4 structure is a multiple of 4 bytes. If Padding is present, it MUST be exactly
+ // 2 bytes long, and each byte MUST be 0x00.
+ is.readShort();
+ }
+ return new String(data, 0, data.length, cs);
+ }
+
+ public static void writeUnicodeLPP4(LittleEndianOutput os, String str) {
+ Charset cs = Charset.forName("UTF-16LE");
+ byte buf[] = str.getBytes(cs);
+ os.writeInt(buf.length);
+ os.write(buf);
+ if (buf.length%4==2) {
+ os.writeShort(0);
+ }
+ }
+
+ public static String readUtf8LPP4(LittleEndianInput is) {
+ int length = is.readInt();
+ if (length == 0 || length == 4) {
+ @SuppressWarnings("unused")
+ int skip = is.readInt(); // ignore
+ return length == 0 ? null : "";
+ }
+
+ byte data[] = new byte[length];
+ is.readFully(data);
+
+ // Padding (variable): A set of bytes that MUST be of correct size such that the size of the UTF-8-LP-P4
+ // structure is a multiple of 4 bytes. If Padding is present, each byte MUST be 0x00. If
+ // the length is exactly 0x00000000, this specifies a null string, and the entire structure uses
+ // exactly 4 bytes. If the length is exactly 0x00000004, this specifies an empty string, and the
+ // entire structure also uses exactly 4 bytes
+ int scratchedBytes = length%4;
+ if (scratchedBytes > 0) {
+ for (int i=0; i<(4-scratchedBytes); i++) {
+ is.readByte();
+ }
+ }
+ Charset cs = Charset.forName("UTF-8");
+ return new String(data, 0, data.length, cs);
+ }
+
+ public static void writeUtf8LPP4(LittleEndianOutput os, String str) {
+ if (str == null || "".equals(str)) {
+ os.writeInt(str == null ? 0 : 4);
+ os.writeInt(0);
+ } else {
+ Charset cs = Charset.forName("UTF-8");
+ byte buf[] = str.getBytes(cs);
+ os.writeInt(buf.length);
+ os.write(buf);
+ int scratchBytes = buf.length%4;
+ if (scratchBytes > 0) {
+ for (int i=0; i<(4-scratchBytes); i++) {
+ os.writeByte(0);
+ }
+ }
+ }
+ }
+
+}
Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java?rev=1553336&r1=1553335&r2=1553336&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/Decryptor.java Tue Dec 24 23:13:21 2013
@@ -18,21 +18,26 @@ package org.apache.poi.poifs.crypt;
import java.io.IOException;
import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
-import java.security.DigestException;
-import java.security.MessageDigest;
import java.security.GeneralSecurityException;
-import java.security.NoSuchAlgorithmException;
+
+import javax.crypto.SecretKey;
+
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import org.apache.poi.poifs.filesystem.DirectoryNode;
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.LittleEndianConsts;
public abstract class Decryptor {
public static final String DEFAULT_PASSWORD="VelvetSweatshop";
+
+ protected final EncryptionInfo info;
+ private SecretKey secretKey;
+ private byte[] verifier, integrityHmacKey, integrityHmacValue;
+ protected Decryptor(EncryptionInfo info) {
+ this.info = info;
+ }
+
/**
* Return a stream with decrypted data.
* <p>
@@ -68,15 +73,11 @@ public abstract class Decryptor {
public abstract long getLength();
public static Decryptor getInstance(EncryptionInfo info) {
- int major = info.getVersionMajor();
- int minor = info.getVersionMinor();
-
- if (major == 4 && minor == 4)
- return new AgileDecryptor(info);
- else if (minor == 2 && (major == 3 || major == 4))
- return new EcmaDecryptor(info);
- else
+ Decryptor d = info.getDecryptor();
+ if (d == null) {
throw new EncryptedDocumentException("Unsupported version");
+ }
+ return d;
}
public InputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
@@ -86,40 +87,37 @@ public abstract class Decryptor {
public InputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException {
return getDataStream(fs.getRoot());
}
+
+ // for tests
+ public byte[] getVerifier() {
+ return verifier;
+ }
- protected byte[] hashPassword(EncryptionInfo info,
- String password) throws NoSuchAlgorithmException {
- // If no password was given, use the default
- if (password == null) {
- password = DEFAULT_PASSWORD;
- }
-
- byte[] pass;
- try {
- pass = password.getBytes("UTF-16LE");
- } catch (UnsupportedEncodingException e) {
- throw new EncryptedDocumentException("UTF16 not supported");
- }
+ public SecretKey getSecretKey() {
+ return secretKey;
+ }
+
+ public byte[] getIntegrityHmacKey() {
+ return integrityHmacKey;
+ }
- byte[] salt = info.getVerifier().getSalt();
-
- MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
- sha1.update(salt);
- byte[] hash = sha1.digest(pass);
- byte[] iterator = new byte[LittleEndianConsts.INT_SIZE];
-
- try {
- for (int i = 0; i < info.getVerifier().getSpinCount(); i++) {
- LittleEndian.putInt(iterator, 0, i);
- sha1.reset();
- sha1.update(iterator);
- sha1.update(hash);
- sha1.digest(hash, 0, hash.length); // don't create hash buffer everytime new
- }
- } catch (DigestException e) {
- throw new EncryptedDocumentException("error in password hashing");
- }
-
- return hash;
+ public byte[] getIntegrityHmacValue() {
+ return integrityHmacValue;
+ }
+
+ protected void setSecretKey(SecretKey secretKey) {
+ this.secretKey = secretKey;
+ }
+
+ protected void setVerifier(byte[] verifier) {
+ this.verifier = verifier;
+ }
+
+ protected void setIntegrityHmacKey(byte[] integrityHmacKey) {
+ this.integrityHmacKey = integrityHmacKey;
+ }
+
+ protected void setIntegrityHmacValue(byte[] integrityHmacValue) {
+ this.integrityHmacValue = integrityHmacValue;
}
}
\ No newline at end of file
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=1553336&r1=1553335&r2=1553336&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 Dec 24 23:13:21 2013
@@ -16,197 +16,148 @@
==================================================================== */
package org.apache.poi.poifs.crypt;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.util.LittleEndianConsts;
-import org.w3c.dom.NamedNodeMap;
/**
* Reads and processes OOXML Encryption Headers
* The constants are largely based on ZIP constants.
*/
-public class EncryptionHeader {
- public static final int ALGORITHM_RC4 = 0x6801;
- public static final int ALGORITHM_AES_128 = 0x660E;
- public static final int ALGORITHM_AES_192 = 0x660F;
- public static final int ALGORITHM_AES_256 = 0x6610;
-
- public static final int HASH_NONE = 0x0000;
- public static final int HASH_SHA1 = 0x8004;
- public static final int HASH_SHA256 = 0x800C;
- public static final int HASH_SHA384 = 0x800D;
- public static final int HASH_SHA512 = 0x800E;
-
- public static final int PROVIDER_RC4 = 1;
- public static final int PROVIDER_AES = 0x18;
-
- public static final int MODE_ECB = 1;
- public static final int MODE_CBC = 2;
- public static final int MODE_CFB = 3;
-
- private final int flags;
- private final int sizeExtra;
- private final int algorithm;
- private final int hashAlgorithm;
- private final int keySize;
- private final int blockSize;
- private final int providerType;
- private final int cipherMode;
- private final byte[] keySalt;
- private final String cspName;
-
- public EncryptionHeader(DocumentInputStream is) throws IOException {
- flags = is.readInt();
- sizeExtra = is.readInt();
- algorithm = is.readInt();
- hashAlgorithm = is.readInt();
- keySize = is.readInt();
- blockSize = keySize;
- providerType = is.readInt();
-
- is.readLong(); // skip reserved
-
- // CSPName may not always be specified
- // In some cases, the sale value of the EncryptionVerifier has the details
- is.mark(LittleEndianConsts.INT_SIZE+1);
- int checkForSalt = is.readInt();
- is.reset();
-
- if (checkForSalt == 16) {
- cspName = "";
- } else {
- StringBuilder builder = new StringBuilder();
- while (true) {
- char c = (char) is.readShort();
- if (c == 0) break;
- builder.append(c);
- }
- cspName = builder.toString();
- }
-
- cipherMode = MODE_ECB;
- keySalt = null;
- }
-
- public EncryptionHeader(String descriptor) throws IOException {
- NamedNodeMap keyData;
- try {
- ByteArrayInputStream is;
- is = new ByteArrayInputStream(descriptor.getBytes());
- keyData = DocumentBuilderFactory.newInstance()
- .newDocumentBuilder().parse(is)
- .getElementsByTagName("keyData").item(0).getAttributes();
- } catch (Exception e) {
- throw new EncryptedDocumentException("Unable to parse keyData");
- }
-
- keySize = Integer.parseInt(keyData.getNamedItem("keyBits")
- .getNodeValue());
- flags = 0;
- sizeExtra = 0;
- cspName = null;
-
- blockSize = Integer.parseInt(keyData.getNamedItem("blockSize").
- getNodeValue());
- String cipher = keyData.getNamedItem("cipherAlgorithm").getNodeValue();
-
- if ("AES".equals(cipher)) {
- providerType = PROVIDER_AES;
- switch (keySize) {
- case 128:
- algorithm = ALGORITHM_AES_128; break;
- case 192:
- algorithm = ALGORITHM_AES_192; break;
- case 256:
- algorithm = ALGORITHM_AES_256; break;
- default:
- throw new EncryptedDocumentException("Unsupported key length " + keySize);
- }
- } else {
- throw new EncryptedDocumentException("Unsupported cipher " + cipher);
- }
-
- String chaining = keyData.getNamedItem("cipherChaining").getNodeValue();
-
- if ("ChainingModeCBC".equals(chaining))
- cipherMode = MODE_CBC;
- else if ("ChainingModeCFB".equals(chaining))
- cipherMode = MODE_CFB;
- else
- throw new EncryptedDocumentException("Unsupported chaining mode " + chaining);
-
- String hashAlg = keyData.getNamedItem("hashAlgorithm").getNodeValue();
- int hashSize = Integer.parseInt(
- keyData.getNamedItem("hashSize").getNodeValue());
-
- if ("SHA1".equals(hashAlg) && hashSize == 20) {
- hashAlgorithm = HASH_SHA1;
- }
- else if ("SHA256".equals(hashAlg) && hashSize == 32) {
- hashAlgorithm = HASH_SHA256;
- }
- else if ("SHA384".equals(hashAlg) && hashSize == 64) {
- hashAlgorithm = HASH_SHA384;
- }
- else if ("SHA512".equals(hashAlg) && hashSize == 64) {
- hashAlgorithm = HASH_SHA512;
- }
- else {
- throw new EncryptedDocumentException("Unsupported hash algorithm: " +
- hashAlg + " @ " + hashSize + " bytes");
- }
-
- String salt = keyData.getNamedItem("saltValue").getNodeValue();
- int saltLength = Integer.parseInt(keyData.getNamedItem("saltSize")
- .getNodeValue());
- keySalt = Base64.decodeBase64(salt.getBytes());
- if (keySalt.length != saltLength)
- throw new EncryptedDocumentException("Invalid salt length");
- }
+public abstract class EncryptionHeader {
+ public static final int ALGORITHM_RC4 = CipherAlgorithm.rc4.ecmaId;
+ public static final int ALGORITHM_AES_128 = CipherAlgorithm.aes128.ecmaId;
+ public static final int ALGORITHM_AES_192 = CipherAlgorithm.aes192.ecmaId;
+ public static final int ALGORITHM_AES_256 = CipherAlgorithm.aes256.ecmaId;
+
+ public static final int HASH_NONE = HashAlgorithm.none.ecmaId;
+ public static final int HASH_SHA1 = HashAlgorithm.sha1.ecmaId;
+ public static final int HASH_SHA256 = HashAlgorithm.sha256.ecmaId;
+ public static final int HASH_SHA384 = HashAlgorithm.sha384.ecmaId;
+ public static final int HASH_SHA512 = HashAlgorithm.sha512.ecmaId;
+
+ public static final int PROVIDER_RC4 = CipherProvider.rc4.ecmaId;
+ public static final int PROVIDER_AES = CipherProvider.aes.ecmaId;
+
+ public static final int MODE_ECB = ChainingMode.ecb.ecmaId;
+ public static final int MODE_CBC = ChainingMode.cbc.ecmaId;
+ public static final int MODE_CFB = ChainingMode.cfb.ecmaId;
+
+ private int flags;
+ private int sizeExtra;
+ private CipherAlgorithm cipherAlgorithm;
+ private HashAlgorithm hashAlgorithm;
+ private int keyBits;
+ private int blockSize;
+ private CipherProvider providerType;
+ private ChainingMode chainingMode;
+ private byte[] keySalt;
+ private String cspName;
+
+ protected EncryptionHeader() {}
+ /**
+ * @deprecated use getChainingMode().ecmaId
+ */
public int getCipherMode() {
- return cipherMode;
+ return chainingMode.ecmaId;
+ }
+
+ public ChainingMode getChainingMode() {
+ return chainingMode;
+ }
+
+ protected void setChainingMode(ChainingMode chainingMode) {
+ this.chainingMode = chainingMode;
}
public int getFlags() {
return flags;
}
+
+ protected void setFlags(int flags) {
+ this.flags = flags;
+ }
public int getSizeExtra() {
return sizeExtra;
}
+
+ protected void setSizeExtra(int sizeExtra) {
+ this.sizeExtra = sizeExtra;
+ }
+ /**
+ * @deprecated use getCipherAlgorithm()
+ */
public int getAlgorithm() {
- return algorithm;
+ return cipherAlgorithm.ecmaId;
}
+ public CipherAlgorithm getCipherAlgorithm() {
+ return cipherAlgorithm;
+ }
+
+ protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
+ this.cipherAlgorithm = cipherAlgorithm;
+ }
+
+ /**
+ * @deprecated use getHashAlgorithmEx()
+ */
public int getHashAlgorithm() {
+ return hashAlgorithm.ecmaId;
+ }
+
+ public HashAlgorithm getHashAlgorithmEx() {
return hashAlgorithm;
}
+
+ protected void setHashAlgorithm(HashAlgorithm hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ }
public int getKeySize() {
- return keySize;
+ return keyBits;
+ }
+
+ protected void setKeySize(int keyBits) {
+ this.keyBits = keyBits;
}
public int getBlockSize() {
return blockSize;
}
+ protected void setBlockSize(int blockSize) {
+ this.blockSize = blockSize;
+ }
+
public byte[] getKeySalt() {
return keySalt;
}
+
+ protected void setKeySalt(byte salt[]) {
+ this.keySalt = salt;
+ }
+ /**
+ * @deprecated use getCipherProvider()
+ */
public int getProviderType() {
- return providerType;
+ return providerType.ecmaId;
}
+ public CipherProvider getCipherProvider() {
+ return providerType;
+ }
+
+ protected void setCipherProvider(CipherProvider providerType) {
+ this.providerType = providerType;
+ }
+
public String getCspName() {
return cspName;
}
+
+ protected void setCspName(String cspName) {
+ this.cspName = cspName;
+ }
}
Modified: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java?rev=1553336&r1=1553335&r2=1553336&view=diff
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java (original)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfo.java Tue Dec 24 23:13:21 2013
@@ -16,56 +16,141 @@
==================================================================== */
package org.apache.poi.poifs.crypt;
+import static org.apache.poi.poifs.crypt.EncryptionMode.agile;
+import static org.apache.poi.poifs.crypt.EncryptionMode.standard;
+
+import java.io.IOException;
+
+import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
-import java.io.IOException;
-
/**
*/
public class EncryptionInfo {
private final int versionMajor;
private final int versionMinor;
private final int encryptionFlags;
-
+
private final EncryptionHeader header;
private final EncryptionVerifier verifier;
+ private final Decryptor decryptor;
+ private final Encryptor encryptor;
public EncryptionInfo(POIFSFileSystem fs) throws IOException {
this(fs.getRoot());
}
+
public EncryptionInfo(NPOIFSFileSystem fs) throws IOException {
this(fs.getRoot());
}
+
public EncryptionInfo(DirectoryNode dir) throws IOException {
DocumentInputStream dis = dir.createDocumentInputStream("EncryptionInfo");
versionMajor = dis.readShort();
versionMinor = dis.readShort();
-
encryptionFlags = dis.readInt();
-
- if (versionMajor == 4 && versionMinor == 4 && encryptionFlags == 0x40) {
- StringBuilder builder = new StringBuilder();
- byte[] xmlDescriptor = new byte[dis.available()];
- dis.read(xmlDescriptor);
- for (byte b : xmlDescriptor)
- builder.append((char)b);
- String descriptor = builder.toString();
- header = new EncryptionHeader(descriptor);
- verifier = new EncryptionVerifier(descriptor);
+
+ EncryptionMode encryptionMode;
+ if (versionMajor == agile.versionMajor
+ && versionMinor == agile.versionMinor
+ && encryptionFlags == agile.encryptionFlags) {
+ encryptionMode = agile;
} else {
- int hSize = dis.readInt();
- header = new EncryptionHeader(dis);
- if (header.getAlgorithm()==EncryptionHeader.ALGORITHM_RC4) {
- verifier = new EncryptionVerifier(dis, 20);
- } else {
- verifier = new EncryptionVerifier(dis, 32);
- }
+ encryptionMode = standard;
+ }
+
+ EncryptionInfoBuilder eib;
+ try {
+ eib = getBuilder(encryptionMode);
+ } catch (ReflectiveOperationException e) {
+ throw new IOException(e);
}
- }
+ eib.initialize(this, dis);
+ header = eib.getHeader();
+ verifier = eib.getVerifier();
+ decryptor = eib.getDecryptor();
+ encryptor = eib.getEncryptor();
+ }
+
+ public EncryptionInfo(POIFSFileSystem fs, EncryptionMode encryptionMode) throws IOException {
+ this(fs.getRoot(), encryptionMode);
+ }
+
+ public EncryptionInfo(NPOIFSFileSystem fs, EncryptionMode encryptionMode) throws IOException {
+ this(fs.getRoot(), encryptionMode);
+ }
+
+ public EncryptionInfo(
+ DirectoryNode dir
+ , EncryptionMode encryptionMode
+ ) throws EncryptedDocumentException {
+ this(dir, encryptionMode, null, null, -1, -1, null);
+ }
+
+ public EncryptionInfo(
+ POIFSFileSystem fs
+ , EncryptionMode encryptionMode
+ , CipherAlgorithm cipherAlgorithm
+ , HashAlgorithm hashAlgorithm
+ , int keyBits
+ , int blockSize
+ , ChainingMode chainingMode
+ ) throws EncryptedDocumentException {
+ this(fs.getRoot(), encryptionMode, cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
+ }
+
+ public EncryptionInfo(
+ NPOIFSFileSystem fs
+ , EncryptionMode encryptionMode
+ , CipherAlgorithm cipherAlgorithm
+ , HashAlgorithm hashAlgorithm
+ , int keyBits
+ , int blockSize
+ , ChainingMode chainingMode
+ ) throws EncryptedDocumentException {
+ this(fs.getRoot(), encryptionMode, cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
+ }
+
+ public EncryptionInfo(
+ DirectoryNode dir
+ , EncryptionMode encryptionMode
+ , CipherAlgorithm cipherAlgorithm
+ , HashAlgorithm hashAlgorithm
+ , int keyBits
+ , int blockSize
+ , ChainingMode chainingMode
+ ) throws EncryptedDocumentException {
+ versionMajor = encryptionMode.versionMajor;
+ versionMinor = encryptionMode.versionMinor;
+ encryptionFlags = encryptionMode.encryptionFlags;
+
+ EncryptionInfoBuilder eib;
+ try {
+ eib = getBuilder(encryptionMode);
+ } catch (ReflectiveOperationException e) {
+ throw new EncryptedDocumentException(e);
+ }
+
+ eib.initialize(this, cipherAlgorithm, hashAlgorithm, keyBits, blockSize, chainingMode);
+
+ header = eib.getHeader();
+ verifier = eib.getVerifier();
+ decryptor = eib.getDecryptor();
+ encryptor = eib.getEncryptor();
+ }
+
+ protected static EncryptionInfoBuilder getBuilder(EncryptionMode encryptionMode)
+ throws ReflectiveOperationException {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ EncryptionInfoBuilder eib;
+ eib = (EncryptionInfoBuilder)cl.loadClass(encryptionMode.builder).newInstance();
+ return eib;
+ }
+
public int getVersionMajor() {
return versionMajor;
}
@@ -85,4 +170,12 @@ public class EncryptionInfo {
public EncryptionVerifier getVerifier() {
return verifier;
}
+
+ public Decryptor getDecryptor() {
+ return decryptor;
+ }
+
+ public Encryptor getEncryptor() {
+ return encryptor;
+ }
}
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionInfoBuilder.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,30 @@
+/* ====================================================================
+ 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.IOException;
+
+import org.apache.poi.poifs.filesystem.DocumentInputStream;
+
+public interface EncryptionInfoBuilder {
+ void initialize(EncryptionInfo ei, DocumentInputStream dis) throws IOException;
+ void initialize(EncryptionInfo ei, CipherAlgorithm cipherAlgorithm, HashAlgorithm hashAlgorithm, int keyBits, int blockSize, ChainingMode chainingMode);
+ EncryptionHeader getHeader();
+ EncryptionVerifier getVerifier();
+ Decryptor getDecryptor();
+ Encryptor getEncryptor();
+}
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/EncryptionMode.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,35 @@
+/* ====================================================================
+ 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;
+
+public enum EncryptionMode {
+ standard("org.apache.poi.poifs.crypt.standard.StandardEncryptionInfoBuilder", 4, 2, 0x24)
+ , agile("org.apache.poi.poifs.crypt.agile.AgileEncryptionInfoBuilder", 4, 4, 0x40);
+
+ public final String builder;
+ public final int versionMajor;
+ public final int versionMinor;
+ public final int encryptionFlags;
+
+ EncryptionMode(String builder, int versionMajor, int versionMinor, int encryptionFlags) {
+ this.builder = builder;
+ this.versionMajor = versionMajor;
+ this.versionMinor = versionMinor;
+ this.encryptionFlags = encryptionFlags;
+ }
+}
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=1553336&r1=1553335&r2=1553336&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 Dec 24 23:13:21 2013
@@ -16,155 +16,117 @@
==================================================================== */
package org.apache.poi.poifs.crypt;
-import java.io.ByteArrayInputStream;
-
-import javax.xml.parsers.DocumentBuilderFactory;
-
-import org.apache.commons.codec.binary.Base64;
-import org.apache.poi.EncryptedDocumentException;
-import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
/**
* Used when checking if a key is valid for a document
*/
-public class EncryptionVerifier {
- private final byte[] salt;
- private final byte[] verifier;
- private final byte[] verifierHash;
- private final byte[] encryptedKey;
- private final int verifierHashSize;
- private final int spinCount;
- private final int algorithm;
- private final int cipherMode;
-
- public EncryptionVerifier(String descriptor) {
- NamedNodeMap keyData = null;
- try {
- ByteArrayInputStream is;
- is = new ByteArrayInputStream(descriptor.getBytes());
- NodeList keyEncryptor = DocumentBuilderFactory.newInstance()
- .newDocumentBuilder().parse(is)
- .getElementsByTagName("keyEncryptor").item(0).getChildNodes();
- for (int i = 0; i < keyEncryptor.getLength(); i++) {
- Node node = keyEncryptor.item(i);
- if (node.getNodeName().equals("p:encryptedKey")) {
- keyData = node.getAttributes();
- break;
- }
- }
- if (keyData == null)
- throw new EncryptedDocumentException("");
- } catch (Exception e) {
- throw new EncryptedDocumentException("Unable to parse keyEncryptor");
- }
-
- spinCount = Integer.parseInt(keyData.getNamedItem("spinCount")
- .getNodeValue());
- verifier = Base64.decodeBase64(keyData
- .getNamedItem("encryptedVerifierHashInput")
- .getNodeValue().getBytes());
- salt = Base64.decodeBase64(keyData.getNamedItem("saltValue")
- .getNodeValue().getBytes());
-
- encryptedKey = Base64.decodeBase64(keyData
- .getNamedItem("encryptedKeyValue")
- .getNodeValue().getBytes());
-
- int saltSize = Integer.parseInt(keyData.getNamedItem("saltSize")
- .getNodeValue());
- if (saltSize != salt.length)
- throw new EncryptedDocumentException("Invalid salt size");
-
- verifierHash = Base64.decodeBase64(keyData
- .getNamedItem("encryptedVerifierHashValue")
- .getNodeValue().getBytes());
-
- int blockSize = Integer.parseInt(keyData.getNamedItem("blockSize")
- .getNodeValue());
-
- String alg = keyData.getNamedItem("cipherAlgorithm").getNodeValue();
-
- int keyBits = Integer.parseInt(keyData.getNamedItem("keyBits")
- .getNodeValue());
-
- if ("AES".equals(alg)) {
- switch (keyBits) {
- case 128:
- algorithm = EncryptionHeader.ALGORITHM_AES_128; break;
- case 192:
- algorithm = EncryptionHeader.ALGORITHM_AES_192; break;
- case 256:
- algorithm = EncryptionHeader.ALGORITHM_AES_256; break;
- default:
- throw new EncryptedDocumentException("Unsupported key size");
- }
- } else {
- throw new EncryptedDocumentException("Unsupported cipher");
- }
-
- String chain = keyData.getNamedItem("cipherChaining").getNodeValue();
- if ("ChainingModeCBC".equals(chain))
- cipherMode = EncryptionHeader.MODE_CBC;
- else if ("ChainingModeCFB".equals(chain))
- cipherMode = EncryptionHeader.MODE_CFB;
- else
- throw new EncryptedDocumentException("Unsupported chaining mode");
-
- verifierHashSize = Integer.parseInt(keyData.getNamedItem("hashSize")
- .getNodeValue());
- }
-
- public EncryptionVerifier(DocumentInputStream is, int encryptedLength) {
- int saltSize = is.readInt();
-
- if (saltSize!=16) {
- throw new RuntimeException("Salt size != 16 !?");
- }
-
- salt = new byte[16];
- is.readFully(salt);
- verifier = new byte[16];
- is.readFully(verifier);
-
- verifierHashSize = is.readInt();
-
- verifierHash = new byte[encryptedLength];
- is.readFully(verifierHash);
-
- spinCount = 50000;
- algorithm = EncryptionHeader.ALGORITHM_AES_128;
- cipherMode = EncryptionHeader.MODE_ECB;
- encryptedKey = null;
- }
+public abstract class EncryptionVerifier {
+ private byte[] salt;
+ private byte[] encryptedVerifier;
+ private byte[] encryptedVerifierHash;
+ private byte[] encryptedKey;
+ // protected int verifierHashSize;
+ private int spinCount;
+ private CipherAlgorithm cipherAlgorithm;
+ private ChainingMode chainingMode;
+ private HashAlgorithm hashAlgorithm;
+
+ protected EncryptionVerifier() {}
public byte[] getSalt() {
return salt;
}
+ /**
+ * The method name is misleading - you'll get the encrypted verifier, not the plain verifier
+ * @deprecated use getEncryptedVerifier()
+ */
public byte[] getVerifier() {
- return verifier;
+ return encryptedVerifier;
}
+ public byte[] getEncryptedVerifier() {
+ return encryptedVerifier;
+ }
+
+ /**
+ * The method name is misleading - you'll get the encrypted verifier hash, not the plain verifier hash
+ * @deprecated use getEnryptedVerifierHash
+ */
public byte[] getVerifierHash() {
- return verifierHash;
+ return encryptedVerifierHash;
}
+ public byte[] getEncryptedVerifierHash() {
+ return encryptedVerifierHash;
+ }
+
public int getSpinCount() {
return spinCount;
}
public int getCipherMode() {
- return cipherMode;
+ return chainingMode.ecmaId;
}
public int getAlgorithm() {
- return algorithm;
+ return cipherAlgorithm.ecmaId;
+ }
+
+ /**
+ * @deprecated use getCipherAlgorithm().jceId
+ */
+ public String getAlgorithmName() {
+ return cipherAlgorithm.jceId;
}
public byte[] getEncryptedKey() {
return encryptedKey;
}
+
+ public CipherAlgorithm getCipherAlgorithm() {
+ return cipherAlgorithm;
+ }
+
+ public HashAlgorithm getHashAlgorithm() {
+ return hashAlgorithm;
+ }
+
+ public ChainingMode getChainingMode() {
+ return chainingMode;
+ }
+
+ protected void setSalt(byte[] salt) {
+ this.salt = salt;
+ }
+
+ protected void setEncryptedVerifier(byte[] encryptedVerifier) {
+ this.encryptedVerifier = encryptedVerifier;
+ }
+
+ protected void setEncryptedVerifierHash(byte[] encryptedVerifierHash) {
+ this.encryptedVerifierHash = encryptedVerifierHash;
+ }
+
+ protected void setEncryptedKey(byte[] encryptedKey) {
+ this.encryptedKey = encryptedKey;
+ }
+
+ protected void setSpinCount(int spinCount) {
+ this.spinCount = spinCount;
+ }
+
+ protected void setCipherAlgorithm(CipherAlgorithm cipherAlgorithm) {
+ this.cipherAlgorithm = cipherAlgorithm;
+ }
+
+ protected void setChainingMode(ChainingMode chainingMode) {
+ this.chainingMode = chainingMode;
+ }
+
+ protected void setHashAlgorithm(HashAlgorithm hashAlgorithm) {
+ this.hashAlgorithm = hashAlgorithm;
+ }
+
+
}
Added: poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java?rev=1553336&view=auto
==============================================================================
--- poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java (added)
+++ poi/trunk/src/java/org/apache/poi/poifs/crypt/Encryptor.java Tue Dec 24 23:13:21 2013
@@ -0,0 +1,65 @@
+/* ====================================================================
+ 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.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.SecretKey;
+
+import org.apache.poi.poifs.filesystem.DirectoryNode;
+import org.apache.poi.poifs.filesystem.NPOIFSFileSystem;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+
+public abstract class Encryptor {
+ private SecretKey secretKey;
+
+ /**
+ * Return a output stream for encrypted data.
+ *
+ * @param dir the node to write to
+ * @return encrypted stream
+ */
+ public abstract OutputStream getDataStream(DirectoryNode dir)
+ throws IOException, GeneralSecurityException;
+
+ // for tests
+ public abstract void confirmPassword(String password, byte keySpec[], byte keySalt[], byte verifier[], byte verifierSalt[], byte integritySalt[]);
+
+ public abstract void confirmPassword(String password);
+
+ public static Encryptor getInstance(EncryptionInfo info) {
+ return info.getEncryptor();
+ }
+
+ public OutputStream getDataStream(NPOIFSFileSystem fs) throws IOException, GeneralSecurityException {
+ return getDataStream(fs.getRoot());
+ }
+
+ public OutputStream getDataStream(POIFSFileSystem fs) throws IOException, GeneralSecurityException {
+ return getDataStream(fs.getRoot());
+ }
+
+ public SecretKey getSecretKey() {
+ return secretKey;
+ }
+
+ protected void setSecretKey(SecretKey secretKey) {
+ this.secretKey = secretKey;
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org