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:
Andreas Beeker 2014-08-10 18:25:10 +00:00
parent 9f7b23bb42
commit 63f8c1dcf0
61 changed files with 7881 additions and 72 deletions

View File

@ -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>

114
build.xml
View File

@ -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"/>
@ -356,7 +366,7 @@ under the License.
</fileset>
</delete>
<condition property="jars.present">
<condition property="jars.present">
<or>
<and>
<available file="${main.commons-logging.jar}"/>
@ -458,19 +468,35 @@ under the License.
<condition property="ooxml-xsds.present">
<or>
<and>
<available file="${ooxml.xsds.izip}"/>
<available file="${ooxml.lib}/${ooxml.xsds.izip.1}"/>
</and>
<isset property="disconnected"/>
</or>
</condition>
</target>
<target name="fetch-ooxml-xsds" unless="ooxml-xsds.present"
depends="check-ooxml-xsds"
depends="check-ooxml-xsds"
description="Fetches needed OOXML xsd files from the Internet">
<get src="${ooxml.xsds.url}" dest="${ooxml.xsds.ozip}"/>
<unzip src="${ooxml.xsds.ozip}" dest="${ooxml.lib}">
<get dest="${ooxml.lib}" skipexisting="true">
<url url="${ooxml.xsds.url.1}"/>
<url url="${ooxml.xsds.url.2}"/>
<url url="${ooxml.xsds.dc.1}"/>
<url url="${ooxml.xsds.dc.2}"/>
<url url="${ooxml.xsds.dc.3}"/>
<url url="${ooxml.xsds.dsig}"/>
<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}"/>
depends="check-jars,fetch-jars,check-compiled-ooxml-xsds"
description="Unpacks the OOXML xsd files, and compiles them into XmlBeans">
<taskdef name="xmlbean"
classname="org.apache.xmlbeans.impl.tool.XMLBean"
classpath="${ooxml.xmlbeans23.jar}"/>
@ -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}"
@ -523,41 +538,40 @@ under the License.
<classpath refid="ooxml.classpath"/>
</xmlbean>
<!-- Now make a jar of the schema sources -->
<jar
<!-- Now make a jar of the schema sources -->
<jar
basedir="${ooxml.xsds.src.dir}"
destfile="${ooxml.xsds.src.jar}"
/>
</target>
<target name="compile-ooxml-encryption-xsds" unless="ooxml-compiled-encryption-xsds.present"
depends="check-jars,fetch-jars,check-compiled-ooxml-xsds"
description="Compiles the OOXML encryption xsd files into XmlBeans">
<taskdef name="xmlbean"
classname="org.apache.xmlbeans.impl.tool.XMLBean"
classpath="${ooxml.xmlbeans23.jar}"/>
<!-- We need a fair amount of memory to compile the xml schema, -->
<!-- but limit it in case it goes wrong! -->
<!-- Pick the right amount based on 32 vs 64 bit jvm -->
<condition property="ooxml.memory" value="768m" else="512m">
<equals arg1="${sun.arch.data.model}" arg2="64" />
</condition>
<!-- Now do the same for the encryption and supporting schemas -->
<property name="ooxml.enc.xsds.tmp.dir" location="build/ooxml-encryption-xsds"/>
<mkdir dir="${ooxml.enc.xsds.tmp.dir}"/>
<unzip src="${ooxml.lib}/${ooxml.xsds.izip.2}" dest="${ooxml.enc.xsds.tmp.dir}"/>
<copy todir="${ooxml.enc.xsds.tmp.dir}">
<fileset dir="${ooxml.lib}" includes="dc*.xsd,xmldsig*.xsd"/>
<fileset dir="${ooxml.encryption.xsd.dir}"/>
</copy>
<!-- noupa/nopvr is set because of the dublincore schemas -->
<!-- https://issues.apache.org/jira/browse/XMLBEANS-340 -->
<!-- javasource > 1.5 will not generate all array accessor -->
<xmlbean
schema="${ooxml.encryption.xsd.dir}"
schema="${ooxml.enc.xsds.tmp.dir}"
srcgendir="${ooxml.encryption.src.dir}"
optimize="yes"
destfile="${ooxml.encryption.jar}"
javasource="1.5"
javasource="1.5"
failonerror="true"
fork="true"
memoryMaximumSize="${ooxml.memory}"
noupa="true"
nopvr="true"
>
<classpath refid="ooxml.classpath"/>
</xmlbean>
<!-- Now make a jar of the schema sources -->
<jar
basedir="${ooxml.encryption.src.dir}"
destfile="${ooxml.encryption.src.jar}"
@ -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}"
@ -1351,7 +1365,7 @@ under the License.
<target name="gump" depends="compile-all, test-all, jar"/>
<target name="jenkins" depends="compile-all, test-all, jar, javadocs, assemble, findbugs, release-notes, rat-check"/>
<available property="maven.ant.tasks.present" classname="org.apache.maven.artifact.ant.Pom"/>
<target name="maven.ant.tasks-check">
<fail unless="maven.ant.tasks.present">
@ -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" />

View File

@ -34,6 +34,8 @@ public enum CipherAlgorithm {
// need bouncycastle provider for this one ...
// see http://stackoverflow.com/questions/4436397/3des-des-encryption-using-the-jce-generating-an-acceptable-key
des3_112(null, "DESede", -1, 128, new int[]{128}, 8, 32, "3DES_112", true),
// only for digital signatures
rsa(null, "RSA", -1, 1024, new int[]{1024, 2048, 3072, 4096}, -1, -1, "", false);
;
public final CipherProvider provider;

View File

@ -19,6 +19,7 @@ package org.apache.poi.poifs.crypt;
import java.nio.charset.Charset;
import java.security.DigestException;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.Provider;
import java.security.Security;
@ -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");

View File

@ -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;
}

View File

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

View File

@ -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());

View File

@ -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.

View File

@ -0,0 +1,38 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case there is something wrong with the incoming eID
* certificate.
*
* @author Frank Cornelis
*
*/
public class CertificateSecurityException extends SecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case the incoming eID certificate is expired.
*
* @author Frank Cornelis
*
*/
public class ExpiredCertificateSecurityException extends
CertificateSecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case the incoming eID certificate has been revoked.
*
* @author Frank Cornelis
*
*/
public class RevokedCertificateSecurityException extends
CertificateSecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,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);
}
}
}

View File

@ -0,0 +1,38 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig;
/**
* Exception thrown in case the incoming eID certificate is not trusted.
*
* @author Frank Cornelis
*
*/
public class TrustCertificateSecurityException extends
CertificateSecurityException {
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,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;
}
}

View File

@ -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" //
};
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -0,0 +1,65 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,131 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.security.cert.CRLException;
import java.security.cert.X509CRL;
import java.util.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();
}
}

View File

@ -0,0 +1,47 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* Interface for a service that retrieves revocation data about some given
* certificate chain.
*
* @author Frank Cornelis
*
*/
public interface RevocationDataService {
/**
* Gives back the revocation data corresponding with the given certificate
* chain.
*
* @param certificateChain
* @return
*/
RevocationData getRevocationData(List<X509Certificate> certificateChain);
}

View File

@ -0,0 +1,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;
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -0,0 +1,51 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.services;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* Interface for trust validator of a TSP.
*
* @author Frank Cornelis
*
*/
public interface TimeStampServiceValidator {
/**
* Validates the given certificate chain.
*
* @param certificateChain
* @param revocationData
* the optional data container that should be filled with
* revocation data that was used to validate the given
* certificate chain.
* @throws Exception
* in case the certificate chain is invalid.
*/
void validate(List<X509Certificate> certificateChain,
RevocationData revocationData) throws Exception;
}

View File

@ -0,0 +1,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);
}
}

View File

@ -0,0 +1,51 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.spi;
import java.io.Serializable;
import java.security.Identity;
/**
* Address Data Transfer Object.
*
* @author Frank Cornelis
* @see Identity
*
*/
public class AddressDTO implements Serializable {
/*
* We implement serializable to allow this class to be used in distributed
* containers as defined in the Servlet v2.4 specification.
*/
private static final long serialVersionUID = 1L;
public String streetAndNumber;
public String zip;
public String city;
}

View File

@ -0,0 +1,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#";
}

View File

@ -0,0 +1,56 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.spi;
import java.io.Serializable;
import org.apache.poi.poifs.crypt.HashAlgorithm;
/**
* Digest Information data transfer class.
*/
public class DigestInfo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Main constructor.
*
* @param digestValue
* @param hashAlgo
* @param description
*/
public DigestInfo(byte[] digestValue, HashAlgorithm hashAlgo, String description) {
this.digestValue = digestValue;
this.hashAlgo = hashAlgo;
this.description = description;
}
public final byte[] digestValue;
public final String description;
public final HashAlgorithm hashAlgo;
}

View File

@ -0,0 +1,75 @@
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
================================================================= */
package org.apache.poi.poifs.crypt.dsig.spi;
import java.io.Serializable;
import java.util.GregorianCalendar;
/**
* Identity Data Transfer Object.
*
* @author Frank Cornelis
*
*/
public class IdentityDTO implements Serializable {
/*
* We implement serializable to allow this class to be used in distributed
* containers as defined in the Servlet v2.4 specification.
*/
private static final long serialVersionUID = 1L;
public String cardNumber;
public String chipNumber;
public GregorianCalendar cardValidityDateBegin;
public GregorianCalendar cardValidityDateEnd;
public String cardDeliveryMunicipality;
public String nationalNumber;
public String name;
public String firstName;
public String middleName;
public String nationality;
public String placeOfBirth;
public GregorianCalendar dateOfBirth;
public boolean male;
public boolean female;
public String nobleCondition;
public String duplicate;
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

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

View File

@ -0,0 +1,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>

View 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>

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
====================================================================
-->
<xsd:schema targetNamespace="http://schemas.microsoft.com/office/2006/digsig" elementFormDefault="qualified" xmlns="http://schemas.microsoft.com/office/2006/digsig" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:simpleType name="ST_PositiveInteger">
<xsd:restriction base="xsd:int">
<xsd:minExclusive value="0"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureComments">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="255"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureProviderUrl">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="2083"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureText">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="100"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_SignatureType">
<xsd:restriction base="xsd:int">
<xsd:enumeration value="1"/>
<xsd:enumeration value="2"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_Version">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="64"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="ST_UniqueIdentifierWithBraces">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\{[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}\}|"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:group name="EG_RequiredChildren">
<xsd:sequence>
<xsd:element name="SetupID" type="ST_UniqueIdentifierWithBraces"/>
<xsd:element name="SignatureText" type="ST_SignatureText"/>
<xsd:element name="SignatureImage" type="xsd:base64Binary"/>
<xsd:element name="SignatureComments" type="ST_SignatureComments"/>
<xsd:element name="WindowsVersion" type="ST_Version"/>
<xsd:element name="OfficeVersion" type="ST_Version"/>
<xsd:element name="ApplicationVersion" type="ST_Version"/>
<xsd:element name="Monitors" type="ST_PositiveInteger"/>
<xsd:element name="HorizontalResolution" type="ST_PositiveInteger"/>
<xsd:element name="VerticalResolution" type="ST_PositiveInteger"/>
<xsd:element name="ColorDepth" type="ST_PositiveInteger"/>
<xsd:element name="SignatureProviderId" type="ST_UniqueIdentifierWithBraces"/>
<xsd:element name="SignatureProviderUrl" type="ST_SignatureProviderUrl"/>
<xsd:element name="SignatureProviderDetails" type="xsd:int"/>
<xsd:element name="SignatureType" type="ST_SignatureType"/>
</xsd:sequence>
</xsd:group>
<xsd:group name="EG_OptionalChildren">
<xsd:sequence>
<xsd:element name="DelegateSuggestedSigner" type="xsd:string"/>
<xsd:element name="DelegateSuggestedSigner2" type="xsd:string"/>
<xsd:element name="DelegateSuggestedSignerEmail" type="xsd:string"/>
<xsd:element name="ManifestHashAlgorithm" type="xsd:anyURI" minOccurs="0"/>
</xsd:sequence>
</xsd:group>
<xsd:group name="EG_OptionalChildrenV2">
<xsd:sequence>
<xsd:element name="Address1" type="xsd:string"/>
<xsd:element name="Address2" type="xsd:string"/>
</xsd:sequence>
</xsd:group>
<xsd:complexType name="CT_SignatureInfoV1">
<xsd:sequence>
<xsd:group ref="EG_RequiredChildren"/>
<xsd:group ref="EG_OptionalChildren" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="CT_SignatureInfoV2">
<xsd:sequence>
<xsd:group ref="EG_OptionalChildrenV2" minOccurs="0"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="SignatureInfoV1" type="CT_SignatureInfoV1"/>
<xsd:element name="SignatureInfoV2" type="CT_SignatureInfoV2"/>
</xsd:schema>

View File

@ -0,0 +1,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;
}
}

View File

@ -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;
}
}

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.