poi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kiwiwi...@apache.org
Subject 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/...
Date Tue, 24 Dec 2013 23:13:22 GMT
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


Mime
View raw message