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:
commit
aa91f244dc
@ -23,7 +23,11 @@
|
||||
<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="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>
|
||||
|
140
build.xml
140
build.xml
@ -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,6 +147,16 @@ 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"/>
|
||||
|
||||
<!-- 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"
|
||||
@ -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">
|
||||
@ -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,7 +514,7 @@ 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>
|
||||
@ -472,10 +523,28 @@ under the License.
|
||||
<target name="fetch-ooxml-xsds" unless="ooxml-xsds.present"
|
||||
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}"/>
|
||||
|
||||
<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}"
|
||||
@ -533,24 +591,22 @@ under the License.
|
||||
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}"/>
|
||||
<!-- 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}"/>
|
||||
|
||||
<!-- 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>
|
||||
<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}"
|
||||
@ -558,11 +614,12 @@ under the License.
|
||||
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>
|
||||
@ -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" />
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
|
@ -182,8 +182,6 @@ public final class PackageRelationship {
|
||||
}
|
||||
|
||||
/**
|
||||
* public URI getSourceUri(){ }
|
||||
*
|
||||
* @return the targetMode
|
||||
*/
|
||||
public TargetMode getTargetMode() {
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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" //
|
||||
};
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
1334
src/ooxml/java/org/apache/poi/util/MethodUtils.java
Normal file
1334
src/ooxml/java/org/apache/poi/util/MethodUtils.java
Normal file
File diff suppressed because it is too large
Load Diff
221
src/ooxml/java/org/apache/poi/util/XmlSort.java
Normal file
221
src/ooxml/java/org/apache/poi/util/XmlSort.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
103
src/ooxml/resources/org/apache/poi/poifs/crypt/signatureInfo.xsd
Normal file
103
src/ooxml/resources/org/apache/poi/poifs/crypt/signatureInfo.xsd
Normal 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>
|
317
src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
Normal file
317
src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
*
|
||||
|
BIN
test-data/xmldsign/Office2010-SP1-XAdES-X-L.docx
Normal file
BIN
test-data/xmldsign/Office2010-SP1-XAdES-X-L.docx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/chaintest.pfx
Normal file
BIN
test-data/xmldsign/chaintest.pfx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
test-data/xmldsign/hello-world-signed-twice.docx
Normal file
BIN
test-data/xmldsign/hello-world-signed-twice.docx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hello-world-signed.docx
Normal file
BIN
test-data/xmldsign/hello-world-signed.docx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hello-world-signed.pptx
Normal file
BIN
test-data/xmldsign/hello-world-signed.pptx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hello-world-signed.xlsx
Normal file
BIN
test-data/xmldsign/hello-world-signed.xlsx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hello-world-unsigned.docx
Normal file
BIN
test-data/xmldsign/hello-world-unsigned.docx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hello-world-unsigned.pptx
Normal file
BIN
test-data/xmldsign/hello-world-unsigned.pptx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hello-world-unsigned.xlsx
Normal file
BIN
test-data/xmldsign/hello-world-unsigned.xlsx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/hyperlink-example-signed.docx
Normal file
BIN
test-data/xmldsign/hyperlink-example-signed.docx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/ms-office-2010-signed.docx
Normal file
BIN
test-data/xmldsign/ms-office-2010-signed.docx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/ms-office-2010-signed.pptx
Normal file
BIN
test-data/xmldsign/ms-office-2010-signed.pptx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/ms-office-2010-signed.xlsx
Normal file
BIN
test-data/xmldsign/ms-office-2010-signed.xlsx
Normal file
Binary file not shown.
BIN
test-data/xmldsign/signed.docx
Normal file
BIN
test-data/xmldsign/signed.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user