Xml signature support - version 1
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1617141 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9f7b23bb42
commit
63f8c1dcf0
@ -24,7 +24,7 @@
|
||||
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
|
||||
<classpathentry kind="lib" path="lib/junit-4.11.jar"/>
|
||||
<classpathentry kind="lib" path="ooxml-lib/ooxml-schemas-1.1.jar" sourcepath="ooxml-lib/ooxml-schemas-src-1.1.jar"/>
|
||||
<classpathentry kind="lib" path="ooxml-lib/ooxml-encryption-1.1.jar" sourcepath="ooxml-lib/ooxml-encryption-src-1.1.jar"/>
|
||||
<classpathentry kind="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="output" path="build/eclipse"/>
|
||||
</classpath>
|
||||
|
96
build.xml
96
build.xml
@ -118,7 +118,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"/>
|
||||
@ -169,17 +168,28 @@ 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" value="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.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"/>
|
||||
@ -458,7 +468,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>
|
||||
@ -467,10 +477,26 @@ 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}"/>
|
||||
<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>
|
||||
@ -481,19 +507,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}"/>
|
||||
@ -505,11 +522,9 @@ under the License.
|
||||
<equals arg1="${sun.arch.data.model}" arg2="64" />
|
||||
</condition>
|
||||
|
||||
<unzip src="${ooxml.xsds.izip}" dest="${ooxml.xsds.tmp.dir}"/>
|
||||
<!--
|
||||
schema="build/ooxml-xsds/"
|
||||
schema="build/ooxml-xsds/sml-workbook.xsd"
|
||||
-->
|
||||
<property name="ooxml.xsds.tmp.dir" location="build/ooxml-xsds"/>
|
||||
<mkdir dir="${ooxml.xsds.tmp.dir}"/>
|
||||
<unzip src="${ooxml.lib}/${ooxml.xsds.izip.1}" dest="${ooxml.xsds.tmp.dir}"/>
|
||||
<xmlbean
|
||||
schema="${ooxml.xsds.tmp.dir}"
|
||||
srcgendir="${ooxml.xsds.src.dir}"
|
||||
@ -528,24 +543,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"/>
|
||||
<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}"
|
||||
@ -553,11 +566,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}"
|
||||
@ -650,7 +664,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}"
|
||||
@ -1447,7 +1461,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="ooxml-lib/dom4j-1.6.1.jar" />
|
||||
<auxClasspath path="lib/commons-codec-1.9.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;
|
||||
@ -189,7 +190,7 @@ public class CryptoFunctions {
|
||||
* @return the requested cipher
|
||||
* @throws GeneralSecurityException
|
||||
*/
|
||||
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";
|
||||
|
||||
@ -274,7 +275,7 @@ 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");
|
||||
|
@ -17,22 +17,24 @@
|
||||
|
||||
package org.apache.poi.poifs.crypt;
|
||||
|
||||
import javax.xml.crypto.dsig.DigestMethod;
|
||||
|
||||
import org.apache.poi.EncryptedDocumentException;
|
||||
|
||||
public enum HashAlgorithm {
|
||||
none ( "", 0x0000, "", 0, "", false),
|
||||
sha1 ( "SHA-1", 0x8004, "SHA1", 20, "HmacSHA1", false),
|
||||
sha256 ( "SHA-256", 0x800C, "SHA256", 32, "HmacSHA256", false),
|
||||
sha384 ( "SHA-384", 0x800D, "SHA384", 48, "HmacSHA384", false),
|
||||
sha512 ( "SHA-512", 0x800E, "SHA512", 64, "HmacSHA512", false),
|
||||
none ( "", 0x0000, "", 0, "", null, false),
|
||||
sha1 ( "SHA-1", 0x8004, "SHA1", 20, "HmacSHA1", DigestMethod.SHA1, false),
|
||||
sha256 ( "SHA-256", 0x800C, "SHA256", 32, "HmacSHA256", DigestMethod.SHA256, false),
|
||||
sha384 ( "SHA-384", 0x800D, "SHA384", 48, "HmacSHA384", null, false),
|
||||
sha512 ( "SHA-512", 0x800E, "SHA512", 64, "HmacSHA512", DigestMethod.SHA512, false),
|
||||
/* only for agile encryption */
|
||||
md5 ( "MD5", -1, "MD5", 16, "HmacMD5", false),
|
||||
md5 ( "MD5", -1, "MD5", 16, "HmacMD5", null, false),
|
||||
// although sunjc2 supports md2, hmac-md2 is only supported by bouncycastle
|
||||
md2 ( "MD2", -1, "MD2", 16, "Hmac-MD2", true),
|
||||
md4 ( "MD4", -1, "MD4", 16, "Hmac-MD4", true),
|
||||
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),
|
||||
md2 ( "MD2", -1, "MD2", 16, "Hmac-MD2", null, true),
|
||||
md4 ( "MD4", -1, "MD4", 16, "Hmac-MD4", null, true),
|
||||
ripemd128("RipeMD128", -1, "RIPEMD-128", 16, "HMac-RipeMD128", null, true),
|
||||
ripemd160("RipeMD160", -1, "RIPEMD-160", 20, "HMac-RipeMD160", DigestMethod.RIPEMD160, true),
|
||||
whirlpool("Whirlpool", -1, "WHIRLPOOL", 64, "HMac-Whirlpool", null, true),
|
||||
;
|
||||
|
||||
public final String jceId;
|
||||
@ -40,14 +42,16 @@ public enum HashAlgorithm {
|
||||
public final String ecmaString;
|
||||
public final int hashSize;
|
||||
public final String jceHmacId;
|
||||
public final String xmlSignUri;
|
||||
public final boolean needsBouncyCastle;
|
||||
|
||||
HashAlgorithm(String jceId, int ecmaId, String ecmaString, int hashSize, String jceHmacId, boolean needsBouncyCastle) {
|
||||
HashAlgorithm(String jceId, int ecmaId, String ecmaString, int hashSize, String jceHmacId, String xmlSignUri, boolean needsBouncyCastle) {
|
||||
this.jceId = jceId;
|
||||
this.ecmaId = ecmaId;
|
||||
this.ecmaString = ecmaString;
|
||||
this.hashSize = hashSize;
|
||||
this.jceHmacId = jceHmacId;
|
||||
this.xmlSignUri = xmlSignUri;
|
||||
this.needsBouncyCastle = needsBouncyCastle;
|
||||
}
|
||||
|
||||
|
@ -182,8 +182,6 @@ public final class PackageRelationship {
|
||||
}
|
||||
|
||||
/**
|
||||
* public URI getSourceUri(){ }
|
||||
*
|
||||
* @return the targetMode
|
||||
*/
|
||||
public TargetMode getTargetMode() {
|
||||
|
@ -306,7 +306,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());
|
||||
|
@ -148,11 +148,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);
|
||||
@ -461,7 +460,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,375 @@
|
||||
package org.apache.poi.poifs.crypt.dsig;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509CRL;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.XMLCryptoContext;
|
||||
import javax.xml.crypto.dom.DOMCryptoContext;
|
||||
import javax.xml.crypto.dsig.XMLSignContext;
|
||||
import javax.xml.crypto.dsig.XMLSignatureException;
|
||||
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxy.ProxyIf;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
public interface HorribleProxies {
|
||||
public static final String xmlSecBase = "org.jcp.xml.dsig.internal.dom";
|
||||
// public static final String xmlSecBase = "org.apache.jcp.xml.dsig.internal.dom";
|
||||
|
||||
public interface ASN1InputStreamIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.ASN1InputStream";
|
||||
|
||||
ASN1OctetStringIf readObject$ASNString() throws IOException;
|
||||
DEROctetStringIf readObject$DERString() throws IOException;
|
||||
DERIntegerIf readObject$Integer() throws IOException;
|
||||
ASN1SequenceIf readObject$Sequence() throws IOException;
|
||||
Object readObject$Object() throws IOException;
|
||||
}
|
||||
|
||||
public interface ASN1ObjectIdentifierIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.ASN1ObjectIdentifier";
|
||||
}
|
||||
|
||||
public interface ASN1OctetStringIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.ASN1OctetString";
|
||||
byte[] getOctets();
|
||||
}
|
||||
|
||||
public interface ASN1SequenceIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.ASN1Sequence";
|
||||
}
|
||||
|
||||
public interface AuthorityInformationAccessIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.AuthorityInformationAccess";
|
||||
}
|
||||
|
||||
public interface AuthorityKeyIdentifierIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.AuthorityKeyIdentifier";
|
||||
byte[] getKeyIdentifier();
|
||||
}
|
||||
|
||||
public interface BasicConstraintsIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.BasicConstraints";
|
||||
}
|
||||
|
||||
public interface BasicOCSPRespIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.BasicOCSPResp";
|
||||
Date getProducedAt();
|
||||
RespIDIf getResponderId();
|
||||
}
|
||||
|
||||
public interface BcDigestCalculatorProviderIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.operator.bc.BcDigestCalculatorProvider";
|
||||
}
|
||||
|
||||
public interface BcRSASignerInfoVerifierBuilderIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cms.bc.BcRSASignerInfoVerifierBuilder";
|
||||
SignerInformationVerifierIf build(X509CertificateHolderIf holder);
|
||||
}
|
||||
|
||||
public interface CanonicalizerIf extends ProxyIf {
|
||||
String delegateClass = "com.sun.org.apache.xml.internal.security.c14n.Canonicalizer";
|
||||
byte[] canonicalizeSubtree(Node node) throws Exception;
|
||||
}
|
||||
|
||||
public interface CRLNumberIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.CRLNumber";
|
||||
}
|
||||
|
||||
public interface DefaultDigestAlgorithmIdentifierFinderIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder";
|
||||
}
|
||||
|
||||
public interface DistributionPointNameIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.DistributionPointName";
|
||||
}
|
||||
|
||||
public interface DistributionPointIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.DistributionPoint";
|
||||
}
|
||||
|
||||
public interface DERIA5StringIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.DERIA5String";
|
||||
}
|
||||
|
||||
public interface DERIntegerIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.DERInteger";
|
||||
BigInteger getPositiveValue();
|
||||
}
|
||||
|
||||
public interface DEROctetStringIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.DEROctetString";
|
||||
byte[] getOctets();
|
||||
}
|
||||
|
||||
public interface DERTaggedObjectIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.DERTaggedObject";
|
||||
int getTagNo();
|
||||
ASN1OctetStringIf getObject$String();
|
||||
Object getObject$Object();
|
||||
}
|
||||
|
||||
public interface DERSequenceIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.DERSequence";
|
||||
}
|
||||
|
||||
public interface DOMKeyInfoIf extends ProxyIf {
|
||||
String delegateClass = xmlSecBase+".DOMKeyInfo";
|
||||
void marshal(Node parent, Node nextSibling, String dsPrefix, DOMCryptoContext context) throws MarshalException;
|
||||
}
|
||||
|
||||
public interface DOMReferenceIf extends ProxyIf {
|
||||
String delegateClass = xmlSecBase+".DOMReference";
|
||||
void digest(XMLSignContext paramXMLSignContext) throws XMLSignatureException;
|
||||
byte[] getDigestValue();
|
||||
}
|
||||
|
||||
public interface DOMSignedInfoIf extends ProxyIf {
|
||||
String delegateClass = xmlSecBase+".DOMSignedInfo";
|
||||
void canonicalize(XMLCryptoContext paramXMLCryptoContext, ByteArrayOutputStream paramByteArrayOutputStream);
|
||||
}
|
||||
|
||||
public interface XMLSignatureIf extends ProxyIf {
|
||||
String delegateClass = "com.sun.org.apache.xml.internal.security.signature.XMLSignature";
|
||||
String ALGO_ID_SIGNATURE_RSA_SHA1();
|
||||
String ALGO_ID_SIGNATURE_RSA_SHA256();
|
||||
String ALGO_ID_SIGNATURE_RSA_SHA384();
|
||||
String ALGO_ID_SIGNATURE_RSA_SHA512();
|
||||
String ALGO_ID_MAC_HMAC_RIPEMD160();
|
||||
}
|
||||
|
||||
public interface DOMXMLSignatureIf extends ProxyIf {
|
||||
String delegateClass = xmlSecBase+".DOMXMLSignature";
|
||||
void marshal(Node node, String prefix, DOMCryptoContext context) throws MarshalException;
|
||||
}
|
||||
|
||||
public interface GeneralNameIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.GeneralName";
|
||||
|
||||
int uniformResourceIdentifier();
|
||||
|
||||
}
|
||||
|
||||
public interface GeneralNamesIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.GeneralNames";
|
||||
}
|
||||
|
||||
public interface InitIf extends ProxyIf {
|
||||
String delegateClass = "com.sun.org.apache.xml.internal.security.Init";
|
||||
void init();
|
||||
}
|
||||
|
||||
public interface KeyUsageIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.KeyUsage";
|
||||
int digitalSignature();
|
||||
}
|
||||
|
||||
public interface OCSPRespIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.OCSPResp";
|
||||
BasicOCSPRespIf getResponseObject();
|
||||
}
|
||||
|
||||
public interface PKIFailureInfoIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.cmp.PKIFailureInfo";
|
||||
int intValue();
|
||||
}
|
||||
|
||||
public interface RespIDIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.RespID";
|
||||
ResponderIDIf toASN1Object();
|
||||
}
|
||||
|
||||
public interface ResponderIDIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.ocsp.ResponderID";
|
||||
DERTaggedObjectIf toASN1Object();
|
||||
}
|
||||
|
||||
public interface SignerIdIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cms.SignerId";
|
||||
BigInteger getSerialNumber();
|
||||
X500Principal getIssuer();
|
||||
}
|
||||
|
||||
public interface SignerInformationVerifierIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cms.SignerInformationVerifier";
|
||||
}
|
||||
|
||||
public interface StoreIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.util.Store";
|
||||
Collection<Certificate> getMatches(Object selector) throws Exception;
|
||||
}
|
||||
|
||||
public interface SubjectKeyIdentifierIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.SubjectKeyIdentifier";
|
||||
byte[] getKeyIdentifier();
|
||||
}
|
||||
|
||||
public interface SubjectPublicKeyInfoIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.SubjectPublicKeyInfo";
|
||||
}
|
||||
|
||||
public interface TimeStampRequestGeneratorIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.tsp.TimeStampRequestGenerator";
|
||||
void setCertReq(boolean certReq);
|
||||
void setReqPolicy(String reqPolicy);
|
||||
TimeStampRequestIf generate(String igestAlgorithmOID, byte[] digest, BigInteger nonce);
|
||||
}
|
||||
|
||||
public interface TimeStampRequestIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.tsp.TimeStampRequest";
|
||||
byte[] getEncoded() throws IOException;
|
||||
}
|
||||
|
||||
public interface TimeStampResponseIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.tsp.TimeStampResponse";
|
||||
void validate(TimeStampRequestIf request) throws Exception;
|
||||
int getStatus();
|
||||
String getStatusString();
|
||||
PKIFailureInfoIf getFailInfo();
|
||||
TimeStampTokenIf getTimeStampToken();
|
||||
}
|
||||
|
||||
public interface TimeStampTokenIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.tsp.TimeStampToken";
|
||||
SignerIdIf getSID();
|
||||
StoreIf getCertificates();
|
||||
StoreIf getCRLs();
|
||||
TimeStampTokenInfoIf getTimeStampInfo();
|
||||
byte[] getEncoded() throws IOException;
|
||||
void validate(SignerInformationVerifierIf verifier) throws Exception;
|
||||
}
|
||||
|
||||
public interface TimeStampTokenInfoIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.tsp.TimeStampTokenInfo";
|
||||
Date getGenTime();
|
||||
}
|
||||
|
||||
public interface X509CertificateHolderIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.X509CertificateHolder";
|
||||
}
|
||||
|
||||
public interface X509NameIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.X509Name";
|
||||
String toString$delegate();
|
||||
}
|
||||
|
||||
public interface X509PrincipalIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.jce.X509Principal";
|
||||
String getName();
|
||||
}
|
||||
|
||||
public interface X509V3CertificateGeneratorIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.x509.X509V3CertificateGenerator";
|
||||
|
||||
void reset();
|
||||
void setPublicKey(PublicKey key);
|
||||
void setSignatureAlgorithm(String signatureAlgorithm);
|
||||
void setNotBefore(Date date);
|
||||
void setNotAfter(Date date);
|
||||
void setIssuerDN(X509PrincipalIf issuerDN);
|
||||
void setSubjectDN(X509PrincipalIf issuerDN);
|
||||
void setSerialNumber(BigInteger serialNumber);
|
||||
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, SubjectKeyIdentifierIf value);
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, AuthorityKeyIdentifierIf value);
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, BasicConstraintsIf value);
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, DERSequenceIf value);
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, AuthorityInformationAccessIf value);
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, KeyUsageIf value);
|
||||
|
||||
X509Certificate generate(PrivateKey issuerPrivateKey);
|
||||
}
|
||||
|
||||
public interface OCSPReqIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.OCSPReq";
|
||||
|
||||
ReqIf[] getRequestList();
|
||||
}
|
||||
|
||||
public interface OCSPReqGeneratorIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.ocsp.OCSPReqGenerator";
|
||||
|
||||
void addRequest(CertificateIDIf certId);
|
||||
OCSPReqIf generate();
|
||||
}
|
||||
|
||||
public interface BasicOCSPRespGeneratorIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.ocsp.BasicOCSPRespGenerator";
|
||||
|
||||
void addResponse(CertificateIDIf certificateID, CertificateStatusIf certificateStatus);
|
||||
BasicOCSPRespIf generate(String signatureAlgorithm, PrivateKey ocspResponderPrivateKey,
|
||||
X509Certificate chain[], Date date, String provider);
|
||||
}
|
||||
|
||||
public interface CertificateIDIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.CertificateID";
|
||||
|
||||
String HASH_SHA1();
|
||||
}
|
||||
|
||||
public interface X509ExtensionsIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.X509Extensions";
|
||||
|
||||
ASN1ObjectIdentifierIf AuthorityKeyIdentifier();
|
||||
ASN1ObjectIdentifierIf SubjectKeyIdentifier();
|
||||
ASN1ObjectIdentifierIf BasicConstraints();
|
||||
ASN1ObjectIdentifierIf CRLDistributionPoints();
|
||||
ASN1ObjectIdentifierIf AuthorityInfoAccess();
|
||||
ASN1ObjectIdentifierIf KeyUsage();
|
||||
ASN1ObjectIdentifierIf CRLNumber();
|
||||
}
|
||||
|
||||
public interface X509ObjectIdentifiersIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.X509ObjectIdentifiers";
|
||||
|
||||
ASN1ObjectIdentifierIf ocspAccessMethod();
|
||||
}
|
||||
|
||||
public interface X509V2CRLGeneratorIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.X509V2CRLGenerator";
|
||||
|
||||
void setIssuerDN(X500Principal issuerDN);
|
||||
void setThisUpdate(Date date);
|
||||
void setNextUpdate(Date date);
|
||||
void setSignatureAlgorithm(String algorithm);
|
||||
|
||||
void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, CRLNumberIf value);
|
||||
X509CRL generate(PrivateKey privateKey);
|
||||
}
|
||||
|
||||
public interface ReqIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.Req";
|
||||
|
||||
CertificateIDIf getCertID();
|
||||
}
|
||||
|
||||
public interface CertificateStatusIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.CertificateStatus";
|
||||
|
||||
CertificateStatusIf GOOD();
|
||||
}
|
||||
|
||||
public interface RevokedStatusIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.cert.ocsp.RevokedStatus";
|
||||
}
|
||||
|
||||
public interface CRLReasonIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.asn1.x509.CRLReason";
|
||||
int unspecified();
|
||||
}
|
||||
|
||||
public interface OCSPRespGeneratorIf extends ProxyIf {
|
||||
String delegateClass = "org.bouncycastle.ocsp.OCSPRespGenerator";
|
||||
int SUCCESSFUL();
|
||||
OCSPRespIf generate(int status, BasicOCSPRespIf basicOCSPResp);
|
||||
}
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
package org.apache.poi.poifs.crypt.dsig;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
|
||||
import org.apache.poi.util.MethodUtils;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
public class HorribleProxy implements InvocationHandler {
|
||||
|
||||
private static final POILogger LOG = POILogFactory.getLogger(HorribleProxy.class);
|
||||
|
||||
protected static interface ProxyIf {
|
||||
Object getDelegate();
|
||||
void setInitDeferred(boolean initDeferred);
|
||||
};
|
||||
|
||||
private final Class<?> delegateClass;
|
||||
private Object delegateRef;
|
||||
private boolean initDeferred = true;
|
||||
|
||||
protected HorribleProxy(Class<?> delegateClass, Object delegateRef) {
|
||||
this.delegateClass = delegateClass;
|
||||
// delegateRef can be null, then we have to deal with deferred initialisation
|
||||
this.delegateRef = delegateRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new instance by constructor
|
||||
*
|
||||
* @param proxyClass
|
||||
* @param initargs
|
||||
* @return
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
* @throws NoSuchMethodException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends ProxyIf> T newProxy(Class<T> proxyClass, Object ... initargs)
|
||||
throws InvocationTargetException, IllegalAccessException, InstantiationException
|
||||
, NoSuchMethodException, ClassNotFoundException, NoSuchFieldException {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
Class<?> delegateClass = getDelegateClass(proxyClass);
|
||||
Object delegateRef;
|
||||
if (initargs.length == 0) {
|
||||
delegateRef = null;
|
||||
} else if (initargs.length == 1 && delegateClass.isAssignableFrom(initargs[0].getClass())) {
|
||||
delegateRef = initargs[0];
|
||||
} else {
|
||||
Class<?> paramTypes[] = updateMethodArgs(null, initargs);
|
||||
Constructor<?> cons = null;
|
||||
try {
|
||||
cons = delegateClass.getConstructor(paramTypes);
|
||||
} catch (Exception e) {
|
||||
// fallback - find constructor with same amount of parameters
|
||||
// horrible et al. ...
|
||||
cons = MethodUtils.getMatchingAccessibleConstructor(delegateClass, paramTypes);
|
||||
|
||||
if (cons == null) {
|
||||
throw new RuntimeException("There's no constructor for the given arguments.");
|
||||
}
|
||||
}
|
||||
|
||||
delegateRef = cons.newInstance(initargs);
|
||||
}
|
||||
|
||||
HorribleProxy hp = new HorribleProxy(delegateClass, delegateRef);
|
||||
return (T)Proxy.newProxyInstance(cl, new Class<?>[]{proxyClass}, hp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new instance by factory method
|
||||
*
|
||||
* @param proxyClass
|
||||
* @param factoryMethod
|
||||
* @param initargs
|
||||
* @return
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
* @throws InstantiationException
|
||||
* @throws NoSuchMethodException
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends ProxyIf> T createProxy(Class<T> proxyClass, String factoryMethod, Object ... initargs)
|
||||
throws InvocationTargetException, IllegalAccessException, InstantiationException
|
||||
, NoSuchMethodException, ClassNotFoundException, NoSuchFieldException {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
Class<?> delegateClass = getDelegateClass(proxyClass);
|
||||
Class<?> paramTypes[] = updateMethodArgs(null, initargs);
|
||||
Method facMethod = delegateClass.getMethod(factoryMethod, paramTypes);
|
||||
Object delegateRef = facMethod.invoke(null, initargs);
|
||||
|
||||
if (delegateRef == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
HorribleProxy hp = new HorribleProxy(delegateClass, delegateRef);
|
||||
return (T)Proxy.newProxyInstance(cl, new Class<?>[]{proxyClass}, hp);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Exception {
|
||||
String methodName = method.getName().replaceFirst("\\$.*", "");
|
||||
if (Object.class == method.getDeclaringClass()) {
|
||||
if ("equals".equals(methodName)) {
|
||||
return proxy == args[0];
|
||||
} else if ("hashCode".equals(methodName)) {
|
||||
return System.identityHashCode(proxy);
|
||||
} else if ("toString".equals(methodName)) {
|
||||
return proxy.getClass().getName() + "@"
|
||||
+ Integer.toHexString(System.identityHashCode(proxy))
|
||||
+ ", with InvocationHandler " + this;
|
||||
} else {
|
||||
throw new IllegalStateException(String.valueOf(method));
|
||||
}
|
||||
}
|
||||
|
||||
if ("getDelegate".equals(methodName)) {
|
||||
initDeferred();
|
||||
return delegateRef;
|
||||
} else if ("setInitDeferred".equals(methodName)) {
|
||||
initDeferred = (Boolean)args[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<?> methodParams[] = updateMethodArgs(method.getParameterTypes(), args);
|
||||
|
||||
Object ret = null;
|
||||
boolean isStaticField = false;
|
||||
if (methodParams.length == 0) {
|
||||
// check for static fields first
|
||||
try {
|
||||
Field f = delegateClass.getDeclaredField(methodName);
|
||||
ret = f.get(delegateRef);
|
||||
isStaticField = true;
|
||||
} catch (NoSuchFieldException e) {
|
||||
LOG.log(POILogger.DEBUG, "No static field '"+methodName+"' in class '"+delegateClass.getCanonicalName()+"' - trying method now.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!isStaticField) {
|
||||
Method methodImpl = null;
|
||||
try {
|
||||
methodImpl = delegateClass.getMethod(methodName, methodParams);
|
||||
} catch (Exception e) {
|
||||
// fallback - if methodName is distinct, try to use it
|
||||
// in case we can't provide method declaration in the Proxy interface
|
||||
// ... and of course, this is horrible ...
|
||||
methodImpl = MethodUtils.getMatchingAccessibleMethod(delegateClass, methodName, methodParams);
|
||||
|
||||
if (methodImpl == null) {
|
||||
throw new RuntimeException("There's no method '"+methodName+"' for the given arguments.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Modifier.isStatic(methodImpl.getModifiers())) {
|
||||
initDeferred();
|
||||
}
|
||||
ret = methodImpl.invoke(delegateRef, args);
|
||||
}
|
||||
|
||||
Class<?> retType = method.getReturnType();
|
||||
if (retType.isArray()) {
|
||||
if (ProxyIf.class.isAssignableFrom(retType.getComponentType())) {
|
||||
Class<? extends ProxyIf> cType = (Class<? extends ProxyIf>)retType.getComponentType();
|
||||
ProxyIf paRet[] = (ProxyIf[])Array.newInstance(cType, ((Object[])ret).length);
|
||||
for (int i=0; i<((Object[])ret).length; i++) {
|
||||
paRet[i] = newProxy(cType, ((Object[])ret)[i]);
|
||||
paRet[i].setInitDeferred(false);
|
||||
}
|
||||
ret = paRet;
|
||||
}
|
||||
} else if (ProxyIf.class.isAssignableFrom(retType)) {
|
||||
ProxyIf pRet = newProxy((Class<? extends ProxyIf>)retType, ret);
|
||||
pRet.setInitDeferred(false);
|
||||
ret = pRet;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<?>[] updateMethodArgs(Class<?> types[], Object args[])
|
||||
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
|
||||
if (args == null) return new Class<?>[0];
|
||||
if (types == null) types = new Class<?>[args.length];
|
||||
if (types.length != args.length) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
for (int i=0; i<types.length; i++) {
|
||||
if (types[i] == null) {
|
||||
if (args[i] == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
types[i] = args[i].getClass();
|
||||
}
|
||||
|
||||
if (ProxyIf.class.isAssignableFrom(types[i])) {
|
||||
types[i] = getDelegateClass((Class<? extends ProxyIf>)types[i]);
|
||||
if (args[i] != null) {
|
||||
args[i] = ((ProxyIf)args[i]).getDelegate();
|
||||
}
|
||||
}
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
private void initDeferred() throws Exception {
|
||||
if (delegateRef != null || !initDeferred) return;
|
||||
// currently works only for empty constructor
|
||||
delegateRef = delegateClass.getConstructor().newInstance();
|
||||
}
|
||||
|
||||
private static Class<?> getDelegateClass(Class<? extends ProxyIf> proxyClass)
|
||||
throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
|
||||
Field delegateField;
|
||||
try {
|
||||
delegateField = proxyClass.getDeclaredField("delegateClass");
|
||||
} catch (NoSuchFieldException e) {
|
||||
// sometimes a proxy interface is returned as proxyClass
|
||||
// this has to be asked for the real ProxyIf interface
|
||||
Class<?> ifs[] = proxyClass.getInterfaces();
|
||||
if (ifs == null || ifs.length != 1) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
delegateField = ifs[0].getDeclaredField("delegateClass");
|
||||
}
|
||||
|
||||
String delegateClassName = (String)delegateField.get(null);
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
Class<?> delegateClass = Class.forName(delegateClassName, true, cl);
|
||||
return delegateClass;
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/* ====================================================================
|
||||
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.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 X509Certificate certificate;
|
||||
|
||||
@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();
|
||||
this.certificate = null;
|
||||
for (XMLStructure keyInfoStructure : keyInfoContent) {
|
||||
if (false == (keyInfoStructure instanceof X509Data)) {
|
||||
continue;
|
||||
}
|
||||
X509Data x509Data = (X509Data) keyInfoStructure;
|
||||
List<Object> x509DataList = x509Data.getContent();
|
||||
for (Object x509DataObject : x509DataList) {
|
||||
if (false == (x509DataObject instanceof X509Certificate)) {
|
||||
continue;
|
||||
}
|
||||
X509Certificate certificate = (X509Certificate) x509DataObject;
|
||||
LOG.log(POILogger.DEBUG, "certificate", certificate.getSubjectX500Principal());
|
||||
if (null == this.certificate) {
|
||||
/*
|
||||
* The first certificate is presumably the signer.
|
||||
*/
|
||||
this.certificate = certificate;
|
||||
}
|
||||
}
|
||||
if (null != this.certificate) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
throw new KeySelectorException("No key found!");
|
||||
}
|
||||
|
||||
public Key getKey() {
|
||||
return this.certificate.getPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the X509 certificate used during the last signature
|
||||
* verification operation.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public X509Certificate getCertificate() {
|
||||
return this.certificate;
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/* ====================================================================
|
||||
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.OPCPackage;
|
||||
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.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* JSR105 URI dereferencer for Office Open XML documents.
|
||||
*/
|
||||
public class OOXMLURIDereferencer implements URIDereferencer {
|
||||
|
||||
private static final POILogger LOG = POILogFactory.getLogger(OOXMLURIDereferencer.class);
|
||||
|
||||
private final OPCPackage pkg;
|
||||
|
||||
private final URIDereferencer baseUriDereferencer;
|
||||
|
||||
public OOXMLURIDereferencer(OPCPackage pkg) {
|
||||
if (null == pkg) {
|
||||
throw new IllegalArgumentException("OPCPackage is null");
|
||||
}
|
||||
this.pkg = pkg;
|
||||
XMLSignatureFactory xmlSignatureFactory = SignatureInfo.getSignatureFactory();
|
||||
this.baseUriDereferencer = xmlSignatureFactory.getURIDereferencer();
|
||||
}
|
||||
|
||||
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 pkg.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,283 @@
|
||||
/* ====================================================================
|
||||
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.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.Key;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.xml.crypto.dsig.XMLSignature;
|
||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
import javax.xml.crypto.dsig.dom.DOMValidateContext;
|
||||
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
|
||||
|
||||
import org.apache.poi.EncryptedDocumentException;
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||
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.poifs.crypt.ChainingMode;
|
||||
import org.apache.poi.poifs.crypt.CipherAlgorithm;
|
||||
import org.apache.poi.poifs.crypt.CryptoFunctions;
|
||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.InitIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.poi.util.SAXHelper;
|
||||
import org.apache.xmlbeans.XmlCursor;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
public class SignatureInfo {
|
||||
|
||||
public static final byte[] SHA1_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14 };
|
||||
|
||||
public static final byte[] SHA224_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x2b, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
||||
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x04, 0x1c };
|
||||
|
||||
public static final byte[] SHA256_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
||||
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20 };
|
||||
|
||||
public static final byte[] SHA384_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
||||
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30 };
|
||||
|
||||
public static final byte[] SHA512_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86
|
||||
, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40 };
|
||||
|
||||
public static final byte[] RIPEMD128_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x1b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x02, 0x04, 0x10 };
|
||||
|
||||
public static final byte[] RIPEMD160_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x04, 0x14 };
|
||||
|
||||
public static final byte[] RIPEMD256_DIGEST_INFO_PREFIX = new byte[]
|
||||
{ 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x03, 0x04, 0x20 };
|
||||
|
||||
|
||||
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
|
||||
private static boolean isInitialized = false;
|
||||
|
||||
private final OPCPackage pkg;
|
||||
|
||||
public SignatureInfo(OPCPackage pkg) {
|
||||
this.pkg = pkg;
|
||||
}
|
||||
|
||||
public boolean verifySignature() {
|
||||
initXmlProvider();
|
||||
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
|
||||
List<X509Certificate> signers = new LinkedList<X509Certificate>();
|
||||
return getSignersAndValidate(signers, true);
|
||||
}
|
||||
|
||||
public void confirmSignature(Key key, X509Certificate x509)
|
||||
throws NoSuchAlgorithmException, IOException {
|
||||
confirmSignature(key, x509, HashAlgorithm.sha1);
|
||||
}
|
||||
|
||||
public void confirmSignature(Key key, X509Certificate x509, HashAlgorithm hashAlgo)
|
||||
throws NoSuchAlgorithmException, IOException {
|
||||
XmlSignatureService signatureService = createSignatureService(hashAlgo, pkg);
|
||||
|
||||
// operate
|
||||
List<X509Certificate> x509Chain = Collections.singletonList(x509);
|
||||
DigestInfo digestInfo = signatureService.preSign(null, x509Chain, null, null, null);
|
||||
|
||||
// setup: key material, signature value
|
||||
|
||||
Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa
|
||||
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");
|
||||
|
||||
byte[] signatureValue;
|
||||
try {
|
||||
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
|
||||
digestInfoValueBuf.write(SHA1_DIGEST_INFO_PREFIX);
|
||||
digestInfoValueBuf.write(digestInfo.digestValue);
|
||||
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
|
||||
signatureValue = cipher.doFinal(digestInfoValue);
|
||||
} catch (Exception e) {
|
||||
throw new EncryptedDocumentException(e);
|
||||
}
|
||||
|
||||
|
||||
// operate: postSign
|
||||
signatureService.postSign(signatureValue, Collections.singletonList(x509));
|
||||
}
|
||||
|
||||
public XmlSignatureService createSignatureService(HashAlgorithm hashAlgo, OPCPackage pkg) {
|
||||
XmlSignatureService signatureService = new XmlSignatureService(hashAlgo, pkg);
|
||||
signatureService.initFacets(new Date());
|
||||
return signatureService;
|
||||
}
|
||||
|
||||
public List<X509Certificate> getSigners() {
|
||||
initXmlProvider();
|
||||
List<X509Certificate> signers = new LinkedList<X509Certificate>();
|
||||
getSignersAndValidate(signers, false);
|
||||
return signers;
|
||||
}
|
||||
|
||||
protected boolean getSignersAndValidate(List<X509Certificate> signers, boolean onlyFirst) {
|
||||
boolean allValid = true;
|
||||
List<PackagePart> signatureParts = getSignatureParts(onlyFirst);
|
||||
if (signatureParts.isEmpty()) {
|
||||
LOG.log(POILogger.DEBUG, "no signature resources");
|
||||
allValid = false;
|
||||
}
|
||||
|
||||
for (PackagePart signaturePart : signatureParts) {
|
||||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
|
||||
|
||||
try {
|
||||
Document doc = SAXHelper.readSAXDocumentW3C(signaturePart.getInputStream());
|
||||
// dummy call to createSignatureService to tweak document afterwards
|
||||
createSignatureService(HashAlgorithm.sha1, pkg).registerIds(doc);
|
||||
|
||||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
|
||||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);
|
||||
OOXMLURIDereferencer dereferencer = new OOXMLURIDereferencer(pkg);
|
||||
domValidateContext.setURIDereferencer(dereferencer);
|
||||
|
||||
XMLSignatureFactory xmlSignatureFactory = getSignatureFactory();
|
||||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);
|
||||
boolean validity = xmlSignature.validate(domValidateContext);
|
||||
allValid &= validity;
|
||||
if (!validity) continue;
|
||||
// TODO: check what has been signed.
|
||||
} catch (Exception e) {
|
||||
LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
X509Certificate signer = keySelector.getCertificate();
|
||||
signers.add(signer);
|
||||
}
|
||||
|
||||
return allValid;
|
||||
}
|
||||
|
||||
protected List<PackagePart> getSignatureParts(boolean onlyFirst) {
|
||||
List<PackagePart> packageParts = new LinkedList<PackagePart>();
|
||||
|
||||
PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
||||
for (PackageRelationship rel : sigOrigRels) {
|
||||
PackagePart sigPart = pkg.getPart(rel);
|
||||
LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);
|
||||
|
||||
try {
|
||||
PackageRelationshipCollection sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE);
|
||||
for (PackageRelationship sigRel : sigRels) {
|
||||
PackagePart sigRelPart = sigPart.getRelatedPart(sigRel);
|
||||
LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);
|
||||
packageParts.add(sigRelPart);
|
||||
if (onlyFirst) break;
|
||||
}
|
||||
} catch (InvalidFormatException e) {
|
||||
LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
|
||||
}
|
||||
|
||||
if (onlyFirst && !packageParts.isEmpty()) break;
|
||||
}
|
||||
|
||||
return packageParts;
|
||||
}
|
||||
|
||||
public static XMLSignatureFactory getSignatureFactory() {
|
||||
Provider p = Security.getProvider("XMLDSig");
|
||||
assert(p != null);
|
||||
return XMLSignatureFactory.getInstance("DOM", p);
|
||||
}
|
||||
|
||||
public static KeyInfoFactory getKeyInfoFactory() {
|
||||
Provider p = Security.getProvider("XMLDSig");
|
||||
assert(p != null);
|
||||
return KeyInfoFactory.getInstance("DOM", p);
|
||||
}
|
||||
|
||||
public static void insertXChild(XmlObject root, XmlObject child) {
|
||||
XmlCursor rootCursor = root.newCursor();
|
||||
insertXChild(rootCursor, child);
|
||||
rootCursor.dispose();
|
||||
}
|
||||
|
||||
public static void insertXChild(XmlCursor rootCursor, XmlObject child) {
|
||||
rootCursor.toEndToken();
|
||||
XmlCursor childCursor = child.newCursor();
|
||||
childCursor.toNextToken();
|
||||
childCursor.moveXml(rootCursor);
|
||||
childCursor.dispose();
|
||||
}
|
||||
|
||||
public static void setPrefix(XmlObject xobj, String ns, String prefix) {
|
||||
for (XmlCursor cur = xobj.newCursor(); cur.hasNextToken(); cur.toNextToken()) {
|
||||
if (cur.isStart()) {
|
||||
Element el = (Element)cur.getDomNode();
|
||||
if (ns.equals(el.getNamespaceURI())) el.setPrefix(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void initXmlProvider() {
|
||||
if (isInitialized) return;
|
||||
isInitialized = true;
|
||||
|
||||
try {
|
||||
InitIf init = HorribleProxy.newProxy(InitIf.class);
|
||||
init.init();
|
||||
|
||||
RelationshipTransformService.registerDsigProvider();
|
||||
|
||||
Provider bcProv = Security.getProvider("BC");
|
||||
if (bcProv == null) {
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
Class<?> c = cl.loadClass("org.bouncycastle.jce.provider.BouncyCastleProvider");
|
||||
bcProv = (Provider)c.newInstance();
|
||||
Security.addProvider(bcProv);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,194 @@
|
||||
/* ====================================================================
|
||||
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.Provider;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.dom.DOMCryptoContext;
|
||||
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.poi.poifs.crypt.dsig.HorribleProxy;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMKeyInfoIf;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.w3.x2000.x09.xmldsig.ObjectType;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureType;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
private final boolean includeEntireCertificateChain;
|
||||
|
||||
private final boolean includeIssuerSerial;
|
||||
|
||||
private final boolean includeKeyValue;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param includeEntireCertificateChain
|
||||
* @param includeIssuerSerial
|
||||
* @param includeKeyValue
|
||||
*/
|
||||
public KeyInfoSignatureFacet(boolean includeEntireCertificateChain,
|
||||
boolean includeIssuerSerial, boolean includeKeyValue) {
|
||||
this.includeEntireCertificateChain = includeEntireCertificateChain;
|
||||
this.includeIssuerSerial = includeIssuerSerial;
|
||||
this.includeKeyValue = includeKeyValue;
|
||||
}
|
||||
|
||||
public void postSign(SignatureType signatureElement,
|
||||
List<X509Certificate> signingCertificateChain) {
|
||||
LOG.log(POILogger.DEBUG, "postSign");
|
||||
|
||||
List<ObjectType> objList = signatureElement.getObjectList();
|
||||
|
||||
/*
|
||||
* Make sure we insert right after the ds:SignatureValue element, just
|
||||
* before the first ds:Object element.
|
||||
*/
|
||||
Node nextSibling = (objList.isEmpty()) ? null : objList.get(0).getDomNode();
|
||||
|
||||
/*
|
||||
* Construct the ds:KeyInfo element using JSR 105.
|
||||
*/
|
||||
String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
|
||||
Provider xmlDSigProv;
|
||||
try {
|
||||
xmlDSigProv = (Provider) Class.forName(providerName).newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!", e);
|
||||
}
|
||||
|
||||
KeyInfoFactory keyInfoFactory = KeyInfoFactory.getInstance("DOM", xmlDSigProv);
|
||||
List<Object> x509DataObjects = new LinkedList<Object>();
|
||||
X509Certificate signingCertificate = signingCertificateChain.get(0);
|
||||
|
||||
List<Object> keyInfoContent = new LinkedList<Object>();
|
||||
|
||||
if (this.includeKeyValue) {
|
||||
KeyValue keyValue;
|
||||
try {
|
||||
keyValue = keyInfoFactory.newKeyValue(signingCertificate.getPublicKey());
|
||||
} catch (KeyException e) {
|
||||
throw new RuntimeException("key exception: " + e.getMessage(), e);
|
||||
}
|
||||
keyInfoContent.add(keyValue);
|
||||
}
|
||||
|
||||
if (this.includeIssuerSerial) {
|
||||
x509DataObjects.add(keyInfoFactory.newX509IssuerSerial(
|
||||
signingCertificate.getIssuerX500Principal().toString(),
|
||||
signingCertificate.getSerialNumber()));
|
||||
}
|
||||
|
||||
if (this.includeEntireCertificateChain) {
|
||||
for (X509Certificate certificate : signingCertificateChain) {
|
||||
x509DataObjects.add(certificate);
|
||||
}
|
||||
} else {
|
||||
x509DataObjects.add(signingCertificate);
|
||||
}
|
||||
|
||||
if (false == x509DataObjects.isEmpty()) {
|
||||
X509Data x509Data = keyInfoFactory.newX509Data(x509DataObjects);
|
||||
keyInfoContent.add(x509Data);
|
||||
}
|
||||
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(keyInfoContent);
|
||||
DOMKeyInfoIf domKeyInfo;
|
||||
try {
|
||||
domKeyInfo = HorribleProxy.newProxy(DOMKeyInfoIf.class, keyInfo);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DOMKeyInfo instance error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
DOMSignContext domSignContext = new DOMSignContext(key, signatureElement.getDomNode());
|
||||
DOMCryptoContext domCryptoContext = domSignContext;
|
||||
String signatureNamespacePrefix = "xd";
|
||||
try {
|
||||
domKeyInfo.marshal(signatureElement.getDomNode(), nextSibling,
|
||||
signatureNamespacePrefix, domCryptoContext);
|
||||
} catch (MarshalException e) {
|
||||
throw new RuntimeException("marshall error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void preSign(XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references,
|
||||
List<XMLObject> objects
|
||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
// empty
|
||||
}
|
||||
|
||||
public Map<String,String> getNamespacePrefixMapping() {
|
||||
Map<String,String> map = new HashMap<String,String>();
|
||||
// map.put("xd", "http://www.w3.org/2000/09/xmldsig#");
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,541 @@
|
||||
/* ====================================================================
|
||||
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.SignatureInfo.setPrefix;
|
||||
|
||||
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.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
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.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.PackageNamespaces;
|
||||
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.HashAlgorithm;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService.RelationshipTransformParameterSpec;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.Constants;
|
||||
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.w3.x2000.x09.xmldsig.SignatureType;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
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);
|
||||
|
||||
public static final String OOXML_DIGSIG_NS = "http://schemas.openxmlformats.org/package/2006/digital-signature";
|
||||
public static final String OFFICE_DIGSIG_NS = "http://schemas.microsoft.com/office/2006/digsig";
|
||||
|
||||
private final XmlSignatureService signatureService;
|
||||
|
||||
private final Date clock;
|
||||
|
||||
private final HashAlgorithm hashAlgo;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
public OOXMLSignatureFacet(XmlSignatureService signatureService, Date clock, HashAlgorithm hashAlgo) {
|
||||
this.signatureService = signatureService;
|
||||
this.clock = (clock == null ? new Date() : clock);
|
||||
this.hashAlgo = (hashAlgo == null ? HashAlgorithm.sha1 : hashAlgo);
|
||||
}
|
||||
|
||||
public void preSign(XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
LOG.log(POILogger.DEBUG, "pre sign");
|
||||
addManifestObject(signatureFactory, signatureId, references, objects);
|
||||
addSignatureInfo(signatureFactory, signatureId, references, objects);
|
||||
}
|
||||
|
||||
private void addManifestObject(XMLSignatureFactory signatureFactory,
|
||||
String signatureId, List<Reference> references,
|
||||
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
Manifest manifest = constructManifest(signatureFactory);
|
||||
String objectId = "idPackageObject"; // really has to be this value.
|
||||
List<XMLStructure> objectContent = new LinkedList<XMLStructure>();
|
||||
objectContent.add(manifest);
|
||||
|
||||
addSignatureTime(signatureFactory, signatureId, objectContent);
|
||||
|
||||
objects.add(signatureFactory.newXMLObject(objectContent, objectId,
|
||||
null, null));
|
||||
|
||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(this.hashAlgo.xmlSignUri, null);
|
||||
Reference reference = signatureFactory.newReference("#" + objectId,
|
||||
digestMethod, null, "http://www.w3.org/2000/09/xmldsig#Object",
|
||||
null);
|
||||
references.add(reference);
|
||||
}
|
||||
|
||||
private Manifest constructManifest(XMLSignatureFactory signatureFactory)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
List<Reference> manifestReferences = new ArrayList<Reference>();
|
||||
|
||||
try {
|
||||
addManifestReferences(signatureFactory, manifestReferences);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return signatureFactory.newManifest(manifestReferences);
|
||||
}
|
||||
|
||||
private void addManifestReferences(XMLSignatureFactory signatureFactory, List<Reference> manifestReferences)
|
||||
throws IOException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException, URISyntaxException, XmlException {
|
||||
|
||||
OPCPackage ooxml = this.signatureService.getOfficeOpenXMLDocument();
|
||||
List<PackagePart> relsEntryNames = ooxml.getPartsByContentType(ContentTypes.RELATIONSHIPS_PART);
|
||||
|
||||
|
||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(this.hashAlgo.xmlSignUri, 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();
|
||||
|
||||
if (TargetMode.EXTERNAL == relationship.getTargetMode()) {
|
||||
/*
|
||||
* 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."
|
||||
*/
|
||||
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 LinkedList<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void addSignatureTime(XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
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(this.clock);
|
||||
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);
|
||||
|
||||
// TODO: find better method to have xmlbeans + export the prefix
|
||||
Node n = ctTime.getDomNode();
|
||||
setPrefix(ctTime, PackageNamespaces.DIGITAL_SIGNATURE, "mdssi");
|
||||
|
||||
List<XMLStructure> signatureTimeContent = new LinkedList<XMLStructure>();
|
||||
signatureTimeContent.add(new DOMStructure(n));
|
||||
SignatureProperty signatureTimeSignatureProperty = signatureFactory
|
||||
.newSignatureProperty(signatureTimeContent, "#" + signatureId,
|
||||
"idSignatureTime");
|
||||
List<SignatureProperty> signaturePropertyContent = new LinkedList<SignatureProperty>();
|
||||
signaturePropertyContent.add(signatureTimeSignatureProperty);
|
||||
SignatureProperties signatureProperties = signatureFactory
|
||||
.newSignatureProperties(signaturePropertyContent,
|
||||
"id-signature-time-" + this.clock.getTime());
|
||||
objectContent.add(signatureProperties);
|
||||
}
|
||||
|
||||
private void addSignatureInfo(XMLSignatureFactory signatureFactory,
|
||||
String signatureId, List<Reference> references,
|
||||
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
List<XMLStructure> objectContent = new LinkedList<XMLStructure>();
|
||||
|
||||
SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
|
||||
CTSignatureInfoV1 ctSigV1 = sigV1.addNewSignatureInfoV1();
|
||||
ctSigV1.setManifestHashAlgorithm("http://www.w3.org/2000/09/xmldsig#sha1");
|
||||
Node n = ctSigV1.getDomNode();
|
||||
((Element)n).setAttributeNS(Constants.NamespaceSpecNS, "xmlns", "http://schemas.microsoft.com/office/2006/digsig");
|
||||
|
||||
List<XMLStructure> signatureInfoContent = new LinkedList<XMLStructure>();
|
||||
signatureInfoContent.add(new DOMStructure(n));
|
||||
SignatureProperty signatureInfoSignatureProperty = signatureFactory
|
||||
.newSignatureProperty(signatureInfoContent, "#" + signatureId,
|
||||
"idOfficeV1Details");
|
||||
|
||||
List<SignatureProperty> signaturePropertyContent = new LinkedList<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(this.hashAlgo.xmlSignUri, null);
|
||||
Reference reference = signatureFactory.newReference("#" + objectId,
|
||||
digestMethod, null, "http://www.w3.org/2000/09/xmldsig#Object",
|
||||
null);
|
||||
references.add(reference);
|
||||
}
|
||||
|
||||
public void postSign(SignatureType signatureElement,
|
||||
List<X509Certificate> signingCertificateChain) {
|
||||
// empty
|
||||
}
|
||||
|
||||
public static String getRelationshipReferenceURI(String zipEntryName) {
|
||||
|
||||
return "/"
|
||||
+ zipEntryName
|
||||
+ "?ContentType=application/vnd.openxmlformats-package.relationships+xml";
|
||||
}
|
||||
|
||||
public static String getResourceReferenceURI(String resourceName,
|
||||
String contentType) {
|
||||
|
||||
return "/" + resourceName + "?ContentType=" + contentType;
|
||||
}
|
||||
|
||||
public static 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" };
|
||||
|
||||
public 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 Map<String,String> getNamespacePrefixMapping() {
|
||||
Map<String,String> m = new HashMap<String,String>();
|
||||
m.put("mdssi", OOXML_DIGSIG_NS);
|
||||
m.put("xd", "http://uri.etsi.org/01903/v1.3.2#");
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Office 2010 list of signed types (extensions).
|
||||
*/
|
||||
public static 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,101 @@
|
||||
/* ====================================================================
|
||||
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.XAdESXLSignatureFacet.XADES_NAMESPACE;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.crypto.dsig.Reference;
|
||||
import javax.xml.crypto.dsig.XMLObject;
|
||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
|
||||
import org.etsi.uri.x01903.v13.UnsignedPropertiesType;
|
||||
import org.etsi.uri.x01903.v13.UnsignedSignaturePropertiesType;
|
||||
import org.w3.x2000.x09.xmldsig.ObjectType;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureType;
|
||||
|
||||
/**
|
||||
* 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 preSign(XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references,
|
||||
List<XMLObject> objects
|
||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
}
|
||||
|
||||
public void postSign(SignatureType signatureElement, List<X509Certificate> signingCertificateChain) {
|
||||
QualifyingPropertiesType qualProps = null;
|
||||
|
||||
try {
|
||||
// check for XAdES-BES
|
||||
for (ObjectType ot : signatureElement.getObjectList()) {
|
||||
XmlObject xo[] = ot.selectChildren(new QName(XADES_NAMESPACE, "QualifyingProperties"));
|
||||
if (xo != null && xo.length > 0) {
|
||||
qualProps = QualifyingPropertiesType.Factory.parse(xo[0].getDomNode());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (XmlException e) {
|
||||
throw new RuntimeException("signature decoding error", e);
|
||||
}
|
||||
|
||||
if (qualProps == null) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String,String> getNamespacePrefixMapping() {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/* ====================================================================
|
||||
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 java.util.Map;
|
||||
|
||||
import javax.xml.crypto.dsig.Reference;
|
||||
import javax.xml.crypto.dsig.XMLObject;
|
||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
|
||||
import org.w3.x2000.x09.xmldsig.SignatureType;
|
||||
|
||||
/**
|
||||
* JSR105 Signature Facet interface.
|
||||
*
|
||||
* @author Frank Cornelis
|
||||
*
|
||||
*/
|
||||
public interface SignatureFacet {
|
||||
|
||||
/**
|
||||
* 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(
|
||||
XMLSignatureFactory signatureFactory
|
||||
, String signatureId
|
||||
, List<X509Certificate> signingCertificateChain
|
||||
, List<Reference> references
|
||||
, List<XMLObject> objects
|
||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
|
||||
|
||||
/**
|
||||
* 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(
|
||||
SignatureType signatureElement
|
||||
, List<X509Certificate> signingCertificateChain);
|
||||
|
||||
Map<String,String> getNamespacePrefixMapping();
|
||||
}
|
@ -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.facets;
|
||||
|
||||
/**
|
||||
* 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,385 @@
|
||||
/* ====================================================================
|
||||
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.SignatureInfo.setPrefix;
|
||||
|
||||
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.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
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.SignatureInfo;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.Constants;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
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.SignatureType;
|
||||
import org.w3.x2000.x09.xmldsig.X509IssuerSerialType;
|
||||
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 final Date clock;
|
||||
|
||||
private final HashAlgorithm hashAlgo;
|
||||
|
||||
private final SignaturePolicyService signaturePolicyService;
|
||||
|
||||
private String idSignedProperties;
|
||||
|
||||
private boolean signaturePolicyImplied;
|
||||
|
||||
private String role;
|
||||
|
||||
private boolean issuerNameNoReverseOrder = false;
|
||||
|
||||
private Map<String, String> dataObjectFormatMimeTypes;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param clock
|
||||
* the clock to be used for determining the xades:SigningTime,
|
||||
* defaults to now when null
|
||||
* @param hashAlgo
|
||||
* the digest algorithm to be used for all required XAdES digest
|
||||
* operations. Possible values: "SHA-1", "SHA-256", or "SHA-512",
|
||||
* defaults to SHA-1 when null
|
||||
* @param signaturePolicyService
|
||||
* the optional signature policy service used for XAdES-EPES.
|
||||
*/
|
||||
public XAdESSignatureFacet(Date clock, HashAlgorithm hashAlgo,
|
||||
SignaturePolicyService signaturePolicyService) {
|
||||
this.clock = (clock == null ? new Date() : clock);
|
||||
this.hashAlgo = (hashAlgo == null ? HashAlgorithm.sha1 : hashAlgo);
|
||||
this.signaturePolicyService = signaturePolicyService;
|
||||
this.dataObjectFormatMimeTypes = new HashMap<String, String>();
|
||||
}
|
||||
|
||||
public void postSign(SignatureType signatureElement,
|
||||
List<X509Certificate> signingCertificateChain) {
|
||||
LOG.log(POILogger.DEBUG, "postSign");
|
||||
}
|
||||
|
||||
public void preSign(XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
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("#" + signatureId);
|
||||
|
||||
// SignedProperties
|
||||
SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties();
|
||||
String signedPropertiesId;
|
||||
if (null != this.idSignedProperties) {
|
||||
signedPropertiesId = this.idSignedProperties;
|
||||
} else {
|
||||
signedPropertiesId = signatureId + "-xades";
|
||||
}
|
||||
signedProperties.setId(signedPropertiesId);
|
||||
|
||||
// SignedSignatureProperties
|
||||
SignedSignaturePropertiesType signedSignatureProperties = signedProperties.addNewSignedSignatureProperties();
|
||||
|
||||
// SigningTime
|
||||
Calendar xmlGregorianCalendar = Calendar.getInstance();
|
||||
xmlGregorianCalendar.setTimeZone(TimeZone.getTimeZone("Z"));
|
||||
xmlGregorianCalendar.setTime(this.clock);
|
||||
xmlGregorianCalendar.clear(Calendar.MILLISECOND);
|
||||
signedSignatureProperties.setSigningTime(xmlGregorianCalendar);
|
||||
|
||||
// SigningCertificate
|
||||
if (null == signingCertificateChain
|
||||
|| signingCertificateChain.isEmpty()) {
|
||||
throw new RuntimeException("no signing certificate chain available");
|
||||
}
|
||||
CertIDListType signingCertificates = signedSignatureProperties.addNewSigningCertificate();
|
||||
CertIDType certId = signingCertificates.addNewCert();
|
||||
X509Certificate signingCertificate = signingCertificateChain.get(0);
|
||||
setCertID(certId, signingCertificate, this.hashAlgo, this.issuerNameNoReverseOrder);
|
||||
|
||||
// ClaimedRole
|
||||
if (null != this.role && false == this.role.isEmpty()) {
|
||||
SignerRoleType signerRole = signedSignatureProperties.addNewSignerRole();
|
||||
signedSignatureProperties.setSignerRole(signerRole);
|
||||
ClaimedRolesListType claimedRolesList = signerRole.addNewClaimedRoles();
|
||||
AnyType claimedRole = claimedRolesList.addNewClaimedRole();
|
||||
XmlString roleString = XmlString.Factory.newInstance();
|
||||
roleString.setStringValue(this.role);
|
||||
SignatureInfo.insertXChild(claimedRole, roleString);
|
||||
}
|
||||
|
||||
// XAdES-EPES
|
||||
if (null != this.signaturePolicyService) {
|
||||
SignaturePolicyIdentifierType signaturePolicyIdentifier =
|
||||
signedSignatureProperties.addNewSignaturePolicyIdentifier();
|
||||
|
||||
SignaturePolicyIdType signaturePolicyId = signaturePolicyIdentifier.addNewSignaturePolicyId();
|
||||
|
||||
ObjectIdentifierType objectIdentifier = signaturePolicyId.addNewSigPolicyId();
|
||||
objectIdentifier.setDescription(this.signaturePolicyService.getSignaturePolicyDescription());
|
||||
|
||||
IdentifierType identifier = objectIdentifier.addNewIdentifier();
|
||||
identifier.setStringValue(this.signaturePolicyService.getSignaturePolicyIdentifier());
|
||||
|
||||
byte[] signaturePolicyDocumentData = this.signaturePolicyService.getSignaturePolicyDocument();
|
||||
DigestAlgAndValueType sigPolicyHash = signaturePolicyId.addNewSigPolicyHash();
|
||||
setDigestAlgAndValue(sigPolicyHash, signaturePolicyDocumentData, this.hashAlgo);
|
||||
|
||||
String signaturePolicyDownloadUrl = this.signaturePolicyService
|
||||
.getSignaturePolicyDownloadUrl();
|
||||
if (null != signaturePolicyDownloadUrl) {
|
||||
SigPolicyQualifiersListType sigPolicyQualifiers = signaturePolicyId.addNewSigPolicyQualifiers();
|
||||
AnyType sigPolicyQualifier = sigPolicyQualifiers.addNewSigPolicyQualifier();
|
||||
XmlString spUriElement = XmlString.Factory.newInstance();
|
||||
spUriElement.setStringValue(signaturePolicyDownloadUrl);
|
||||
SignatureInfo.insertXChild(sigPolicyQualifier, spUriElement);
|
||||
}
|
||||
} else if (this.signaturePolicyImplied) {
|
||||
SignaturePolicyIdentifierType signaturePolicyIdentifier =
|
||||
signedSignatureProperties.addNewSignaturePolicyIdentifier();
|
||||
signaturePolicyIdentifier.addNewSignaturePolicyImplied();
|
||||
}
|
||||
|
||||
// DataObjectFormat
|
||||
if (false == this.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);
|
||||
}
|
||||
}
|
||||
|
||||
// marshall XAdES QualifyingProperties
|
||||
// ((Element)qualifyingProperties.getSignedProperties().getDomNode()).setIdAttribute("Id", true);
|
||||
|
||||
// add XAdES ds:Object
|
||||
List<XMLStructure> xadesObjectContent = new LinkedList<XMLStructure>();
|
||||
Element qualDocEl = (Element)qualifyingProperties.getDomNode();
|
||||
qualDocEl.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:xd", "http://uri.etsi.org/01903/v1.3.2#");
|
||||
setPrefix(qualifyingProperties, "http://uri.etsi.org/01903/v1.3.2#", "xd");
|
||||
xadesObjectContent.add(new DOMStructure(qualDocEl));
|
||||
XMLObject xadesObject = signatureFactory.newXMLObject(xadesObjectContent, null, null, null);
|
||||
objects.add(xadesObject);
|
||||
|
||||
// add XAdES ds:Reference
|
||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(hashAlgo.xmlSignUri, null);
|
||||
List<Transform> transforms = new LinkedList<Transform>();
|
||||
Transform exclusiveTransform = signatureFactory
|
||||
.newTransform(CanonicalizationMethod.INCLUSIVE,
|
||||
(TransformParameterSpec) null);
|
||||
transforms.add(exclusiveTransform);
|
||||
Reference reference = signatureFactory.newReference("#"
|
||||
+ signedPropertiesId, 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 hashAlgo) {
|
||||
DigestMethodType digestMethod = digestAlgAndValue.addNewDigestMethod();
|
||||
digestMethod.setAlgorithm(hashAlgo.xmlSignUri);
|
||||
|
||||
MessageDigest messageDigest = CryptoFunctions.getMessageDigest(hashAlgo);
|
||||
byte[] digestValue = messageDigest.digest(data);
|
||||
digestAlgAndValue.setDigestValue(digestValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the JAXB CertID data structure.
|
||||
*
|
||||
* @param certificate
|
||||
* @param xadesObjectFactory
|
||||
* @param xmldsigObjectFactory
|
||||
* @param digestAlgorithm
|
||||
* @return
|
||||
*/
|
||||
protected static void setCertID(
|
||||
CertIDType certId,
|
||||
X509Certificate certificate,
|
||||
HashAlgorithm digestAlgorithm, boolean issuerNameNoReverseOrder) {
|
||||
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, digestAlgorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Id that will be used on the SignedProperties element;
|
||||
*
|
||||
* @param idSignedProperties
|
||||
*/
|
||||
public void setIdSignedProperties(String idSignedProperties) {
|
||||
this.idSignedProperties = idSignedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the signature policy to implied.
|
||||
*
|
||||
* @param signaturePolicyImplied
|
||||
*/
|
||||
public void setSignaturePolicyImplied(boolean signaturePolicyImplied) {
|
||||
this.signaturePolicyImplied = signaturePolicyImplied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the XAdES claimed role.
|
||||
*
|
||||
* @param role
|
||||
*/
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Work-around for Office 2010 IssuerName encoding.
|
||||
*
|
||||
* @param reverseOrder
|
||||
*/
|
||||
public void setIssuerNameNoReverseOrder(boolean reverseOrder) {
|
||||
this.issuerNameNoReverseOrder = reverseOrder;
|
||||
}
|
||||
|
||||
|
||||
public Map<String,String> getNamespacePrefixMapping() {
|
||||
Map<String,String> map = new HashMap<String,String>();
|
||||
map.put("xd", "http://uri.etsi.org/01903/v1.3.2#");
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,492 @@
|
||||
/* ====================================================================
|
||||
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.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.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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 javax.xml.namespace.QName;
|
||||
|
||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxy;
|
||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1InputStreamIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1OctetStringIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BasicOCSPRespIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.CanonicalizerIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DERIntegerIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DERTaggedObjectIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.InitIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPRespIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.RespIDIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ResponderIDIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509NameIf;
|
||||
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.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
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.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.w3.x2000.x09.xmldsig.ObjectType;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureType;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureValueType;
|
||||
import org.w3c.dom.Node;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
public static final String XADES_NAMESPACE = "http://uri.etsi.org/01903/v1.3.2#";
|
||||
|
||||
public static final String XADES141_NAMESPACE = "http://uri.etsi.org/01903/v1.4.1#";
|
||||
|
||||
private final TimeStampService timeStampService;
|
||||
|
||||
private String c14nAlgoId;
|
||||
|
||||
private final RevocationDataService revocationDataService;
|
||||
|
||||
private final CertificateFactory certificateFactory;
|
||||
|
||||
private final HashAlgorithm hashAlgo;
|
||||
|
||||
static {
|
||||
try {
|
||||
HorribleProxy.createProxy(InitIf.class, "init");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Can't initialize JDK xml signature classes - feature unsupported by the this JDK?!", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience constructor.
|
||||
*
|
||||
* @param timeStampService
|
||||
* the time-stamp service used for XAdES-T and XAdES-X.
|
||||
* @param revocationDataService
|
||||
* 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.
|
||||
*/
|
||||
public XAdESXLSignatureFacet(TimeStampService timeStampService,
|
||||
RevocationDataService revocationDataService) {
|
||||
this(timeStampService, revocationDataService, HashAlgorithm.sha1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param timeStampService
|
||||
* the time-stamp service used for XAdES-T and XAdES-X.
|
||||
* @param revocationDataService
|
||||
* 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.
|
||||
* @param digestAlgorithm
|
||||
* the digest algorithm to be used for construction of the
|
||||
* XAdES-X-L elements.
|
||||
*/
|
||||
public XAdESXLSignatureFacet(TimeStampService timeStampService,
|
||||
RevocationDataService revocationDataService,
|
||||
HashAlgorithm digestAlgorithm) {
|
||||
this.c14nAlgoId = CanonicalizationMethod.EXCLUSIVE;
|
||||
this.hashAlgo = digestAlgorithm;
|
||||
this.timeStampService = timeStampService;
|
||||
this.revocationDataService = revocationDataService;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void postSign(SignatureType signatureElement,
|
||||
List<X509Certificate> signingCertificateChain) {
|
||||
LOG.log(POILogger.DEBUG, "XAdES-X-L post sign phase");
|
||||
|
||||
QualifyingPropertiesType qualProps = null;
|
||||
|
||||
try {
|
||||
// check for XAdES-BES
|
||||
for (ObjectType ot : signatureElement.getObjectList()) {
|
||||
XmlObject xo[] = ot.selectChildren(new QName(XADES_NAMESPACE, "QualifyingProperties"));
|
||||
if (xo != null && xo.length > 0) {
|
||||
qualProps = QualifyingPropertiesType.Factory.parse(xo[0].getDomNode());
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (XmlException e) {
|
||||
throw new RuntimeException("signature decoding error", e);
|
||||
}
|
||||
|
||||
if (qualProps == null) {
|
||||
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
|
||||
SignatureValueType svt = signatureElement.getSignatureValue();
|
||||
|
||||
RevocationData tsaRevocationDataXadesT = new RevocationData();
|
||||
LOG.log(POILogger.DEBUG, "creating XAdES-T time-stamp");
|
||||
XAdESTimeStampType signatureTimeStamp = createXAdESTimeStamp(
|
||||
Collections.singletonList(svt.getDomNode()),
|
||||
tsaRevocationDataXadesT, this.c14nAlgoId,
|
||||
this.timeStampService);
|
||||
|
||||
// marshal the XAdES-T extension
|
||||
unsignedSigProps.addNewSignatureTimeStamp().set(signatureTimeStamp);
|
||||
|
||||
// xadesv141::TimeStampValidationData
|
||||
if (tsaRevocationDataXadesT.hasRevocationDataEntries()) {
|
||||
ValidationDataType validationData = createValidationData(tsaRevocationDataXadesT);
|
||||
SignatureInfo.insertXChild(unsignedSigProps, validationData);
|
||||
}
|
||||
|
||||
if (null == this.revocationDataService) {
|
||||
/*
|
||||
* 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();
|
||||
for (int certIdx = 1; certIdx < signingCertificateChain.size(); certIdx++) {
|
||||
/*
|
||||
* We skip the signing certificate itself according to section
|
||||
* 4.4.3.2 of the XAdES 1.4.1 specification.
|
||||
*/
|
||||
X509Certificate certificate = signingCertificateChain.get(certIdx);
|
||||
CertIDType certId = certIdList.addNewCert();
|
||||
XAdESSignatureFacet.setCertID(certId, certificate, this.hashAlgo, false);
|
||||
}
|
||||
|
||||
// XAdES-C: complete revocation refs
|
||||
CompleteRevocationRefsType completeRevocationRefs =
|
||||
unsignedSigProps.addNewCompleteRevocationRefs();
|
||||
RevocationData revocationData = this.revocationDataService
|
||||
.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, this.hashAlgo);
|
||||
}
|
||||
}
|
||||
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, this.hashAlgo);
|
||||
|
||||
OCSPIdentifierType ocspIdentifier = ocspRef.addNewOCSPIdentifier();
|
||||
|
||||
OCSPRespIf ocspResp = HorribleProxy.newProxy(OCSPRespIf.class, ocsp);
|
||||
|
||||
BasicOCSPRespIf basicOcspResp = ocspResp.getResponseObject();
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(basicOcspResp.getProducedAt());
|
||||
ocspIdentifier.setProducedAt(cal);
|
||||
|
||||
ResponderIDType responderId = ocspIdentifier.addNewResponderID();
|
||||
|
||||
RespIDIf respId = basicOcspResp.getResponderId();
|
||||
ResponderIDIf ocspResponderId = respId.toASN1Object();
|
||||
DERTaggedObjectIf derTaggedObject = ocspResponderId.toASN1Object();
|
||||
if (2 == derTaggedObject.getTagNo()) {
|
||||
ASN1OctetStringIf keyHashOctetString = derTaggedObject.getObject$String();
|
||||
byte key[] = keyHashOctetString.getOctets();
|
||||
responderId.setByKey(key);
|
||||
} else {
|
||||
X509NameIf name = HorribleProxy.createProxy(X509NameIf.class, "getInstance", derTaggedObject.getObject$Object());
|
||||
String nameStr = name.toString$delegate();
|
||||
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 LinkedList<Node>();
|
||||
timeStampNodesXadesX1.add(signatureElement.getDomNode());
|
||||
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,
|
||||
this.c14nAlgoId, this.timeStampService);
|
||||
if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) {
|
||||
ValidationDataType timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1);
|
||||
SignatureInfo.insertXChild(unsignedSigProps, timeStampXadesX1ValidationData);
|
||||
}
|
||||
|
||||
// marshal XAdES-X
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
public static byte[] getC14nValue(List<Node> nodeList, String c14nAlgoId) {
|
||||
byte[] c14nValue = null;
|
||||
try {
|
||||
for (Node node : nodeList) {
|
||||
/*
|
||||
* Re-initialize the c14n else the namespaces will get cached
|
||||
* and will be missing from the c14n resulting nodes.
|
||||
*/
|
||||
CanonicalizerIf c14n = HorribleProxy.createProxy(CanonicalizerIf.class, "newInstance", c14nAlgoId);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bos.write(c14nValue);
|
||||
bos.write(c14n.canonicalizeSubtree(node));
|
||||
c14nValue = bos.toByteArray();
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("c14n error: " + e.getMessage(), e);
|
||||
}
|
||||
return c14nValue;
|
||||
}
|
||||
|
||||
public void preSign(XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
private BigInteger getCrlNumber(X509CRL crl) {
|
||||
byte[] crlNumberExtensionValue = crl.getExtensionValue("2.5.29.20" /*CRLNumber*/);
|
||||
if (null == crlNumberExtensionValue) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
ASN1InputStreamIf asn1InputStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, crlNumberExtensionValue);
|
||||
ASN1OctetStringIf octetString = asn1InputStream.readObject$ASNString();
|
||||
byte[] octets = octetString.getOctets();
|
||||
asn1InputStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, octets);
|
||||
DERIntegerIf integer = asn1InputStream.readObject$Integer();
|
||||
BigInteger crlNumber = integer.getPositiveValue();
|
||||
return crlNumber;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("I/O error: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public static XAdESTimeStampType createXAdESTimeStamp(
|
||||
List<Node> nodeList,
|
||||
RevocationData revocationData,
|
||||
String c14nAlgoId,
|
||||
TimeStampService timeStampService) {
|
||||
byte[] c14nSignatureValueElement = getC14nValue(nodeList, c14nAlgoId);
|
||||
|
||||
return createXAdESTimeStamp(c14nSignatureValueElement, revocationData,
|
||||
c14nAlgoId, timeStampService);
|
||||
}
|
||||
|
||||
public static XAdESTimeStampType createXAdESTimeStamp(
|
||||
byte[] data,
|
||||
RevocationData revocationData,
|
||||
String c14nAlgoId,
|
||||
TimeStampService timeStampService) {
|
||||
// create the time-stamp
|
||||
byte[] timeStampToken;
|
||||
try {
|
||||
timeStampToken = timeStampService.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String,String> getNamespacePrefixMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
/* ====================================================================
|
||||
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.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
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.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 LinkedList<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 LinkedList<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) {
|
||||
RelationshipReferenceDocument refDoc =
|
||||
RelationshipReferenceDocument.Factory.parse(xo.getDomNode());
|
||||
String sourceId = refDoc.getRelationshipReference().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(Constants.NamespaceSpecNS, "xmlns:mdssi", DIGITAL_SIGNATURE);
|
||||
Document doc = parentNode.getOwnerDocument();
|
||||
|
||||
for (String sourceId : this.sourceIds) {
|
||||
RelationshipReferenceDocument relRef = RelationshipReferenceDocument.Factory.newInstance();
|
||||
relRef.addNewRelationshipReference().setSourceId(sourceId);
|
||||
Node n = relRef.getRelationshipReference().getDomNode();
|
||||
// TODO: is there a more elegant way to do this?
|
||||
n.setPrefix("mdssi");
|
||||
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.LinkedList;
|
||||
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 LinkedList<byte[]>();
|
||||
this.ocsps = new LinkedList<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,101 @@
|
||||
/* ====================================================================
|
||||
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.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.poi.poifs.crypt.dsig.CertificateSecurityException;
|
||||
import org.apache.poi.poifs.crypt.dsig.ExpiredCertificateSecurityException;
|
||||
import org.apache.poi.poifs.crypt.dsig.RevokedCertificateSecurityException;
|
||||
import org.apache.poi.poifs.crypt.dsig.TrustCertificateSecurityException;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;
|
||||
|
||||
/**
|
||||
* Interface for signature service component.
|
||||
*
|
||||
* @author Frank Cornelis
|
||||
*
|
||||
*/
|
||||
public interface SignatureService {
|
||||
|
||||
/**
|
||||
* Gives back the digest algorithm to be used for construction of the digest
|
||||
* infos of the preSign method. Return a digest algorithm here if you want
|
||||
* to let the client sign some locally stored files. Return
|
||||
* <code>null</code> if no pre-sign digest infos are required.
|
||||
*
|
||||
* @return the digest algorithm to be used when digesting local files.
|
||||
* @see #preSign(List, List)
|
||||
*/
|
||||
String getFilesDigestAlgorithm();
|
||||
|
||||
/**
|
||||
* Pre-sign callback method. Depending on the configuration some parameters
|
||||
* are passed. The returned value will be signed by the eID Applet.
|
||||
*
|
||||
* <p>
|
||||
* TODO: service must be able to throw some exception on failure.
|
||||
* </p>
|
||||
*
|
||||
* @param digestInfos
|
||||
* the optional list of digest infos.
|
||||
* @param signingCertificateChain
|
||||
* the optional list of certificates.
|
||||
* @param identity
|
||||
* the optional identity.
|
||||
* @param address
|
||||
* the optional identity address.
|
||||
* @param photo
|
||||
* the optional identity photo.
|
||||
* @param timestamp
|
||||
* the optional timestamp, defaults to now
|
||||
* @return the digest to be signed.
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
DigestInfo preSign(List<DigestInfo> digestInfos,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
IdentityDTO identity, AddressDTO address, byte[] photo)
|
||||
throws NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Post-sign callback method. Received the signature value. Depending on the
|
||||
* configuration the signing certificate chain is also obtained.
|
||||
*
|
||||
* @param signatureValue
|
||||
* @param signingCertificateChain
|
||||
* the optional chain of signing certificates.
|
||||
*/
|
||||
void postSign(byte[] signatureValue,
|
||||
List<X509Certificate> signingCertificateChain)
|
||||
throws ExpiredCertificateSecurityException,
|
||||
RevokedCertificateSecurityException,
|
||||
TrustCertificateSecurityException, CertificateSecurityException,
|
||||
SecurityException, IOException;
|
||||
}
|
@ -0,0 +1,392 @@
|
||||
/* ====================================================================
|
||||
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.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.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.poi.poifs.crypt.CryptoFunctions;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1InputStreamIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1OctetStringIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.AuthorityKeyIdentifierIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BcDigestCalculatorProviderIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BcRSASignerInfoVerifierBuilderIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DEROctetStringIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DefaultDigestAlgorithmIdentifierFinderIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.PKIFailureInfoIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SignerIdIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SignerInformationVerifierIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SubjectKeyIdentifierIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampRequestGeneratorIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampRequestIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampResponseIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampTokenIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509CertificateHolderIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxy;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
|
||||
/**
|
||||
* A TSP time-stamp service implementation.
|
||||
*
|
||||
* @author Frank Cornelis
|
||||
*
|
||||
*/
|
||||
public class TSPTimeStampService implements TimeStampService {
|
||||
|
||||
private static final POILogger LOG = POILogFactory.getLogger(TSPTimeStampService.class);
|
||||
|
||||
static {
|
||||
CryptoFunctions.registerBouncyCastle();
|
||||
}
|
||||
|
||||
public static final String DEFAULT_USER_AGENT = "eID Applet Service TSP Client";
|
||||
|
||||
private final String tspServiceUrl;
|
||||
|
||||
private String requestPolicy;
|
||||
|
||||
private final String userAgent;
|
||||
|
||||
private final TimeStampServiceValidator validator;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String proxyHost;
|
||||
|
||||
private int proxyPort;
|
||||
|
||||
private String digestAlgo;
|
||||
|
||||
private String digestAlgoOid;
|
||||
|
||||
public TSPTimeStampService(String tspServiceUrl,
|
||||
TimeStampServiceValidator validator) {
|
||||
this(tspServiceUrl, validator, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*
|
||||
* @param tspServiceUrl
|
||||
* the URL of the TSP service.
|
||||
* @param validator
|
||||
* the trust validator used to validate incoming TSP response
|
||||
* signatures.
|
||||
* @param requestPolicy
|
||||
* the optional TSP request policy.
|
||||
* @param userAgent
|
||||
* the optional User-Agent TSP request header value.
|
||||
*/
|
||||
public TSPTimeStampService(String tspServiceUrl,
|
||||
TimeStampServiceValidator validator, String requestPolicy,
|
||||
String userAgent) {
|
||||
if (null == tspServiceUrl) {
|
||||
throw new IllegalArgumentException("TSP service URL required");
|
||||
}
|
||||
this.tspServiceUrl = tspServiceUrl;
|
||||
|
||||
if (null == validator) {
|
||||
throw new IllegalArgumentException("TSP validator required");
|
||||
}
|
||||
this.validator = validator;
|
||||
|
||||
this.requestPolicy = requestPolicy;
|
||||
|
||||
if (null != userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
} else {
|
||||
this.userAgent = DEFAULT_USER_AGENT;
|
||||
}
|
||||
|
||||
this.digestAlgo = "SHA-1";
|
||||
this.digestAlgoOid = "1.3.14.3.2.26";
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the request policy OID.
|
||||
*
|
||||
* @param policyOid
|
||||
*/
|
||||
public void setRequestPolicy(String policyOid) {
|
||||
this.requestPolicy = policyOid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the credentials used in case the TSP service requires
|
||||
* authentication.
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
*/
|
||||
public void setAuthenticationCredentials(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the authentication credentials.
|
||||
*/
|
||||
public void resetAuthenticationCredentials() {
|
||||
this.username = null;
|
||||
this.password = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the digest algorithm used for time-stamping data. Example value:
|
||||
* "SHA-1".
|
||||
*
|
||||
* @param digestAlgo
|
||||
*/
|
||||
public void setDigestAlgo(String digestAlgo) {
|
||||
if ("SHA-1".equals(digestAlgo)) {
|
||||
this.digestAlgoOid = "1.3.14.3.2.26";
|
||||
} else if ("SHA-256".equals(digestAlgo)) {
|
||||
this.digestAlgoOid = "2.16.840.1.101.3.4.2.1";
|
||||
} else if ("SHA-384".equals(digestAlgo)) {
|
||||
this.digestAlgoOid = "2.16.840.1.101.3.4.2.2";
|
||||
} else if ("SHA-512".equals(digestAlgo)) {
|
||||
this.digestAlgoOid = "2.16.840.1.101.3.4.2.3";
|
||||
} else {
|
||||
throw new IllegalArgumentException("unsupported digest algo: " + digestAlgo);
|
||||
}
|
||||
|
||||
this.digestAlgo = digestAlgo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the HTTP proxy settings to be used to connect to the TSP
|
||||
* service.
|
||||
*
|
||||
* @param proxyHost
|
||||
* @param proxyPort
|
||||
*/
|
||||
public void setProxy(String proxyHost, int proxyPort) {
|
||||
this.proxyHost = proxyHost;
|
||||
this.proxyPort = proxyPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the HTTP proxy settings.
|
||||
*/
|
||||
public void resetProxy() {
|
||||
this.proxyHost = null;
|
||||
this.proxyPort = 0;
|
||||
}
|
||||
|
||||
public byte[] timeStamp(byte[] data, RevocationData revocationData)
|
||||
throws Exception {
|
||||
// digest the message
|
||||
MessageDigest messageDigest = MessageDigest
|
||||
.getInstance(this.digestAlgo);
|
||||
byte[] digest = messageDigest.digest(data);
|
||||
|
||||
// generate the TSP request
|
||||
BigInteger nonce = new BigInteger(128, new SecureRandom());
|
||||
TimeStampRequestGeneratorIf requestGenerator = HorribleProxy.newProxy(TimeStampRequestGeneratorIf.class);
|
||||
requestGenerator.setCertReq(true);
|
||||
if (null != this.requestPolicy) {
|
||||
requestGenerator.setReqPolicy(this.requestPolicy);
|
||||
}
|
||||
TimeStampRequestIf request = requestGenerator.generate(this.digestAlgoOid, digest, nonce);
|
||||
byte[] encodedRequest = request.getEncoded();
|
||||
|
||||
// create the HTTP POST request
|
||||
Proxy proxy = (this.proxyHost != null)
|
||||
? new Proxy(Proxy.Type.HTTP, new InetSocketAddress(this.proxyHost, this.proxyPort))
|
||||
: Proxy.NO_PROXY;
|
||||
HttpURLConnection huc = (HttpURLConnection)new URL(this.tspServiceUrl).openConnection(proxy);
|
||||
|
||||
if (null != this.username) {
|
||||
String userPassword = this.username + ":" + this.password;
|
||||
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", this.userAgent);
|
||||
huc.setRequestProperty("Content-Type", "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 ", this.tspServiceUrl);
|
||||
throw new Exception("Error contacting TSP server " + this.tspServiceUrl);
|
||||
}
|
||||
|
||||
// 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("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
|
||||
TimeStampResponseIf timeStampResponse = HorribleProxy.newProxy(TimeStampResponseIf.class, bos.toByteArray());
|
||||
timeStampResponse.validate(request);
|
||||
|
||||
if (0 != timeStampResponse.getStatus()) {
|
||||
LOG.log(POILogger.DEBUG, "status: " + timeStampResponse.getStatus());
|
||||
LOG.log(POILogger.DEBUG, "status string: " + timeStampResponse.getStatusString());
|
||||
PKIFailureInfoIf 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());
|
||||
}
|
||||
TimeStampTokenIf timeStampToken = timeStampResponse.getTimeStampToken();
|
||||
SignerIdIf signerId = timeStampToken.getSID();
|
||||
BigInteger signerCertSerialNumber = signerId.getSerialNumber();
|
||||
X500Principal 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<Certificate> certificates = timeStampToken.getCertificates().getMatches(null);
|
||||
|
||||
X509Certificate signerCert = null;
|
||||
Map<String, X509Certificate> certificateMap = new HashMap<String, X509Certificate>();
|
||||
for (Certificate certificate : certificates) {
|
||||
X509Certificate x509Certificate = (X509Certificate) certificate;
|
||||
if (signerCertIssuer.equals(x509Certificate
|
||||
.getIssuerX500Principal())
|
||||
&& signerCertSerialNumber.equals(x509Certificate
|
||||
.getSerialNumber())) {
|
||||
signerCert = x509Certificate;
|
||||
}
|
||||
String ski = Hex.encodeHexString(getSubjectKeyId(x509Certificate));
|
||||
certificateMap.put(ski, x509Certificate);
|
||||
LOG.log(POILogger.DEBUG, "embedded certificate: "
|
||||
+ x509Certificate.getSubjectX500Principal() + "; SKI="
|
||||
+ ski);
|
||||
}
|
||||
|
||||
// TSP signer cert path building
|
||||
if (null == signerCert) {
|
||||
throw new RuntimeException(
|
||||
"TSP response token has no signer certificate");
|
||||
}
|
||||
List<X509Certificate> tspCertificateChain = new LinkedList<X509Certificate>();
|
||||
X509Certificate certificate = signerCert;
|
||||
do {
|
||||
LOG.log(POILogger.DEBUG, "adding to certificate chain: "
|
||||
+ certificate.getSubjectX500Principal());
|
||||
tspCertificateChain.add(certificate);
|
||||
if (certificate.getSubjectX500Principal().equals(
|
||||
certificate.getIssuerX500Principal())) {
|
||||
break;
|
||||
}
|
||||
String aki = Hex.encodeHexString(getAuthorityKeyId(certificate));
|
||||
certificate = certificateMap.get(aki);
|
||||
} while (null != certificate);
|
||||
|
||||
// verify TSP signer signature
|
||||
X509CertificateHolderIf holder = HorribleProxy.newProxy(X509CertificateHolderIf.class, tspCertificateChain.get(0).getEncoded());
|
||||
DefaultDigestAlgorithmIdentifierFinderIf finder = HorribleProxy.newProxy(DefaultDigestAlgorithmIdentifierFinderIf.class);
|
||||
BcDigestCalculatorProviderIf calculator = HorribleProxy.newProxy(BcDigestCalculatorProviderIf.class);
|
||||
BcRSASignerInfoVerifierBuilderIf verifierBuilder = HorribleProxy.newProxy(BcRSASignerInfoVerifierBuilderIf.class, finder, calculator);
|
||||
SignerInformationVerifierIf verifier = verifierBuilder.build(holder);
|
||||
|
||||
timeStampToken.validate(verifier);
|
||||
|
||||
// verify TSP signer certificate
|
||||
this.validator.validate(tspCertificateChain, revocationData);
|
||||
|
||||
LOG.log(POILogger.DEBUG, "time-stamp token time: "
|
||||
+ timeStampToken.getTimeStampInfo().getGenTime());
|
||||
|
||||
byte[] timestamp = timeStampToken.getEncoded();
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
private byte[] getSubjectKeyId(X509Certificate cert) throws Exception {
|
||||
// X509Extensions.SubjectKeyIdentifier.getId()
|
||||
byte[] extvalue = cert.getExtensionValue("2.5.29.14");
|
||||
if (extvalue == null) return null;
|
||||
|
||||
ASN1InputStreamIf keyCntStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue));
|
||||
ASN1OctetStringIf cntStr = HorribleProxy.createProxy(ASN1OctetStringIf.class, "getInstance", keyCntStream.readObject$Object());
|
||||
ASN1InputStreamIf keyIdStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets()));
|
||||
SubjectKeyIdentifierIf keyId = HorribleProxy.createProxy(SubjectKeyIdentifierIf.class, "getInstance", keyIdStream.readObject$Object());
|
||||
|
||||
return keyId.getKeyIdentifier();
|
||||
}
|
||||
|
||||
private byte[] getAuthorityKeyId(X509Certificate cert) throws Exception {
|
||||
// X509Extensions.AuthorityKeyIdentifier.getId()
|
||||
byte[] extvalue = cert.getExtensionValue("2.5.29.35");
|
||||
if (extvalue == null) return null;
|
||||
|
||||
ASN1InputStreamIf keyCntStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue));
|
||||
DEROctetStringIf cntStr = keyCntStream.readObject$DERString();
|
||||
ASN1InputStreamIf keyIdStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets()));
|
||||
AuthorityKeyIdentifierIf keyId = HorribleProxy.newProxy(AuthorityKeyIdentifierIf.class, keyIdStream.readObject$Sequence());
|
||||
|
||||
return keyId.getKeyIdentifier();
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* ====================================================================
|
||||
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 a time-stamp service.
|
||||
*
|
||||
* @author Frank Cornelis
|
||||
*
|
||||
*/
|
||||
public interface TimeStampService {
|
||||
|
||||
/**
|
||||
* 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,610 @@
|
||||
/* ====================================================================
|
||||
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.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.Key;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.crypto.MarshalException;
|
||||
import javax.xml.crypto.URIDereferencer;
|
||||
import javax.xml.crypto.XMLStructure;
|
||||
import javax.xml.crypto.dom.DOMCryptoContext;
|
||||
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.XMLSignatureFactory;
|
||||
import javax.xml.crypto.dsig.dom.DOMSignContext;
|
||||
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||
import org.apache.poi.openxml4j.opc.PackageNamespaces;
|
||||
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.CryptoFunctions;
|
||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMReferenceIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMSignedInfoIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMXMLSignatureIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.XMLSignatureIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxy;
|
||||
import org.apache.poi.poifs.crypt.dsig.OOXMLURIDereferencer;
|
||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
|
||||
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.spi.AddressDTO;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.Constants;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.apache.xmlbeans.XmlException;
|
||||
import org.apache.xmlbeans.XmlOptions;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureDocument;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureType;
|
||||
import org.w3.x2000.x09.xmldsig.SignatureValueType;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base class for an XML Signature Service implementation.
|
||||
*/
|
||||
public class XmlSignatureService implements SignatureService {
|
||||
private static final POILogger LOG = POILogFactory.getLogger(XmlSignatureService.class);
|
||||
|
||||
protected final List<SignatureFacet> signatureFacets;
|
||||
|
||||
private String signatureNamespacePrefix;
|
||||
private String signatureId;
|
||||
private final HashAlgorithm hashAlgo;
|
||||
private final OPCPackage opcPackage;
|
||||
private SignatureDocument sigDoc;
|
||||
private XAdESSignatureFacet xadesSignatureFacet;
|
||||
|
||||
/**
|
||||
* Main constructor.
|
||||
*/
|
||||
public XmlSignatureService(HashAlgorithm digestAlgo, OPCPackage opcPackage) {
|
||||
this.signatureFacets = new LinkedList<SignatureFacet>();
|
||||
this.signatureNamespacePrefix = null;
|
||||
this.signatureId = null;
|
||||
this.hashAlgo = digestAlgo;
|
||||
this.opcPackage = opcPackage;
|
||||
this.sigDoc = null;
|
||||
}
|
||||
|
||||
public void initFacets(Date clock) {
|
||||
if (clock == null) clock = new Date();
|
||||
addSignatureFacet(new OOXMLSignatureFacet(this, clock, hashAlgo));
|
||||
addSignatureFacet(new KeyInfoSignatureFacet(true, false, false));
|
||||
|
||||
this.xadesSignatureFacet = new XAdESSignatureFacet(clock, hashAlgo, null);
|
||||
this.xadesSignatureFacet.setIdSignedProperties("idSignedProperties");
|
||||
this.xadesSignatureFacet.setSignaturePolicyImplied(true);
|
||||
/*
|
||||
* Work-around for Office 2010.
|
||||
*/
|
||||
this.xadesSignatureFacet.setIssuerNameNoReverseOrder(true);
|
||||
setSignatureId("idPackageSignature");
|
||||
addSignatureFacet(this.xadesSignatureFacet);
|
||||
addSignatureFacet(new Office2010SignatureFacet());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the signature Id attribute value used to create the XML signature. A
|
||||
* <code>null</code> value will trigger an automatically generated signature
|
||||
* Id.
|
||||
*
|
||||
* @param signatureId
|
||||
*/
|
||||
protected void setSignatureId(String signatureId) {
|
||||
this.signatureId = signatureId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the XML Signature namespace prefix to be used for signature
|
||||
* creation. A <code>null</code> value will omit the prefixing.
|
||||
*
|
||||
* @param signatureNamespacePrefix
|
||||
*/
|
||||
protected void setSignatureNamespacePrefix(String signatureNamespacePrefix) {
|
||||
this.signatureNamespacePrefix = signatureNamespacePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a signature facet to this XML signature service.
|
||||
*
|
||||
* @param signatureFacet
|
||||
*/
|
||||
protected void addSignatureFacet(SignatureFacet signatureFacet) {
|
||||
this.signatureFacets.add(signatureFacet);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the signature digest algorithm. Allowed values are SHA-1,
|
||||
* SHA-256, SHA-384, SHA-512, RIPEND160. The default algorithm is SHA-1.
|
||||
* Override this method to select another signature digest algorithm.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected HashAlgorithm getSignatureDigestAlgorithm() {
|
||||
return null != this.hashAlgo ? this.hashAlgo : HashAlgorithm.sha1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method to change the URI dereferener used by the signing
|
||||
* engine.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected URIDereferencer getURIDereferencer() {
|
||||
OPCPackage ooxmlDocument = getOfficeOpenXMLDocument();
|
||||
return new OOXMLURIDereferencer(ooxmlDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the human-readable description of what the citizen will be
|
||||
* signing. The default value is "XML Document". Override this method to
|
||||
* provide the citizen with another description.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected String getSignatureDescription() {
|
||||
return "Office OpenXML Document";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the URL of the OOXML to be signed.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public OPCPackage getOfficeOpenXMLDocument() {
|
||||
return opcPackage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gives back the output stream to which to write the signed XML document.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
// protected abstract OutputStream getSignedDocumentOutputStream();
|
||||
|
||||
public DigestInfo preSign(List<DigestInfo> digestInfos,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
IdentityDTO identity, AddressDTO address, byte[] photo)
|
||||
throws NoSuchAlgorithmException {
|
||||
SignatureInfo.initXmlProvider();
|
||||
|
||||
LOG.log(POILogger.DEBUG, "preSign");
|
||||
HashAlgorithm hashAlgo = getSignatureDigestAlgorithm();
|
||||
|
||||
byte[] digestValue;
|
||||
try {
|
||||
digestValue = getXmlSignatureDigestValue(hashAlgo, digestInfos, signingCertificateChain);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("XML signature error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
String description = getSignatureDescription();
|
||||
return new DigestInfo(digestValue, hashAlgo, description);
|
||||
}
|
||||
|
||||
public void postSign(byte[] signatureValue, List<X509Certificate> signingCertificateChain)
|
||||
throws IOException {
|
||||
LOG.log(POILogger.DEBUG, "postSign");
|
||||
SignatureInfo.initXmlProvider();
|
||||
|
||||
/*
|
||||
* Retrieve the intermediate XML signature document from the temporary
|
||||
* data storage.
|
||||
*/
|
||||
SignatureType sigType = sigDoc.getSignature();
|
||||
|
||||
/*
|
||||
* Check ds:Signature node.
|
||||
*/
|
||||
if (!signatureId.equals(sigType.getId())) {
|
||||
throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert signature value into the ds:SignatureValue element
|
||||
*/
|
||||
SignatureValueType sigVal = sigType.getSignatureValue();
|
||||
sigVal.setByteArrayValue(signatureValue);
|
||||
|
||||
/*
|
||||
* Allow signature facets to inject their own stuff.
|
||||
*/
|
||||
for (SignatureFacet signatureFacet : this.signatureFacets) {
|
||||
signatureFacet.postSign(sigType, signingCertificateChain);
|
||||
}
|
||||
|
||||
writeDocument();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private byte[] getXmlSignatureDigestValue(HashAlgorithm hashAlgo,
|
||||
List<DigestInfo> digestInfos,
|
||||
List<X509Certificate> signingCertificateChain)
|
||||
throws ParserConfigurationException, NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException, MarshalException,
|
||||
javax.xml.crypto.dsig.XMLSignatureException,
|
||||
TransformerFactoryConfigurationError, TransformerException,
|
||||
IOException, SAXException, NoSuchProviderException, XmlException {
|
||||
/*
|
||||
* DOM Document construction.
|
||||
*/
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
dbf.setNamespaceAware(true);
|
||||
Document doc = dbf.newDocumentBuilder().newDocument();
|
||||
|
||||
/*
|
||||
* Signature context construction.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// As of JDK 7, can't use sigDoc here directly, because the
|
||||
// setAttributeID will be called and it's not implemented in xmlbeans
|
||||
XMLSignContext xmlSignContext = new DOMSignContext(key, doc);
|
||||
URIDereferencer uriDereferencer = getURIDereferencer();
|
||||
if (null != uriDereferencer) {
|
||||
xmlSignContext.setURIDereferencer(uriDereferencer);
|
||||
}
|
||||
|
||||
xmlSignContext.putNamespacePrefix(
|
||||
"http://schemas.openxmlformats.org/package/2006/digital-signature",
|
||||
"mdssi");
|
||||
|
||||
if (this.signatureNamespacePrefix != null) {
|
||||
/*
|
||||
* OOo doesn't like ds namespaces so per default prefixing is off.
|
||||
*/
|
||||
xmlSignContext.putNamespacePrefix(
|
||||
javax.xml.crypto.dsig.XMLSignature.XMLNS,
|
||||
this.signatureNamespacePrefix);
|
||||
}
|
||||
|
||||
XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance("DOM", "XMLDSig");
|
||||
|
||||
/*
|
||||
* Add ds:References that come from signing client local files.
|
||||
*/
|
||||
List<Reference> references = new LinkedList<Reference>();
|
||||
addDigestInfosAsReferences(digestInfos, signatureFactory, references);
|
||||
|
||||
/*
|
||||
* Invoke the signature facets.
|
||||
*/
|
||||
String localSignatureId;
|
||||
if (null == this.signatureId) {
|
||||
localSignatureId = "xmldsig-" + UUID.randomUUID().toString();
|
||||
} else {
|
||||
localSignatureId = this.signatureId;
|
||||
}
|
||||
List<XMLObject> objects = new LinkedList<XMLObject>();
|
||||
for (SignatureFacet signatureFacet : this.signatureFacets) {
|
||||
LOG.log(POILogger.DEBUG, "invoking signature facet: "
|
||||
+ signatureFacet.getClass().getSimpleName());
|
||||
signatureFacet.preSign(signatureFactory, localSignatureId, signingCertificateChain, references, objects);
|
||||
}
|
||||
|
||||
/*
|
||||
* ds:SignedInfo
|
||||
*/
|
||||
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(
|
||||
getSignatureMethod(hashAlgo), null);
|
||||
CanonicalizationMethod canonicalizationMethod = signatureFactory
|
||||
.newCanonicalizationMethod(getCanonicalizationMethod(),
|
||||
(C14NMethodParameterSpec) null);
|
||||
SignedInfo signedInfo = signatureFactory.newSignedInfo(
|
||||
canonicalizationMethod, signatureMethod, references);
|
||||
|
||||
/*
|
||||
* JSR105 ds:Signature creation
|
||||
*/
|
||||
String signatureValueId = localSignatureId + "-signature-value";
|
||||
javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
|
||||
.newXMLSignature(signedInfo, null, objects, localSignatureId,
|
||||
signatureValueId);
|
||||
|
||||
/*
|
||||
* ds:Signature Marshalling.
|
||||
*/
|
||||
DOMXMLSignatureIf domXmlSignature;
|
||||
try {
|
||||
domXmlSignature = HorribleProxy.newProxy(DOMXMLSignatureIf.class, xmlSignature);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DomXmlSignature instance error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
domXmlSignature.marshal(doc, this.signatureNamespacePrefix, (DOMCryptoContext) xmlSignContext);
|
||||
|
||||
registerIds(doc);
|
||||
Element el = doc.getElementById("idPackageObject");
|
||||
assert (el != null);
|
||||
el.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE);
|
||||
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
DOMReferenceIf manifestDOMReference;
|
||||
try {
|
||||
manifestDOMReference = HorribleProxy.newProxy(DOMReferenceIf.class, manifestReference);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DOMReference instance error: " + e.getMessage(), e);
|
||||
}
|
||||
manifestDOMReference.digest(xmlSignContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Completion of undigested ds:References.
|
||||
*/
|
||||
List<Reference> signedInfoReferences = signedInfo.getReferences();
|
||||
for (Reference signedInfoReference : signedInfoReferences) {
|
||||
DOMReferenceIf domReference;
|
||||
try {
|
||||
domReference = HorribleProxy.newProxy(DOMReferenceIf.class, signedInfoReference);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DOMReference instance error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
// ds:Reference with external digest value
|
||||
if (domReference.getDigestValue() != null) continue;
|
||||
|
||||
domReference.digest(xmlSignContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculation of XML signature digest value.
|
||||
*/
|
||||
DOMSignedInfoIf domSignedInfo;
|
||||
try {
|
||||
domSignedInfo = HorribleProxy.newProxy(DOMSignedInfoIf.class, signedInfo);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("DOMSignedInfo instance error: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
|
||||
domSignedInfo.canonicalize(xmlSignContext, dataStream);
|
||||
byte[] octets = dataStream.toByteArray();
|
||||
|
||||
sigDoc = SignatureDocument.Factory.parse(doc.getDocumentElement());
|
||||
|
||||
|
||||
/*
|
||||
* TODO: we could be using DigestOutputStream here to optimize memory
|
||||
* usage.
|
||||
*/
|
||||
|
||||
MessageDigest jcaMessageDigest = CryptoFunctions.getMessageDigest(hashAlgo);
|
||||
byte[] digestValue = jcaMessageDigest.digest(octets);
|
||||
return digestValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* the resulting document needs to be tweaked before it can be digested -
|
||||
* this applies to the verification and signing step
|
||||
*
|
||||
* @param doc
|
||||
*/
|
||||
public void registerIds(Document doc) {
|
||||
NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Object");
|
||||
registerIdAttribute(nl);
|
||||
nl = doc.getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "SignedProperties");
|
||||
registerIdAttribute(nl);
|
||||
}
|
||||
|
||||
protected void registerIdAttribute(NodeList nl) {
|
||||
for (int i=0; i<nl.getLength(); i++) {
|
||||
Element el = (Element)nl.item(i);
|
||||
if (el.hasAttribute("Id")) {
|
||||
el.setIdAttribute("Id", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addDigestInfosAsReferences(List<DigestInfo> digestInfos,
|
||||
XMLSignatureFactory signatureFactory, List<Reference> references)
|
||||
throws NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException, MalformedURLException {
|
||||
if (digestInfos == null) return;
|
||||
for (DigestInfo digestInfo : digestInfos) {
|
||||
byte[] documentDigestValue = digestInfo.digestValue;
|
||||
|
||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(
|
||||
digestInfo.hashAlgo.xmlSignUri, null);
|
||||
|
||||
String uri = new File(digestInfo.description).getName();
|
||||
|
||||
Reference reference = signatureFactory.newReference(uri,
|
||||
digestMethod, null, null, null, documentDigestValue);
|
||||
references.add(reference);
|
||||
}
|
||||
}
|
||||
|
||||
private String getSignatureMethod(HashAlgorithm hashAlgo) {
|
||||
if (null == hashAlgo) {
|
||||
throw new RuntimeException("digest algo is null");
|
||||
}
|
||||
|
||||
XMLSignatureIf XmlSignature;
|
||||
try {
|
||||
XmlSignature = HorribleProxy.newProxy(XMLSignatureIf.class);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("JDK doesn't support XmlSignature", e);
|
||||
}
|
||||
|
||||
switch (hashAlgo) {
|
||||
case sha1: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA1();
|
||||
case sha256: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA256();
|
||||
case sha384: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA384();
|
||||
case sha512: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA512();
|
||||
case ripemd160: return XmlSignature.ALGO_ID_MAC_HMAC_RIPEMD160();
|
||||
default: break;
|
||||
}
|
||||
|
||||
throw new RuntimeException("unsupported sign algo: " + hashAlgo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives back the used XAdES signature facet.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected XAdESSignatureFacet getXAdESSignatureFacet() {
|
||||
return this.xadesSignatureFacet;
|
||||
}
|
||||
|
||||
public String getFilesDigestAlgorithm() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected String getCanonicalizationMethod() {
|
||||
return CanonicalizationMethod.INCLUSIVE;
|
||||
}
|
||||
|
||||
protected void writeDocument() throws IOException {
|
||||
XmlOptions xo = new XmlOptions();
|
||||
Map<String,String> namespaceMap = new HashMap<String,String>();
|
||||
for (SignatureFacet sf : this.signatureFacets) {
|
||||
Map<String,String> sfm = sf.getNamespacePrefixMapping();
|
||||
if (sfm != null) {
|
||||
namespaceMap.putAll(sfm);
|
||||
}
|
||||
}
|
||||
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 = this.getOfficeOpenXMLDocument();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
String sigContentType = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";
|
||||
PackagePart sigPart = pkg.getPart(sigPartName);
|
||||
if (sigPart == null) {
|
||||
sigPart = pkg.createPart(sigPartName, sigContentType);
|
||||
}
|
||||
|
||||
OutputStream os = sigPart.getOutputStream();
|
||||
sigDoc.save(os, xo);
|
||||
os.close();
|
||||
|
||||
String sigsContentType = "application/vnd.openxmlformats-package.digital-signature-origin";
|
||||
PackagePart sigsPart = pkg.getPart(sigsPartName);
|
||||
if (sigsPart == null) {
|
||||
// touch empty marker file
|
||||
sigsPart = pkg.createPart(sigsPartName, sigsContentType);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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,30 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
|
||||
/* ====================================================================
|
||||
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;
|
||||
|
||||
public interface Constants {
|
||||
String NamespaceSpecNS = "http://www.w3.org/2000/xmlns/";
|
||||
String SignatureSpecNS = "http://www.w3.org/2000/09/xmldsig#";
|
||||
}
|
@ -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
@ -23,6 +23,9 @@ import java.io.StringReader;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentException;
|
||||
@ -89,4 +92,72 @@ public final class SAXHelper {
|
||||
public static Document readSAXDocument(InputStream inp) throws DocumentException {
|
||||
return getSAXReader().read(inp);
|
||||
}
|
||||
|
||||
private static final EntityResolver IGNORING_ENTITY_RESOLVER = new EntityResolver() {
|
||||
@Override
|
||||
public InputSource resolveEntity(String publicId, String systemId)
|
||||
throws SAXException, IOException {
|
||||
return new InputSource(new StringReader(""));
|
||||
}
|
||||
};
|
||||
|
||||
private static void trySetSAXFeature(DocumentBuilderFactory documentBuilderFactory, String feature, boolean enabled) {
|
||||
try {
|
||||
documentBuilderFactory.setFeature(feature, enabled);
|
||||
} catch (Exception e) {
|
||||
logger.log(POILogger.INFO, "SAX Feature unsupported", feature, e);
|
||||
}
|
||||
}
|
||||
private static void trySetXercesSecurityManager(DocumentBuilderFactory documentBuilderFactory) {
|
||||
// Try built-in JVM one first, standalone if not
|
||||
for (String securityManagerClassName : new String[] {
|
||||
"com.sun.org.apache.xerces.internal.util.SecurityManager",
|
||||
"org.apache.xerces.util.SecurityManager"
|
||||
}) {
|
||||
try {
|
||||
Object mgr = Class.forName(securityManagerClassName).newInstance();
|
||||
Method setLimit = mgr.getClass().getMethod("setEntityExpansionLimit", Integer.TYPE);
|
||||
setLimit.invoke(mgr, 4096);
|
||||
documentBuilderFactory.setAttribute("http://apache.org/xml/properties/security-manager", mgr);
|
||||
// Stop once one can be setup without error
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
logger.log(POILogger.INFO, "SAX Security Manager could not be setup", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final ThreadLocal<DocumentBuilder> documentBuilder = new ThreadLocal<DocumentBuilder>() {
|
||||
@Override
|
||||
protected DocumentBuilder initialValue() {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setNamespaceAware(true);
|
||||
factory.setValidating(false);
|
||||
trySetSAXFeature(factory, XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||
trySetXercesSecurityManager(factory);
|
||||
try {
|
||||
return factory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new IllegalStateException("cannot create a DocumentBuilder", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocumentBuilder get() {
|
||||
DocumentBuilder documentBuilder = super.get();
|
||||
documentBuilder.reset();
|
||||
documentBuilder.setEntityResolver(IGNORING_ENTITY_RESOLVER);
|
||||
return documentBuilder;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses the given stream via the default (sensible)
|
||||
* SAX Reader
|
||||
* @param inp Stream to read the XML data from
|
||||
* @return the SAX processed Document
|
||||
*/
|
||||
public static org.w3c.dom.Document readSAXDocumentW3C(InputStream inp) throws IOException, SAXException {
|
||||
return documentBuilder.get().parse(inp);
|
||||
}
|
||||
}
|
||||
|
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
466
src/ooxml/resources/org/apache/poi/poifs/crypt/XAdES.xsd
Normal file
466
src/ooxml/resources/org/apache/poi/poifs/crypt/XAdES.xsd
Normal file
@ -0,0 +1,466 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema targetNamespace="http://uri.etsi.org/01903/v1.3.2#" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://uri.etsi.org/01903/v1.3.2#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" elementFormDefault="qualified">
|
||||
<xsd:import namespace="http://www.w3.org/2000/09/xmldsig#" schemaLocation="http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/xmldsig-core-schema.xsd"/>
|
||||
<!-- Start auxiliary types definitions: AnyType, ObjectIdentifierType,
|
||||
EncapsulatedPKIDataType and containers for time-stamp tokens -->
|
||||
<!-- Start AnyType -->
|
||||
<xsd:element name="Any" type="AnyType"/>
|
||||
<xsd:complexType name="AnyType" mixed="true">
|
||||
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:any namespace="##any" processContents="lax"/>
|
||||
</xsd:sequence>
|
||||
<xsd:anyAttribute namespace="##any"/>
|
||||
</xsd:complexType>
|
||||
<!-- End AnyType -->
|
||||
<!-- Start ObjectIdentifierType-->
|
||||
<xsd:element name="ObjectIdentifier" type="ObjectIdentifierType"/>
|
||||
<xsd:complexType name="ObjectIdentifierType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="Identifier" type="IdentifierType"/>
|
||||
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="DocumentationReferences" type="DocumentationReferencesType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="IdentifierType">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:anyURI">
|
||||
<xsd:attribute name="Qualifier" type="QualifierType" use="optional"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
<xsd:simpleType name="QualifierType">
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="OIDAsURI"/>
|
||||
<xsd:enumeration value="OIDAsURN"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
<xsd:complexType name="DocumentationReferencesType">
|
||||
<xsd:sequence maxOccurs="unbounded">
|
||||
<xsd:element name="DocumentationReference" type="xsd:anyURI"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End ObjectIdentifierType-->
|
||||
<!-- Start EncapsulatedPKIDataType-->
|
||||
<xsd:element name="EncapsulatedPKIData" type="EncapsulatedPKIDataType"/>
|
||||
<xsd:complexType name="EncapsulatedPKIDataType">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:base64Binary">
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
<xsd:attribute name="Encoding" type="xsd:anyURI" use="optional"/>
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
<!-- End EncapsulatedPKIDataType -->
|
||||
<!-- Start time-stamp containers types -->
|
||||
<!-- Start GenericTimeStampType -->
|
||||
<xsd:element name="Include" type="IncludeType"/>
|
||||
<xsd:complexType name="IncludeType">
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="required"/>
|
||||
<xsd:attribute name="referencedData" type="xsd:boolean" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:element name="ReferenceInfo" type="ReferenceInfoType"/>
|
||||
<xsd:complexType name="ReferenceInfoType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="ds:DigestMethod"/>
|
||||
<xsd:element ref="ds:DigestValue"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="GenericTimeStampType" abstract="true">
|
||||
<xsd:sequence>
|
||||
<xsd:choice minOccurs="0">
|
||||
<xsd:element ref="Include" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element ref="ReferenceInfo" maxOccurs="unbounded"/>
|
||||
</xsd:choice>
|
||||
<xsd:element ref="ds:CanonicalizationMethod" minOccurs="0"/>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="EncapsulatedTimeStamp" type="EncapsulatedPKIDataType"/>
|
||||
<xsd:element name="XMLTimeStamp" type="AnyType"/>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End GenericTimeStampType -->
|
||||
<!-- Start XAdESTimeStampType -->
|
||||
<xsd:element name="XAdESTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:complexType name="XAdESTimeStampType">
|
||||
<xsd:complexContent>
|
||||
<xsd:restriction base="GenericTimeStampType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="Include" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element ref="ds:CanonicalizationMethod" minOccurs="0"/>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="EncapsulatedTimeStamp" type="EncapsulatedPKIDataType"/>
|
||||
<xsd:element name="XMLTimeStamp" type="AnyType"/>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:restriction>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
<!-- End XAdESTimeStampType -->
|
||||
<!-- Start OtherTimeStampType -->
|
||||
<xsd:element name="OtherTimeStamp" type="OtherTimeStampType"/>
|
||||
<xsd:complexType name="OtherTimeStampType">
|
||||
<xsd:complexContent>
|
||||
<xsd:restriction base="GenericTimeStampType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="ReferenceInfo" maxOccurs="unbounded"/>
|
||||
<xsd:element ref="ds:CanonicalizationMethod" minOccurs="0"/>
|
||||
<xsd:choice>
|
||||
<xsd:element name="EncapsulatedTimeStamp" type="EncapsulatedPKIDataType"/>
|
||||
<xsd:element name="XMLTimeStamp" type="AnyType"/>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:restriction>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
<!-- End OtherTimeStampType -->
|
||||
<!-- End time-stamp containers types -->
|
||||
<!-- End auxiliary types definitions-->
|
||||
<!-- Start container types -->
|
||||
<!-- Start QualifyingProperties -->
|
||||
<xsd:element name="QualifyingProperties" type="QualifyingPropertiesType"/>
|
||||
<xsd:complexType name="QualifyingPropertiesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="SignedProperties" type="SignedPropertiesType" minOccurs="0"/>
|
||||
<xsd:element name="UnsignedProperties" type="UnsignedPropertiesType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Target" type="xsd:anyURI" use="required"/>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End QualifyingProperties -->
|
||||
<!-- Start SignedProperties-->
|
||||
<xsd:element name="SignedProperties" type="SignedPropertiesType"/>
|
||||
<xsd:complexType name="SignedPropertiesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="SignedSignatureProperties" type="SignedSignaturePropertiesType" minOccurs="0"/>
|
||||
<xsd:element name="SignedDataObjectProperties" type="SignedDataObjectPropertiesType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End SignedProperties-->
|
||||
<!-- Start UnsignedProperties-->
|
||||
<xsd:element name="UnsignedProperties" type="UnsignedPropertiesType"/>
|
||||
<xsd:complexType name="UnsignedPropertiesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="UnsignedSignatureProperties" type="UnsignedSignaturePropertiesType" minOccurs="0"/>
|
||||
<xsd:element name="UnsignedDataObjectProperties" type="UnsignedDataObjectPropertiesType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End UnsignedProperties-->
|
||||
<!-- Start SignedSignatureProperties-->
|
||||
<xsd:element name="SignedSignatureProperties" type="SignedSignaturePropertiesType"/>
|
||||
<xsd:complexType name="SignedSignaturePropertiesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="SigningTime" type="xsd:dateTime" minOccurs="0"/>
|
||||
<xsd:element name="SigningCertificate" type="CertIDListType" minOccurs="0"/>
|
||||
<xsd:element name="SignaturePolicyIdentifier" type="SignaturePolicyIdentifierType" minOccurs="0"/>
|
||||
<xsd:element name="SignatureProductionPlace" type="SignatureProductionPlaceType" minOccurs="0"/>
|
||||
<xsd:element name="SignerRole" type="SignerRoleType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End SignedSignatureProperties-->
|
||||
<!-- Start SignedDataObjectProperties-->
|
||||
<xsd:element name="SignedDataObjectProperties" type="SignedDataObjectPropertiesType"/>
|
||||
<xsd:complexType name="SignedDataObjectPropertiesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="DataObjectFormat" type="DataObjectFormatType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="CommitmentTypeIndication" type="CommitmentTypeIndicationType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="AllDataObjectsTimeStamp" type="XAdESTimeStampType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="IndividualDataObjectsTimeStamp" type="XAdESTimeStampType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End SignedDataObjectProperties-->
|
||||
<!-- Start UnsignedSignatureProperties-->
|
||||
<xsd:element name="UnsignedSignatureProperties" type="UnsignedSignaturePropertiesType"/>
|
||||
<xsd:complexType name="UnsignedSignaturePropertiesType">
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="CounterSignature" type="CounterSignatureType"/>
|
||||
<xsd:element name="SignatureTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:element name="CompleteCertificateRefs" type="CompleteCertificateRefsType"/>
|
||||
<xsd:element name="CompleteRevocationRefs" type="CompleteRevocationRefsType"/>
|
||||
<xsd:element name="AttributeCertificateRefs" type="CompleteCertificateRefsType"/>
|
||||
<xsd:element name="AttributeRevocationRefs" type="CompleteRevocationRefsType"/>
|
||||
<xsd:element name="SigAndRefsTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:element name="RefsOnlyTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:element name="CertificateValues" type="CertificateValuesType"/>
|
||||
<xsd:element name="RevocationValues" type="RevocationValuesType"/>
|
||||
<xsd:element name="AttrAuthoritiesCertValues" type="CertificateValuesType"/>
|
||||
<xsd:element name="AttributeRevocationValues" type="RevocationValuesType"/>
|
||||
<xsd:element name="ArchiveTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:any namespace="##other"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End UnsignedSignatureProperties-->
|
||||
<!-- Start UnsignedDataObjectProperties-->
|
||||
<xsd:element name="UnsignedDataObjectProperties" type="UnsignedDataObjectPropertiesType"/>
|
||||
<xsd:complexType name="UnsignedDataObjectPropertiesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="UnsignedDataObjectProperty" type="AnyType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End UnsignedDataObjectProperties-->
|
||||
<!-- Start QualifyingPropertiesReference-->
|
||||
<xsd:element name="QualifyingPropertiesReference" type="QualifyingPropertiesReferenceType"/>
|
||||
<xsd:complexType name="QualifyingPropertiesReferenceType">
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="required"/>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End QualifyingPropertiesReference-->
|
||||
<!-- End container types -->
|
||||
<!-- Start SigningTime element -->
|
||||
<xsd:element name="SigningTime" type="xsd:dateTime"/>
|
||||
<!-- End SigningTime element -->
|
||||
<!-- Start SigningCertificate -->
|
||||
<xsd:element name="SigningCertificate" type="CertIDListType"/>
|
||||
<xsd:complexType name="CertIDListType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="Cert" type="CertIDType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CertIDType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CertDigest" type="DigestAlgAndValueType"/>
|
||||
<xsd:element name="IssuerSerial" type="ds:X509IssuerSerialType"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="DigestAlgAndValueType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="ds:DigestMethod"/>
|
||||
<xsd:element ref="ds:DigestValue"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End SigningCertificate -->
|
||||
<!-- Start SignaturePolicyIdentifier -->
|
||||
<xsd:element name="SignaturePolicyIdentifier" type="SignaturePolicyIdentifierType"/>
|
||||
<xsd:complexType name="SignaturePolicyIdentifierType">
|
||||
<xsd:choice>
|
||||
<xsd:element name="SignaturePolicyId" type="SignaturePolicyIdType"/>
|
||||
<xsd:element name="SignaturePolicyImplied"/>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="SignaturePolicyIdType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="SigPolicyId" type="ObjectIdentifierType"/>
|
||||
<xsd:element ref="ds:Transforms" minOccurs="0"/>
|
||||
<xsd:element name="SigPolicyHash" type="DigestAlgAndValueType"/>
|
||||
<xsd:element name="SigPolicyQualifiers" type="SigPolicyQualifiersListType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="SigPolicyQualifiersListType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="SigPolicyQualifier" type="AnyType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:element name="SPURI" type="xsd:anyURI"/>
|
||||
<xsd:element name="SPUserNotice" type="SPUserNoticeType"/>
|
||||
<xsd:complexType name="SPUserNoticeType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="NoticeRef" type="NoticeReferenceType" minOccurs="0"/>
|
||||
<xsd:element name="ExplicitText" type="xsd:string" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="NoticeReferenceType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="Organization" type="xsd:string"/>
|
||||
<xsd:element name="NoticeNumbers" type="IntegerListType"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="IntegerListType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="int" type="xsd:integer" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End SignaturePolicyIdentifier -->
|
||||
<!-- Start CounterSignature -->
|
||||
<xsd:element name="CounterSignature" type="CounterSignatureType"/>
|
||||
<xsd:complexType name="CounterSignatureType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="ds:Signature"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End CounterSignature -->
|
||||
<!-- Start DataObjectFormat -->
|
||||
<xsd:element name="DataObjectFormat" type="DataObjectFormatType"/>
|
||||
<xsd:complexType name="DataObjectFormatType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="Description" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="ObjectIdentifier" type="ObjectIdentifierType" minOccurs="0"/>
|
||||
<xsd:element name="MimeType" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="Encoding" type="xsd:anyURI" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="ObjectReference" type="xsd:anyURI" use="required"/>
|
||||
</xsd:complexType>
|
||||
<!-- End DataObjectFormat -->
|
||||
<!-- Start CommitmentTypeIndication -->
|
||||
<xsd:element name="CommitmentTypeIndication" type="CommitmentTypeIndicationType"/>
|
||||
<xsd:complexType name="CommitmentTypeIndicationType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CommitmentTypeId" type="ObjectIdentifierType"/>
|
||||
<xsd:choice>
|
||||
<xsd:element name="ObjectReference" type="xsd:anyURI" maxOccurs="unbounded"/>
|
||||
<xsd:element name="AllSignedDataObjects"/>
|
||||
</xsd:choice>
|
||||
<xsd:element name="CommitmentTypeQualifiers" type="CommitmentTypeQualifiersListType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CommitmentTypeQualifiersListType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CommitmentTypeQualifier" type="AnyType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End CommitmentTypeIndication -->
|
||||
<!-- Start SignatureProductionPlace -->
|
||||
<xsd:element name="SignatureProductionPlace" type="SignatureProductionPlaceType"/>
|
||||
<xsd:complexType name="SignatureProductionPlaceType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="City" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="StateOrProvince" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="PostalCode" type="xsd:string" minOccurs="0"/>
|
||||
<xsd:element name="CountryName" type="xsd:string" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End SignatureProductionPlace -->
|
||||
<!-- Start SignerRole -->
|
||||
<xsd:element name="SignerRole" type="SignerRoleType"/>
|
||||
<xsd:complexType name="SignerRoleType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="ClaimedRoles" type="ClaimedRolesListType" minOccurs="0"/>
|
||||
<xsd:element name="CertifiedRoles" type="CertifiedRolesListType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="ClaimedRolesListType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="ClaimedRole" type="AnyType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CertifiedRolesListType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CertifiedRole" type="EncapsulatedPKIDataType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End SignerRole -->
|
||||
<xsd:element name="AllDataObjectsTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:element name="IndividualDataObjectsTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:element name="SignatureTimeStamp" type="XAdESTimeStampType"/>
|
||||
<!-- Start CompleteCertificateRefs -->
|
||||
<xsd:element name="CompleteCertificateRefs" type="CompleteCertificateRefsType"/>
|
||||
<xsd:complexType name="CompleteCertificateRefsType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CertRefs" type="CertIDListType"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End CompleteCertificateRefs -->
|
||||
<!-- Start CompleteRevocationRefs-->
|
||||
<xsd:element name="CompleteRevocationRefs" type="CompleteRevocationRefsType"/>
|
||||
<xsd:complexType name="CompleteRevocationRefsType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CRLRefs" type="CRLRefsType" minOccurs="0"/>
|
||||
<xsd:element name="OCSPRefs" type="OCSPRefsType" minOccurs="0"/>
|
||||
<xsd:element name="OtherRefs" type="OtherCertStatusRefsType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CRLRefsType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CRLRef" type="CRLRefType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CRLRefType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="DigestAlgAndValue" type="DigestAlgAndValueType"/>
|
||||
<xsd:element name="CRLIdentifier" type="CRLIdentifierType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CRLIdentifierType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="Issuer" type="xsd:string"/>
|
||||
<xsd:element name="IssueTime" type="xsd:dateTime"/>
|
||||
<xsd:element name="Number" type="xsd:integer" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="OCSPRefsType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="OCSPRef" type="OCSPRefType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="OCSPRefType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="OCSPIdentifier" type="OCSPIdentifierType"/>
|
||||
<xsd:element name="DigestAlgAndValue" type="DigestAlgAndValueType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="ResponderIDType">
|
||||
<xsd:choice>
|
||||
<xsd:element name="ByName" type="xsd:string"/>
|
||||
<xsd:element name="ByKey" type="xsd:base64Binary"/>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="OCSPIdentifierType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="ResponderID" type="ResponderIDType"/>
|
||||
<xsd:element name="ProducedAt" type="xsd:dateTime"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="OtherCertStatusRefsType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="OtherRef" type="AnyType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End CompleteRevocationRefs-->
|
||||
<xsd:element name="AttributeCertificateRefs" type="CompleteCertificateRefsType"/>
|
||||
<xsd:element name="AttributeRevocationRefs" type="CompleteRevocationRefsType"/>
|
||||
<xsd:element name="SigAndRefsTimeStamp" type="XAdESTimeStampType"/>
|
||||
<xsd:element name="RefsOnlyTimeStamp" type="XAdESTimeStampType"/>
|
||||
<!-- Start CertificateValues -->
|
||||
<xsd:element name="CertificateValues" type="CertificateValuesType"/>
|
||||
<xsd:complexType name="CertificateValuesType">
|
||||
<xsd:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="EncapsulatedX509Certificate" type="EncapsulatedPKIDataType"/>
|
||||
<xsd:element name="OtherCertificate" type="AnyType"/>
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<!-- End CertificateValues -->
|
||||
<!-- Start RevocationValues-->
|
||||
<xsd:element name="RevocationValues" type="RevocationValuesType"/>
|
||||
<xsd:complexType name="RevocationValuesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="CRLValues" type="CRLValuesType" minOccurs="0"/>
|
||||
<xsd:element name="OCSPValues" type="OCSPValuesType" minOccurs="0"/>
|
||||
<xsd:element name="OtherValues" type="OtherCertStatusValuesType" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="CRLValuesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="EncapsulatedCRLValue" type="EncapsulatedPKIDataType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="OCSPValuesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="EncapsulatedOCSPValue" type="EncapsulatedPKIDataType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<xsd:complexType name="OtherCertStatusValuesType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="OtherValue" type="AnyType" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
<!-- End RevocationValues-->
|
||||
<xsd:element name="AttrAuthoritiesCertValues" type="CertificateValuesType"/>
|
||||
<xsd:element name="AttributeRevocationValues" type="RevocationValuesType"/>
|
||||
<xsd:element name="ArchiveTimeStamp" type="XAdESTimeStampType"/>
|
||||
</xsd:schema>
|
15
src/ooxml/resources/org/apache/poi/poifs/crypt/XAdESv141.xsd
Normal file
15
src/ooxml/resources/org/apache/poi/poifs/crypt/XAdESv141.xsd
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema targetNamespace="http://uri.etsi.org/01903/v1.4.1#" xmlns="http://uri.etsi.org/01903/v1.4.1#" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" elementFormDefault="qualified">
|
||||
<xsd:import namespace="http://uri.etsi.org/01903/v1.3.2#" schemaLocation="http://uri.etsi.org/01903/v1.3.2/XAdES.xsd"/>
|
||||
<!-- Start CertificateValues -->
|
||||
<xsd:element name="TimeStampValidationData" type="ValidationDataType"/>
|
||||
<xsd:complexType name="ValidationDataType">
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="xades:CertificateValues" minOccurs="0"/>
|
||||
<xsd:element ref="xades:RevocationValues" minOccurs="0"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="Id" type="xsd:ID" use="optional"/>
|
||||
<xsd:attribute name="URI" type="xsd:anyURI" use="optional"/>
|
||||
</xsd:complexType>
|
||||
<xsd:element name="ArchiveTimeStamp" type="xades:XAdESTimeStampType"/>
|
||||
</xsd:schema>
|
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>
|
328
src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
Normal file
328
src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
Normal file
@ -0,0 +1,328 @@
|
||||
/* ====================================================================
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
(the "License"); you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
==================================================================== */
|
||||
package org.apache.poi.poifs.crypt;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.security.cert.CRLException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509CRL;
|
||||
import java.security.cert.X509Certificate;
|
||||
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.apache.poi.poifs.crypt.dsig.HorribleProxy;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1InputStreamIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.AuthorityInformationAccessIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.AuthorityKeyIdentifierIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BasicConstraintsIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BasicOCSPRespGeneratorIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BasicOCSPRespIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.CRLNumberIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.CRLReasonIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.CertificateIDIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.CertificateStatusIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DERIA5StringIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DERSequenceIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DistributionPointIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DistributionPointNameIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.GeneralNameIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.GeneralNamesIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.KeyUsageIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPReqGeneratorIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPReqIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPRespGeneratorIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPRespIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ReqIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.RevokedStatusIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SubjectKeyIdentifierIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SubjectPublicKeyInfoIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509ExtensionsIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509ObjectIdentifiersIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509PrincipalIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509V2CRLGeneratorIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509V3CertificateGeneratorIf;
|
||||
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;
|
||||
}
|
||||
|
||||
private static SubjectKeyIdentifierIf createSubjectKeyId(PublicKey publicKey)
|
||||
throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException
|
||||
, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(publicKey.getEncoded());
|
||||
ASN1InputStreamIf asnObj = HorribleProxy.newProxy(ASN1InputStreamIf.class, bais);
|
||||
SubjectPublicKeyInfoIf info =
|
||||
HorribleProxy.newProxy(SubjectPublicKeyInfoIf.class, asnObj.readObject$Sequence());
|
||||
SubjectKeyIdentifierIf keyId = HorribleProxy.newProxy(SubjectKeyIdentifierIf.class, info);
|
||||
return keyId;
|
||||
}
|
||||
|
||||
private static AuthorityKeyIdentifierIf createAuthorityKeyId(PublicKey publicKey)
|
||||
throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException
|
||||
, IllegalAccessException, InvocationTargetException, NoSuchFieldException {
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(publicKey.getEncoded());
|
||||
ASN1InputStreamIf asnObj = HorribleProxy.newProxy(ASN1InputStreamIf.class, bais);
|
||||
SubjectPublicKeyInfoIf info =
|
||||
HorribleProxy.newProxy(SubjectPublicKeyInfoIf.class, asnObj.readObject$Sequence());
|
||||
AuthorityKeyIdentifierIf keyId = HorribleProxy.newProxy(AuthorityKeyIdentifierIf.class, info);
|
||||
|
||||
return keyId;
|
||||
}
|
||||
|
||||
static X509Certificate generateCertificate(PublicKey subjectPublicKey,
|
||||
String subjectDn, Date notBefore, Date notAfter,
|
||||
X509Certificate issuerCertificate, PrivateKey issuerPrivateKey,
|
||||
boolean caFlag, int pathLength, String crlUri, String ocspUri,
|
||||
KeyUsageIf keyUsage)
|
||||
throws IOException, InvalidKeyException, IllegalStateException, NoSuchAlgorithmException
|
||||
, SignatureException, CertificateException, InvocationTargetException, IllegalAccessException
|
||||
, InstantiationException, NoSuchMethodException, ClassNotFoundException, NoSuchFieldException
|
||||
{
|
||||
String signatureAlgorithm = "SHA1withRSA";
|
||||
X509V3CertificateGeneratorIf certificateGenerator = HorribleProxy.newProxy(X509V3CertificateGeneratorIf.class);
|
||||
certificateGenerator.reset();
|
||||
certificateGenerator.setPublicKey(subjectPublicKey);
|
||||
certificateGenerator.setSignatureAlgorithm(signatureAlgorithm);
|
||||
certificateGenerator.setNotBefore(notBefore);
|
||||
certificateGenerator.setNotAfter(notAfter);
|
||||
X509PrincipalIf subjectDN = HorribleProxy.newProxy(X509PrincipalIf.class, subjectDn);
|
||||
X509PrincipalIf issuerDN;
|
||||
if (null != issuerCertificate) {
|
||||
issuerDN = HorribleProxy.newProxy(X509PrincipalIf.class, issuerCertificate
|
||||
.getSubjectX500Principal().toString());
|
||||
} else {
|
||||
issuerDN = subjectDN;
|
||||
}
|
||||
certificateGenerator.setIssuerDN(issuerDN);
|
||||
certificateGenerator.setSubjectDN(subjectDN);
|
||||
certificateGenerator.setSerialNumber(new BigInteger(128,
|
||||
new SecureRandom()));
|
||||
|
||||
X509ExtensionsIf X509Extensions = HorribleProxy.newProxy(X509ExtensionsIf.class);
|
||||
|
||||
certificateGenerator.addExtension(X509Extensions.SubjectKeyIdentifier(),
|
||||
false, createSubjectKeyId(subjectPublicKey));
|
||||
PublicKey issuerPublicKey;
|
||||
issuerPublicKey = subjectPublicKey;
|
||||
certificateGenerator.addExtension(
|
||||
X509Extensions.AuthorityKeyIdentifier(), false,
|
||||
createAuthorityKeyId(issuerPublicKey));
|
||||
|
||||
if (caFlag) {
|
||||
BasicConstraintsIf bc;
|
||||
|
||||
if (-1 == pathLength) {
|
||||
bc = HorribleProxy.newProxy(BasicConstraintsIf.class, true);
|
||||
} else {
|
||||
bc = HorribleProxy.newProxy(BasicConstraintsIf.class, pathLength);
|
||||
}
|
||||
certificateGenerator.addExtension(X509Extensions.BasicConstraints(), false, bc);
|
||||
}
|
||||
|
||||
if (null != crlUri) {
|
||||
GeneralNameIf gn = HorribleProxy.newProxy(GeneralNameIf.class);
|
||||
int uri = gn.uniformResourceIdentifier();
|
||||
DERIA5StringIf crlUriDer = HorribleProxy.newProxy(DERIA5StringIf.class, crlUri);
|
||||
gn = HorribleProxy.newProxy(GeneralNameIf.class, uri, crlUriDer);
|
||||
|
||||
DERSequenceIf gnDer = HorribleProxy.newProxy(DERSequenceIf.class, gn);
|
||||
GeneralNamesIf gns = HorribleProxy.newProxy(GeneralNamesIf.class, gnDer);
|
||||
|
||||
DistributionPointNameIf dpn = HorribleProxy.newProxy(DistributionPointNameIf.class, 0, gns);
|
||||
DistributionPointIf distp = HorribleProxy.newProxy(DistributionPointIf.class, dpn, null, null);
|
||||
DERSequenceIf distpDer = HorribleProxy.newProxy(DERSequenceIf.class, distp);
|
||||
certificateGenerator.addExtension(X509Extensions.CRLDistributionPoints(), false, distpDer);
|
||||
}
|
||||
|
||||
if (null != ocspUri) {
|
||||
GeneralNameIf ocspName = HorribleProxy.newProxy(GeneralNameIf.class);
|
||||
int uri = ocspName.uniformResourceIdentifier();
|
||||
ocspName = HorribleProxy.newProxy(GeneralNameIf.class, uri, ocspUri);
|
||||
|
||||
X509ObjectIdentifiersIf X509ObjectIdentifiers = HorribleProxy.newProxy(X509ObjectIdentifiersIf.class);
|
||||
AuthorityInformationAccessIf authorityInformationAccess =
|
||||
HorribleProxy.newProxy(AuthorityInformationAccessIf.class
|
||||
, X509ObjectIdentifiers.ocspAccessMethod(), ocspName);
|
||||
|
||||
certificateGenerator.addExtension(
|
||||
X509Extensions.AuthorityInfoAccess(), false,
|
||||
authorityInformationAccess);
|
||||
}
|
||||
|
||||
if (null != keyUsage) {
|
||||
certificateGenerator.addExtension(X509Extensions.KeyUsage(), true, keyUsage);
|
||||
}
|
||||
|
||||
X509Certificate certificate;
|
||||
certificate = certificateGenerator.generate(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 certificate;
|
||||
}
|
||||
|
||||
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 InvalidKeyException,
|
||||
CRLException, IllegalStateException, NoSuchAlgorithmException,
|
||||
SignatureException, InvocationTargetException, IllegalAccessException,
|
||||
InstantiationException, NoSuchMethodException, ClassNotFoundException, NoSuchFieldException {
|
||||
X509V2CRLGeneratorIf crlGenerator = HorribleProxy.newProxy(X509V2CRLGeneratorIf.class);
|
||||
crlGenerator.setIssuerDN(issuer.getSubjectX500Principal());
|
||||
Date now = new Date();
|
||||
crlGenerator.setThisUpdate(now);
|
||||
crlGenerator.setNextUpdate(new Date(now.getTime() + 100000));
|
||||
crlGenerator.setSignatureAlgorithm("SHA1withRSA");
|
||||
|
||||
X509ExtensionsIf X509Extensions = HorribleProxy.newProxy(X509ExtensionsIf.class);
|
||||
CRLNumberIf crlNumber = HorribleProxy.newProxy(CRLNumberIf.class, new BigInteger("1234"));
|
||||
|
||||
crlGenerator.addExtension(X509Extensions.CRLNumber(), false, crlNumber);
|
||||
X509CRL x509Crl = crlGenerator.generate(issuerPrivateKey);
|
||||
return x509Crl;
|
||||
}
|
||||
|
||||
public static OCSPRespIf createOcspResp(X509Certificate certificate,
|
||||
boolean revoked, X509Certificate issuerCertificate,
|
||||
X509Certificate ocspResponderCertificate,
|
||||
PrivateKey ocspResponderPrivateKey, String signatureAlgorithm)
|
||||
throws Exception {
|
||||
// request
|
||||
OCSPReqGeneratorIf ocspReqGenerator = HorribleProxy.newProxy(OCSPReqGeneratorIf.class);
|
||||
CertificateIDIf certId = HorribleProxy.newProxy(CertificateIDIf.class);
|
||||
certId = HorribleProxy.newProxy(CertificateIDIf.class, certId.HASH_SHA1(),
|
||||
issuerCertificate, certificate.getSerialNumber());
|
||||
ocspReqGenerator.addRequest(certId);
|
||||
OCSPReqIf ocspReq = ocspReqGenerator.generate();
|
||||
|
||||
BasicOCSPRespGeneratorIf basicOCSPRespGenerator =
|
||||
HorribleProxy.newProxy(BasicOCSPRespGeneratorIf.class, ocspResponderCertificate.getPublicKey());
|
||||
|
||||
// request processing
|
||||
ReqIf[] requestList = ocspReq.getRequestList();
|
||||
for (ReqIf ocspRequest : requestList) {
|
||||
CertificateIDIf certificateID = ocspRequest.getCertID();
|
||||
CertificateStatusIf certificateStatus;
|
||||
if (revoked) {
|
||||
CRLReasonIf crlr = HorribleProxy.newProxy(CRLReasonIf.class);
|
||||
RevokedStatusIf rs = HorribleProxy.newProxy(RevokedStatusIf.class, new Date(), crlr.unspecified());
|
||||
certificateStatus = HorribleProxy.newProxy(CertificateStatusIf.class, rs.getDelegate());
|
||||
} else {
|
||||
CertificateStatusIf cs = HorribleProxy.newProxy(CertificateStatusIf.class);
|
||||
certificateStatus = cs.GOOD();
|
||||
}
|
||||
basicOCSPRespGenerator
|
||||
.addResponse(certificateID, certificateStatus);
|
||||
}
|
||||
|
||||
// basic response generation
|
||||
X509Certificate[] chain = null;
|
||||
if (!ocspResponderCertificate.equals(issuerCertificate)) {
|
||||
chain = new X509Certificate[] { ocspResponderCertificate,
|
||||
issuerCertificate };
|
||||
}
|
||||
|
||||
BasicOCSPRespIf basicOCSPResp = basicOCSPRespGenerator.generate(
|
||||
signatureAlgorithm, ocspResponderPrivateKey, chain, new Date(),
|
||||
"BC");
|
||||
|
||||
// response generation
|
||||
OCSPRespGeneratorIf ocspRespGenerator = HorribleProxy.newProxy(OCSPRespGeneratorIf.class);
|
||||
OCSPRespIf ocspResp = ocspRespGenerator.generate(
|
||||
ocspRespGenerator.SUCCESSFUL(), basicOCSPResp);
|
||||
|
||||
return ocspResp;
|
||||
}
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
/* ====================================================================
|
||||
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.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
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.X509Certificate;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
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.HorribleProxy;
|
||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
|
||||
import org.apache.poi.poifs.crypt.dsig.HorribleProxies.KeyUsageIf;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TestSignatureInfo {
|
||||
private static final POILogger LOG = POILogFactory.getLogger(TestSignatureInfo.class);
|
||||
private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance();
|
||||
|
||||
private KeyPair keyPair = null;
|
||||
private X509Certificate x509 = null;
|
||||
|
||||
|
||||
|
||||
@BeforeClass
|
||||
public static void initBouncy() throws MalformedURLException {
|
||||
File bcJar = testdata.getFile("bcprov-ext-jdk15on-1.49.jar");
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
URLClassLoader ucl = new URLClassLoader(new URL[]{bcJar.toURI().toURL()}, cl);
|
||||
Thread.currentThread().setContextClassLoader(ucl);
|
||||
}
|
||||
|
||||
@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);
|
||||
SignatureInfo si = new SignatureInfo(pkg);
|
||||
List<X509Certificate> result = si.getSigners();
|
||||
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);
|
||||
SignatureInfo si = new SignatureInfo(pkg);
|
||||
List<X509Certificate> result = si.getSigners();
|
||||
|
||||
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);
|
||||
SignatureInfo si = new SignatureInfo(pkg);
|
||||
List<X509Certificate> result = si.getSigners();
|
||||
|
||||
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 testSignSpreadsheetWithSignatureInfo() throws Exception {
|
||||
String testFile = "hello-world-unsigned.xlsx";
|
||||
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
|
||||
SignatureInfo si = new SignatureInfo(pkg);
|
||||
initKeyPair("Test", "CN=Test");
|
||||
si.confirmSignature(keyPair.getPrivate(), x509, HashAlgorithm.sha1);
|
||||
List<X509Certificate> signer = si.getSigners();
|
||||
assertEquals(1, signer.size());
|
||||
pkg.close();
|
||||
}
|
||||
|
||||
|
||||
private OPCPackage sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
|
||||
/*** TODO : set cal to now ... only set to fixed date for debugging ... */
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.clear();
|
||||
cal.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
cal.set(2014, 7, 6, 21, 42, 12);
|
||||
|
||||
XmlSignatureService signatureService = new XmlSignatureService(HashAlgorithm.sha1, pkgCopy);
|
||||
signatureService.initFacets(cal.getTime());
|
||||
initKeyPair(alias, signerDn);
|
||||
|
||||
// operate
|
||||
List<X509Certificate> x509Chain = Collections.singletonList(x509);
|
||||
DigestInfo digestInfo = signatureService.preSign(null, x509Chain, null, null, 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
|
||||
|
||||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());
|
||||
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
|
||||
digestInfoValueBuf.write(SignatureInfo.SHA1_DIGEST_INFO_PREFIX);
|
||||
digestInfoValueBuf.write(digestInfo.digestValue);
|
||||
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
|
||||
byte[] signatureValue = cipher.doFinal(digestInfoValue);
|
||||
|
||||
// operate: postSign
|
||||
signatureService.postSign(signatureValue, Collections.singletonList(x509));
|
||||
|
||||
// verify: signature
|
||||
SignatureInfo si = new SignatureInfo(pkgCopy);
|
||||
List<X509Certificate> signers = si.getSigners();
|
||||
assertEquals(signerCount, signers.size());
|
||||
|
||||
return pkgCopy;
|
||||
}
|
||||
|
||||
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();
|
||||
KeyUsageIf keyUsage = HorribleProxy.newProxy(KeyUsageIf.class);
|
||||
keyUsage = HorribleProxy.newProxy(KeyUsageIf.class, 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/bcprov-ext-jdk15on-1.49.jar
Normal file
BIN
test-data/xmldsign/bcprov-ext-jdk15on-1.49.jar
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