Bug 56836 - XML signature support

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1628348 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Andreas Beeker 2014-09-30 01:23:17 +00:00
commit aa91f244dc
56 changed files with 6907 additions and 91 deletions

View File

@ -1,29 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/java"/>
<classpathentry kind="src" path="src/testcases"/>
<classpathentry kind="src" path="src/resources/main"/>
<classpathentry kind="src" path="src/ooxml/java"/>
<classpathentry kind="src" path="src/ooxml/testcases"/>
<classpathentry kind="src" path="src/resources/ooxml"/>
<classpathentry kind="src" path="src/scratchpad/src"/>
<classpathentry kind="src" path="src/scratchpad/testcases"/>
<classpathentry kind="src" path="src/resources/scratchpad"/>
<classpathentry kind="src" path="src/contrib/poi-ruby/java"/>
<classpathentry kind="src" path="src/examples/src"/>
<classpathentry kind="src" path="src/excelant/java"/>
<classpathentry kind="src" path="src/excelant/testcases"/>
<classpathentry kind="src" path="src/excelant/resources"/>
<classpathentry kind="lib" path="lib/ant-1.9.4.jar"/>
<classpathentry kind="lib" path="lib/ant-launcher-1.9.4.jar"/>
<classpathentry kind="lib" path="lib/commons-codec-1.9.jar"/>
<classpathentry kind="lib" path="lib/commons-logging-1.1.3.jar"/>
<classpathentry kind="lib" path="lib/log4j-1.2.17.jar"/>
<classpathentry kind="lib" path="ooxml-lib/xmlbeans-2.6.0.jar"/>
<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>
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/java"/>
<classpathentry kind="src" path="src/testcases"/>
<classpathentry kind="src" path="src/resources/main"/>
<classpathentry kind="src" path="src/ooxml/java"/>
<classpathentry kind="src" path="src/ooxml/testcases"/>
<classpathentry kind="src" path="src/resources/ooxml"/>
<classpathentry kind="src" path="src/scratchpad/src"/>
<classpathentry kind="src" path="src/scratchpad/testcases"/>
<classpathentry kind="src" path="src/resources/scratchpad"/>
<classpathentry kind="src" path="src/contrib/poi-ruby/java"/>
<classpathentry kind="src" path="src/examples/src"/>
<classpathentry kind="src" path="src/excelant/java"/>
<classpathentry kind="src" path="src/excelant/testcases"/>
<classpathentry kind="src" path="src/excelant/resources"/>
<classpathentry kind="lib" path="lib/ant-1.9.4.jar"/>
<classpathentry kind="lib" path="lib/ant-launcher-1.9.4.jar"/>
<classpathentry kind="lib" path="lib/commons-codec-1.9.jar"/>
<classpathentry kind="lib" path="lib/commons-logging-1.1.3.jar"/>
<classpathentry kind="lib" path="lib/log4j-1.2.17.jar"/>
<classpathentry kind="lib" path="ooxml-lib/xmlbeans-2.6.0.jar"/>
<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.2.jar" sourcepath="ooxml-lib/ooxml-encryption-src-1.2.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="compile-lib/slf4j-api-1.7.7.jar"/>
<classpathentry kind="lib" path="compile-lib/bcpkix-jdk15on-1.51.jar"/>
<classpathentry kind="lib" path="compile-lib/bcprov-ext-jdk15on-1.51.jar"/>
<classpathentry kind="lib" path="compile-lib/xmlsec-2.0.1.jar"/>
<classpathentry kind="output" path="build/eclipse"/>
</classpath>

162
build.xml
View File

@ -61,6 +61,7 @@ under the License.
<property name="main.lib" location="lib"/>
<property name="ooxml.lib" location="ooxml-lib"/>
<property name="compile.lib" location="compile-lib"/>
<property name="forrest.home" value="${env.FORREST_HOME}"/>
<!-- compiler options options -->
@ -118,7 +119,6 @@ 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"/>
@ -147,7 +147,17 @@ under the License.
<property name="main.antlauncher.jar" location="${main.lib}/ant-launcher-1.9.4.jar"/>
<property name="main.antlauncher.url" value="${repository.m2}/maven2/org/apache/ant/ant-launcher/1.9.4/ant-launcher-1.9.4.jar"/>
<!-- jars in the lib-ooxml directory, see the fetch-ooxml-jars target-->
<!-- xml signature libs -->
<property name="dsig.xmlsec.jar" location="${compile.lib}/xmlsec-2.0.1.jar"/>
<property name="dsig.xmlsec.url" value="${repository.m2}/maven2/org/apache/santuario/xmlsec/2.0.1/xmlsec-2.0.1.jar"/>
<property name="dsig.bouncycastle-prov.jar" location="${compile.lib}/bcprov-ext-jdk15on-1.51.jar"/>
<property name="dsig.bouncycastle-prov.url" value="${repository.m2}/maven2/org/bouncycastle/bcprov-ext-jdk15on/1.51/bcprov-ext-jdk15on-1.51.jar"/>
<property name="dsig.bouncycastle-pkix.jar" location="${compile.lib}/bcpkix-jdk15on-1.51.jar"/>
<property name="dsig.bouncycastle-pkix.url" value="${repository.m2}/maven2/org/bouncycastle/bcpkix-jdk15on/1.51/bcpkix-jdk15on-1.51.jar"/>
<property name="dsig.sl4j-api.jar" location="${compile.lib}/slf4j-api-1.7.7.jar"/>
<property name="dsig.sl4j-api.url" value="${repository.m2}/maven2/org/slf4j/slf4j-api/1.7.7/slf4j-api-1.7.7.jar"/>
<!-- jars in the lib-ooxml directory, see the fetch-ooxml-jars target-->
<property name="ooxml.xmlbeans23.jar" location="${ooxml.lib}/xmlbeans-2.3.0.jar"/>
<property name="ooxml.xmlbeans23.url"
value="${repository.m2}/maven2/org/apache/xmlbeans/xmlbeans/2.3.0/xmlbeans-2.3.0.jar"/>
@ -160,7 +170,7 @@ under the License.
<property name="jacoco.url" value="${repository.m2}/maven2/org/jacoco/jacoco/0.7.1.201405082137/jacoco-0.7.1.201405082137.zip"/>
<property name="asm.jar" location="${main.lib}/asm-all-5.0.3.jar"/>
<property name="asm.url" value="${repository.m2}/maven2/org/ow2/asm/asm-all/5.0.3/asm-all-5.0.3.jar"/>
<!-- for testing with older Xerces implementation -->
<property name="xerces.jar" location="${main.lib}/xercesImpl-2.6.1.jar"/>
<property name="xerces.url" value="${repository.m2}/maven2/xerces/xercesImpl/2.6.1//xercesImpl-2.6.1.jar"/>
@ -171,17 +181,30 @@ under the License.
<!-- See http://www.ecma-international.org/publications/standards/Ecma-376.htm -->
<!-- "Copy these file(s), free of charge" -->
<property name="ooxml.xsds.ozip" location="${ooxml.lib}/OfficeOpenXML-Part4.zip"/>
<property name="ooxml.xsds.izip" location="${ooxml.lib}/OfficeOpenXML-XMLSchema.zip"/>
<property name="ooxml.xsds.url"
<property name="ooxml.xsds.ozip.1" value="OfficeOpenXML-Part4.zip"/>
<property name="ooxml.xsds.izip.1" value="OfficeOpenXML-XMLSchema.zip"/>
<property name="ooxml.xsds.url.1"
value="http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%201st%20edition%20Part%204%20(PDF).zip"/>
<property name="ooxml.xsds.src.dir" location="build/ooxml-xsds-src"/>
<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"/>
<!-- additional schemas are packed into the poi schemas jar, -->
<!-- so we don't have to care about a seperate versioning of the original ooxml schemas -->
<property name="ooxml.xsds.dc.1" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dc.xsd"/>
<property name="ooxml.xsds.dc.2" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dcterms.xsd"/>
<property name="ooxml.xsds.dc.3" value="http://dublincore.org/schemas/xmls/qdc/2003/04/02/dcmitype.xsd"/>
<property name="ooxml.xsds.dsig.1" value="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/>
<property name="ooxml.xsds.dsig.2" value="http://uri.etsi.org/01903/v1.3.2/XAdES.xsd"/>
<property name="ooxml.xsds.dsig.3" value="http://uri.etsi.org/01903/v1.4.1/XAdESv141.xsd"/>
<property name="ooxml.xsds.ozip.2" value="OfficeOpenXML-Part2.zip"/>
<property name="ooxml.xsds.izip.2" value="OpenPackagingConventions-XMLSchema.zip"/>
<property name="ooxml.xsds.url.2"
value="http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%201st%20edition%20Part%202%20(PDF).zip"/>
<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="ooxml.encryption.src.jar" location="${ooxml.lib}/ooxml-encryption-src-1.2.jar"/>
<property name="ooxml.encryption.jar" location="${ooxml.lib}/ooxml-encryption-1.2.jar"/>
<property name="ooxml.encryption.xsd.dir" location="src/ooxml/resources/org/apache/poi/poifs/crypt"/>
<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"/>
@ -222,6 +245,13 @@ under the License.
<pathelement location="${main.output.dir}"/>
</path>
<path id="ooxml.xmlsec.classpath">
<pathelement location="${dsig.xmlsec.jar}"/>
<pathelement location="${dsig.bouncycastle-prov.jar}"/>
<pathelement location="${dsig.bouncycastle-pkix.jar}"/>
<pathelement location="${dsig.sl4j-api.jar}"/>
</path>
<path id="ooxml.classpath">
<pathelement location="${ooxml.xmlbeans26.jar}"/>
<pathelement location="${ooxml.xsds.jar}"/>
@ -229,6 +259,7 @@ under the License.
<pathelement location="${main.output.dir}"/>
<pathelement location="${scratchpad.output.dir}"/>
<pathelement location="${ooxml.encryption.jar}"/>
<path refid="ooxml.xmlsec.classpath"/>
</path>
<path id="test.classpath">
@ -361,7 +392,7 @@ under the License.
</fileset>
</delete>
<condition property="jars.present">
<condition property="jars.present">
<or>
<and>
<available file="${main.commons-logging.jar}"/>
@ -375,6 +406,10 @@ under the License.
<available file="${jacoco.zip}"/>
<available file="${rat.jar}"/>
<available file="${xerces.jar}"/>
<available file="${dsig.bouncycastle-prov.jar}"/>
<available file="${dsig.bouncycastle-pkix.jar}"/>
<available file="${dsig.xmlsec.jar}"/>
<available file="${dsig.sl4j-api.jar}"/>
</and>
<isset property="disconnected"/>
</or>
@ -433,6 +468,22 @@ under the License.
<param name="sourcefile" value="${rat.url}"/>
<param name="destfile" value="${rat.jar}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${dsig.bouncycastle-prov.url}"/>
<param name="destfile" value="${dsig.bouncycastle-prov.jar}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${dsig.bouncycastle-pkix.url}"/>
<param name="destfile" value="${dsig.bouncycastle-pkix.jar}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${dsig.xmlsec.url}"/>
<param name="destfile" value="${dsig.xmlsec.jar}"/>
</antcall>
<antcall target="downloadfile">
<param name="sourcefile" value="${dsig.sl4j-api.url}"/>
<param name="destfile" value="${dsig.sl4j-api.jar}"/>
</antcall>
</target>
<target name="check-ooxml-jars">
@ -463,19 +514,37 @@ under the License.
<condition property="ooxml-xsds.present">
<or>
<and>
<available file="${ooxml.xsds.izip}"/>
<available file="${ooxml.lib}/${ooxml.xsds.izip.1}"/>
</and>
<isset property="disconnected"/>
</or>
</condition>
</target>
<target name="fetch-ooxml-xsds" unless="ooxml-xsds.present"
depends="check-ooxml-xsds"
depends="check-ooxml-xsds"
description="Fetches needed OOXML xsd files from the Internet">
<get src="${ooxml.xsds.url}" dest="${ooxml.xsds.ozip}"/>
<unzip src="${ooxml.xsds.ozip}" dest="${ooxml.lib}">
<get dest="${ooxml.lib}" skipexisting="true">
<url url="${ooxml.xsds.url.1}"/>
<url url="${ooxml.xsds.url.2}"/>
<url url="${ooxml.xsds.dc.1}"/>
<url url="${ooxml.xsds.dc.2}"/>
<url url="${ooxml.xsds.dc.3}"/>
<url url="${ooxml.xsds.dsig.1}"/>
<url url="${ooxml.xsds.dsig.2}"/>
<url url="${ooxml.xsds.dsig.3}"/>
<chainedmapper>
<flattenmapper/>
<firstmatchmapper>
<globmapper from="Office%20Open%20XML%201st%20edition%20Part%20*%20(PDF).zip" to="OfficeOpenXML-Part*.zip"/>
<identitymapper/>
</firstmatchmapper>
</chainedmapper>
</get>
<unzip src="${ooxml.lib}/${ooxml.xsds.ozip.1}" dest="${ooxml.lib}">
<fileset dir="${ooxml.lib}" includes="OfficeOpenXML-Part*.zip"/>
<patternset>
<include name="OfficeOpenXML-XMLSchema.zip"/>
<include name="${ooxml.xsds.izip.1}"/>
<include name="${ooxml.xsds.izip.2}"/>
</patternset>
</unzip>
</target>
@ -486,19 +555,10 @@ under the License.
<isset property="disconnected"/>
</or>
</condition>
<condition property="ooxml-compiled-encryption-xsds.present">
<or>
<available file="${ooxml.encryption.jar}"/>
<isset property="disconnected"/>
</or>
</condition>
</target>
<target name="compile-ooxml-xsds" unless="ooxml-compiled-xsds.present"
depends="check-jars,fetch-jars,check-compiled-ooxml-xsds"
description="Unpacks the OOXML xsd files, and compiles them into XmlBeans">
<property name="ooxml.xsds.tmp.dir" location="build/ooxml-xsds"/>
<mkdir dir="${ooxml.xsds.tmp.dir}"/>
depends="check-jars,fetch-jars,check-compiled-ooxml-xsds"
description="Unpacks the OOXML xsd files, and compiles them into XmlBeans">
<taskdef name="xmlbean"
classname="org.apache.xmlbeans.impl.tool.XMLBean"
classpath="${ooxml.xmlbeans23.jar}"/>
@ -510,11 +570,9 @@ under the License.
<equals arg1="${sun.arch.data.model}" arg2="64" />
</condition>
<unzip src="${ooxml.xsds.izip}" dest="${ooxml.xsds.tmp.dir}"/>
<!--
schema="build/ooxml-xsds/"
schema="build/ooxml-xsds/sml-workbook.xsd"
-->
<property name="ooxml.xsds.tmp.dir" location="build/ooxml-xsds"/>
<mkdir dir="${ooxml.xsds.tmp.dir}"/>
<unzip src="${ooxml.lib}/${ooxml.xsds.izip.1}" dest="${ooxml.xsds.tmp.dir}"/>
<xmlbean
schema="${ooxml.xsds.tmp.dir}"
srcgendir="${ooxml.xsds.src.dir}"
@ -528,41 +586,40 @@ under the License.
<classpath refid="ooxml.classpath"/>
</xmlbean>
<!-- Now make a jar of the schema sources -->
<jar
<!-- Now make a jar of the schema sources -->
<jar
basedir="${ooxml.xsds.src.dir}"
destfile="${ooxml.xsds.src.jar}"
/>
</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.xmlbeans23.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>
<!-- Now do the same for the encryption and supporting schemas -->
<property name="ooxml.enc.xsds.tmp.dir" location="build/ooxml-encryption-xsds"/>
<mkdir dir="${ooxml.enc.xsds.tmp.dir}"/>
<unzip src="${ooxml.lib}/${ooxml.xsds.izip.2}" dest="${ooxml.enc.xsds.tmp.dir}"/>
<copy todir="${ooxml.enc.xsds.tmp.dir}">
<fileset dir="${ooxml.lib}" includes="dc*.xsd,xmldsig*.xsd,XAdES*.xsd"/>
<fileset dir="${ooxml.encryption.xsd.dir}"/>
</copy>
<!-- noupa/nopvr is set because of the dublincore schemas -->
<!-- https://issues.apache.org/jira/browse/XMLBEANS-340 -->
<!-- javasource > 1.5 will not generate all array accessor -->
<xmlbean
schema="${ooxml.encryption.xsd.dir}"
schema="${ooxml.enc.xsds.tmp.dir}"
srcgendir="${ooxml.encryption.src.dir}"
optimize="yes"
destfile="${ooxml.encryption.jar}"
javasource="1.5"
javasource="1.5"
failonerror="true"
fork="true"
memoryMaximumSize="${ooxml.memory}"
noupa="true"
nopvr="true"
>
<classpath refid="ooxml.classpath"/>
</xmlbean>
<!-- Now make a jar of the schema sources -->
<jar
basedir="${ooxml.encryption.src.dir}"
destfile="${ooxml.encryption.src.jar}"
@ -655,7 +712,7 @@ under the License.
</copy>
</target>
<target name="compile-ooxml" depends="compile-main,compile-scratchpad,compile-ooxml-xsds,compile-ooxml-encryption-xsds">
<target name="compile-ooxml" depends="compile-main,compile-scratchpad,compile-ooxml-xsds">
<javac target="${jdk.version.class}"
source="${jdk.version.source}"
destdir="${ooxml.output.dir}"
@ -676,6 +733,7 @@ under the License.
includeantruntime="false">
<classpath>
<path refid="ooxml.classpath"/>
<path refid="test.ooxml.classpath"/>
<pathelement path="${ooxml.output.dir}"/>
<pathelement path="${main.output.test.dir}"/>
</classpath>
@ -1354,7 +1412,7 @@ under the License.
<target name="gump" depends="compile-all, test-all, jar"/>
<target name="jenkins" depends="compile-all, test-all, jar, javadocs, assemble, findbugs, release-notes, rat-check"/>
<available property="maven.ant.tasks.present" classname="org.apache.maven.artifact.ant.Pom"/>
<target name="maven.ant.tasks-check">
<fail unless="maven.ant.tasks.present">
@ -1450,7 +1508,7 @@ under the License.
<exclude name="poi-*${version.id}-sources-*.jar"/>
</fileset>
<auxClasspath path="ooxml-lib/ooxml-schemas-1.1.jar" />
<auxClasspath path="ooxml-lib/ooxml-encryption-1.1.jar" />
<auxClasspath path="ooxml-lib/ooxml-encryption-1.2.jar" />
<auxClasspath path="ooxml-lib/xmlbeans-2.6.0.jar" />
<auxClasspath path="lib/commons-codec-1.9.jar" />
<auxClasspath path="lib/commons-logging-1.1.3.jar" />

View File

@ -34,6 +34,8 @@ public enum CipherAlgorithm {
// 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),
// only for digital signatures
rsa(null, "RSA", -1, 1024, new int[]{1024, 2048, 3072, 4096}, -1, -1, "", false);
;
public final CipherProvider provider;

View File

@ -19,6 +19,7 @@ package org.apache.poi.poifs.crypt;
import java.nio.charset.Charset;
import java.security.DigestException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.Provider;
import java.security.Security;
@ -195,7 +196,7 @@ public class CryptoFunctions {
* @throws EncryptedDocumentException if the initialization failed or if an algorithm was specified,
* which depends on a missing bouncy castle provider
*/
public static Cipher getCipher(SecretKey key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
public static Cipher getCipher(Key key, CipherAlgorithm cipherAlgorithm, ChainingMode chain, byte[] vec, int cipherMode, String padding) {
int keySizeInBytes = key.getEncoded().length;
if (padding == null) padding = "NoPadding";
@ -296,10 +297,12 @@ public class CryptoFunctions {
}
@SuppressWarnings("unchecked")
private static void registerBouncyCastle() {
public static void registerBouncyCastle() {
if (Security.getProvider("BC") != null) return;
try {
Class<Provider> clazz = (Class<Provider>)Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider");
ClassLoader cl = Thread.currentThread().getContextClassLoader();
String bcProviderName = "org.bouncycastle.jce.provider.BouncyCastleProvider";
Class<Provider> clazz = (Class<Provider>)cl.loadClass(bcProviderName);
Security.addProvider(clazz.newInstance());
} catch (Exception e) {
throw new EncryptedDocumentException("Only the BouncyCastle provider supports your encryption settings - please add it to the classpath.");

View File

@ -33,6 +33,8 @@ public enum HashAlgorithm {
ripemd128("RipeMD128", -1, "RIPEMD-128", 16, "HMac-RipeMD128", true),
ripemd160("RipeMD160", -1, "RIPEMD-160", 20, "HMac-RipeMD160", true),
whirlpool("Whirlpool", -1, "WHIRLPOOL", 64, "HMac-Whirlpool", true),
// only for xml signing
sha224 ( "SHA-224", -1, "SHA224", 28, "HmacSHA224", true);
;
public final String jceId;

View File

@ -182,8 +182,6 @@ public final class PackageRelationship {
}
/**
* public URI getSourceUri(){ }
*
* @return the targetMode
*/
public TargetMode getTargetMode() {

View File

@ -307,7 +307,7 @@ public final class PackageRelationshipCollection implements
* @throws InvalidFormatException
* Throws if the relationship part is invalid.
*/
private void parseRelationshipsPart(PackagePart relPart)
public void parseRelationshipsPart(PackagePart relPart)
throws InvalidFormatException {
try {
logger.log(POILogger.DEBUG, "Parsing relationship: " + relPart.getPartName());

View File

@ -145,11 +145,10 @@ public abstract class ContentTypeManager {
* </p>
*/
public void addContentType(PackagePartName partName, String contentType) {
boolean defaultCTExists = false;
boolean defaultCTExists = this.defaultContentType.containsValue(contentType);
String extension = partName.getExtension().toLowerCase();
if ((extension.length() == 0)
|| (this.defaultContentType.containsKey(extension) && !(defaultCTExists = this.defaultContentType
.containsValue(contentType))))
|| (this.defaultContentType.containsKey(extension) && !defaultCTExists))
this.addOverrideContentType(partName, contentType);
else if (!defaultCTExists)
this.addDefaultContentType(extension, contentType);
@ -452,7 +451,7 @@ public abstract class ContentTypeManager {
}
/**
* Use to append default types XML elements, use by the save() metid.
* Use to append default types XML elements, use by the save() method.
*
* @param root
* XML parent element use to append this default type element.

View File

@ -0,0 +1,38 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case there is something wrong with the incoming eID
* certificate.
*
* @author Frank Cornelis
*
*/
public class CertificateSecurityException extends SecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case the incoming eID certificate is expired.
*
* @author Frank Cornelis
*
*/
public class ExpiredCertificateSecurityException extends
CertificateSecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,103 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
import java.security.Key;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
* JSR105 key selector implementation using the ds:KeyInfo data of the signature
* itself.
*/
public class KeyInfoKeySelector extends KeySelector implements KeySelectorResult {
private static final POILogger LOG = POILogFactory.getLogger(KeyInfoKeySelector.class);
private List<X509Certificate> certChain = new ArrayList<X509Certificate>();
@SuppressWarnings("unchecked")
@Override
public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException {
LOG.log(POILogger.DEBUG, "select key");
if (null == keyInfo) {
throw new KeySelectorException("no ds:KeyInfo present");
}
List<XMLStructure> keyInfoContent = keyInfo.getContent();
certChain.clear();
for (XMLStructure keyInfoStructure : keyInfoContent) {
if (!(keyInfoStructure instanceof X509Data)) {
continue;
}
X509Data x509Data = (X509Data) keyInfoStructure;
List<Object> x509DataList = x509Data.getContent();
for (Object x509DataObject : x509DataList) {
if (!(x509DataObject instanceof X509Certificate)) {
continue;
}
X509Certificate certificate = (X509Certificate) x509DataObject;
LOG.log(POILogger.DEBUG, "certificate", certificate.getSubjectX500Principal());
certChain.add(certificate);
}
}
if (certChain.isEmpty()) {
throw new KeySelectorException("No key found!");
}
return this;
}
public Key getKey() {
// The first certificate is presumably the signer.
return certChain.isEmpty() ? null : certChain.get(0).getPublicKey();
}
/**
* Gives back the X509 certificate used during the last signature
* verification operation.
*
* @return
*/
public X509Certificate getSigner() {
// The first certificate is presumably the signer.
return certChain.isEmpty() ? null : certChain.get(0);
}
public List<X509Certificate> getCertChain() {
return certChain;
}
}

View File

@ -0,0 +1,113 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import javax.xml.crypto.Data;
import javax.xml.crypto.OctetStreamData;
import javax.xml.crypto.URIDereferencer;
import javax.xml.crypto.URIReference;
import javax.xml.crypto.URIReferenceException;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
/**
* JSR105 URI dereferencer for Office Open XML documents.
*/
public class OOXMLURIDereferencer implements URIDereferencer, SignatureConfigurable {
private static final POILogger LOG = POILogFactory.getLogger(OOXMLURIDereferencer.class);
private SignatureConfig signatureConfig;
private URIDereferencer baseUriDereferencer;
public OOXMLURIDereferencer() {
XMLSignatureFactory xmlSignatureFactory = SignatureInfo.getSignatureFactory();
this.baseUriDereferencer = xmlSignatureFactory.getURIDereferencer();
}
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
public Data dereference(URIReference uriReference, XMLCryptoContext context) throws URIReferenceException {
if (null == uriReference) {
throw new NullPointerException("URIReference cannot be null");
}
if (null == context) {
throw new NullPointerException("XMLCrytoContext cannot be null");
}
URI uri;
try {
uri = new URI(uriReference.getURI());
} catch (URISyntaxException e) {
throw new URIReferenceException("could not URL decode the uri: "+uriReference.getURI(), e);
}
PackagePart part = findPart(uri);
if (part == null) {
LOG.log(POILogger.DEBUG, "cannot resolve, delegating to base DOM URI dereferencer", uri);
return this.baseUriDereferencer.dereference(uriReference, context);
}
try {
return new OctetStreamData(part.getInputStream(), uri.toString(), null);
} catch (IOException e) {
throw new URIReferenceException("I/O error: " + e.getMessage(), e);
}
}
private PackagePart findPart(URI uri) {
LOG.log(POILogger.DEBUG, "dereference", uri);
String path = uri.getPath();
if (path == null || "".equals(path)) {
LOG.log(POILogger.DEBUG, "illegal part name (expected)", uri);
return null;
}
PackagePartName ppn;
try {
ppn = PackagingURIHelper.createPartName(path);
} catch (InvalidFormatException e) {
LOG.log(POILogger.WARN, "illegal part name (not expected)", uri);
return null;
}
return signatureConfig.getOpcPackage().getPart(ppn);
}
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case the incoming eID certificate has been revoked.
*
* @author Frank Cornelis
*
*/
public class RevokedCertificateSecurityException extends
CertificateSecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,505 @@
/* ====================================================================
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.dsig;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XADES_132_NS;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.xml.crypto.URIDereferencer;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService;
import org.apache.poi.poifs.crypt.dsig.services.TSPTimeStampService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO;
import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;
import org.w3c.dom.events.EventListener;
/**
* This class bundles the configuration options used for the existing
* signature facets.
* Apart of the opc-package (thread local) most values will probably be constant, so
* it might be configured centrally (e.g. by spring)
*/
public class SignatureConfig {
public static interface SignatureConfigurable {
void setSignatureConfig(SignatureConfig signatureConfig);
}
private ThreadLocal<OPCPackage> opcPackage = new ThreadLocal<OPCPackage>();
private List<SignatureFacet> signatureFacets = new ArrayList<SignatureFacet>();
private HashAlgorithm digestAlgo = HashAlgorithm.sha1;
private Date executionTime = new Date();
private PrivateKey key;
private List<X509Certificate> signingCertificateChain;
private IdentityDTO identity;
private AddressDTO address;
private byte[] photo;
/**
* the optional signature policy service used for XAdES-EPES.
*/
private SignaturePolicyService signaturePolicyService;
private URIDereferencer uriDereferencer = new OOXMLURIDereferencer();
private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE;
private boolean includeEntireCertificateChain = true;
private boolean includeIssuerSerial = false;
private boolean includeKeyValue = false;
private TimeStampService tspService = new TSPTimeStampService();
// timestamp service provider URL
private String tspUrl;
private boolean tspOldProtocol = false;
/**
* if not defined, it's the same as the main digest
*/
private HashAlgorithm tspDigestAlgo = null;
private String tspUser;
private String tspPass;
private TimeStampServiceValidator tspValidator;
/**
* the optional TSP request policy OID.
*/
private String tspRequestPolicy = "1.3.6.1.4.1.13762.3";
private String userAgent = "POI XmlSign Service TSP Client";
private String proxyUrl;
/**
* the optional revocation data service used for XAdES-C and XAdES-X-L.
* When <code>null</code> the signature will be limited to XAdES-T only.
*/
private RevocationDataService revocationDataService;
/**
* if not defined, it's the same as the main digest
*/
private HashAlgorithm xadesDigestAlgo = null;
private String xadesRole = null;
private String xadesSignatureId = null;
private boolean xadesSignaturePolicyImplied = true;
/**
* Work-around for Office 2010 IssuerName encoding.
*/
private boolean xadesIssuerNameNoReverseOrder = true;
/**
* The signature Id attribute value used to create the XML signature. A
* <code>null</code> value will trigger an automatically generated signature Id.
*/
private String packageSignatureId = "idPackageSignature";
/**
* Gives back the human-readable description of what the citizen will be
* signing. The default value is "Office OpenXML Document".
*/
private String signatureDescription = "Office OpenXML Document";
/**
* The process of signing includes the marshalling of xml structures.
* This also includes the canonicalization. Currently this leads to problems
* with certain namespaces, so this EventListener is used to interfere
* with the marshalling process.
*/
EventListener signatureMarshalListener = null;
/**
* Map of namespace uris to prefix
* If a mapping is specified, the corresponding elements will be prefixed
*/
Map<String,String> namespacePrefixes = new HashMap<String,String>();
protected void init(boolean onlyValidation) {
if (uriDereferencer == null) {
throw new EncryptedDocumentException("uriDereferencer is null");
}
if (opcPackage == null) {
throw new EncryptedDocumentException("opcPackage is null");
}
if (uriDereferencer instanceof SignatureConfigurable) {
((SignatureConfigurable)uriDereferencer).setSignatureConfig(this);
}
if (namespacePrefixes.isEmpty()) {
/*
* OOo doesn't like ds namespaces so per default prefixing is off.
*/
// namespacePrefixes.put(XML_DIGSIG_NS, "");
namespacePrefixes.put(OO_DIGSIG_NS, "mdssi");
namespacePrefixes.put(XADES_132_NS, "xd");
}
if (onlyValidation) return;
if (signatureMarshalListener == null) {
signatureMarshalListener = new SignatureMarshalListener();
}
if (signatureMarshalListener instanceof SignatureConfigurable) {
((SignatureConfigurable)signatureMarshalListener).setSignatureConfig(this);
}
if (tspService != null) {
tspService.setSignatureConfig(this);
}
if (xadesSignatureId == null || xadesSignatureId.isEmpty()) {
xadesSignatureId = "idSignedProperties";
}
if (signatureFacets.isEmpty()) {
addSignatureFacet(new OOXMLSignatureFacet());
addSignatureFacet(new KeyInfoSignatureFacet());
addSignatureFacet(new XAdESSignatureFacet());
addSignatureFacet(new Office2010SignatureFacet());
}
for (SignatureFacet sf : signatureFacets) {
sf.setSignatureConfig(this);
}
}
public void addSignatureFacet(SignatureFacet sf) {
signatureFacets.add(sf);
}
/**
* Gives back the used XAdES signature facet.
*
* @return
*/
public XAdESSignatureFacet getXAdESSignatureFacet() {
for (SignatureFacet sf : getSignatureFacets()) {
if (sf instanceof XAdESSignatureFacet) {
return (XAdESSignatureFacet)sf;
}
}
return null;
}
public List<SignatureFacet> getSignatureFacets() {
return signatureFacets;
}
public void setSignatureFacets(List<SignatureFacet> signatureFacets) {
this.signatureFacets = signatureFacets;
}
public HashAlgorithm getDigestAlgo() {
return digestAlgo;
}
public void setDigestAlgo(HashAlgorithm digestAlgo) {
this.digestAlgo = digestAlgo;
}
public OPCPackage getOpcPackage() {
return opcPackage.get();
}
public void setOpcPackage(OPCPackage opcPackage) {
this.opcPackage.set(opcPackage);
}
public PrivateKey getKey() {
return key;
}
public void setKey(PrivateKey key) {
this.key = key;
}
public List<X509Certificate> getSigningCertificateChain() {
return signingCertificateChain;
}
public void setSigningCertificateChain(
List<X509Certificate> signingCertificateChain) {
this.signingCertificateChain = signingCertificateChain;
}
public IdentityDTO getIdentity() {
return identity;
}
public void setIdentity(IdentityDTO identity) {
this.identity = identity;
}
public AddressDTO getAddress() {
return address;
}
public void setAddress(AddressDTO address) {
this.address = address;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
public Date getExecutionTime() {
return executionTime;
}
public void setExecutionTime(Date executionTime) {
this.executionTime = executionTime;
}
public SignaturePolicyService getSignaturePolicyService() {
return signaturePolicyService;
}
public void setSignaturePolicyService(SignaturePolicyService signaturePolicyService) {
this.signaturePolicyService = signaturePolicyService;
}
public URIDereferencer getUriDereferencer() {
return uriDereferencer;
}
public void setUriDereferencer(URIDereferencer uriDereferencer) {
this.uriDereferencer = uriDereferencer;
}
public String getSignatureDescription() {
return signatureDescription;
}
public void setSignatureDescription(String signatureDescription) {
this.signatureDescription = signatureDescription;
}
public String getCanonicalizationMethod() {
return canonicalizationMethod;
}
public void setCanonicalizationMethod(String canonicalizationMethod) {
this.canonicalizationMethod = canonicalizationMethod;
}
public String getPackageSignatureId() {
return packageSignatureId;
}
public void setPackageSignatureId(String packageSignatureId) {
this.packageSignatureId = nvl(packageSignatureId,"xmldsig-"+UUID.randomUUID());
}
public String getTspUrl() {
return tspUrl;
}
public void setTspUrl(String tspUrl) {
this.tspUrl = tspUrl;
}
public boolean isTspOldProtocol() {
return tspOldProtocol;
}
public void setTspOldProtocol(boolean tspOldProtocol) {
this.tspOldProtocol = tspOldProtocol;
}
public HashAlgorithm getTspDigestAlgo() {
return nvl(tspDigestAlgo,digestAlgo);
}
public void setTspDigestAlgo(HashAlgorithm tspDigestAlgo) {
this.tspDigestAlgo = tspDigestAlgo;
}
public String getProxyUrl() {
return proxyUrl;
}
public void setProxyUrl(String proxyUrl) {
this.proxyUrl = proxyUrl;
}
public TimeStampService getTspService() {
return tspService;
}
public void setTspService(TimeStampService tspService) {
this.tspService = tspService;
}
public String getTspUser() {
return tspUser;
}
public void setTspUser(String tspUser) {
this.tspUser = tspUser;
}
public String getTspPass() {
return tspPass;
}
public void setTspPass(String tspPass) {
this.tspPass = tspPass;
}
public TimeStampServiceValidator getTspValidator() {
return tspValidator;
}
public void setTspValidator(TimeStampServiceValidator tspValidator) {
this.tspValidator = tspValidator;
}
public RevocationDataService getRevocationDataService() {
return revocationDataService;
}
public void setRevocationDataService(RevocationDataService revocationDataService) {
this.revocationDataService = revocationDataService;
}
public HashAlgorithm getXadesDigestAlgo() {
return nvl(xadesDigestAlgo,digestAlgo);
}
public void setXadesDigestAlgo(HashAlgorithm xadesDigestAlgo) {
this.xadesDigestAlgo = xadesDigestAlgo;
}
public String getUserAgent() {
return userAgent;
}
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}
public String getTspRequestPolicy() {
return tspRequestPolicy;
}
public void setTspRequestPolicy(String tspRequestPolicy) {
this.tspRequestPolicy = tspRequestPolicy;
}
public boolean isIncludeEntireCertificateChain() {
return includeEntireCertificateChain;
}
public void setIncludeEntireCertificateChain(boolean includeEntireCertificateChain) {
this.includeEntireCertificateChain = includeEntireCertificateChain;
}
public boolean isIncludeIssuerSerial() {
return includeIssuerSerial;
}
public void setIncludeIssuerSerial(boolean includeIssuerSerial) {
this.includeIssuerSerial = includeIssuerSerial;
}
public boolean isIncludeKeyValue() {
return includeKeyValue;
}
public void setIncludeKeyValue(boolean includeKeyValue) {
this.includeKeyValue = includeKeyValue;
}
public String getXadesRole() {
return xadesRole;
}
public void setXadesRole(String xadesRole) {
this.xadesRole = xadesRole;
}
public String getXadesSignatureId() {
return xadesSignatureId;
}
public void setXadesSignatureId(String xadesSignatureId) {
this.xadesSignatureId = xadesSignatureId;
}
public boolean isXadesSignaturePolicyImplied() {
return xadesSignaturePolicyImplied;
}
public void setXadesSignaturePolicyImplied(boolean xadesSignaturePolicyImplied) {
this.xadesSignaturePolicyImplied = xadesSignaturePolicyImplied;
}
public boolean isXadesIssuerNameNoReverseOrder() {
return xadesIssuerNameNoReverseOrder;
}
public void setXadesIssuerNameNoReverseOrder(boolean xadesIssuerNameNoReverseOrder) {
this.xadesIssuerNameNoReverseOrder = xadesIssuerNameNoReverseOrder;
}
public EventListener getSignatureMarshalListener() {
return signatureMarshalListener;
}
public void setSignatureMarshalListener(EventListener signatureMarshalListener) {
this.signatureMarshalListener = signatureMarshalListener;
}
public Map<String, String> getNamespacePrefixes() {
return namespacePrefixes;
}
public void setNamespacePrefixes(Map<String, String> namespacePrefixes) {
this.namespacePrefixes = namespacePrefixes;
}
protected static <T> T nvl(T value, T defaultValue) {
return value == null ? defaultValue : value;
}
public byte[] getHashMagic() {
// see https://www.ietf.org/rfc/rfc3110.txt
// RSA/SHA1 SIG Resource Records
byte result[];
switch (getDigestAlgo()) {
case sha1: result = new byte[]
{ 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e
, 0x03, 0x02, 0x1a, 0x04, 0x14 };
break;
case sha224: result = new byte[]
{ 0x30, 0x2b, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x04, 0x1c };
break;
case sha256: result = new byte[]
{ 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20 };
break;
case sha384: result = new byte[]
{ 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30 };
break;
case sha512: result = new byte[]
{ 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40 };
break;
case ripemd128: result = new byte[]
{ 0x30, 0x1b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
, 0x03, 0x02, 0x02, 0x04, 0x10 };
break;
case ripemd160: result = new byte[]
{ 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
, 0x03, 0x02, 0x01, 0x04, 0x14 };
break;
// case ripemd256: result = new byte[]
// { 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24
// , 0x03, 0x02, 0x03, 0x04, 0x20 };
// break;
default: throw new EncryptedDocumentException("Hash algorithm "
+getDigestAlgo()+" not supported for signing.");
}
return result;
}
public String getSignatureMethod() {
switch (getDigestAlgo()) {
case sha1: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
case sha224: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224;
case sha256: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;
case sha384: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;
case sha512: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;
case ripemd160: return org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_RIPEMD160;
default: throw new EncryptedDocumentException("Hash algorithm "
+getDigestAlgo()+" not supported for signing.");
}
}
public String getDigestMethodUri() {
return getDigestMethodUri(getDigestAlgo());
}
public static String getDigestMethodUri(HashAlgorithm digestAlgo) {
switch (digestAlgo) {
case sha1: return DigestMethod.SHA1;
case sha224: return "http://www.w3.org/2001/04/xmldsig-more#sha224";
case sha256: return DigestMethod.SHA256;
case sha384: return "http://www.w3.org/2001/04/xmldsig-more#sha384";
case sha512: return DigestMethod.SHA512;
case ripemd160: return DigestMethod.RIPEMD160;
default: throw new EncryptedDocumentException("Hash algorithm "
+digestAlgo+" not supported for signing.");
}
}
}

View File

@ -0,0 +1,546 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_DIGSIG_NS;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Provider;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.crypto.Cipher;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.URIDereferencer;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Manifest;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.SignedInfo;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignContext;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.jcp.xml.dsig.internal.dom.DOMReference;
import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.ContentTypes;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.poifs.crypt.ChainingMode;
import org.apache.poi.poifs.crypt.CipherAlgorithm;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.xml.security.Init;
import org.apache.xml.security.utils.Base64;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.w3.x2000.x09.xmldsig.SignatureDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.xml.sax.SAXException;
public class SignatureInfo implements SignatureConfigurable {
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
private static boolean isInitialized = false;
private SignatureConfig signatureConfig;
public class SignaturePart {
private final PackagePart signaturePart;
private X509Certificate signer;
private List<X509Certificate> certChain;
private SignaturePart(PackagePart signaturePart) {
this.signaturePart = signaturePart;
}
public PackagePart getPackagePart() {
return signaturePart;
}
public X509Certificate getSigner() {
return signer;
}
public List<X509Certificate> getCertChain() {
return certChain;
}
public SignatureDocument getSignatureDocument() throws IOException, XmlException {
// TODO: check for XXE
return SignatureDocument.Factory.parse(signaturePart.getInputStream());
}
public boolean validate() {
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
try {
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());
XPath xpath = XPathFactory.newInstance().newXPath();
NodeList nl = (NodeList)xpath.compile("//*[@Id]").evaluate(doc, XPathConstants.NODESET);
for (int i=0; i<nl.getLength(); i++) {
((Element)nl.item(i)).setIdAttribute("Id", true);
}
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);
domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());
XMLSignatureFactory xmlSignatureFactory = getSignatureFactory();
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);
boolean valid = xmlSignature.validate(domValidateContext);
if (valid) {
signer = keySelector.getSigner();
certChain = keySelector.getCertChain();
}
return valid;
} catch (Exception e) {
LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e);
return false;
}
}
}
public SignatureInfo() {
initXmlProvider();
}
public SignatureConfig getSignatureConfig() {
return signatureConfig;
}
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
public boolean verifySignature() {
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
for (SignaturePart sp : getSignatureParts()){
// only validate first part
return sp.validate();
}
return false;
}
public void confirmSignature()
throws NoSuchAlgorithmException, IOException, MarshalException, ParserConfigurationException, XmlException, InvalidAlgorithmParameterException, NoSuchProviderException, XMLSignatureException, TransformerFactoryConfigurationError, TransformerException, SAXException, URISyntaxException {
Document document = DocumentHelper.createDocument();
// operate
DigestInfo digestInfo = preSign(document, null);
// setup: key material, signature value
byte[] signatureValue = signDigest(digestInfo.digestValue);
// operate: postSign
postSign(document, signatureValue);
}
public byte[] signDigest(byte digest[]) {
Cipher cipher = CryptoFunctions.getCipher(signatureConfig.getKey(), CipherAlgorithm.rsa
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");
try {
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
digestInfoValueBuf.write(signatureConfig.getHashMagic());
digestInfoValueBuf.write(digest);
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
byte[] signatureValue = cipher.doFinal(digestInfoValue);
return signatureValue;
} catch (Exception e) {
throw new EncryptedDocumentException(e);
}
}
public Iterable<SignaturePart> getSignatureParts() {
signatureConfig.init(true);
return new Iterable<SignaturePart>() {
public Iterator<SignaturePart> iterator() {
return new Iterator<SignaturePart>() {
OPCPackage pkg = signatureConfig.getOpcPackage();
Iterator<PackageRelationship> sigOrigRels =
pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();
Iterator<PackageRelationship> sigRels = null;
PackagePart sigPart = null;
public boolean hasNext() {
while (sigRels == null || !sigRels.hasNext()) {
if (!sigOrigRels.hasNext()) return false;
sigPart = pkg.getPart(sigOrigRels.next());
LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);
try {
sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();
} catch (InvalidFormatException e) {
LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
}
}
return true;
}
public SignaturePart next() {
PackagePart sigRelPart = null;
do {
try {
if (!hasNext()) throw new NoSuchElementException();
sigRelPart = sigPart.getRelatedPart(sigRels.next());
LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);
} catch (InvalidFormatException e) {
LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
}
} while (sigPart == null);
return new SignaturePart(sigRelPart);
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
};
}
public static XMLSignatureFactory getSignatureFactory() {
return XMLSignatureFactory.getInstance("DOM", getProvider());
}
public static KeyInfoFactory getKeyInfoFactory() {
return KeyInfoFactory.getInstance("DOM", getProvider());
}
// currently classes are linked to Apache Santuario, so this might be superfluous
public static Provider getProvider() {
String dsigProviderNames[] = {
System.getProperty("jsr105Provider"),
"org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI", // Santuario xmlsec
"org.jcp.xml.dsig.internal.dom.XMLDSigRI" // JDK xmlsec
};
for (String pn : dsigProviderNames) {
if (pn == null) continue;
try {
return (Provider)Class.forName(pn).newInstance();
} catch (Exception e) {
LOG.log(POILogger.DEBUG, "XMLDsig-Provider '"+pn+"' can't be found - trying next.");
}
}
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!");
}
protected static synchronized void initXmlProvider() {
if (isInitialized) return;
isInitialized = true;
try {
Init.init();
RelationshipTransformService.registerDsigProvider();
CryptoFunctions.registerBouncyCastle();
} catch (Exception e) {
throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
}
}
/**
* Helper method for adding informations before the signing.
* Normally {@link #confirmSignature()} is sufficient to be used.
*/
@SuppressWarnings("unchecked")
public DigestInfo preSign(Document document, List<DigestInfo> digestInfos)
throws ParserConfigurationException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, MarshalException,
javax.xml.crypto.dsig.XMLSignatureException,
TransformerFactoryConfigurationError, TransformerException,
IOException, SAXException, NoSuchProviderException, XmlException, URISyntaxException {
signatureConfig.init(false);
// it's necessary to explicitly set the mdssi namespace, but the sign() method has no
// normal way to interfere with, so we need to add the namespace under the hand ...
EventTarget target = (EventTarget)document;
EventListener creationListener = signatureConfig.getSignatureMarshalListener();
if (creationListener != null) {
if (creationListener instanceof SignatureMarshalListener) {
((SignatureMarshalListener)creationListener).setEventTarget(target);
}
SignatureMarshalListener.setListener(target, creationListener, true);
}
/*
* Signature context construction.
*/
XMLSignContext xmlSignContext = new DOMSignContext(signatureConfig.getKey(), document);
URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer();
if (null != uriDereferencer) {
xmlSignContext.setURIDereferencer(uriDereferencer);
}
for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue());
}
xmlSignContext.setDefaultNamespacePrefix(""); // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS));
XMLSignatureFactory signatureFactory = SignatureInfo.getSignatureFactory();
/*
* Add ds:References that come from signing client local files.
*/
List<Reference> references = new ArrayList<Reference>();
for (DigestInfo digestInfo : safe(digestInfos)) {
byte[] documentDigestValue = digestInfo.digestValue;
DigestMethod digestMethod = signatureFactory.newDigestMethod
(signatureConfig.getDigestMethodUri(), null);
String uri = new File(digestInfo.description).getName();
Reference reference = signatureFactory.newReference
(uri, digestMethod, null, null, null, documentDigestValue);
references.add(reference);
}
/*
* Invoke the signature facets.
*/
List<XMLObject> objects = new ArrayList<XMLObject>();
for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());
signatureFacet.preSign(document, signatureFactory, references, objects);
}
/*
* ds:SignedInfo
*/
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod
(signatureConfig.getSignatureMethod(), null);
CanonicalizationMethod canonicalizationMethod = signatureFactory
.newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),
(C14NMethodParameterSpec) null);
SignedInfo signedInfo = signatureFactory.newSignedInfo(
canonicalizationMethod, signatureMethod, references);
/*
* JSR105 ds:Signature creation
*/
String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value";
javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
.newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(),
signatureValueId);
/*
* ds:Signature Marshalling.
*/
xmlSignature.sign(xmlSignContext);
/*
* Completion of undigested ds:References in the ds:Manifests.
*/
for (XMLObject object : objects) {
LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());
List<XMLStructure> objectContentList = object.getContent();
for (XMLStructure objectContent : objectContentList) {
LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());
if (!(objectContent instanceof Manifest)) continue;
Manifest manifest = (Manifest) objectContent;
List<Reference> manifestReferences = manifest.getReferences();
for (Reference manifestReference : manifestReferences) {
if (manifestReference.getDigestValue() != null) continue;
DOMReference manifestDOMReference = (DOMReference)manifestReference;
manifestDOMReference.digest(xmlSignContext);
}
}
}
/*
* Completion of undigested ds:References.
*/
List<Reference> signedInfoReferences = signedInfo.getReferences();
for (Reference signedInfoReference : signedInfoReferences) {
DOMReference domReference = (DOMReference)signedInfoReference;
// ds:Reference with external digest value
if (domReference.getDigestValue() != null) continue;
domReference.digest(xmlSignContext);
}
/*
* Calculation of XML signature digest value.
*/
DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo;
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
domSignedInfo.canonicalize(xmlSignContext, dataStream);
byte[] octets = dataStream.toByteArray();
/*
* TODO: we could be using DigestOutputStream here to optimize memory
* usage.
*/
MessageDigest md = CryptoFunctions.getMessageDigest(signatureConfig.getDigestAlgo());
byte[] digestValue = md.digest(octets);
String description = signatureConfig.getSignatureDescription();
return new DigestInfo(digestValue, signatureConfig.getDigestAlgo(), description);
}
/**
* Helper method for adding informations after the signing.
* Normally {@link #confirmSignature()} is sufficient to be used.
*/
public void postSign(Document document, byte[] signatureValue)
throws IOException, MarshalException, ParserConfigurationException, XmlException {
LOG.log(POILogger.DEBUG, "postSign");
/*
* Check ds:Signature node.
*/
String signatureId = signatureConfig.getPackageSignatureId();
if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {
throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
}
/*
* Insert signature value into the ds:SignatureValue element
*/
NodeList sigValNl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue");
if (sigValNl.getLength() != 1) {
throw new RuntimeException("preSign has to be called before postSign");
}
sigValNl.item(0).setTextContent(Base64.encode(signatureValue));
/*
* Allow signature facets to inject their own stuff.
*/
for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
signatureFacet.postSign(document, signatureConfig.getSigningCertificateChain());
}
writeDocument(document);
}
protected void writeDocument(Document document) throws IOException, XmlException {
XmlOptions xo = new XmlOptions();
Map<String,String> namespaceMap = new HashMap<String,String>();
for(Map.Entry<String,String> entry : signatureConfig.getNamespacePrefixes().entrySet()){
namespaceMap.put(entry.getValue(), entry.getKey());
}
xo.setSaveSuggestedPrefixes(namespaceMap);
xo.setUseDefaultNamespace();
LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");
/*
* Copy the original OOXML content to the signed OOXML package. During
* copying some files need to changed.
*/
OPCPackage pkg = signatureConfig.getOpcPackage();
PackagePartName sigPartName, sigsPartName;
try {
// <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");
// <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>
sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");
} catch (InvalidFormatException e) {
throw new IOException(e);
}
PackagePart sigPart = pkg.getPart(sigPartName);
if (sigPart == null) {
sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART);
}
OutputStream os = sigPart.getOutputStream();
SignatureDocument sigDoc = SignatureDocument.Factory.parse(document);
sigDoc.save(os, xo);
os.close();
PackagePart sigsPart = pkg.getPart(sigsPartName);
if (sigsPart == null) {
// touch empty marker file
sigsPart = pkg.createPart(sigsPartName, ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART);
}
PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
for (PackageRelationship pr : relCol) {
pkg.removeRelationship(pr.getId());
}
pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);
}
@SuppressWarnings("unchecked")
private static <T> List<T> safe(List<T> other) {
return other == null ? Collections.EMPTY_LIST : other;
}
}

View File

@ -0,0 +1,92 @@
/* ====================================================================
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.dsig;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS;
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_NS;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.Event;
import org.w3c.dom.events.EventListener;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.events.MutationEvent;
/**
* This listener class is used, to modify the to be digested xml document,
* e.g. to register id attributes or set prefixes for registered namespaces
*/
public class SignatureMarshalListener implements EventListener, SignatureConfigurable {
ThreadLocal<EventTarget> target = new ThreadLocal<EventTarget>();
SignatureConfig signatureConfig;
public void setEventTarget(EventTarget target) {
this.target.set(target);
}
public void handleEvent(Event e) {
if (!(e instanceof MutationEvent)) return;
MutationEvent mutEvt = (MutationEvent)e;
EventTarget et = mutEvt.getTarget();
if (!(et instanceof Element)) return;
handleElement((Element)et);
}
public void handleElement(Element el) {
EventTarget target = this.target.get();
String packageId = signatureConfig.getPackageSignatureId();
if (el.hasAttribute("Id")) {
el.setIdAttribute("Id", true);
}
setListener(target, this, false);
if (packageId.equals(el.getAttribute("Id"))) {
el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
}
setPrefix(el);
setListener(target, this, true);
}
// helper method to keep it in one place
public static void setListener(EventTarget target, EventListener listener, boolean enabled) {
String type = "DOMSubtreeModified";
boolean useCapture = false;
if (enabled) {
target.addEventListener(type, listener, useCapture);
} else {
target.removeEventListener(type, listener, useCapture);
}
}
protected void setPrefix(Node el) {
String prefix = signatureConfig.getNamespacePrefixes().get(el.getNamespaceURI());
if (prefix != null && el.getPrefix() == null) {
el.setPrefix(prefix);
}
NodeList nl = el.getChildNodes();
for (int i=0; i<nl.getLength(); i++) {
setPrefix(nl.item(i));
}
}
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case the incoming eID certificate is not trusted.
*
* @author Frank Cornelis
*
*/
public class TrustCertificateSecurityException extends
CertificateSecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,85 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.w3c.dom.Document;
/**
* Signature Facet implementation to create enveloped signatures.
*
* @author Frank Cornelis
*
*/
public class EnvelopedSignatureFacet implements SignatureFacet {
private SignatureConfig signatureConfig;
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
@Override
public void postSign(Document document, List<X509Certificate> signingCertificateChain) {
// empty
}
@Override
public void preSign(Document document
, XMLSignatureFactory signatureFactory
, List<Reference> references
, List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
DigestMethod digestMethod = signatureFactory.newDigestMethod
(signatureConfig.getDigestMethodUri(), null);
List<Transform> transforms = new ArrayList<Transform>();
Transform envelopedTransform = signatureFactory.newTransform
(CanonicalizationMethod.ENVELOPED, (TransformParameterSpec) null);
transforms.add(envelopedTransform);
Transform exclusiveTransform = signatureFactory.newTransform
(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null);
transforms.add(exclusiveTransform);
Reference reference = signatureFactory.newReference("", digestMethod,
transforms, null, null);
references.add(reference);
}
}

View File

@ -0,0 +1,168 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import java.security.InvalidAlgorithmParameterException;
import java.security.Key;
import java.security.KeyException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
import javax.xml.crypto.dsig.keyinfo.X509Data;
import org.apache.jcp.xml.dsig.internal.dom.DOMKeyInfo;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Signature Facet implementation that adds ds:KeyInfo to the XML signature.
*
* @author Frank Cornelis
*
*/
public class KeyInfoSignatureFacet implements SignatureFacet {
private static final POILogger LOG = POILogFactory.getLogger(KeyInfoSignatureFacet.class);
SignatureConfig signatureConfig;
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
@Override
public void postSign(Document document, List<X509Certificate> signingCertificateChain)
throws MarshalException {
LOG.log(POILogger.DEBUG, "postSign");
NodeList nl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "Object");
/*
* Make sure we insert right after the ds:SignatureValue element, just
* before the first ds:Object element.
*/
Node nextSibling = (nl.getLength() == 0) ? null : nl.item(0);
/*
* Construct the ds:KeyInfo element using JSR 105.
*/
KeyInfoFactory keyInfoFactory = SignatureInfo.getKeyInfoFactory();
List<Object> x509DataObjects = new ArrayList<Object>();
X509Certificate signingCertificate = signingCertificateChain.get(0);
List<Object> keyInfoContent = new ArrayList<Object>();
if (signatureConfig.isIncludeKeyValue()) {
KeyValue keyValue;
try {
keyValue = keyInfoFactory.newKeyValue(signingCertificate.getPublicKey());
} catch (KeyException e) {
throw new RuntimeException("key exception: " + e.getMessage(), e);
}
keyInfoContent.add(keyValue);
}
if (signatureConfig.isIncludeIssuerSerial()) {
x509DataObjects.add(keyInfoFactory.newX509IssuerSerial(
signingCertificate.getIssuerX500Principal().toString(),
signingCertificate.getSerialNumber()));
}
if (signatureConfig.isIncludeEntireCertificateChain()) {
x509DataObjects.addAll(signingCertificateChain);
} else {
x509DataObjects.add(signingCertificate);
}
if (!x509DataObjects.isEmpty()) {
X509Data x509Data = keyInfoFactory.newX509Data(x509DataObjects);
keyInfoContent.add(x509Data);
}
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(keyInfoContent);
DOMKeyInfo domKeyInfo = (DOMKeyInfo)keyInfo;
Key key = new Key() {
private static final long serialVersionUID = 1L;
public String getAlgorithm() {
return null;
}
public byte[] getEncoded() {
return null;
}
public String getFormat() {
return null;
}
};
Element n = document.getDocumentElement();
DOMSignContext domSignContext = new DOMSignContext(key, n, nextSibling);
for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
domSignContext.putNamespacePrefix(me.getKey(), me.getValue());
}
DOMStructure domStructure = new DOMStructure(n);
domKeyInfo.marshal(domStructure, domSignContext);
// move keyinfo into the right place
if (nextSibling != null) {
NodeList kiNl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "KeyInfo");
if (kiNl.getLength() != 1) {
throw new RuntimeException("KeyInfo wasn't set");
}
nextSibling.getParentNode().insertBefore(kiNl.item(0), nextSibling);
}
}
@Override
public void preSign(
Document document
, XMLSignatureFactory signatureFactory
, List<Reference> references
, List<XMLObject> objects
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
// empty
}
}

View File

@ -0,0 +1,504 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
import javax.xml.XMLConstants;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Manifest;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.SignatureProperties;
import javax.xml.crypto.dsig.SignatureProperty;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.ContentTypes;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService.RelationshipTransformParameterSpec;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.xmlbeans.XmlException;
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTSignatureTime;
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.SignatureTimeDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.microsoft.schemas.office.x2006.digsig.CTSignatureInfoV1;
import com.microsoft.schemas.office.x2006.digsig.SignatureInfoV1Document;
/**
* Office OpenXML Signature Facet implementation.
*
* @author fcorneli
* @see http://msdn.microsoft.com/en-us/library/cc313071.aspx
*/
public class OOXMLSignatureFacet implements SignatureFacet {
private static final POILogger LOG = POILogFactory.getLogger(OOXMLSignatureFacet.class);
private SignatureConfig signatureConfig;
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
@Override
public void preSign(
Document document
, XMLSignatureFactory signatureFactory
, List<Reference> references
, List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
LOG.log(POILogger.DEBUG, "pre sign");
addManifestObject(document, signatureFactory, references, objects);
addSignatureInfo(document, signatureFactory, references, objects);
}
protected void addManifestObject(
Document document
, XMLSignatureFactory signatureFactory
, List<Reference> references
, List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
List<Reference> manifestReferences = new ArrayList<Reference>();
addManifestReferences(signatureFactory, manifestReferences);
Manifest manifest = signatureFactory.newManifest(manifestReferences);
String objectId = "idPackageObject"; // really has to be this value.
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
objectContent.add(manifest);
addSignatureTime(document, signatureFactory, objectContent);
XMLObject xo = signatureFactory.newXMLObject(objectContent, objectId, null, null);
objects.add(xo);
DigestMethod digestMethod = signatureFactory.newDigestMethod
(signatureConfig.getDigestMethodUri(), null);
Reference reference = signatureFactory.newReference
("#" + objectId, digestMethod, null, XML_DIGSIG_NS+"Object", null);
references.add(reference);
}
protected void addManifestReferences
(XMLSignatureFactory signatureFactory, List<Reference> manifestReferences)
throws IOException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, URISyntaxException, XmlException {
OPCPackage ooxml = signatureConfig.getOpcPackage();
List<PackagePart> relsEntryNames = ooxml.getPartsByContentType(ContentTypes.RELATIONSHIPS_PART);
DigestMethod digestMethod = signatureFactory.newDigestMethod
(signatureConfig.getDigestMethodUri(), null);
Set<String> digestedPartNames = new HashSet<String>();
for (PackagePart pp : relsEntryNames) {
String baseUri = pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
PackageRelationshipCollection prc;
try {
prc = new PackageRelationshipCollection(ooxml);
prc.parseRelationshipsPart(pp);
} catch (InvalidFormatException e) {
throw new IOException("Invalid relationship descriptor: "+pp.getPartName().getName(), e);
}
RelationshipTransformParameterSpec parameterSpec = new RelationshipTransformParameterSpec();
for (PackageRelationship relationship : prc) {
String relationshipType = relationship.getRelationshipType();
/*
* ECMA-376 Part 2 - 3rd edition
* 13.2.4.16 Manifest Element
* "The producer shall not create a Manifest element that references any data outside of the package."
*/
if (TargetMode.EXTERNAL == relationship.getTargetMode()) {
continue;
}
if (!isSignedRelationship(relationshipType)) continue;
parameterSpec.addRelationshipReference(relationship.getId());
// TODO: find a better way ...
String partName = baseUri + relationship.getTargetURI().toString();
partName = new URI(partName).normalize().getPath().replace('\\', '/');
LOG.log(POILogger.DEBUG, "part name: " + partName);
String contentType;
try {
PackagePartName relName = PackagingURIHelper.createPartName(partName);
PackagePart pp2 = ooxml.getPart(relName);
contentType = pp2.getContentType();
} catch (InvalidFormatException e) {
throw new IOException(e);
}
if (relationshipType.endsWith("customXml")
&& !(contentType.equals("inkml+xml") || contentType.equals("text/xml"))) {
LOG.log(POILogger.DEBUG, "skipping customXml with content type: " + contentType);
continue;
}
if (!digestedPartNames.contains(partName)) {
// We only digest a part once.
String uri = partName + "?ContentType=" + contentType;
Reference reference = signatureFactory.newReference(uri, digestMethod);
manifestReferences.add(reference);
digestedPartNames.add(partName);
}
}
if (parameterSpec.hasSourceIds()) {
List<Transform> transforms = new ArrayList<Transform>();
transforms.add(signatureFactory.newTransform(
RelationshipTransformService.TRANSFORM_URI,
parameterSpec));
transforms.add(signatureFactory.newTransform(
CanonicalizationMethod.INCLUSIVE,
(TransformParameterSpec) null));
String uri = pp.getPartName().getName()
+ "?ContentType=application/vnd.openxmlformats-package.relationships+xml";
Reference reference = signatureFactory.newReference(uri, digestMethod, transforms, null, null);
manifestReferences.add(reference);
}
}
}
protected void addSignatureTime(
Document document
, XMLSignatureFactory signatureFactory
, List<XMLStructure> objectContent) {
/*
* SignatureTime
*/
DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
fmt.setTimeZone(TimeZone.getTimeZone("UTC"));
String nowStr = fmt.format(signatureConfig.getExecutionTime());
LOG.log(POILogger.DEBUG, "now: " + nowStr);
SignatureTimeDocument sigTime = SignatureTimeDocument.Factory.newInstance();
CTSignatureTime ctTime = sigTime.addNewSignatureTime();
ctTime.setFormat("YYYY-MM-DDThh:mm:ssTZD");
ctTime.setValue(nowStr);
Element n = (Element)document.importNode(ctTime.getDomNode(),true);
List<XMLStructure> signatureTimeContent = new ArrayList<XMLStructure>();
signatureTimeContent.add(new DOMStructure(n));
SignatureProperty signatureTimeSignatureProperty = signatureFactory
.newSignatureProperty(signatureTimeContent, "#" + signatureConfig.getPackageSignatureId(),
"idSignatureTime");
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
signaturePropertyContent.add(signatureTimeSignatureProperty);
SignatureProperties signatureProperties = signatureFactory
.newSignatureProperties(signaturePropertyContent,
"id-signature-time-" + signatureConfig.getExecutionTime());
objectContent.add(signatureProperties);
}
protected void addSignatureInfo(Document document,
XMLSignatureFactory signatureFactory,
List<Reference> references,
List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
CTSignatureInfoV1 ctSigV1 = sigV1.addNewSignatureInfoV1();
ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
signatureInfoContent.add(new DOMStructure(n));
SignatureProperty signatureInfoSignatureProperty = signatureFactory
.newSignatureProperty(signatureInfoContent, "#" + signatureConfig.getPackageSignatureId(),
"idOfficeV1Details");
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
signaturePropertyContent.add(signatureInfoSignatureProperty);
SignatureProperties signatureProperties = signatureFactory
.newSignatureProperties(signaturePropertyContent, null);
objectContent.add(signatureProperties);
String objectId = "idOfficeObject";
objects.add(signatureFactory.newXMLObject(objectContent, objectId, null, null));
DigestMethod digestMethod = signatureFactory.newDigestMethod
(signatureConfig.getDigestMethodUri(), null);
Reference reference = signatureFactory.newReference
("#" + objectId, digestMethod, null, XML_DIGSIG_NS+"Object", null);
references.add(reference);
}
@Override
public void postSign(Document document, List<X509Certificate> signingCertificateChain) {
// empty
}
protected static String getRelationshipReferenceURI(String zipEntryName) {
return "/"
+ zipEntryName
+ "?ContentType=application/vnd.openxmlformats-package.relationships+xml";
}
protected static String getResourceReferenceURI(String resourceName, String contentType) {
return "/" + resourceName + "?ContentType=" + contentType;
}
protected static boolean isSignedRelationship(String relationshipType) {
LOG.log(POILogger.DEBUG, "relationship type: " + relationshipType);
for (String signedTypeExtension : signed) {
if (relationshipType.endsWith(signedTypeExtension)) {
return true;
}
}
if (relationshipType.endsWith("customXml")) {
LOG.log(POILogger.DEBUG, "customXml relationship type");
return true;
}
return false;
}
public static final String[] contentTypes = {
/*
* Word
*/
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
"application/vnd.openxmlformats-officedocument.theme+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml",
"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
/*
* Word 2010
*/
"application/vnd.ms-word.stylesWithEffects+xml",
/*
* Excel
*/
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
/*
* Powerpoint
*/
"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml",
"application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml",
"application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml",
"application/vnd.openxmlformats-officedocument.presentationml.slide+xml",
"application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml",
/*
* Powerpoint 2010
*/
"application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml",
"application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"
};
/**
* Office 2010 list of signed types (extensions).
*/
public static final String[] signed = {
"powerPivotData", //
"activeXControlBinary", //
"attachedToolbars", //
"connectorXml", //
"downRev", //
"functionPrototypes", //
"graphicFrameDoc", //
"groupShapeXml", //
"ink", //
"keyMapCustomizations", //
"legacyDiagramText", //
"legacyDocTextInfo", //
"officeDocument", //
"pictureXml", //
"shapeXml", //
"smartTags", //
"ui/altText", //
"ui/buttonSize", //
"ui/controlID", //
"ui/description", //
"ui/enabled", //
"ui/extensibility", //
"ui/helperText", //
"ui/imageID", //
"ui/imageMso", //
"ui/keyTip", //
"ui/label", //
"ui/lcid", //
"ui/loud", //
"ui/pressed", //
"ui/progID", //
"ui/ribbonID", //
"ui/showImage", //
"ui/showLabel", //
"ui/supertip", //
"ui/target", //
"ui/text", //
"ui/title", //
"ui/tooltip", //
"ui/userCustomization", //
"ui/visible", //
"userXmlData", //
"vbaProject", //
"wordVbaData", //
"wsSortMap", //
"xlBinaryIndex", //
"xlExternalLinkPath/xlAlternateStartup", //
"xlExternalLinkPath/xlLibrary", //
"xlExternalLinkPath/xlPathMissing", //
"xlExternalLinkPath/xlStartup", //
"xlIntlMacrosheet", //
"xlMacrosheet", //
"customData", //
"diagramDrawing", //
"hdphoto", //
"inkXml", //
"media", //
"slicer", //
"slicerCache", //
"stylesWithEffects", //
"ui/extensibility", //
"chartColorStyle", //
"chartLayout", //
"chartStyle", //
"dictionary", //
"timeline", //
"timelineCache", //
"aFChunk", //
"attachedTemplate", //
"audio", //
"calcChain", //
"chart", //
"chartsheet", //
"chartUserShapes", //
"commentAuthors", //
"comments", //
"connections", //
"control", //
"customProperty", //
"customXml", //
"diagramColors", //
"diagramData", //
"diagramLayout", //
"diagramQuickStyle", //
"dialogsheet", //
"drawing", //
"endnotes", //
"externalLink", //
"externalLinkPath", //
"font", //
"fontTable", //
"footer", //
"footnotes", //
"glossaryDocument", //
"handoutMaster", //
"header", //
"hyperlink", //
"image", //
"mailMergeHeaderSource", //
"mailMergeRecipientData", //
"mailMergeSource", //
"notesMaster", //
"notesSlide", //
"numbering", //
"officeDocument", //
"oleObject", //
"package", //
"pivotCacheDefinition", //
"pivotCacheRecords", //
"pivotTable", //
"presProps", //
"printerSettings", //
"queryTable", //
"recipientData", //
"settings", //
"sharedStrings", //
"sheetMetadata", //
"slide", //
"slideLayout", //
"slideMaster", //
"slideUpdateInfo", //
"slideUpdateUrl", //
"styles", //
"table", //
"tableSingleCells", //
"tableStyles", //
"tags", //
"theme", //
"themeOverride", //
"transform", //
"video", //
"viewProps", //
"volatileDependencies", //
"webSettings", //
"worksheet", //
"xmlMaps", //
"ctrlProp", //
"customData", //
"diagram", //
"diagramColorsHeader", //
"diagramLayoutHeader", //
"diagramQuickStyleHeader", //
"documentParts", //
"slicer", //
"slicerCache", //
"vmlDrawing" //
};
}

View File

@ -0,0 +1,94 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.List;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.xmlbeans.XmlException;
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
import org.etsi.uri.x01903.v13.UnsignedPropertiesType;
import org.etsi.uri.x01903.v13.UnsignedSignaturePropertiesType;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Work-around for Office2010 to accept the XAdES-BES/EPES signature.
*
* xades:UnsignedProperties/xades:UnsignedSignatureProperties needs to be
* present.
*
* @author Frank Cornelis
*
*/
public class Office2010SignatureFacet implements SignatureFacet {
public void setSignatureConfig(SignatureConfig signatureConfig) {
// this.signatureConfig = signatureConfig;
}
@Override
public void preSign(
Document document
, XMLSignatureFactory signatureFactory
, List<Reference> references
, List<XMLObject> objects
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
}
@Override
public void postSign(Document document, List<X509Certificate> signingCertificateChain)
throws XmlException {
// check for XAdES-BES
NodeList nl = document.getElementsByTagNameNS(XADES_132_NS, "QualifyingProperties");
if (nl.getLength() != 1) {
throw new IllegalArgumentException("no XAdES-BES extension present");
}
QualifyingPropertiesType qualProps =
QualifyingPropertiesType.Factory.parse(nl.item(0));
// create basic XML container structure
UnsignedPropertiesType unsignedProps = qualProps.getUnsignedProperties();
if (unsignedProps == null) {
unsignedProps = qualProps.addNewUnsignedProperties();
}
UnsignedSignaturePropertiesType unsignedSigProps = unsignedProps.getUnsignedSignatureProperties();
if (unsignedSigProps == null) {
unsignedSigProps = unsignedProps.addNewUnsignedSignatureProperties();
}
Node n = document.importNode(qualProps.getDomNode().getFirstChild(), true);
nl.item(0).getParentNode().replaceChild(n, nl.item(0));
}
}

View File

@ -0,0 +1,96 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import java.io.IOException;
import java.net.URISyntaxException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.List;
import javax.xml.XMLConstants;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import org.apache.poi.openxml4j.opc.PackageNamespaces;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
import org.apache.xmlbeans.XmlException;
import org.w3c.dom.Document;
/**
* JSR105 Signature Facet interface.
*
* @author Frank Cornelis
*
*/
public interface SignatureFacet extends SignatureConfigurable {
String XML_NS = XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
String XML_DIGSIG_NS = XMLSignature.XMLNS;
String OO_DIGSIG_NS = PackageNamespaces.DIGITAL_SIGNATURE;
String MS_DIGSIG_NS = "http://schemas.microsoft.com/office/2006/digsig";
String XADES_132_NS = "http://uri.etsi.org/01903/v1.3.2#";
String XADES_141_NS = "http://uri.etsi.org/01903/v1.4.1#";
/**
* This method is being invoked by the XML signature service engine during
* pre-sign phase. Via this method a signature facet implementation can add
* signature facets to an XML signature.
*
* @param signatureFactory
* @param document
* @param signatureId
* @param signingCertificateChain
* the optional signing certificate chain
* @param references
* @param objects
* @throws InvalidAlgorithmParameterException
* @throws NoSuchAlgorithmException
*/
void preSign(
Document document
, XMLSignatureFactory signatureFactory
, List<Reference> references
, List<XMLObject> objects
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException;
/**
* This method is being invoked by the XML signature service engine during
* the post-sign phase. Via this method a signature facet can extend the XML
* signatures with for example key information.
*
* @param signatureElement
* @param signingCertificateChain
*/
void postSign(
Document document
, List<X509Certificate> signingCertificateChain
) throws MarshalException, XmlException;
}

View File

@ -0,0 +1,304 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import java.security.InvalidAlgorithmParameterException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.DigestMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.Transform;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlString;
import org.etsi.uri.x01903.v13.AnyType;
import org.etsi.uri.x01903.v13.CertIDListType;
import org.etsi.uri.x01903.v13.CertIDType;
import org.etsi.uri.x01903.v13.ClaimedRolesListType;
import org.etsi.uri.x01903.v13.DataObjectFormatType;
import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
import org.etsi.uri.x01903.v13.IdentifierType;
import org.etsi.uri.x01903.v13.ObjectIdentifierType;
import org.etsi.uri.x01903.v13.QualifyingPropertiesDocument;
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
import org.etsi.uri.x01903.v13.SigPolicyQualifiersListType;
import org.etsi.uri.x01903.v13.SignaturePolicyIdType;
import org.etsi.uri.x01903.v13.SignaturePolicyIdentifierType;
import org.etsi.uri.x01903.v13.SignedDataObjectPropertiesType;
import org.etsi.uri.x01903.v13.SignedPropertiesType;
import org.etsi.uri.x01903.v13.SignedSignaturePropertiesType;
import org.etsi.uri.x01903.v13.SignerRoleType;
import org.w3.x2000.x09.xmldsig.DigestMethodType;
import org.w3.x2000.x09.xmldsig.X509IssuerSerialType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* XAdES Signature Facet. Implements XAdES v1.4.1 which is compatible with XAdES
* v1.3.2. The implemented XAdES format is XAdES-BES/EPES. It's up to another
* part of the signature service to upgrade the XAdES-BES to a XAdES-X-L.
*
* This implementation has been tested against an implementation that
* participated multiple ETSI XAdES plugtests.
*
* @author Frank Cornelis
* @see http://en.wikipedia.org/wiki/XAdES
*
*/
public class XAdESSignatureFacet implements SignatureFacet {
private static final POILogger LOG = POILogFactory.getLogger(XAdESSignatureFacet.class);
private static final String XADES_TYPE = "http://uri.etsi.org/01903#SignedProperties";
private SignatureConfig signatureConfig;
private Map<String, String> dataObjectFormatMimeTypes = new HashMap<String, String>();
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
@Override
public void postSign(Document document, List<X509Certificate> signingCertificateChain) {
LOG.log(POILogger.DEBUG, "postSign");
}
@Override
public void preSign(Document document,
XMLSignatureFactory signatureFactory,
List<Reference> references, List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
LOG.log(POILogger.DEBUG, "preSign");
// QualifyingProperties
QualifyingPropertiesDocument qualDoc = QualifyingPropertiesDocument.Factory.newInstance();
QualifyingPropertiesType qualifyingProperties = qualDoc.addNewQualifyingProperties();
qualifyingProperties.setTarget("#" + signatureConfig.getPackageSignatureId());
// SignedProperties
SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties();
signedProperties.setId(signatureConfig.getXadesSignatureId());
// SignedSignatureProperties
SignedSignaturePropertiesType signedSignatureProperties = signedProperties.addNewSignedSignatureProperties();
// SigningTime
Calendar xmlGregorianCalendar = Calendar.getInstance();
xmlGregorianCalendar.setTimeZone(TimeZone.getTimeZone("Z"));
xmlGregorianCalendar.setTime(signatureConfig.getExecutionTime());
xmlGregorianCalendar.clear(Calendar.MILLISECOND);
signedSignatureProperties.setSigningTime(xmlGregorianCalendar);
// SigningCertificate
if (signatureConfig.getSigningCertificateChain() == null
|| signatureConfig.getSigningCertificateChain().isEmpty()) {
throw new RuntimeException("no signing certificate chain available");
}
CertIDListType signingCertificates = signedSignatureProperties.addNewSigningCertificate();
CertIDType certId = signingCertificates.addNewCert();
X509Certificate certificate = signatureConfig.getSigningCertificateChain().get(0);
setCertID(certId, signatureConfig, signatureConfig.isXadesIssuerNameNoReverseOrder(), certificate);
// ClaimedRole
String role = signatureConfig.getXadesRole();
if (role != null && !role.isEmpty()) {
SignerRoleType signerRole = signedSignatureProperties.addNewSignerRole();
signedSignatureProperties.setSignerRole(signerRole);
ClaimedRolesListType claimedRolesList = signerRole.addNewClaimedRoles();
AnyType claimedRole = claimedRolesList.addNewClaimedRole();
XmlString roleString = XmlString.Factory.newInstance();
roleString.setStringValue(role);
insertXChild(claimedRole, roleString);
}
// XAdES-EPES
SignaturePolicyService policyService = signatureConfig.getSignaturePolicyService();
if (policyService != null) {
SignaturePolicyIdentifierType signaturePolicyIdentifier =
signedSignatureProperties.addNewSignaturePolicyIdentifier();
SignaturePolicyIdType signaturePolicyId = signaturePolicyIdentifier.addNewSignaturePolicyId();
ObjectIdentifierType objectIdentifier = signaturePolicyId.addNewSigPolicyId();
objectIdentifier.setDescription(policyService.getSignaturePolicyDescription());
IdentifierType identifier = objectIdentifier.addNewIdentifier();
identifier.setStringValue(policyService.getSignaturePolicyIdentifier());
byte[] signaturePolicyDocumentData = policyService.getSignaturePolicyDocument();
DigestAlgAndValueType sigPolicyHash = signaturePolicyId.addNewSigPolicyHash();
setDigestAlgAndValue(sigPolicyHash, signaturePolicyDocumentData, signatureConfig.getDigestAlgo());
String signaturePolicyDownloadUrl = policyService.getSignaturePolicyDownloadUrl();
if (null != signaturePolicyDownloadUrl) {
SigPolicyQualifiersListType sigPolicyQualifiers = signaturePolicyId.addNewSigPolicyQualifiers();
AnyType sigPolicyQualifier = sigPolicyQualifiers.addNewSigPolicyQualifier();
XmlString spUriElement = XmlString.Factory.newInstance();
spUriElement.setStringValue(signaturePolicyDownloadUrl);
insertXChild(sigPolicyQualifier, spUriElement);
}
} else if (signatureConfig.isXadesSignaturePolicyImplied()) {
SignaturePolicyIdentifierType signaturePolicyIdentifier =
signedSignatureProperties.addNewSignaturePolicyIdentifier();
signaturePolicyIdentifier.addNewSignaturePolicyImplied();
}
// DataObjectFormat
if (!dataObjectFormatMimeTypes.isEmpty()) {
SignedDataObjectPropertiesType signedDataObjectProperties =
signedProperties.addNewSignedDataObjectProperties();
List<DataObjectFormatType> dataObjectFormats = signedDataObjectProperties
.getDataObjectFormatList();
for (Map.Entry<String, String> dataObjectFormatMimeType : this.dataObjectFormatMimeTypes
.entrySet()) {
DataObjectFormatType dataObjectFormat = DataObjectFormatType.Factory.newInstance();
dataObjectFormat.setObjectReference("#" + dataObjectFormatMimeType.getKey());
dataObjectFormat.setMimeType(dataObjectFormatMimeType.getValue());
dataObjectFormats.add(dataObjectFormat);
}
}
// add XAdES ds:Object
List<XMLStructure> xadesObjectContent = new ArrayList<XMLStructure>();
Element qualDocElSrc = (Element)qualifyingProperties.getDomNode();
Element qualDocEl = (Element)document.importNode(qualDocElSrc, true);
xadesObjectContent.add(new DOMStructure(qualDocEl));
XMLObject xadesObject = signatureFactory.newXMLObject(xadesObjectContent, null, null, null);
objects.add(xadesObject);
// add XAdES ds:Reference
DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestMethodUri(), null);
List<Transform> transforms = new ArrayList<Transform>();
Transform exclusiveTransform = signatureFactory
.newTransform(CanonicalizationMethod.INCLUSIVE,
(TransformParameterSpec) null);
transforms.add(exclusiveTransform);
Reference reference = signatureFactory.newReference
("#"+signatureConfig.getXadesSignatureId(), digestMethod, transforms, XADES_TYPE, null);
references.add(reference);
}
/**
* Gives back the JAXB DigestAlgAndValue data structure.
*
* @param data
* @param xadesObjectFactory
* @param xmldsigObjectFactory
* @param hashAlgo
* @return
*/
protected static void setDigestAlgAndValue(
DigestAlgAndValueType digestAlgAndValue,
byte[] data,
HashAlgorithm digestAlgo) {
DigestMethodType digestMethod = digestAlgAndValue.addNewDigestMethod();
digestMethod.setAlgorithm(SignatureConfig.getDigestMethodUri(digestAlgo));
MessageDigest messageDigest = CryptoFunctions.getMessageDigest(digestAlgo);
byte[] digestValue = messageDigest.digest(data);
digestAlgAndValue.setDigestValue(digestValue);
}
/**
* Gives back the JAXB CertID data structure.
*/
protected static void setCertID
(CertIDType certId, SignatureConfig signatureConfig, boolean issuerNameNoReverseOrder, X509Certificate certificate) {
X509IssuerSerialType issuerSerial = certId.addNewIssuerSerial();
String issuerName;
if (issuerNameNoReverseOrder) {
/*
* Make sure the DN is encoded using the same order as present
* within the certificate. This is an Office2010 work-around.
* Should be reverted back.
*
* XXX: not correct according to RFC 4514.
*/
// TODO: check if issuerName is different on getTBSCertificate
// issuerName = PrincipalUtil.getIssuerX509Principal(certificate).getName().replace(",", ", ");
issuerName = certificate.getIssuerDN().getName().replace(",", ", ");
} else {
issuerName = certificate.getIssuerX500Principal().toString();
}
issuerSerial.setX509IssuerName(issuerName);
issuerSerial.setX509SerialNumber(certificate.getSerialNumber());
byte[] encodedCertificate;
try {
encodedCertificate = certificate.getEncoded();
} catch (CertificateEncodingException e) {
throw new RuntimeException("certificate encoding error: "
+ e.getMessage(), e);
}
DigestAlgAndValueType certDigest = certId.addNewCertDigest();
setDigestAlgAndValue(certDigest, encodedCertificate, signatureConfig.getXadesDigestAlgo());
}
/**
* Adds a mime-type for the given ds:Reference (referred via its @URI). This
* information is added via the xades:DataObjectFormat element.
*
* @param dsReferenceUri
* @param mimetype
*/
public void addMimeType(String dsReferenceUri, String mimetype) {
this.dataObjectFormatMimeTypes.put(dsReferenceUri, mimetype);
}
protected static void insertXChild(XmlObject root, XmlObject child) {
XmlCursor rootCursor = root.newCursor();
rootCursor.toEndToken();
XmlCursor childCursor = child.newCursor();
childCursor.toNextToken();
childCursor.moveXml(rootCursor);
childCursor.dispose();
rootCursor.dispose();
}
}

View File

@ -0,0 +1,433 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
import static org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet.insertXChild;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.Reference;
import javax.xml.crypto.dsig.XMLObject;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.xml.security.c14n.Canonicalizer;
import org.apache.xmlbeans.XmlException;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.ResponderID;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cert.ocsp.RespID;
import org.etsi.uri.x01903.v13.CRLIdentifierType;
import org.etsi.uri.x01903.v13.CRLRefType;
import org.etsi.uri.x01903.v13.CRLRefsType;
import org.etsi.uri.x01903.v13.CRLValuesType;
import org.etsi.uri.x01903.v13.CertIDListType;
import org.etsi.uri.x01903.v13.CertIDType;
import org.etsi.uri.x01903.v13.CertificateValuesType;
import org.etsi.uri.x01903.v13.CompleteCertificateRefsType;
import org.etsi.uri.x01903.v13.CompleteRevocationRefsType;
import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
import org.etsi.uri.x01903.v13.EncapsulatedPKIDataType;
import org.etsi.uri.x01903.v13.OCSPIdentifierType;
import org.etsi.uri.x01903.v13.OCSPRefType;
import org.etsi.uri.x01903.v13.OCSPRefsType;
import org.etsi.uri.x01903.v13.OCSPValuesType;
import org.etsi.uri.x01903.v13.QualifyingPropertiesDocument;
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
import org.etsi.uri.x01903.v13.ResponderIDType;
import org.etsi.uri.x01903.v13.RevocationValuesType;
import org.etsi.uri.x01903.v13.UnsignedPropertiesType;
import org.etsi.uri.x01903.v13.UnsignedSignaturePropertiesType;
import org.etsi.uri.x01903.v13.XAdESTimeStampType;
import org.etsi.uri.x01903.v14.ValidationDataType;
import org.w3.x2000.x09.xmldsig.CanonicalizationMethodType;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* XAdES-X-L v1.4.1 signature facet. This signature facet implementation will
* upgrade a given XAdES-BES/EPES signature to XAdES-X-L.
*
* We don't inherit from XAdESSignatureFacet as we also want to be able to use
* this facet out of the context of a signature creation. This signature facet
* assumes that the signature is already XAdES-BES/EPES compliant.
*
* This implementation has been tested against an implementation that
* participated multiple ETSI XAdES plugtests.
*
* @author Frank Cornelis
* @see XAdESSignatureFacet
*/
public class XAdESXLSignatureFacet implements SignatureFacet {
private static final POILogger LOG = POILogFactory.getLogger(XAdESXLSignatureFacet.class);
private SignatureConfig signatureConfig;
private String c14nAlgoId = CanonicalizationMethod.EXCLUSIVE;
private final CertificateFactory certificateFactory;
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
/**
* Convenience constructor.
*
* @param timeStampService
* the time-stamp service used for XAdES-T and XAdES-X.
* @param revocationDataService
*/
public XAdESXLSignatureFacet() {
try {
this.certificateFactory = CertificateFactory.getInstance("X.509");
} catch (CertificateException e) {
throw new RuntimeException("X509 JCA error: " + e.getMessage(), e);
}
}
public void setCanonicalizerAlgorithm(String c14nAlgoId) {
this.c14nAlgoId = c14nAlgoId;
}
@Override
public void postSign(Document document,
List<X509Certificate> signingCertificateChain
) throws XmlException {
LOG.log(POILogger.DEBUG, "XAdES-X-L post sign phase");
QualifyingPropertiesDocument qualDoc = null;
QualifyingPropertiesType qualProps = null;
// check for XAdES-BES
NodeList qualNl = document.getElementsByTagNameNS(XADES_132_NS, "QualifyingProperties");
if (qualNl.getLength() == 1) {
qualDoc = QualifyingPropertiesDocument.Factory.parse(qualNl.item(0));
qualProps = qualDoc.getQualifyingProperties();
} else {
throw new IllegalArgumentException("no XAdES-BES extension present");
}
// create basic XML container structure
UnsignedPropertiesType unsignedProps = qualProps.getUnsignedProperties();
if (unsignedProps == null) {
unsignedProps = qualProps.addNewUnsignedProperties();
}
UnsignedSignaturePropertiesType unsignedSigProps = unsignedProps.getUnsignedSignatureProperties();
if (unsignedSigProps == null) {
unsignedSigProps = unsignedProps.addNewUnsignedSignatureProperties();
}
// create the XAdES-T time-stamp
NodeList nlSigVal = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue");
if (nlSigVal.getLength() != 1) {
throw new IllegalArgumentException("SignatureValue is not set.");
}
RevocationData tsaRevocationDataXadesT = new RevocationData();
LOG.log(POILogger.DEBUG, "creating XAdES-T time-stamp");
XAdESTimeStampType signatureTimeStamp = createXAdESTimeStamp
(Collections.singletonList(nlSigVal.item(0)), tsaRevocationDataXadesT);
// marshal the XAdES-T extension
unsignedSigProps.addNewSignatureTimeStamp().set(signatureTimeStamp);
// xadesv141::TimeStampValidationData
if (tsaRevocationDataXadesT.hasRevocationDataEntries()) {
ValidationDataType validationData = createValidationData(tsaRevocationDataXadesT);
insertXChild(unsignedSigProps, validationData);
}
if (signatureConfig.getRevocationDataService() == null) {
/*
* Without revocation data service we cannot construct the XAdES-C
* extension.
*/
return;
}
// XAdES-C: complete certificate refs
CompleteCertificateRefsType completeCertificateRefs =
unsignedSigProps.addNewCompleteCertificateRefs();
CertIDListType certIdList = completeCertificateRefs.addNewCertRefs();
/*
* We skip the signing certificate itself according to section
* 4.4.3.2 of the XAdES 1.4.1 specification.
*/
int chainSize = signingCertificateChain.size();
if (chainSize > 1) {
for (X509Certificate cert : signingCertificateChain.subList(1, chainSize)) {
CertIDType certId = certIdList.addNewCert();
XAdESSignatureFacet.setCertID(certId, signatureConfig, false, cert);
}
}
// XAdES-C: complete revocation refs
CompleteRevocationRefsType completeRevocationRefs =
unsignedSigProps.addNewCompleteRevocationRefs();
RevocationData revocationData = signatureConfig.getRevocationDataService()
.getRevocationData(signingCertificateChain);
if (revocationData.hasCRLs()) {
CRLRefsType crlRefs = completeRevocationRefs.addNewCRLRefs();
completeRevocationRefs.setCRLRefs(crlRefs);
for (byte[] encodedCrl : revocationData.getCRLs()) {
CRLRefType crlRef = crlRefs.addNewCRLRef();
X509CRL crl;
try {
crl = (X509CRL) this.certificateFactory
.generateCRL(new ByteArrayInputStream(encodedCrl));
} catch (CRLException e) {
throw new RuntimeException("CRL parse error: "
+ e.getMessage(), e);
}
CRLIdentifierType crlIdentifier = crlRef.addNewCRLIdentifier();
String issuerName = crl.getIssuerDN().getName().replace(",", ", ");
crlIdentifier.setIssuer(issuerName);
Calendar cal = Calendar.getInstance();
cal.setTime(crl.getThisUpdate());
crlIdentifier.setIssueTime(cal);
crlIdentifier.setNumber(getCrlNumber(crl));
DigestAlgAndValueType digestAlgAndValue = crlRef.addNewDigestAlgAndValue();
XAdESSignatureFacet.setDigestAlgAndValue(digestAlgAndValue, encodedCrl, signatureConfig.getDigestAlgo());
}
}
if (revocationData.hasOCSPs()) {
OCSPRefsType ocspRefs = completeRevocationRefs.addNewOCSPRefs();
for (byte[] ocsp : revocationData.getOCSPs()) {
try {
OCSPRefType ocspRef = ocspRefs.addNewOCSPRef();
DigestAlgAndValueType digestAlgAndValue = ocspRef.addNewDigestAlgAndValue();
XAdESSignatureFacet.setDigestAlgAndValue(digestAlgAndValue, ocsp, signatureConfig.getDigestAlgo());
OCSPIdentifierType ocspIdentifier = ocspRef.addNewOCSPIdentifier();
OCSPResp ocspResp = new OCSPResp(ocsp);
BasicOCSPResp basicOcspResp = (BasicOCSPResp)ocspResp.getResponseObject();
Calendar cal = Calendar.getInstance();
cal.setTime(basicOcspResp.getProducedAt());
ocspIdentifier.setProducedAt(cal);
ResponderIDType responderId = ocspIdentifier.addNewResponderID();
RespID respId = basicOcspResp.getResponderId();
ResponderID ocspResponderId = respId.toASN1Object();
DERTaggedObject derTaggedObject = (DERTaggedObject)ocspResponderId.toASN1Primitive();
if (2 == derTaggedObject.getTagNo()) {
ASN1OctetString keyHashOctetString = (ASN1OctetString)derTaggedObject.getObject();
byte key[] = keyHashOctetString.getOctets();
responderId.setByKey(key);
} else {
X500Name name = X500Name.getInstance(derTaggedObject.getObject());
String nameStr = name.toString();
responderId.setByName(nameStr);
}
} catch (Exception e) {
throw new RuntimeException("OCSP decoding error: " + e.getMessage(), e);
}
}
}
// marshal XAdES-C
// XAdES-X Type 1 timestamp
List<Node> timeStampNodesXadesX1 = new ArrayList<Node>();
timeStampNodesXadesX1.add(nlSigVal.item(0));
timeStampNodesXadesX1.add(signatureTimeStamp.getDomNode());
timeStampNodesXadesX1.add(completeCertificateRefs.getDomNode());
timeStampNodesXadesX1.add(completeRevocationRefs.getDomNode());
RevocationData tsaRevocationDataXadesX1 = new RevocationData();
LOG.log(POILogger.DEBUG, "creating XAdES-X time-stamp");
XAdESTimeStampType timeStampXadesX1 = createXAdESTimeStamp
(timeStampNodesXadesX1, tsaRevocationDataXadesX1);
if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) {
ValidationDataType timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1);
insertXChild(unsignedSigProps, timeStampXadesX1ValidationData);
}
// marshal XAdES-X
unsignedSigProps.addNewSigAndRefsTimeStamp().set(timeStampXadesX1);
// XAdES-X-L
CertificateValuesType certificateValues = unsignedSigProps.addNewCertificateValues();
for (X509Certificate certificate : signingCertificateChain) {
EncapsulatedPKIDataType encapsulatedPKIDataType = certificateValues.addNewEncapsulatedX509Certificate();
try {
encapsulatedPKIDataType.setByteArrayValue(certificate.getEncoded());
} catch (CertificateEncodingException e) {
throw new RuntimeException("certificate encoding error: " + e.getMessage(), e);
}
}
RevocationValuesType revocationValues = unsignedSigProps.addNewRevocationValues();
createRevocationValues(revocationValues, revocationData);
// marshal XAdES-X-L
Node n = document.importNode(qualProps.getDomNode(), true);
qualNl.item(0).getParentNode().replaceChild(n, qualNl.item(0));
}
public static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) {
ByteArrayOutputStream c14nValue = new ByteArrayOutputStream();
try {
for (Node node : nodeList) {
/*
* Re-initialize the c14n else the namespaces will get cached
* and will be missing from the c14n resulting nodes.
*/
Canonicalizer c14n = Canonicalizer.getInstance(c14nAlgoId);
c14nValue.write(c14n.canonicalizeSubtree(node));
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("c14n error: " + e.getMessage(), e);
}
return c14nValue.toByteArray();
}
@Override
public void preSign(Document document,
XMLSignatureFactory signatureFactory,
List<Reference> references, List<XMLObject> objects)
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
// nothing to do here
}
private BigInteger getCrlNumber(X509CRL crl) {
try {
byte[] crlNumberExtensionValue = crl.getExtensionValue(Extension.cRLNumber.getId());
if (null == crlNumberExtensionValue) {
return null;
}
@SuppressWarnings("resource")
ASN1InputStream asn1InputStream = new ASN1InputStream(crlNumberExtensionValue);
ASN1OctetString octetString = (ASN1OctetString)asn1InputStream.readObject();
byte[] octets = octetString.getOctets();
asn1InputStream = new ASN1InputStream(octets);
ASN1Integer integer = (ASN1Integer)asn1InputStream.readObject();
BigInteger crlNumber = integer.getPositiveValue();
return crlNumber;
} catch (Exception e) {
throw new RuntimeException("I/O error: " + e.getMessage(), e);
}
}
private XAdESTimeStampType createXAdESTimeStamp(
List<Node> nodeList,
RevocationData revocationData) {
byte[] c14nSignatureValueElement = getC14nValue(nodeList, c14nAlgoId);
return createXAdESTimeStamp(c14nSignatureValueElement, revocationData);
}
private XAdESTimeStampType createXAdESTimeStamp(byte[] data, RevocationData revocationData) {
// create the time-stamp
byte[] timeStampToken;
try {
timeStampToken = signatureConfig.getTspService().timeStamp(data, revocationData);
} catch (Exception e) {
throw new RuntimeException("error while creating a time-stamp: "
+ e.getMessage(), e);
}
// create a XAdES time-stamp container
XAdESTimeStampType xadesTimeStamp = XAdESTimeStampType.Factory.newInstance();
xadesTimeStamp.setId("time-stamp-" + UUID.randomUUID().toString());
CanonicalizationMethodType c14nMethod = xadesTimeStamp.addNewCanonicalizationMethod();
c14nMethod.setAlgorithm(c14nAlgoId);
// embed the time-stamp
EncapsulatedPKIDataType encapsulatedTimeStamp = xadesTimeStamp.addNewEncapsulatedTimeStamp();
encapsulatedTimeStamp.setByteArrayValue(timeStampToken);
encapsulatedTimeStamp.setId("time-stamp-token-" + UUID.randomUUID().toString());
return xadesTimeStamp;
}
private ValidationDataType createValidationData(
RevocationData revocationData) {
ValidationDataType validationData = ValidationDataType.Factory.newInstance();
RevocationValuesType revocationValues = validationData.addNewRevocationValues();
createRevocationValues(revocationValues, revocationData);
return validationData;
}
private void createRevocationValues(
RevocationValuesType revocationValues, RevocationData revocationData) {
if (revocationData.hasCRLs()) {
CRLValuesType crlValues = revocationValues.addNewCRLValues();
for (byte[] crl : revocationData.getCRLs()) {
EncapsulatedPKIDataType encapsulatedCrlValue = crlValues.addNewEncapsulatedCRLValue();
encapsulatedCrlValue.setByteArrayValue(crl);
}
}
if (revocationData.hasOCSPs()) {
OCSPValuesType ocspValues = revocationValues.addNewOCSPValues();
for (byte[] ocsp : revocationData.getOCSPs()) {
EncapsulatedPKIDataType encapsulatedOcspValue = ocspValues.addNewEncapsulatedOCSPValue();
encapsulatedOcspValue.setByteArrayValue(ocsp);
}
}
}
}

View File

@ -0,0 +1,246 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.Provider;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import javax.xml.crypto.Data;
import javax.xml.crypto.MarshalException;
import javax.xml.crypto.OctetStreamData;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dom.DOMStructure;
import javax.xml.crypto.dsig.TransformException;
import javax.xml.crypto.dsig.TransformService;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.util.XmlSort;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTRelationshipReference;
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.RelationshipReferenceDocument;
import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationship;
import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationships;
import org.openxmlformats.schemas.xpackage.x2006.relationships.RelationshipsDocument;
import org.openxmlformats.schemas.xpackage.x2006.relationships.STTargetMode;
import org.w3.x2000.x09.xmldsig.TransformDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* JSR105 implementation of the RelationshipTransform transformation.
*
* <p>
* Specs: http://openiso.org/Ecma/376/Part2/12.2.4#26
* </p>
*/
public class RelationshipTransformService extends TransformService {
public static final String TRANSFORM_URI = "http://schemas.openxmlformats.org/package/2006/RelationshipTransform";
private final List<String> sourceIds;
private static final POILogger LOG = POILogFactory.getLogger(RelationshipTransformService.class);
/**
* Relationship Transform parameter specification class.
*/
public static class RelationshipTransformParameterSpec implements TransformParameterSpec {
List<String> sourceIds = new ArrayList<String>();
public void addRelationshipReference(String relationshipId) {
sourceIds.add(relationshipId);
}
public boolean hasSourceIds() {
return !sourceIds.isEmpty();
}
}
public RelationshipTransformService() {
super();
LOG.log(POILogger.DEBUG, "constructor");
this.sourceIds = new ArrayList<String>();
}
/**
* Register the provider for this TransformService
*
* @see javax.xml.crypto.dsig.TransformService
*/
public static synchronized void registerDsigProvider() {
// the xml signature classes will try to find a special TransformerService,
// which is ofcourse unknown to JCE before ...
final String dsigProvider = "POIXmlDsigProvider";
if (Security.getProperty(dsigProvider) == null) {
Provider p = new Provider(dsigProvider, 1.0, dsigProvider){
static final long serialVersionUID = 1L;
};
p.put("TransformService." + TRANSFORM_URI, RelationshipTransformService.class.getName());
p.put("TransformService." + TRANSFORM_URI + " MechanismType", "DOM");
Security.addProvider(p);
}
}
@Override
public void init(TransformParameterSpec params) throws InvalidAlgorithmParameterException {
LOG.log(POILogger.DEBUG, "init(params)");
if (!(params instanceof RelationshipTransformParameterSpec)) {
throw new InvalidAlgorithmParameterException();
}
RelationshipTransformParameterSpec relParams = (RelationshipTransformParameterSpec) params;
for (String sourceId : relParams.sourceIds) {
this.sourceIds.add(sourceId);
}
}
@Override
public void init(XMLStructure parent, XMLCryptoContext context) throws InvalidAlgorithmParameterException {
LOG.log(POILogger.DEBUG, "init(parent,context)");
LOG.log(POILogger.DEBUG, "parent java type: " + parent.getClass().getName());
DOMStructure domParent = (DOMStructure) parent;
Node parentNode = domParent.getNode();
try {
TransformDocument transDoc = TransformDocument.Factory.parse(parentNode);
XmlObject xoList[] = transDoc.getTransform().selectChildren(RelationshipReferenceDocument.type.getDocumentElementName());
if (xoList.length == 0) {
LOG.log(POILogger.WARN, "no RelationshipReference/@SourceId parameters present");
}
for (XmlObject xo : xoList) {
String sourceId = ((CTRelationshipReference)xo).getSourceId();
LOG.log(POILogger.DEBUG, "sourceId: ", sourceId);
this.sourceIds.add(sourceId);
}
} catch (XmlException e) {
throw new InvalidAlgorithmParameterException(e);
}
}
@Override
public void marshalParams(XMLStructure parent, XMLCryptoContext context) throws MarshalException {
LOG.log(POILogger.DEBUG, "marshallParams(parent,context)");
DOMStructure domParent = (DOMStructure) parent;
Element parentNode = (Element)domParent.getNode();
// parentNode.setAttributeNS(XML_NS, "xmlns:mdssi", XML_DIGSIG_NS);
Document doc = parentNode.getOwnerDocument();
for (String sourceId : this.sourceIds) {
RelationshipReferenceDocument relRef = RelationshipReferenceDocument.Factory.newInstance();
relRef.addNewRelationshipReference().setSourceId(sourceId);
Node n = relRef.getRelationshipReference().getDomNode();
n = doc.importNode(n, true);
parentNode.appendChild(n);
}
}
public AlgorithmParameterSpec getParameterSpec() {
LOG.log(POILogger.DEBUG, "getParameterSpec");
return null;
}
public Data transform(Data data, XMLCryptoContext context) throws TransformException {
LOG.log(POILogger.DEBUG, "transform(data,context)");
LOG.log(POILogger.DEBUG, "data java type: " + data.getClass().getName());
OctetStreamData octetStreamData = (OctetStreamData) data;
LOG.log(POILogger.DEBUG, "URI: " + octetStreamData.getURI());
InputStream octetStream = octetStreamData.getOctetStream();
RelationshipsDocument relDoc;
try {
relDoc = RelationshipsDocument.Factory.parse(octetStream);
} catch (Exception e) {
throw new TransformException(e.getMessage(), e);
}
LOG.log(POILogger.DEBUG, "relationships document", relDoc);
CTRelationships rels = relDoc.getRelationships();
List<CTRelationship> relList = rels.getRelationshipList();
Iterator<CTRelationship> relIter = rels.getRelationshipList().iterator();
while (relIter.hasNext()) {
CTRelationship rel = relIter.next();
/*
* See: ISO/IEC 29500-2:2008(E) - 13.2.4.24 Relationships Transform
* Algorithm.
*/
if (!this.sourceIds.contains(rel.getId())) {
LOG.log(POILogger.DEBUG, "removing element: " + rel.getId());
relIter.remove();
} else {
if (!rel.isSetTargetMode()) {
rel.setTargetMode(STTargetMode.INTERNAL);
}
}
}
// TODO: remove non element nodes ???
LOG.log(POILogger.DEBUG, "# Relationship elements", relList.size());
XmlSort.sort(rels, new Comparator<XmlCursor>(){
public int compare(XmlCursor c1, XmlCursor c2) {
String id1 = ((CTRelationship)c1.getObject()).getId();
String id2 = ((CTRelationship)c2.getObject()).getId();
return id1.compareTo(id2);
}
});
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
XmlOptions xo = new XmlOptions();
xo.setSaveNoXmlDecl();
relDoc.save(bos, xo);
return new OctetStreamData(new ByteArrayInputStream(bos.toByteArray()));
} catch (IOException e) {
throw new TransformException(e.getMessage(), e);
}
}
public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException {
LOG.log(POILogger.DEBUG, "transform(data,context,os)");
return null;
}
public boolean isFeatureSupported(String feature) {
LOG.log(POILogger.DEBUG, "isFeatureSupported(feature)");
return false;
}
}

View File

@ -0,0 +1,131 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.security.cert.CRLException;
import java.security.cert.X509CRL;
import java.util.ArrayList;
import java.util.List;
/**
* Container class for PKI revocation data.
*
* @author Frank Cornelis
*
*/
public class RevocationData {
private final List<byte[]> crls;
private final List<byte[]> ocsps;
/**
* Default constructor.
*/
public RevocationData() {
this.crls = new ArrayList<byte[]>();
this.ocsps = new ArrayList<byte[]>();
}
/**
* Adds a CRL to this revocation data set.
*
* @param encodedCrl
*/
public void addCRL(byte[] encodedCrl) {
this.crls.add(encodedCrl);
}
/**
* Adds a CRL to this revocation data set.
*
* @param crl
*/
public void addCRL(X509CRL crl) {
byte[] encodedCrl;
try {
encodedCrl = crl.getEncoded();
} catch (CRLException e) {
throw new IllegalArgumentException("CRL coding error: "
+ e.getMessage(), e);
}
addCRL(encodedCrl);
}
/**
* Adds an OCSP response to this revocation data set.
*
* @param encodedOcsp
*/
public void addOCSP(byte[] encodedOcsp) {
this.ocsps.add(encodedOcsp);
}
/**
* Gives back a list of all CRLs.
*
* @return
*/
public List<byte[]> getCRLs() {
return this.crls;
}
/**
* Gives back a list of all OCSP responses.
*
* @return
*/
public List<byte[]> getOCSPs() {
return this.ocsps;
}
/**
* Returns <code>true</code> if this revocation data set holds OCSP
* responses.
*
* @return
*/
public boolean hasOCSPs() {
return false == this.ocsps.isEmpty();
}
/**
* Returns <code>true</code> if this revocation data set holds CRLs.
*
* @return
*/
public boolean hasCRLs() {
return false == this.crls.isEmpty();
}
/**
* Returns <code>true</code> if this revocation data is not empty.
*
* @return
*/
public boolean hasRevocationDataEntries() {
return hasOCSPs() || hasCRLs();
}
}

View File

@ -0,0 +1,47 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* Interface for a service that retrieves revocation data about some given
* certificate chain.
*
* @author Frank Cornelis
*
*/
public interface RevocationDataService {
/**
* Gives back the revocation data corresponding with the given certificate
* chain.
*
* @param certificateChain
* @return
*/
RevocationData getRevocationData(List<X509Certificate> certificateChain);
}

View File

@ -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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
/**
* Interface for the signature policy service.
*
* @author Frank Cornelis
*
*/
public interface SignaturePolicyService {
/**
* Gives back the signature policy identifier URI.
*
* @return
*/
String getSignaturePolicyIdentifier();
/**
* Gives back the short description of the signature policy or
* <code>null</code> if a description is not available.
*
* @return the description, or <code>null</code>.
*/
String getSignaturePolicyDescription();
/**
* Gives back the download URL where the signature policy document can be
* found. Can be <code>null</code> in case such a download location does not
* exist.
*
* @return the download URL, or <code>null</code>.
*/
String getSignaturePolicyDownloadUrl();
/**
* Gives back the signature policy document.
*
* @return the bytes of the signature policy document.
*/
byte[] getSignaturePolicyDocument();
}

View File

@ -0,0 +1,253 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.DatatypeConverter;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cmp.PKIFailureInfo;
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cms.DefaultCMSSignatureAlgorithmNameGenerator;
import org.bouncycastle.cms.SignerId;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.bc.BcRSASignerInfoVerifierBuilder;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;
/**
* A TSP time-stamp service implementation.
*
* @author Frank Cornelis
*
*/
public class TSPTimeStampService implements TimeStampService {
private static final POILogger LOG = POILogFactory.getLogger(TSPTimeStampService.class);
private SignatureConfig signatureConfig;
/**
* Maps the digest algorithm to corresponding OID value.
*/
public ASN1ObjectIdentifier mapDigestAlgoToOID(HashAlgorithm digestAlgo) {
switch (digestAlgo) {
case sha1: return X509ObjectIdentifiers.id_SHA1;
case sha256: return NISTObjectIdentifiers.id_sha256;
case sha384: return NISTObjectIdentifiers.id_sha384;
case sha512: return NISTObjectIdentifiers.id_sha512;
default:
throw new IllegalArgumentException("unsupported digest algo: " + digestAlgo);
}
}
@SuppressWarnings("unchecked")
public byte[] timeStamp(byte[] data, RevocationData revocationData)
throws Exception {
// digest the message
MessageDigest messageDigest = CryptoFunctions.getMessageDigest(signatureConfig.getTspDigestAlgo());
byte[] digest = messageDigest.digest(data);
// generate the TSP request
BigInteger nonce = new BigInteger(128, new SecureRandom());
TimeStampRequestGenerator requestGenerator = new TimeStampRequestGenerator();
requestGenerator.setCertReq(true);
String requestPolicy = signatureConfig.getTspRequestPolicy();
if (requestPolicy != null) {
requestGenerator.setReqPolicy(new ASN1ObjectIdentifier(requestPolicy));
}
ASN1ObjectIdentifier digestAlgoOid = mapDigestAlgoToOID(signatureConfig.getTspDigestAlgo());
TimeStampRequest request = requestGenerator.generate(digestAlgoOid, digest, nonce);
byte[] encodedRequest = request.getEncoded();
// create the HTTP POST request
Proxy proxy = Proxy.NO_PROXY;
if (signatureConfig.getProxyUrl() != null) {
URL proxyUrl = new URL(signatureConfig.getProxyUrl());
String host = proxyUrl.getHost();
int port = proxyUrl.getPort();
proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, (port == -1 ? 80 : port)));
}
HttpURLConnection huc = (HttpURLConnection)new URL(signatureConfig.getTspUrl()).openConnection(proxy);
if (signatureConfig.getTspUser() != null) {
String userPassword = signatureConfig.getTspUser() + ":" + signatureConfig.getTspPass();
String encoding = DatatypeConverter.printBase64Binary(userPassword.getBytes(Charset.forName("iso-8859-1")));
huc.setRequestProperty("Authorization", "Basic " + encoding);
}
huc.setDoOutput(true); // also sets method to POST.
huc.setRequestProperty("User-Agent", signatureConfig.getUserAgent());
huc.setRequestProperty("Content-Type", signatureConfig.isTspOldProtocol()
? "application/timestamp-request"
: "application/timestamp-query;charset=ISO-8859-1");
OutputStream hucOut = huc.getOutputStream();
hucOut.write(encodedRequest);
// invoke TSP service
huc.connect();
int statusCode = huc.getResponseCode();
if (statusCode != 200) {
LOG.log(POILogger.ERROR, "Error contacting TSP server ", signatureConfig.getTspUrl());
throw new IOException("Error contacting TSP server " + signatureConfig.getTspUrl());
}
// HTTP input validation
String contentType = huc.getHeaderField("Content-Type");
if (null == contentType) {
throw new RuntimeException("missing Content-Type header");
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
IOUtils.copy(huc.getInputStream(), bos);
LOG.log(POILogger.DEBUG, "response content: ", bos.toString());
if (!contentType.startsWith(signatureConfig.isTspOldProtocol()
? "application/timestamp-response"
: "application/timestamp-reply"
)) {
throw new RuntimeException("invalid Content-Type: " + contentType);
}
if (bos.size() == 0) {
throw new RuntimeException("Content-Length is zero");
}
// TSP response parsing and validation
TimeStampResponse timeStampResponse = new TimeStampResponse(bos.toByteArray());
timeStampResponse.validate(request);
if (0 != timeStampResponse.getStatus()) {
LOG.log(POILogger.DEBUG, "status: " + timeStampResponse.getStatus());
LOG.log(POILogger.DEBUG, "status string: " + timeStampResponse.getStatusString());
PKIFailureInfo failInfo = timeStampResponse.getFailInfo();
if (null != failInfo) {
LOG.log(POILogger.DEBUG, "fail info int value: " + failInfo.intValue());
if (/*PKIFailureInfo.unacceptedPolicy*/(1 << 8) == failInfo.intValue()) {
LOG.log(POILogger.DEBUG, "unaccepted policy");
}
}
throw new RuntimeException("timestamp response status != 0: "
+ timeStampResponse.getStatus());
}
TimeStampToken timeStampToken = timeStampResponse.getTimeStampToken();
SignerId signerId = timeStampToken.getSID();
BigInteger signerCertSerialNumber = signerId.getSerialNumber();
X500Name signerCertIssuer = signerId.getIssuer();
LOG.log(POILogger.DEBUG, "signer cert serial number: " + signerCertSerialNumber);
LOG.log(POILogger.DEBUG, "signer cert issuer: " + signerCertIssuer);
// TSP signer certificates retrieval
Collection<X509CertificateHolder> certificates = timeStampToken.getCertificates().getMatches(null);
X509CertificateHolder signerCert = null;
Map<X500Name, X509CertificateHolder> certificateMap = new HashMap<X500Name, X509CertificateHolder>();
for (X509CertificateHolder certificate : certificates) {
if (signerCertIssuer.equals(certificate.getIssuer())
&& signerCertSerialNumber.equals(certificate.getSerialNumber())) {
signerCert = certificate;
}
certificateMap.put(certificate.getSubject(), certificate);
}
// TSP signer cert path building
if (signerCert == null) {
throw new RuntimeException("TSP response token has no signer certificate");
}
List<X509Certificate> tspCertificateChain = new ArrayList<X509Certificate>();
JcaX509CertificateConverter x509converter = new JcaX509CertificateConverter();
x509converter.setProvider("BC");
X509CertificateHolder certificate = signerCert;
do {
LOG.log(POILogger.DEBUG, "adding to certificate chain: " + certificate.getSubject());
tspCertificateChain.add(x509converter.getCertificate(certificate));
if (certificate.getSubject().equals(certificate.getIssuer())) {
break;
}
certificate = certificateMap.get(certificate.getIssuer());
} while (null != certificate);
// verify TSP signer signature
X509CertificateHolder holder = new X509CertificateHolder(tspCertificateChain.get(0).getEncoded());
DefaultCMSSignatureAlgorithmNameGenerator nameGen = new DefaultCMSSignatureAlgorithmNameGenerator();
DefaultSignatureAlgorithmIdentifierFinder sigAlgoFinder = new DefaultSignatureAlgorithmIdentifierFinder();
DefaultDigestAlgorithmIdentifierFinder hashAlgoFinder = new DefaultDigestAlgorithmIdentifierFinder();
BcDigestCalculatorProvider calculator = new BcDigestCalculatorProvider();
BcRSASignerInfoVerifierBuilder verifierBuilder = new BcRSASignerInfoVerifierBuilder(nameGen, sigAlgoFinder, hashAlgoFinder, calculator);
SignerInformationVerifier verifier = verifierBuilder.build(holder);
timeStampToken.validate(verifier);
// verify TSP signer certificate
if (signatureConfig.getTspValidator() != null) {
signatureConfig.getTspValidator().validate(tspCertificateChain, revocationData);
}
LOG.log(POILogger.DEBUG, "time-stamp token time: "
+ timeStampToken.getTimeStampInfo().getGenTime());
byte[] timestamp = timeStampToken.getEncoded();
return timestamp;
}
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
}

View File

@ -0,0 +1,54 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
/**
* Interface for a time-stamp service.
*
* @author Frank Cornelis
*
*/
public interface TimeStampService extends SignatureConfigurable {
/**
* Gives back the encoded time-stamp token for the given array of data
* bytes. We assume that the time-stamp token itself contains its full
* certificate chain required for proper validation.
*
* @param data
* the data to be time-stamped.
* @param revocationData
* the optional container that needs to be filled up with the
* revocation data used to validate the TSA certificate chain.
* @return the DER encoded time-stamp token.
* @throws Exception
* in case something went wrong.
*/
byte[] timeStamp(byte[] data, RevocationData revocationData)
throws Exception;
}

View File

@ -0,0 +1,51 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* Interface for trust validator of a TSP.
*
* @author Frank Cornelis
*
*/
public interface TimeStampServiceValidator {
/**
* Validates the given certificate chain.
*
* @param certificateChain
* @param revocationData
* the optional data container that should be filled with
* revocation data that was used to validate the given
* certificate chain.
* @throws Exception
* in case the certificate chain is invalid.
*/
void validate(List<X509Certificate> certificateChain,
RevocationData revocationData) throws Exception;
}

View File

@ -0,0 +1,51 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.spi;
import java.io.Serializable;
import java.security.Identity;
/**
* Address Data Transfer Object.
*
* @author Frank Cornelis
* @see Identity
*
*/
public class AddressDTO implements Serializable {
/*
* We implement serializable to allow this class to be used in distributed
* containers as defined in the Servlet v2.4 specification.
*/
private static final long serialVersionUID = 1L;
public String streetAndNumber;
public String zip;
public String city;
}

View File

@ -0,0 +1,56 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.spi;
import java.io.Serializable;
import org.apache.poi.poifs.crypt.HashAlgorithm;
/**
* Digest Information data transfer class.
*/
public class DigestInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Main constructor.
*
* @param digestValue
* @param hashAlgo
* @param description
*/
public DigestInfo(byte[] digestValue, HashAlgorithm hashAlgo, String description) {
this.digestValue = digestValue;
this.hashAlgo = hashAlgo;
this.description = description;
}
public final byte[] digestValue;
public final String description;
public final HashAlgorithm hashAlgo;
}

View File

@ -0,0 +1,75 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.spi;
import java.io.Serializable;
import java.util.GregorianCalendar;
/**
* Identity Data Transfer Object.
*
* @author Frank Cornelis
*
*/
public class IdentityDTO implements Serializable {
/*
* We implement serializable to allow this class to be used in distributed
* containers as defined in the Servlet v2.4 specification.
*/
private static final long serialVersionUID = 1L;
public String cardNumber;
public String chipNumber;
public GregorianCalendar cardValidityDateBegin;
public GregorianCalendar cardValidityDateEnd;
public String cardDeliveryMunicipality;
public String nationalNumber;
public String name;
public String firstName;
public String middleName;
public String nationality;
public String placeOfBirth;
public GregorianCalendar dateOfBirth;
public boolean male;
public boolean female;
public String nobleCondition;
public String duplicate;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,221 @@
/* ====================================================================
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.util;
import java.io.File;
import java.io.IOException;
import java.util.Comparator;
import javax.xml.namespace.QName;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
/**
*/
public final class XmlSort
{
/**
* Receives an XML element instance and sorts the children of this
* element in lexicographical (by default) order.
*
* @param args An array in which the first item is a
* path to the XML instance file and the second item (optional) is
* an XPath inside the document identifying the element to be sorted
*/
public static void main(String[] args)
{
if (args.length < 1 || args.length > 2)
{
System.out.println(" java XmlSort <XML_File> [<XPath>]");
return;
}
File f = new File(args[0]);
try
{
XmlObject docInstance = XmlObject.Factory.parse(f);
XmlObject element = null;
if (args.length > 1)
{
String xpath = args[1];
XmlObject[] result = docInstance.selectPath(xpath);
if (result.length == 0)
{
System.out.println("ERROR: XPath \"" + xpath + "\" did not return any results");
}
else if (result.length > 1)
{
System.out.println("ERROR: XPath \"" + xpath + "\" returned more than one " +
"node (" + result.length + ")");
}
else
element = result[0];
}
else
{
// Navigate to the root element
XmlCursor c = docInstance.newCursor();
c.toFirstChild();
element = c.getObject();
c.dispose();
}
if (element != null)
sort(element, new QNameComparator(QNameComparator.ASCENDING));
System.out.println(docInstance.xmlText());
}
catch (IOException ioe)
{
System.out.println("ERROR: Could not open file: \"" + args[0] + "\": " +
ioe.getMessage());
}
catch (XmlException xe)
{
System.out.println("ERROR: Could not parse file: \"" + args[0] + "\": " +
xe.getMessage());
}
}
/**
* Sorts the children of <code>element</code> according to the order indicated by the
* comparator.
* @param element the element whose content is to be sorted. Only element children are sorted,
* attributes are not touched. When elements are reordered, all the text, comments and PIs
* follow the element that they come immediately after.
* @param comp a comparator that is to be used when comparing the <code>QName</code>s of two
* elements. See {@link org.apache.xmlbeans.samples.cursor.XmlSort.QNameComparator} for a simple
* implementation that compares two elements based on the value of their QName, but more
* complicated implementations are possible, for instance, ones that compare two elements based
* on the value of a specifc attribute etc.
* @throws IllegalArgumentException if the input <code>XmlObject</code> does not represent
* an element
*/
public static void sort(XmlObject element, Comparator<XmlCursor> comp)
{
XmlCursor headCursor = element.newCursor();
if (!headCursor.isStart())
throw new IllegalStateException("The element parameter must point to a STARTDOC");
// We use insertion sort to minimize the number of swaps, because each swap means
// moving a part of the document
/* headCursor points to the beginning of the list of the already sorted items and
listCursor points to the beginning of the list of unsorted items
At the beginning, headCursor points to the first element and listCursor points to the
second element. The algorithm ends when listCursor cannot be moved to the "next"
element in the unsorted list, i.e. the unsorted list becomes empty */
boolean moved = headCursor.toFirstChild();
if (!moved)
{
// Cursor was not moved, which means that the given element has no children and
// therefore there is nothing to sort
return;
}
XmlCursor listCursor = headCursor.newCursor();
boolean moreElements = listCursor.toNextSibling();
while (moreElements)
{
moved = false;
// While we can move the head of the unsorted list, it means that there are still
// items (elements) that need to be sorted
while (headCursor.comparePosition(listCursor) < 0)
{
if (comp.compare(headCursor, listCursor) > 0)
{
// We have found the position in the sorted list, insert the element and the
// text following the element in the current position
/*
* Uncomment this code to cause the text before the element to move along
* with the element, rather than the text after the element. Notice that this
* is more difficult to do, because the cursor's "type" refers to the position
* to the right of the cursor, so to get the type of the token to the left, the
* cursor needs to be first moved to the left (previous token)
*
headCursor.toPrevToken();
while (headCursor.isComment() || headCursor.isProcinst() || headCursor.isText())
headCursor.toPrevToken();
headCursor.toNextToken();
listCursor.toPrevToken();
while (listCursor.isComment() || listCursor.isProcinst() || listCursor.isText())
listCursor.toPrevToken();
listCursor.toNextToken();
while (!listCursor.isStart())
listCursor.moveXml(headCursor);
listCursor.moveXml(headCursor);
*/
// Move the element
listCursor.moveXml(headCursor);
// Move the text following the element
while (!listCursor.isStart() && !listCursor.isEnd())
listCursor.moveXml(headCursor);
moreElements = listCursor.isStart();
moved = true;
break;
}
headCursor.toNextSibling();
}
if (!moved)
{
// Because during the move of a fragment of XML, the listCursor is also moved, in
// case we didn't need to move XML (the new element to be inserted happened to
// be the last one in order), we need to move this cursor
moreElements = listCursor.toNextSibling();
}
// Reposition the head of the sorted list
headCursor.toParent();
headCursor.toFirstChild();
}
}
/**
* Implements a <code>java.util.Comparator</code> for comparing <code>QName</code>values.
* The namespace URIs are compared first and if they are equal, the local parts are compared.
* <p/>
* The constructor accepts an argument indicating whether the comparison order is the same as
* the lexicographic order of the strings or the reverse.
*/
public static final class QNameComparator implements Comparator
{
public static final int ASCENDING = 1;
public static final int DESCENDING = 2;
private int order;
public QNameComparator(int order)
{
this.order = order;
if (order != ASCENDING && order != DESCENDING)
throw new IllegalArgumentException("Please specify one of ASCENDING or DESCENDING "+
"comparison orders");
}
public int compare(Object o, Object o1)
{
XmlCursor cursor1 = (XmlCursor) o;
XmlCursor cursor2 = (XmlCursor) o1;
QName qname1 = cursor1.getName();
QName qname2 = cursor2.getName();
int qnameComparisonRes = qname1.getNamespaceURI().compareTo(qname2.getNamespaceURI());
if (qnameComparisonRes == 0)
return order == ASCENDING ?
qname1.getLocalPart().compareTo(qname2.getLocalPart()) :
-qname1.getLocalPart().compareTo(qname2.getLocalPart());
else
return order == ASCENDING ? qnameComparisonRes : -qnameComparisonRes;
}
}
}

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
====================================================================
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.
====================================================================
-->
<xsd:schema targetNamespace="http://schemas.microsoft.com/office/2006/digsig" elementFormDefault="qualified" xmlns="http://schemas.microsoft.com/office/2006/digsig" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:simpleType name="ST_PositiveInteger">
<xsd:restriction base="xsd:int">
<xsd:minExclusive value="0"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureComments">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="255"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureProviderUrl">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="2083"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureText">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="100"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureType">
<xsd:restriction base="xsd:int">
<xsd:enumeration value="1"/>
<xsd:enumeration value="2"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_Version">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="64"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_UniqueIdentifierWithBraces">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}|"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:group name="EG_RequiredChildren">
<xsd:sequence>
<xsd:element name="SetupID" type="ST_UniqueIdentifierWithBraces"/>
<xsd:element name="SignatureText" type="ST_SignatureText"/>
<xsd:element name="SignatureImage" type="xsd:base64Binary"/>
<xsd:element name="SignatureComments" type="ST_SignatureComments"/>
<xsd:element name="WindowsVersion" type="ST_Version"/>
<xsd:element name="OfficeVersion" type="ST_Version"/>
<xsd:element name="ApplicationVersion" type="ST_Version"/>
<xsd:element name="Monitors" type="ST_PositiveInteger"/>
<xsd:element name="HorizontalResolution" type="ST_PositiveInteger"/>
<xsd:element name="VerticalResolution" type="ST_PositiveInteger"/>
<xsd:element name="ColorDepth" type="ST_PositiveInteger"/>
<xsd:element name="SignatureProviderId" type="ST_UniqueIdentifierWithBraces"/>
<xsd:element name="SignatureProviderUrl" type="ST_SignatureProviderUrl"/>
<xsd:element name="SignatureProviderDetails" type="xsd:int"/>
<xsd:element name="SignatureType" type="ST_SignatureType"/>
</xsd:sequence>
</xsd:group>
<xsd:group name="EG_OptionalChildren">
<xsd:sequence>
<xsd:element name="DelegateSuggestedSigner" type="xsd:string"/>
<xsd:element name="DelegateSuggestedSigner2" type="xsd:string"/>
<xsd:element name="DelegateSuggestedSignerEmail" type="xsd:string"/>
<xsd:element name="ManifestHashAlgorithm" type="xsd:anyURI" minOccurs="0"/>
</xsd:sequence>
</xsd:group>
<xsd:group name="EG_OptionalChildrenV2">
<xsd:sequence>
<xsd:element name="Address1" type="xsd:string"/>
<xsd:element name="Address2" type="xsd:string"/>
</xsd:sequence>
</xsd:group>
<xsd:complexType name="CT_SignatureInfoV1">
<xsd:sequence>
<xsd:group ref="EG_RequiredChildren"/>
<xsd:group ref="EG_OptionalChildren" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CT_SignatureInfoV2">
<xsd:sequence>
<xsd:group ref="EG_OptionalChildrenV2" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="SignatureInfoV1" type="CT_SignatureInfoV1"/>
<xsd:element name="SignatureInfoV2" type="CT_SignatureInfoV2"/>
</xsd:schema>

View File

@ -0,0 +1,317 @@
/* ====================================================================
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.InputStream;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.cert.CRLException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.Date;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.CRLNumber;
import org.bouncycastle.asn1.x509.CRLReason;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.Extensions;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.X509CRLHolder;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509ExtensionUtils;
import org.bouncycastle.cert.X509v2CRLBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.ocsp.BasicOCSPResp;
import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
import org.bouncycastle.cert.ocsp.CertificateID;
import org.bouncycastle.cert.ocsp.CertificateStatus;
import org.bouncycastle.cert.ocsp.OCSPReq;
import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
import org.bouncycastle.cert.ocsp.Req;
import org.bouncycastle.cert.ocsp.RevokedStatus;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class PkiTestUtils {
private PkiTestUtils() {
super();
}
static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom random = new SecureRandom();
keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024,
RSAKeyGenParameterSpec.F4), random);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
return keyPair;
}
static X509Certificate generateCertificate(PublicKey subjectPublicKey,
String subjectDn, Date notBefore, Date notAfter,
X509Certificate issuerCertificate, PrivateKey issuerPrivateKey,
boolean caFlag, int pathLength, String crlUri, String ocspUri,
KeyUsage keyUsage)
throws IOException, OperatorCreationException, CertificateException
{
String signatureAlgorithm = "SHA1withRSA";
X500Name issuerName;
if (issuerCertificate != null) {
issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer();
} else {
issuerName = new X500Name(subjectDn);
}
RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
SubjectPublicKeyInfo subjectPublicKeyInfo =
SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
.setProvider("BC").build().get(CertificateID.HASH_SHA1);
X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
issuerName
, new BigInteger(128, new SecureRandom())
, notBefore
, notAfter
, new X500Name(subjectDn)
, subjectPublicKeyInfo
);
X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
AuthorityKeyIdentifier autKeyId = (issuerCertificate != null)
? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded()))
: exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
if (caFlag) {
BasicConstraints bc;
if (-1 == pathLength) {
bc = new BasicConstraints(true);
} else {
bc = new BasicConstraints(pathLength);
}
certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
}
if (null != crlUri) {
int uri = GeneralName.uniformResourceIdentifier;
DERIA5String crlUriDer = new DERIA5String(crlUri);
GeneralName gn = new GeneralName(uri, crlUriDer);
DERSequence gnDer = new DERSequence(gn);
GeneralNames gns = GeneralNames.getInstance(gnDer);
DistributionPointName dpn = new DistributionPointName(0, gns);
DistributionPoint distp = new DistributionPoint(dpn, null, null);
DERSequence distpDer = new DERSequence(distp);
certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer);
}
if (null != ocspUri) {
int uri = GeneralName.uniformResourceIdentifier;
GeneralName ocspName = new GeneralName(uri, ocspUri);
AuthorityInformationAccess authorityInformationAccess =
new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName);
certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess);
}
if (null != keyUsage) {
certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
}
JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
signerBuilder.setProvider("BC");
X509CertificateHolder certHolder =
certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
/*
* Next certificate factory trick is needed to make sure that the
* certificate delivered to the caller is provided by the default
* security provider instead of BouncyCastle. If we don't do this trick
* we might run into trouble when trying to use the CertPath validator.
*/
// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// certificate = (X509Certificate) certificateFactory
// .generateCertificate(new ByteArrayInputStream(certificate
// .getEncoded()));
return new JcaX509CertificateConverter().getCertificate(certHolder);
}
static Document loadDocument(InputStream documentInputStream)
throws ParserConfigurationException, SAXException, IOException {
InputSource inputSource = new InputSource(documentInputStream);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder documentBuilder = documentBuilderFactory
.newDocumentBuilder();
Document document = documentBuilder.parse(inputSource);
return document;
}
static String toString(Node dom) throws TransformerException {
Source source = new DOMSource(dom);
StringWriter stringWriter = new StringWriter();
Result result = new StreamResult(stringWriter);
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
/*
* We have to omit the ?xml declaration if we want to embed the
* document.
*/
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(source, result);
return stringWriter.getBuffer().toString();
}
public static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey)
throws CertificateEncodingException, IOException, CRLException, OperatorCreationException {
X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
}
public static OCSPResp createOcspResp(X509Certificate certificate,
boolean revoked, X509Certificate issuerCertificate,
X509Certificate ocspResponderCertificate,
PrivateKey ocspResponderPrivateKey, String signatureAlgorithm,
long nonceTimeinMillis)
throws Exception {
DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
.setProvider("BC").build().get(CertificateID.HASH_SHA1);
X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
// request
//create a nonce to avoid replay attack
BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
Extensions exts = new Extensions(ext);
OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
ocspReqBuilder.addRequest(certId);
ocspReqBuilder.setRequestExtensions(exts);
OCSPReq ocspReq = ocspReqBuilder.build();
SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
(CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
basicOCSPRespBuilder.setResponseExtensions(exts);
// request processing
Req[] requestList = ocspReq.getRequestList();
for (Req ocspRequest : requestList) {
CertificateID certificateID = ocspRequest.getCertID();
CertificateStatus certificateStatus = CertificateStatus.GOOD;
if (revoked) {
certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn);
}
basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
}
// basic response generation
X509CertificateHolder[] chain = null;
if (!ocspResponderCertificate.equals(issuerCertificate)) {
// TODO: HorribleProxy can't convert array input params yet
chain = new X509CertificateHolder[] {
new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
issuerHolder
};
}
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
.setProvider("BC").build(ocspResponderPrivateKey);
BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
OCSPResp ocspResp = ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
return ocspResp;
}
}

View File

@ -0,0 +1,544 @@
/* ====================================================================
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.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import org.apache.poi.POIDataSamples;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
import org.apache.poi.poifs.crypt.dsig.SignatureInfo.SignaturePart;
import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.facets.XAdESXLSignatureFacet;
import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.IOUtils;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.xmlbeans.XmlObject;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.ocsp.OCSPResp;
import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
import org.junit.BeforeClass;
import org.junit.Test;
import org.w3.x2000.x09.xmldsig.ReferenceType;
import org.w3.x2000.x09.xmldsig.SignatureDocument;
import org.w3c.dom.Document;
public class TestSignatureInfo {
private static final POILogger LOG = POILogFactory.getLogger(TestSignatureInfo.class);
private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance();
private static Calendar cal;
private KeyPair keyPair = null;
private X509Certificate x509 = null;
@BeforeClass
public static void initBouncy() throws MalformedURLException {
File bcProvJar = new File("lib/bcprov-ext-jdk15on-1.51.jar");
File bcPkixJar = new File("lib/bcpkix-jdk15on-151.jar");
ClassLoader cl = Thread.currentThread().getContextClassLoader();
URLClassLoader ucl = new URLClassLoader(new URL[]{bcProvJar.toURI().toURL(),bcPkixJar.toURI().toURL()}, cl);
Thread.currentThread().setContextClassLoader(ucl);
CryptoFunctions.registerBouncyCastle();
/*** TODO : set cal to now ... only set to fixed date for debugging ... */
cal = Calendar.getInstance();
cal.clear();
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
cal.set(2014, 7, 6, 21, 42, 12);
}
@Test
public void getSignerUnsigned() throws Exception {
String testFiles[] = {
"hello-world-unsigned.docx",
"hello-world-unsigned.pptx",
"hello-world-unsigned.xlsx",
"hello-world-office-2010-technical-preview-unsigned.docx"
};
for (String testFile : testFiles) {
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
List<X509Certificate> result = new ArrayList<X509Certificate>();
for (SignaturePart sp : si.getSignatureParts()) {
if (sp.validate()) {
result.add(sp.getSigner());
}
}
pkg.revert();
pkg.close();
assertNotNull(result);
assertTrue(result.isEmpty());
}
}
@Test
public void getSigner() throws Exception {
String testFiles[] = {
"hyperlink-example-signed.docx",
"hello-world-signed.docx",
"hello-world-signed.pptx",
"hello-world-signed.xlsx",
"hello-world-office-2010-technical-preview.docx",
"ms-office-2010-signed.docx",
"ms-office-2010-signed.pptx",
"ms-office-2010-signed.xlsx",
"Office2010-SP1-XAdES-X-L.docx",
"signed.docx",
};
for (String testFile : testFiles) {
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
List<X509Certificate> result = new ArrayList<X509Certificate>();
for (SignaturePart sp : si.getSignatureParts()) {
if (sp.validate()) {
result.add(sp.getSigner());
}
}
assertNotNull(result);
assertEquals("test-file: "+testFile, 1, result.size());
X509Certificate signer = result.get(0);
LOG.log(POILogger.DEBUG, "signer: " + signer.getSubjectX500Principal());
boolean b = si.verifySignature();
assertTrue("test-file: "+testFile, b);
pkg.revert();
}
}
@Test
public void getMultiSigners() throws Exception {
String testFile = "hello-world-signed-twice.docx";
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
List<X509Certificate> result = new ArrayList<X509Certificate>();
for (SignaturePart sp : si.getSignatureParts()) {
if (sp.validate()) {
result.add(sp.getSigner());
}
}
assertNotNull(result);
assertEquals("test-file: "+testFile, 2, result.size());
X509Certificate signer1 = result.get(0);
X509Certificate signer2 = result.get(1);
LOG.log(POILogger.DEBUG, "signer 1: " + signer1.getSubjectX500Principal());
LOG.log(POILogger.DEBUG, "signer 2: " + signer2.getSubjectX500Principal());
boolean b = si.verifySignature();
assertTrue("test-file: "+testFile, b);
pkg.revert();
}
@Test
public void testSignSpreadsheet() throws Exception {
String testFile = "hello-world-unsigned.xlsx";
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
sign(pkg, "Test", "CN=Test", 1);
pkg.close();
}
@Test
public void testManipulation() throws Exception {
// sign & validate
String testFile = "hello-world-unsigned.xlsx";
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
sign(pkg, "Test", "CN=Test", 1);
// manipulate
XSSFWorkbook wb = new XSSFWorkbook(pkg);
wb.setSheetName(0, "manipulated");
// ... I don't know, why commit is protected ...
Method m = XSSFWorkbook.class.getDeclaredMethod("commit");
m.setAccessible(true);
m.invoke(wb);
// todo: test a manipulation on a package part, which is not signed
// ... maybe in combination with #56164
// validate
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
boolean b = si.verifySignature();
assertFalse("signature should be broken", b);
wb.close();
}
@Test
public void testSignSpreadsheetWithSignatureInfo() throws Exception {
initKeyPair("Test", "CN=Test");
String testFile = "hello-world-unsigned.xlsx";
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
sic.setKey(keyPair.getPrivate());
sic.setSigningCertificateChain(Collections.singletonList(x509));
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
// hash > sha1 doesn't work in excel viewer ...
si.confirmSignature();
List<X509Certificate> result = new ArrayList<X509Certificate>();
for (SignaturePart sp : si.getSignatureParts()) {
if (sp.validate()) {
result.add(sp.getSigner());
}
}
assertEquals(1, result.size());
pkg.close();
}
@Test
public void testSignEnvelopingDocument() throws Exception {
String testFile = "hello-world-unsigned.xlsx";
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
initKeyPair("Test", "CN=Test");
final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate());
// setup
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setOpcPackage(pkg);
signatureConfig.setKey(keyPair.getPrivate());
/*
* We need at least 2 certificates for the XAdES-C complete certificate
* refs construction.
*/
List<X509Certificate> certificateChain = new ArrayList<X509Certificate>();
certificateChain.add(x509);
certificateChain.add(x509);
signatureConfig.setSigningCertificateChain(certificateChain);
signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet());
signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet());
signatureConfig.addSignatureFacet(new XAdESSignatureFacet());
signatureConfig.addSignatureFacet(new XAdESXLSignatureFacet());
boolean mockTsp = false;
// http://timestamping.edelweb.fr/service/tsp
// http://tsa.belgium.be/connect
signatureConfig.setTspUrl("http://timestamping.edelweb.fr/service/tsp");
signatureConfig.setTspOldProtocol(true);
if (mockTsp) {
TimeStampService tspService = new TimeStampService(){
public byte[] timeStamp(byte[] data, RevocationData revocationData) throws Exception {
revocationData.addCRL(crl);
return "time-stamp-token".getBytes();
}
public void setSignatureConfig(SignatureConfig config) {}
};
signatureConfig.setTspService(tspService);
} else {
TimeStampServiceValidator tspValidator = new TimeStampServiceValidator() {
@Override
public void validate(List<X509Certificate> certificateChain,
RevocationData revocationData) throws Exception {
for (X509Certificate certificate : certificateChain) {
LOG.log(POILogger.DEBUG, "certificate: " + certificate.getSubjectX500Principal());
LOG.log(POILogger.DEBUG, "validity: " + certificate.getNotBefore() + " - " + certificate.getNotAfter());
}
}
};
signatureConfig.setTspValidator(tspValidator);
signatureConfig.setTspOldProtocol(signatureConfig.getTspUrl().contains("edelweb"));
}
final RevocationData revocationData = new RevocationData();
revocationData.addCRL(crl);
OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false,
x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis());
revocationData.addOCSP(ocspResp.getEncoded());
RevocationDataService revocationDataService = new RevocationDataService(){
public RevocationData getRevocationData(List<X509Certificate> certificateChain) {
return revocationData;
}
};
signatureConfig.setRevocationDataService(revocationDataService);
// operate
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
si.confirmSignature();
// verify
Iterator<SignaturePart> spIter = si.getSignatureParts().iterator();
assertTrue(spIter.hasNext());
SignaturePart sp = spIter.next();
boolean valid = sp.validate();
assertTrue(valid);
SignatureDocument sigDoc = sp.getSignatureDocument();
String declareNS =
"declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "
+ "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; ";
String digestValXQuery = declareNS +
"$this/ds:Signature/ds:SignedInfo/ds:Reference";
for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) {
assertNotNull(rt.getDigestValue());
assertEquals(signatureConfig.getDigestMethodUri(), rt.getDigestMethod().getAlgorithm());
}
String certDigestXQuery = declareNS +
"$this//xades:SigningCertificate/xades:Cert/xades:CertDigest";
XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery);
assertEquals(xoList.length, 1);
DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0];
assertNotNull(certDigest.getDigestValue());
String qualPropXQuery = declareNS +
"$this/ds:Signature/ds:Object/xades:QualifyingProperties";
xoList = sigDoc.selectPath(qualPropXQuery);
assertEquals(xoList.length, 1);
QualifyingPropertiesType qualProp = (QualifyingPropertiesType)xoList[0];
boolean qualPropXsdOk = qualProp.validate();
assertTrue(qualPropXsdOk);
pkg.close();
}
@Test
public void testCertChain() throws Exception {
KeyStore keystore = KeyStore.getInstance("PKCS12");
String password = "test";
InputStream is = testdata.openResourceAsStream("chaintest.pfx");
keystore.load(is, password.toCharArray());
is.close();
Key key = keystore.getKey("poitest", password.toCharArray());
Certificate chainList[] = keystore.getCertificateChain("poitest");
List<X509Certificate> certChain = new ArrayList<X509Certificate>();
for (Certificate c : chainList) {
certChain.add((X509Certificate)c);
}
x509 = certChain.get(0);
keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
String testFile = "hello-world-unsigned.xlsx";
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setKey(keyPair.getPrivate());
signatureConfig.setSigningCertificateChain(certChain);
Calendar cal = Calendar.getInstance();
cal.set(2007, 7, 1);
signatureConfig.setExecutionTime(cal.getTime());
signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
signatureConfig.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
si.confirmSignature();
for (SignaturePart sp : si.getSignatureParts()){
boolean b = sp.validate();
assertTrue(b);
X509Certificate signer = sp.getSigner();
assertNotNull("signer undefined?!", signer);
List<X509Certificate> certChainRes = sp.getCertChain();
assertEquals(3, certChainRes.size());
}
pkg.close();
}
@Test
public void testNonSha1() throws Exception {
String testFile = "hello-world-unsigned.xlsx";
initKeyPair("Test", "CN=Test");
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setKey(keyPair.getPrivate());
signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
HashAlgorithm testAlgo[] = { HashAlgorithm.sha224, HashAlgorithm.sha256
, HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160 };
for (HashAlgorithm ha : testAlgo) {
signatureConfig.setDigestAlgo(ha);
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
signatureConfig.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
si.confirmSignature();
boolean b = si.verifySignature();
pkg.close();
assertTrue(b);
}
}
private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
initKeyPair(alias, signerDn);
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setKey(keyPair.getPrivate());
signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
signatureConfig.setExecutionTime(cal.getTime());
signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
signatureConfig.setOpcPackage(pkgCopy);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
Document document = DocumentHelper.createDocument();
// operate
DigestInfo digestInfo = si.preSign(document, null);
// verify
assertNotNull(digestInfo);
LOG.log(POILogger.DEBUG, "digest algo: " + digestInfo.hashAlgo);
LOG.log(POILogger.DEBUG, "digest description: " + digestInfo.description);
assertEquals("Office OpenXML Document", digestInfo.description);
assertNotNull(digestInfo.hashAlgo);
assertNotNull(digestInfo.digestValue);
// setup: key material, signature value
byte[] signatureValue = si.signDigest(digestInfo.digestValue);
// operate: postSign
si.postSign(document, signatureValue);
// verify: signature
si.getSignatureConfig().setOpcPackage(pkgCopy);
List<X509Certificate> result = new ArrayList<X509Certificate>();
for (SignaturePart sp : si.getSignatureParts()) {
if (sp.validate()) {
result.add(sp.getSigner());
}
}
assertEquals(signerCount, result.size());
}
private void initKeyPair(String alias, String subjectDN) throws Exception {
final char password[] = "test".toCharArray();
File file = new File("build/test.pfx");
KeyStore keystore = KeyStore.getInstance("PKCS12");
if (file.exists()) {
FileInputStream fis = new FileInputStream(file);
keystore.load(fis, password);
fis.close();
} else {
keystore.load(null, password);
}
if (keystore.isKeyEntry(alias)) {
Key key = keystore.getKey(alias, password);
x509 = (X509Certificate)keystore.getCertificate(alias);
keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
} else {
keyPair = PkiTestUtils.generateKeyPair();
Calendar cal = Calendar.getInstance();
Date notBefore = cal.getTime();
cal.add(Calendar.YEAR, 1);
Date notAfter = cal.getTime();
KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN
, notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage);
keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
FileOutputStream fos = new FileOutputStream(file);
keystore.store(fos, password);
fos.close();
}
}
private static File copy(File input) throws IOException {
String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1");
if (extension == null || "".equals(extension)) extension = ".zip";
File tmpFile = new File("build", "sigtest"+extension);
FileOutputStream fos = new FileOutputStream(tmpFile);
FileInputStream fis = new FileInputStream(input);
IOUtils.copy(fis, fos);
fis.close();
fos.close();
return tmpFile;
}
}

View File

@ -44,6 +44,7 @@ public final class POIDataSamples {
private static POIDataSamples _instHPSF;
private static POIDataSamples _instHPBF;
private static POIDataSamples _instHSMF;
private static POIDataSamples _instXmlDSign;
private File _resolvedDataDir;
/** <code>true</code> if standard system propery is not set,
@ -114,6 +115,12 @@ public final class POIDataSamples {
if(_instHSMF == null) _instHSMF = new POIDataSamples("hsmf");
return _instHSMF;
}
public static POIDataSamples getXmlDSignInstance(){
if(_instXmlDSign == null) _instXmlDSign = new POIDataSamples("xmldsign");
return _instXmlDSign;
}
/**
* Opens a sample file from the test data directory
*

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.