git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@825637 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_6
@@ -1,5 +1,5 @@ | |||
<?xml version="1.0"?> | |||
<!-- | |||
<!-- | |||
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 | |||
@@ -30,7 +30,7 @@ under the License. | |||
Bruno Girin brunogirin@gmail.com | |||
This build was tested with ant 1.6.2 although it will probably work with | |||
other versions. The following jar files should be available on the | |||
other versions. The following jar files should be available on the | |||
classpath when running ant: | |||
LIBRARY LOCATION | |||
@@ -44,7 +44,7 @@ under the License. | |||
To build the documentation you will need to install forrest and set | |||
the FORREST_HOME environment variable. Forrest 0.5.1 required. | |||
Since POI 3.5 you will need JDK 1.5 or newer to build POI. | |||
Since POI 3.5 you will need JDK 1.5 or newer to build POI. | |||
Some people may find the tests hang when run through Ant. If this | |||
happens to you, try giving Ant some more memory when you run it, eg: | |||
@@ -123,7 +123,7 @@ under the License. | |||
<property name="ooxml.output.test.dir" location="build/ooxml-test-classes"/> | |||
<property name="ooxml.testokfile" location="build/ooxml-testokfile.txt"/> | |||
<!-- The following jars are downloaded by the fetch-ooxml-jars task --> | |||
<!-- The following jars are downloaded by the fetch-ooxml-jars task --> | |||
<property name="ooxml.dom4j.jar" location="${ooxml.lib}/dom4j-1.6.1.jar"/> | |||
<property name="ooxml.dom4j.url" value="${repository.m2}/maven2/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar"/> | |||
<property name="ooxml.xmlbeans.jar" location="${ooxml.lib}/xmlbeans-2.3.0.jar"/> | |||
@@ -132,19 +132,6 @@ under the License. | |||
<property name="ooxml.jsr173.url" value="${repository.m2}/maven2/org/apache/geronimo/specs/geronimo-stax-api_1.0_spec/1.0/geronimo-stax-api_1.0_spec-1.0.jar"/> | |||
<property name="ooxml.schemas.jar" location="${ooxml.lib}/ooxml-schemas-1.0.jar"/> | |||
<property name="ooxml.schemas.url" value="${repository.m2}/maven2/org/apache/poi/ooxml-schemas/1.0/ooxml-schemas-1.0.jar"/> | |||
<property name="ooxml.commons-lang.jar" location="${ooxml.lib}/commons-lang-2.4.jar"/> | |||
<property name="ooxml.commons-lang.url" value="${repository.m2}/maven2/commons-lang/commons-lang/2.4/commons-lang-2.4.jar"/> | |||
<property name="ooxml.commons-io.jar" location="${ooxml.lib}/commons-io-1.4.jar"/> | |||
<property name="ooxml.commons-io.url" value="${repository.m2}/maven2/commons-io/commons-io/1.4/commons-io-1.4.jar"/> | |||
<property name="ooxml.xmlsec.jar" location="${ooxml.lib}/xmlsec-1.4.3.jar"/> | |||
<property name="ooxml.xmlsec.url" value="${repository.m2}/maven2/org/apache/santuario/xmlsec/1.4.3/xmlsec-1.4.3.jar"/> | |||
<property name="ooxml.xalan.jar" location="${ooxml.lib}/xalan-2.7.1.jar"/> | |||
<property name="ooxml.xalan.url" value="${repository.m2}/maven2/xalan/xalan/2.7.1/xalan-2.7.1.jar"/> | |||
<property name="ooxml.xalan-serializer.jar" location="${ooxml.lib}/serializer-2.7.1.jar"/> | |||
<property name="ooxml.xalan-serializer.url" value="${repository.m2}/maven2/xalan/serializer/2.7.1/serializer-2.7.1.jar"/> | |||
<!-- BouncyCastle is used only for OOXML Digital Signature tests --> | |||
<property name="ooxml.bcprov.jar" location="${ooxml.lib}/bcprov-jdk15-140.jar"/> | |||
<property name="ooxml.bcprov.url" value="${repository.m2}/maven2/bouncycastle/bcprov-jdk15/140/bcprov-jdk15-140.jar"/> | |||
<!-- See http://www.ecma-international.org/publications/standards/Ecma-376.htm --> | |||
<!-- "Copy these file(s), free of charge" --> | |||
@@ -155,7 +142,7 @@ under the License. | |||
<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"/> | |||
<property name="build.site" location="build/tmp/site/build/site"/> | |||
<property name="build.site.src" location="build/tmp/site"/> | |||
<property name="junit.report.dir" location="${build.site}/junit"/> | |||
@@ -173,12 +160,12 @@ under the License. | |||
<property name="jdk.version.source" value="1.5" | |||
description="JDK version of source code"/> | |||
<property name="jdk.version.class" value="1.5" | |||
description="JDK version of generated class files"/> | |||
description="JDK version of generated class files"/> | |||
<path id="main.classpath"> | |||
<fileset dir="${main.lib}"> | |||
<include name="*.jar"/> | |||
</fileset> | |||
<fileset dir="${main.lib}"> | |||
<include name="*.jar"/> | |||
</fileset> | |||
<pathelement location="${main.resource1.dir}"/> | |||
</path> | |||
@@ -196,9 +183,6 @@ under the License. | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${contrib.output.dir}"/> | |||
<pathelement location="${contrib.output.test.dir}"/> | |||
<fileset dir="${ooxml.lib}"> | |||
<include name="*.jar" /> | |||
</fileset> | |||
</path> | |||
<path id="ooxml.classpath"> | |||
@@ -222,9 +206,8 @@ under the License. | |||
<pathelement location="${ooxml.output.dir}"/> | |||
<pathelement location="${ooxml.output.test.dir}"/> | |||
<pathelement location="${main.output.test.dir}"/> <!-- ooxml tests use some utilities from main tests --> | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${junit.jar1.dir}"/> | |||
<pathelement location="${ooxml.src.test}"/> | |||
</path> | |||
@@ -288,7 +271,7 @@ under the License. | |||
<available resource="clovertasks" property="clover.present"/> | |||
<antcall target="with.clover"/> | |||
<mkdir dir="build"/> | |||
<mkdir dir="build/non-ant-classes"/> | |||
<mkdir dir="${main.output.dir}"/> | |||
@@ -368,12 +351,6 @@ under the License. | |||
<available file="${ooxml.xmlbeans.jar}"/> | |||
<available file="${ooxml.jsr173.jar}"/> | |||
<available file="${ooxml.schemas.jar}"/> | |||
<available file="${ooxml.commons-lang.jar}"/> | |||
<available file="${ooxml.commons-io.jar}"/> | |||
<available file="${ooxml.xmlsec.jar}"/> | |||
<available file="${ooxml.xalan.jar}"/> | |||
<available file="${ooxml.xalan-serializer.jar}"/> | |||
<available file="${ooxml.bcprov.jar}"/> | |||
</and> | |||
<isset property="disconnected"/> | |||
</or> | |||
@@ -396,30 +373,6 @@ under the License. | |||
<param name="sourcefile" value="${ooxml.schemas.url}"/> | |||
<param name="destfile" value="${ooxml.schemas.jar}"/> | |||
</antcall> | |||
<antcall target="downloadfile"> | |||
<param name="sourcefile" value="${ooxml.commons-lang.url}"/> | |||
<param name="destfile" value="${ooxml.commons-lang.jar}"/> | |||
</antcall> | |||
<antcall target="downloadfile"> | |||
<param name="sourcefile" value="${ooxml.commons-io.url}"/> | |||
<param name="destfile" value="${ooxml.commons-io.jar}"/> | |||
</antcall> | |||
<antcall target="downloadfile"> | |||
<param name="sourcefile" value="${ooxml.xmlsec.url}"/> | |||
<param name="destfile" value="${ooxml.xmlsec.jar}"/> | |||
</antcall> | |||
<antcall target="downloadfile"> | |||
<param name="sourcefile" value="${ooxml.xalan.url}"/> | |||
<param name="destfile" value="${ooxml.xalan.jar}"/> | |||
</antcall> | |||
<antcall target="downloadfile"> | |||
<param name="sourcefile" value="${ooxml.xalan-serializer.url}"/> | |||
<param name="destfile" value="${ooxml.xalan-serializer.jar}"/> | |||
</antcall> | |||
<antcall target="downloadfile"> | |||
<param name="sourcefile" value="${ooxml.bcprov.url}"/> | |||
<param name="destfile" value="${ooxml.bcprov.jar}"/> | |||
</antcall> | |||
</target> | |||
<target name="check-ooxml-xsds"> | |||
@@ -475,7 +428,7 @@ under the License. | |||
</xmlbean> | |||
</target> | |||
<target name="compile" depends="init, compile-main, | |||
<target name="compile" depends="init, compile-main, | |||
compile-scratchpad, compile-contrib, compile-examples, compile-scratchpad-examples" | |||
description="Compiles the POI main classes, scratchpad, contrib, examples, and scratchpad examples"/> | |||
@@ -598,7 +551,7 @@ under the License. | |||
<!-- Generate the .java file --> | |||
<property name="version.java" value="${main.output.dir}/org/apache/poi/Version.java" /> | |||
<delete file="${version.java}" /> | |||
<copy | |||
<copy | |||
file="src/resources/version/Version.java.template" | |||
tofile="${version.java}"> | |||
<filterset> | |||
@@ -631,9 +584,9 @@ under the License. | |||
<junit fork="yes" forkmode="once" printsummary="yes" haltonfailure="${halt.on.test.failure}" | |||
failureproperty="main.test.failed" showoutput="true"> | |||
<classpath refid="test.classpath"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain"/> | |||
<formatter type="xml"/> | |||
@@ -662,9 +615,9 @@ under the License. | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${junit.jar1.dir}"/> | |||
</classpath> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain" usefile="no"/> | |||
<batchtest todir="${main.reports.test}"> | |||
@@ -674,7 +627,7 @@ under the License. | |||
</fileset> | |||
</batchtest> | |||
</junit> | |||
</target> | |||
</target> | |||
@@ -688,9 +641,9 @@ under the License. | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${junit.jar1.dir}"/> | |||
</classpath> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain" usefile="no"/> | |||
<formatter type="xml"/> | |||
@@ -724,9 +677,9 @@ under the License. | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${junit.jar1.dir}"/> | |||
</classpath> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain"/> | |||
<formatter type="xml"/> | |||
@@ -755,9 +708,9 @@ under the License. | |||
<pathelement location="${scratchpad.output.test.dir}"/> | |||
<pathelement location="${junit.jar1.dir}"/> | |||
</classpath> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain" usefile="no"/> | |||
@@ -765,7 +718,7 @@ under the License. | |||
<test name="${testcase}"/> | |||
</junit> | |||
</target> | |||
<target name="-test-contrib-check"> | |||
<uptodate property="contrib.test.notRequired" targetfile="${contrib.testokfile}"> | |||
<srcfiles dir="${contrib.src}"/> | |||
@@ -782,8 +735,8 @@ under the License. | |||
<pathelement location="${contrib.output.test.dir}"/> | |||
<pathelement location="${junit.jar1.dir}"/> | |||
</classpath> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain"/> | |||
<formatter type="xml"/> | |||
@@ -812,9 +765,9 @@ under the License. | |||
<target name="test-ooxml" depends="compile-main,compile-ooxml,-test-ooxml-check" unless="ooxml.test.notRequired"> | |||
<junit printsummary="yes" fork="yes" forkmode="once" haltonfailure="${halt.on.test.failure}" failureproperty="ooxml.test.failed"> | |||
<classpath refid="test.ooxml.classpath" /> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain"/> | |||
<formatter type="xml"/> | |||
@@ -836,9 +789,9 @@ under the License. | |||
<target name="single-test-ooxml" depends="-test-property-check,compile-main,compile-ooxml" description="Runs a single ooxml test case specified with -Dtestcase=classname"> | |||
<junit printsummary="yes" showoutput="true" filtertrace="no" haltonfailure="false" > | |||
<classpath refid="test.ooxml.classpath" /> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="user.language" value="en"/> | |||
<sysproperty key="user.country" value="US"/> | |||
<sysproperty key="POI.testdata.path" file="${poi.test.dir}"/> | |||
<sysproperty key="java.awt.headless" value="true"/> | |||
<formatter type="plain" usefile="no"/> | |||
<formatter type="xml"/> | |||
@@ -1087,7 +1040,7 @@ FORREST_HOME environment variable!</echo> | |||
<attribute name="Implementation-Title" value="Apache POI"/> | |||
<attribute name="Implementation-Version" value="${version.id}-${DSTAMP}"/> | |||
<attribute name="Implementation-Vendor" value="Apache"/> | |||
</manifest> | |||
</manifest> | |||
</jar> | |||
<jar destfile="${dist.dir}/${jar.name}-contrib-${version.id}-${DSTAMP}.jar"> | |||
<fileset dir="${contrib.output.dir}" /> | |||
@@ -1100,7 +1053,7 @@ FORREST_HOME environment variable!</echo> | |||
<attribute name="Implementation-Title" value="Apache POI"/> | |||
<attribute name="Implementation-Version" value="${version.id}-${DSTAMP}"/> | |||
<attribute name="Implementation-Vendor" value="Apache"/> | |||
</manifest> | |||
</manifest> | |||
</jar> | |||
<jar destfile="${dist.dir}/${jar.name}-scratchpad-${version.id}-${DSTAMP}.jar"> | |||
<fileset dir="${scratchpad.output.dir}" /> | |||
@@ -1113,7 +1066,7 @@ FORREST_HOME environment variable!</echo> | |||
<attribute name="Implementation-Title" value="Apache POI"/> | |||
<attribute name="Implementation-Version" value="${version.id}-${DSTAMP}"/> | |||
<attribute name="Implementation-Vendor" value="Apache"/> | |||
</manifest> | |||
</manifest> | |||
</jar> | |||
<jar destfile="${dist.dir}/${jar.name}-ooxml-${version.id}-${DSTAMP}.jar"> | |||
<fileset dir="${ooxml.output.dir}" /> | |||
@@ -1126,7 +1079,7 @@ FORREST_HOME environment variable!</echo> | |||
<attribute name="Implementation-Title" value="Apache POI"/> | |||
<attribute name="Implementation-Version" value="${version.id}-${DSTAMP}"/> | |||
<attribute name="Implementation-Vendor" value="Apache"/> | |||
</manifest> | |||
</manifest> | |||
</jar> | |||
</target> | |||
<target name="jar-examples" depends="compile, compile-version" description="Creates a jar file of the examples, in case people want to use them as-is"> | |||
@@ -1141,7 +1094,7 @@ FORREST_HOME environment variable!</echo> | |||
<attribute name="Implementation-Title" value="Apache POI"/> | |||
<attribute name="Implementation-Version" value="${version.id}-${DSTAMP}"/> | |||
<attribute name="Implementation-Vendor" value="Apache"/> | |||
</manifest> | |||
</manifest> | |||
</jar> | |||
</target> | |||
@@ -1244,7 +1197,7 @@ FORREST_HOME environment variable!</echo> | |||
JDepend is not available. You must download JDepend from | |||
<http://www.clarkware.com/software/JDepend.html> and include the | |||
JAR file in your classpath. | |||
</echo> | |||
</echo> | |||
<fail message="JDepend is not available."/> | |||
</target> | |||
@@ -19,6 +19,3 @@ This product contains the Piccolo XML Parser for Java | |||
This product contains the chunks_parse_cmds.tbl file from the vsdump program. | |||
Copyright (C) 2006-2007 Valek Filippov (frob@df.ru) | |||
This product contains parts that were originally based on the eID Applet project | |||
(http://code.google.com/p/eid-applet/). Copyright (C) 2008-2009 FedICT. |
@@ -1,610 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
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.cert.X509Certificate; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
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.Transform; | |||
import javax.xml.crypto.dsig.XMLObject; | |||
import javax.xml.crypto.dsig.XMLSignContext; | |||
import javax.xml.crypto.dsig.XMLSignatureException; | |||
import javax.xml.crypto.dsig.XMLSignatureFactory; | |||
import javax.xml.crypto.dsig.dom.DOMSignContext; | |||
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; | |||
import javax.xml.crypto.dsig.spec.TransformParameterSpec; | |||
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.TransformerConfigurationException; | |||
import javax.xml.transform.TransformerException; | |||
import javax.xml.transform.TransformerFactory; | |||
import javax.xml.transform.TransformerFactoryConfigurationError; | |||
import javax.xml.transform.dom.DOMSource; | |||
import javax.xml.transform.stream.StreamResult; | |||
import org.apache.commons.io.FilenameUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.ooxml.signature.service.spi.DigestInfo; | |||
import org.apache.poi.ooxml.signature.service.spi.SignatureService; | |||
import org.apache.xml.security.signature.XMLSignature; | |||
import org.apache.xml.security.utils.Base64; | |||
import org.apache.xml.security.utils.Constants; | |||
import org.apache.xpath.XPathAPI; | |||
import org.jcp.xml.dsig.internal.dom.DOMReference; | |||
import org.jcp.xml.dsig.internal.dom.DOMSignedInfo; | |||
import org.jcp.xml.dsig.internal.dom.DOMXMLSignature; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
/** | |||
* Abstract base class for an XML Signature Service implementation. | |||
*/ | |||
public abstract class AbstractXmlSignatureService implements SignatureService { | |||
static final Log LOG = LogFactory.getLog(AbstractXmlSignatureService.class); | |||
private static final String SIGNATURE_ID_ATTRIBUTE = "signature-id"; | |||
// TODO refactor everything using the signature aspect design pattern | |||
private final List<SignatureAspect> signatureAspects; | |||
/** | |||
* Main constructor. | |||
*/ | |||
public AbstractXmlSignatureService() { | |||
this.signatureAspects = new LinkedList<SignatureAspect>(); | |||
} | |||
/** | |||
* Adds a signature aspect to this XML signature service. | |||
* | |||
* @param signatureAspect | |||
*/ | |||
protected void addSignatureAspect(SignatureAspect signatureAspect) { | |||
this.signatureAspects.add(signatureAspect); | |||
} | |||
/** | |||
* 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 String getSignatureDigestAlgorithm() { | |||
return "SHA-1"; | |||
} | |||
/** | |||
* Gives back a list of service digest infos. Override this method to | |||
* provide digest infos of files located in the service itself. | |||
* | |||
* @return | |||
*/ | |||
protected List<DigestInfo> getServiceDigestInfos() { | |||
return new LinkedList<DigestInfo>(); | |||
} | |||
/** | |||
* Gives back the enveloping document. Return <code>null</code> in case | |||
* ds:Signature should be the top-level element. Implementations can | |||
* override this method to provide a custom enveloping document. | |||
* | |||
* @return | |||
* @throws SAXException | |||
* @throws IOException | |||
*/ | |||
protected Document getEnvelopingDocument() throws ParserConfigurationException, IOException, SAXException { | |||
return null; | |||
} | |||
/** | |||
* Gives back a list of reference URIs that need to be signed. These URIs | |||
* can refer to elements inside the enveloping document or to external | |||
* resources. Override this method to feed in other ds:Reference URIs. | |||
* | |||
* @return | |||
*/ | |||
protected List<String> getReferenceUris() { | |||
return new LinkedList<String>(); | |||
} | |||
public static class ReferenceInfo { | |||
private final String uri; | |||
private final String transform; | |||
public ReferenceInfo(String uri, String transform) { | |||
this.uri = uri; | |||
this.transform = transform; | |||
} | |||
public ReferenceInfo(String uri) { | |||
this(uri, null); | |||
} | |||
public String getUri() { | |||
return this.uri; | |||
} | |||
public String getTransform() { | |||
return this.transform; | |||
} | |||
} | |||
/** | |||
* Gives back a list of references that need to be signed. Implementation | |||
* can override this method. | |||
* | |||
* @return | |||
*/ | |||
protected List<ReferenceInfo> getReferences() { | |||
return new LinkedList<ReferenceInfo>(); | |||
} | |||
/** | |||
* Override this method to change the URI dereferener used by the signing | |||
* engine. | |||
* | |||
* @return | |||
*/ | |||
protected URIDereferencer getURIDereferencer() { | |||
return null; | |||
} | |||
/** | |||
* Gives back the human-readable description of what the citizen will be | |||
* signing. The default value is "XML Signature". Override this method to | |||
* provide the citizen with another description. | |||
* | |||
* @return | |||
*/ | |||
protected String getSignatureDescription() { | |||
return "XML Signature"; | |||
} | |||
/** | |||
* Gives back a temporary data storage component. This component is used for | |||
* temporary storage of the XML signature documents. | |||
* | |||
* @return | |||
*/ | |||
protected abstract TemporaryDataStorage getTemporaryDataStorage(); | |||
/** | |||
* 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) throws NoSuchAlgorithmException { | |||
LOG.debug("preSign"); | |||
String digestAlgo = getSignatureDigestAlgorithm(); | |||
byte[] digestValue; | |||
try { | |||
digestValue = getXmlSignatureDigestValue(digestAlgo, digestInfos); | |||
} catch (Exception e) { | |||
throw new RuntimeException("XML signature error: " + e.getMessage(), e); | |||
} | |||
String description = getSignatureDescription(); | |||
return new DigestInfo(digestValue, digestAlgo, description); | |||
} | |||
/** | |||
* Can be overridden by XML signature service implementation to further | |||
* process the signed XML document. | |||
* | |||
* @param sinatureElement | |||
* @param signingCertificateChain | |||
*/ | |||
protected void postSign(Element sinatureElement, List<X509Certificate> signingCertificateChain) { | |||
// empty | |||
} | |||
public void postSign(byte[] signatureValue, List<X509Certificate> signingCertificateChain) { | |||
LOG.debug("postSign"); | |||
/* | |||
* Retrieve the intermediate XML signature document from the temporary | |||
* data storage. | |||
*/ | |||
TemporaryDataStorage temporaryDataStorage = getTemporaryDataStorage(); | |||
InputStream documentInputStream = temporaryDataStorage.getTempInputStream(); | |||
String signatureId = (String) temporaryDataStorage.getAttribute(SIGNATURE_ID_ATTRIBUTE); | |||
LOG.debug("signature Id: " + signatureId); | |||
/* | |||
* Load the signature DOM document. | |||
*/ | |||
Document document; | |||
try { | |||
document = loadDocument(documentInputStream); | |||
} catch (Exception e) { | |||
throw new RuntimeException("DOM error: " + e.getMessage(), e); | |||
} | |||
/* | |||
* Locate the correct ds:Signature node. | |||
*/ | |||
Element nsElement = document.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
Element signatureElement; | |||
try { | |||
signatureElement = (Element) XPathAPI.selectSingleNode(document, "//ds:Signature[@Id='" + signatureId + "']", nsElement); | |||
} catch (TransformerException e) { | |||
throw new RuntimeException("XPATH error: " + e.getMessage(), e); | |||
} | |||
if (null == signatureElement) { | |||
throw new RuntimeException("ds:Signature not found for @Id: " + signatureId); | |||
} | |||
/* | |||
* Insert signature value into the ds:SignatureValue element | |||
*/ | |||
NodeList signatureValueNodeList = signatureElement.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "SignatureValue"); | |||
Element signatureValueElement = (Element) signatureValueNodeList.item(0); | |||
signatureValueElement.setTextContent(Base64.encode(signatureValue)); | |||
/* | |||
* Allow implementation classes to inject their own stuff. | |||
*/ | |||
postSign(signatureElement, signingCertificateChain); | |||
OutputStream signedDocumentOutputStream = getSignedDocumentOutputStream(); | |||
if (null == signedDocumentOutputStream) { | |||
throw new IllegalArgumentException("signed document output stream is null"); | |||
} | |||
try { | |||
writeDocument(document, signedDocumentOutputStream); | |||
} catch (Exception e) { | |||
LOG.debug("error writing the signed XML document: " + e.getMessage(), e); | |||
throw new RuntimeException("error writing the signed XML document: " + e.getMessage(), e); | |||
} | |||
} | |||
protected String getCanonicalizationMethod() { | |||
// CanonicalizationMethod.INCLUSIVE fails for OOo | |||
return CanonicalizationMethod.EXCLUSIVE; | |||
} | |||
private byte[] getXmlSignatureDigestValue(String digestAlgo, List<DigestInfo> digestInfos) throws ParserConfigurationException, NoSuchAlgorithmException, | |||
InvalidAlgorithmParameterException, MarshalException, javax.xml.crypto.dsig.XMLSignatureException, | |||
TransformerFactoryConfigurationError, TransformerException, IOException, SAXException { | |||
/* | |||
* DOM Document construction. | |||
*/ | |||
Document document = getEnvelopingDocument(); | |||
if (null == document) { | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
document = documentBuilder.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; | |||
} | |||
}; | |||
XMLSignContext xmlSignContext = new DOMSignContext(key, document); | |||
URIDereferencer uriDereferencer = getURIDereferencer(); | |||
if (null != uriDereferencer) { | |||
xmlSignContext.setURIDereferencer(uriDereferencer); | |||
} | |||
// OOo doesn't like ds namespaces. | |||
// xmlSignContext.putNamespacePrefix( | |||
// javax.xml.crypto.dsig.XMLSignature.XMLNS, "ds"); | |||
XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance("DOM", new org.jcp.xml.dsig.internal.dom.XMLDSigRI()); | |||
/* | |||
* ds:Reference | |||
*/ | |||
List<Reference> references = new LinkedList<Reference>(); | |||
addDigestInfosAsReferences(digestInfos, signatureFactory, references); | |||
List<DigestInfo> serviceDigestInfos = getServiceDigestInfos(); | |||
addDigestInfosAsReferences(serviceDigestInfos, signatureFactory, references); | |||
addReferenceIds(signatureFactory, xmlSignContext, references); | |||
addReferences(signatureFactory, references); | |||
/* | |||
* Invoke the signature aspects. | |||
*/ | |||
String signatureId = "xmldsig-" + UUID.randomUUID().toString(); | |||
List<XMLObject> objects = new LinkedList<XMLObject>(); | |||
for (SignatureAspect signatureAspect : this.signatureAspects) { | |||
LOG.debug("invoking signature aspect: " + signatureAspect.getClass().getSimpleName()); | |||
signatureAspect.preSign(signatureFactory, document, signatureId, references, objects); | |||
} | |||
/* | |||
* ds:SignedInfo | |||
*/ | |||
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(getSignatureMethod(digestAlgo), null); | |||
CanonicalizationMethod canonicalizationMethod = signatureFactory.newCanonicalizationMethod(getCanonicalizationMethod(), (C14NMethodParameterSpec) null); | |||
SignedInfo signedInfo = signatureFactory.newSignedInfo(canonicalizationMethod, signatureMethod, references); | |||
/* | |||
* JSR105 ds:Signature creation | |||
*/ | |||
String signatureValueId = signatureId + "-signature-value"; | |||
javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory.newXMLSignature(signedInfo, null, objects, signatureId, signatureValueId); | |||
/* | |||
* ds:Signature Marshalling. | |||
*/ | |||
DOMXMLSignature domXmlSignature = (DOMXMLSignature) xmlSignature; | |||
Node documentNode = document.getDocumentElement(); | |||
if (null == documentNode) { | |||
/* | |||
* In case of an empty DOM document. | |||
*/ | |||
documentNode = document; | |||
} | |||
String dsPrefix = null; | |||
// String dsPrefix = "ds"; | |||
domXmlSignature.marshal(documentNode, dsPrefix, (DOMCryptoContext) xmlSignContext); | |||
/* | |||
* Completion of undigested ds:References in the ds:Manifests. | |||
*/ | |||
for (XMLObject object : objects) { | |||
LOG.debug("object java type: " + object.getClass().getName()); | |||
List<XMLStructure> objectContentList = object.getContent(); | |||
for (XMLStructure objectContent : objectContentList) { | |||
LOG.debug("object content java type: " + objectContent.getClass().getName()); | |||
if (false == objectContent instanceof Manifest) { | |||
continue; | |||
} | |||
Manifest manifest = (Manifest) objectContent; | |||
List<Reference> manifestReferences = manifest.getReferences(); | |||
for (Reference manifestReference : manifestReferences) { | |||
if (null != manifestReference.getDigestValue()) { | |||
continue; | |||
} | |||
DOMReference manifestDOMReference = (DOMReference) manifestReference; | |||
manifestDOMReference.digest(xmlSignContext); | |||
} | |||
} | |||
} | |||
/* | |||
* Completion of undigested ds:References. | |||
*/ | |||
List<Reference> signedInfoReferences = signedInfo.getReferences(); | |||
for (Reference signedInfoReference : signedInfoReferences) { | |||
DOMReference domReference = (DOMReference) signedInfoReference; | |||
if (null != domReference.getDigestValue()) { | |||
// ds:Reference with external digest value | |||
continue; | |||
} | |||
domReference.digest(xmlSignContext); | |||
} | |||
/* | |||
* Store the intermediate XML signature document. | |||
*/ | |||
TemporaryDataStorage temporaryDataStorage = getTemporaryDataStorage(); | |||
OutputStream tempDocumentOutputStream = temporaryDataStorage.getTempOutputStream(); | |||
writeDocument(document, tempDocumentOutputStream); | |||
temporaryDataStorage.setAttribute(SIGNATURE_ID_ATTRIBUTE, signatureId); | |||
/* | |||
* Calculation of XML signature digest value. | |||
*/ | |||
DOMSignedInfo domSignedInfo = (DOMSignedInfo) signedInfo; | |||
ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); | |||
domSignedInfo.canonicalize(xmlSignContext, dataStream); | |||
byte[] octets = dataStream.toByteArray(); | |||
/* | |||
* TODO: we could be using DigestOutputStream here to optimize memory | |||
* usage. | |||
*/ | |||
MessageDigest jcaMessageDigest = MessageDigest.getInstance(digestAlgo); | |||
byte[] digestValue = jcaMessageDigest.digest(octets); | |||
return digestValue; | |||
} | |||
private void addReferenceIds(XMLSignatureFactory signatureFactory, XMLSignContext xmlSignContext, List<Reference> references) | |||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, XMLSignatureException { | |||
List<String> referenceUris = getReferenceUris(); | |||
if (null == referenceUris) { | |||
return; | |||
} | |||
DigestMethod digestMethod = signatureFactory.newDigestMethod(DigestMethod.SHA1, null); | |||
for (String referenceUri : referenceUris) { | |||
Reference reference = signatureFactory.newReference(referenceUri, digestMethod); | |||
references.add(reference); | |||
} | |||
} | |||
private void addReferences(XMLSignatureFactory xmlSignatureFactory, List<Reference> references) throws NoSuchAlgorithmException, | |||
InvalidAlgorithmParameterException { | |||
List<ReferenceInfo> referenceInfos = getReferences(); | |||
if (null == referenceInfos) { | |||
return; | |||
} | |||
if (referenceInfos.isEmpty()) { | |||
return; | |||
} | |||
DigestMethod digestMethod = xmlSignatureFactory.newDigestMethod(DigestMethod.SHA1, null); | |||
for (ReferenceInfo referenceInfo : referenceInfos) { | |||
List<Transform> transforms = new LinkedList<Transform>(); | |||
if (null != referenceInfo.getTransform()) { | |||
Transform transform = xmlSignatureFactory.newTransform(referenceInfo.getTransform(), (TransformParameterSpec) null); | |||
transforms.add(transform); | |||
} | |||
LOG.debug("adding ds:Reference " + referenceInfo.getUri()); | |||
Reference reference = xmlSignatureFactory.newReference(referenceInfo.getUri(), digestMethod, transforms, null, null); | |||
references.add(reference); | |||
} | |||
} | |||
private void addDigestInfosAsReferences(List<DigestInfo> digestInfos, XMLSignatureFactory signatureFactory, List<Reference> references) | |||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, MalformedURLException { | |||
if (null == digestInfos) { | |||
return; | |||
} | |||
for (DigestInfo digestInfo : digestInfos) { | |||
byte[] documentDigestValue = digestInfo.digestValue; | |||
DigestMethod digestMethod = signatureFactory.newDigestMethod(getXmlDigestAlgo(digestInfo.digestAlgo), null); | |||
String uri = FilenameUtils.getName(new File(digestInfo.description).toURI().toURL().getFile()); | |||
Reference reference = signatureFactory.newReference(uri, digestMethod, null, null, null, documentDigestValue); | |||
references.add(reference); | |||
} | |||
} | |||
private String getXmlDigestAlgo(String digestAlgo) { | |||
if ("SHA-1".equals(digestAlgo)) { | |||
return DigestMethod.SHA1; | |||
} | |||
if ("SHA-256".equals(digestAlgo)) { | |||
return DigestMethod.SHA256; | |||
} | |||
if ("SHA-512".equals(digestAlgo)) { | |||
return DigestMethod.SHA512; | |||
} | |||
throw new RuntimeException("unsupported digest algo: " + digestAlgo); | |||
} | |||
private String getSignatureMethod(String digestAlgo) { | |||
if (null == digestAlgo) { | |||
throw new RuntimeException("digest algo is null"); | |||
} | |||
if ("SHA-1".equals(digestAlgo)) { | |||
return SignatureMethod.RSA_SHA1; | |||
} | |||
if ("SHA-256".equals(digestAlgo)) { | |||
return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256; | |||
} | |||
if ("SHA-512".equals(digestAlgo)) { | |||
return XMLSignature.ALGO_ID_MAC_HMAC_SHA512; | |||
} | |||
if ("SHA-384".equals(digestAlgo)) { | |||
return XMLSignature.ALGO_ID_MAC_HMAC_SHA384; | |||
} | |||
if ("RIPEMD160".equals(digestAlgo)) { | |||
return XMLSignature.ALGO_ID_MAC_HMAC_RIPEMD160; | |||
} | |||
throw new RuntimeException("unsupported sign algo: " + digestAlgo); | |||
} | |||
protected void writeDocument(Document document, OutputStream documentOutputStream) throws TransformerConfigurationException, | |||
TransformerFactoryConfigurationError, TransformerException, IOException { | |||
writeDocumentNoClosing(document, documentOutputStream); | |||
documentOutputStream.close(); | |||
} | |||
protected void writeDocumentNoClosing(Document document, OutputStream documentOutputStream) throws TransformerConfigurationException, | |||
TransformerFactoryConfigurationError, TransformerException, IOException { | |||
// we need the XML processing initial line for OOXML | |||
writeDocumentNoClosing(document, documentOutputStream, false); | |||
} | |||
protected void writeDocumentNoClosing(Document document, OutputStream documentOutputStream, boolean omitXmlDeclaration) | |||
throws TransformerConfigurationException, TransformerFactoryConfigurationError, TransformerException, IOException { | |||
NoCloseOutputStream outputStream = new NoCloseOutputStream(documentOutputStream); | |||
Result result = new StreamResult(outputStream); | |||
Transformer xformer = TransformerFactory.newInstance().newTransformer(); | |||
if (omitXmlDeclaration) { | |||
xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); | |||
} | |||
Source source = new DOMSource(document); | |||
xformer.transform(source, result); | |||
} | |||
protected 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; | |||
} | |||
protected Document loadDocumentNoClose(InputStream documentInputStream) throws ParserConfigurationException, SAXException, IOException { | |||
NoCloseInputStream noCloseInputStream = new NoCloseInputStream(documentInputStream); | |||
InputSource inputSource = new InputSource(noCloseInputStream); | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.parse(inputSource); | |||
return document; | |||
} | |||
} |
@@ -1,99 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
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.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
/** | |||
* JSR105 key selector implementation using the ds:KeyInfo data of the signature | |||
* itself. | |||
*/ | |||
public class KeyInfoKeySelector extends KeySelector implements KeySelectorResult { | |||
private static final Log LOG = LogFactory.getLog(KeyInfoKeySelector.class); | |||
private X509Certificate certificate; | |||
@Override | |||
public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException { | |||
LOG.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.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; | |||
} | |||
} |
@@ -1,53 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import org.apache.commons.io.input.ProxyInputStream; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
/** | |||
* Input Stream proxy that doesn't close the underlying input stream. | |||
*/ | |||
public class NoCloseInputStream extends ProxyInputStream { | |||
private static final Log LOG = LogFactory.getLog(NoCloseInputStream.class); | |||
/** | |||
* Main constructor. | |||
* | |||
* @param proxy | |||
*/ | |||
public NoCloseInputStream(InputStream proxy) { | |||
super(proxy); | |||
} | |||
@Override | |||
public void close() throws IOException { | |||
LOG.debug("close"); | |||
} | |||
} |
@@ -1,54 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import org.apache.commons.io.output.ProxyOutputStream; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
/** | |||
* Output Stream proxy that doesn't close the underlying stream. | |||
*/ | |||
public class NoCloseOutputStream extends ProxyOutputStream { | |||
private static final Log LOG = LogFactory.getLog(NoCloseOutputStream.class); | |||
/** | |||
* Main constructor. | |||
* | |||
* @param proxy | |||
*/ | |||
public NoCloseOutputStream(OutputStream proxy) { | |||
super(proxy); | |||
} | |||
@Override | |||
public void close() throws IOException { | |||
LOG.debug("close"); | |||
// empty | |||
} | |||
} |
@@ -1,56 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.security.InvalidAlgorithmParameterException; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.util.List; | |||
import javax.xml.crypto.dsig.Reference; | |||
import javax.xml.crypto.dsig.XMLObject; | |||
import javax.xml.crypto.dsig.XMLSignatureFactory; | |||
import org.w3c.dom.Document; | |||
/** | |||
* JSR105 Signature Aspect interface. | |||
*/ | |||
public interface SignatureAspect { | |||
/** | |||
* This method is being invoked by the XML signature service engine during | |||
* pre-sign phase. Via this method a signature aspect implementation can add | |||
* signature aspects to an XML signature. | |||
* | |||
* @param signatureFactory | |||
* @param document | |||
* @param signatureId | |||
* @param references | |||
* @param objects | |||
* @throws InvalidAlgorithmParameterException | |||
* @throws NoSuchAlgorithmException | |||
*/ | |||
void preSign(XMLSignatureFactory signatureFactory, Document document, String signatureId, List<Reference> references, List<XMLObject> objects) | |||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException; | |||
} |
@@ -1,65 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.Serializable; | |||
/** | |||
* Interface for temporary data storage. | |||
*/ | |||
public interface TemporaryDataStorage { | |||
/** | |||
* Gives back the temporary output stream that can be used for data storage. | |||
* | |||
* @return | |||
*/ | |||
OutputStream getTempOutputStream(); | |||
/** | |||
* Gives back the temporary input stream for retrieval of the previously | |||
* stored data. | |||
* | |||
* @return | |||
*/ | |||
InputStream getTempInputStream(); | |||
/** | |||
* Stores an attribute to the temporary data storage. | |||
* | |||
* @param attributeName | |||
* @param attributeValue | |||
*/ | |||
void setAttribute(String attributeName, Serializable attributeValue); | |||
/** | |||
* Retrieves an attribute from the temporary data storage. | |||
* | |||
* @param attributeName | |||
* @return | |||
*/ | |||
Serializable getAttribute(String attributeName); | |||
} |
@@ -1,343 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.OutputStream; | |||
import java.net.URL; | |||
import java.security.Key; | |||
import java.security.KeyException; | |||
import java.security.cert.X509Certificate; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.UUID; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipInputStream; | |||
import java.util.zip.ZipOutputStream; | |||
import javax.xml.crypto.MarshalException; | |||
import javax.xml.crypto.URIDereferencer; | |||
import javax.xml.crypto.dom.DOMCryptoContext; | |||
import javax.xml.crypto.dsig.CanonicalizationMethod; | |||
import javax.xml.crypto.dsig.XMLSignContext; | |||
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 javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import javax.xml.transform.TransformerConfigurationException; | |||
import javax.xml.transform.TransformerException; | |||
import javax.xml.transform.TransformerFactoryConfigurationError; | |||
import org.apache.commons.io.FilenameUtils; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.ooxml.signature.service.signer.AbstractXmlSignatureService; | |||
import org.apache.xml.security.utils.Constants; | |||
import org.apache.xpath.XPathAPI; | |||
import org.jcp.xml.dsig.internal.dom.DOMKeyInfo; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.xml.sax.SAXException; | |||
/** | |||
* Signature Service implementation for Office OpenXML document format XML | |||
* signatures. | |||
*/ | |||
public abstract class AbstractOOXMLSignatureService extends AbstractXmlSignatureService { | |||
static final Log LOG = LogFactory.getLog(AbstractOOXMLSignatureService.class); | |||
protected AbstractOOXMLSignatureService() { | |||
addSignatureAspect(new OOXMLSignatureAspect(this)); | |||
} | |||
@Override | |||
protected String getSignatureDescription() { | |||
return "Office OpenXML Document"; | |||
} | |||
public String getFilesDigestAlgorithm() { | |||
return null; | |||
} | |||
@Override | |||
protected final URIDereferencer getURIDereferencer() { | |||
URL ooxmlUrl = getOfficeOpenXMLDocumentURL(); | |||
return new OOXMLURIDereferencer(ooxmlUrl); | |||
} | |||
@Override | |||
protected String getCanonicalizationMethod() { | |||
return CanonicalizationMethod.INCLUSIVE; | |||
} | |||
@Override | |||
protected void postSign(Element signatureElement, List<X509Certificate> signingCertificateChain) { | |||
// TODO: implement as SignatureAspect | |||
LOG.debug("postSign: adding ds:KeyInfo"); | |||
/* | |||
* Make sure we insert right after the ds:SignatureValue element. | |||
*/ | |||
Node nextSibling; | |||
NodeList objectNodeList = signatureElement.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Object"); | |||
if (0 == objectNodeList.getLength()) { | |||
nextSibling = null; | |||
} else { | |||
nextSibling = objectNodeList.item(0); | |||
} | |||
/* | |||
* Add a ds:KeyInfo entry. | |||
*/ | |||
KeyInfoFactory keyInfoFactory = CryptoFactoryFactory.getKeyInfoFactory(); | |||
List<Object> x509DataObjects = new LinkedList<Object>(); | |||
X509Certificate signingCertificate = signingCertificateChain.get(0); | |||
KeyValue keyValue; | |||
try { | |||
keyValue = keyInfoFactory.newKeyValue(signingCertificate.getPublicKey()); | |||
} catch (KeyException e) { | |||
throw new RuntimeException("key exception: " + e.getMessage(), e); | |||
} | |||
for (X509Certificate certificate : signingCertificateChain) { | |||
x509DataObjects.add(certificate); | |||
} | |||
X509Data x509Data = keyInfoFactory.newX509Data(x509DataObjects); | |||
List<Object> keyInfoContent = new LinkedList<Object>(); | |||
keyInfoContent.add(keyValue); | |||
keyInfoContent.add(x509Data); | |||
KeyInfo keyInfo = keyInfoFactory.newKeyInfo(keyInfoContent); | |||
DOMKeyInfo domKeyInfo = (DOMKeyInfo) keyInfo; | |||
Key key = new Key() { | |||
private static final long serialVersionUID = 1L; | |||
public String getAlgorithm() { | |||
return null; | |||
} | |||
public byte[] getEncoded() { | |||
return null; | |||
} | |||
public String getFormat() { | |||
return null; | |||
} | |||
}; | |||
XMLSignContext xmlSignContext = new DOMSignContext(key, signatureElement); | |||
DOMCryptoContext domCryptoContext = (DOMCryptoContext) xmlSignContext; | |||
String dsPrefix = null; | |||
// String dsPrefix = "ds"; | |||
try { | |||
domKeyInfo.marshal(signatureElement, nextSibling, dsPrefix, domCryptoContext); | |||
} catch (MarshalException e) { | |||
throw new RuntimeException("marshall error: " + e.getMessage(), e); | |||
} | |||
} | |||
private class OOXMLSignedDocumentOutputStream extends ByteArrayOutputStream { | |||
@Override | |||
public void close() throws IOException { | |||
LOG.debug("close OOXML signed document output stream"); | |||
super.close(); | |||
try { | |||
outputSignedOfficeOpenXMLDocument(this.toByteArray()); | |||
} catch (Exception e) { | |||
throw new IOException(e.getMessage()); | |||
} | |||
} | |||
} | |||
/** | |||
* @return The output stream to which to write the signed Office OpenXML file. | |||
*/ | |||
abstract protected OutputStream getSignedOfficeOpenXMLDocumentOutputStream(); | |||
/** | |||
* @return the URL of the OOXML to be signed. | |||
*/ | |||
abstract protected URL getOfficeOpenXMLDocumentURL(); | |||
private void outputSignedOfficeOpenXMLDocument(byte[] signatureData) throws IOException, ParserConfigurationException, SAXException, TransformerException { | |||
LOG.debug("output signed Office OpenXML document"); | |||
OutputStream signedOOXMLOutputStream = getSignedOfficeOpenXMLDocumentOutputStream(); | |||
if (null == signedOOXMLOutputStream) { | |||
throw new NullPointerException("signedOOXMLOutputStream is null"); | |||
} | |||
String signatureZipEntryName = "_xmlsignatures/sig-" + UUID.randomUUID().toString() + ".xml"; | |||
LOG.debug("signature ZIP entry name: " + signatureZipEntryName); | |||
/* | |||
* Copy the original OOXML content to the signed OOXML package. During | |||
* copying some files need to changed. | |||
*/ | |||
ZipOutputStream zipOutputStream = copyOOXMLContent(signatureZipEntryName, signedOOXMLOutputStream); | |||
/* | |||
* Add the OOXML XML signature file to the OOXML package. | |||
*/ | |||
ZipEntry zipEntry = new ZipEntry(signatureZipEntryName); | |||
zipOutputStream.putNextEntry(zipEntry); | |||
IOUtils.write(signatureData, zipOutputStream); | |||
zipOutputStream.close(); | |||
} | |||
private ZipOutputStream copyOOXMLContent(String signatureZipEntryName, OutputStream signedOOXMLOutputStream) throws IOException, | |||
ParserConfigurationException, SAXException, TransformerConfigurationException, TransformerFactoryConfigurationError, | |||
TransformerException { | |||
ZipOutputStream zipOutputStream = new ZipOutputStream(signedOOXMLOutputStream); | |||
ZipInputStream zipInputStream = new ZipInputStream(this.getOfficeOpenXMLDocumentURL().openStream()); | |||
ZipEntry zipEntry; | |||
boolean hasOriginSigsRels = false; | |||
while (null != (zipEntry = zipInputStream.getNextEntry())) { | |||
LOG.debug("copy ZIP entry: " + zipEntry.getName()); | |||
ZipEntry newZipEntry = new ZipEntry(zipEntry.getName()); | |||
zipOutputStream.putNextEntry(newZipEntry); | |||
if ("[Content_Types].xml".equals(zipEntry.getName())) { | |||
Document contentTypesDocument = loadDocumentNoClose(zipInputStream); | |||
Element typesElement = contentTypesDocument.getDocumentElement(); | |||
/* | |||
* We need to add an Override element. | |||
*/ | |||
Element overrideElement = contentTypesDocument.createElementNS("http://schemas.openxmlformats.org/package/2006/content-types", "Override"); | |||
overrideElement.setAttribute("PartName", "/" + signatureZipEntryName); | |||
overrideElement.setAttribute("ContentType", "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"); | |||
typesElement.appendChild(overrideElement); | |||
Element nsElement = contentTypesDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "http://schemas.openxmlformats.org/package/2006/content-types"); | |||
NodeList nodeList = XPathAPI.selectNodeList(contentTypesDocument, "/tns:Types/tns:Default[@Extension='sigs']", nsElement); | |||
if (0 == nodeList.getLength()) { | |||
/* | |||
* Add Default element for 'sigs' extension. | |||
*/ | |||
Element defaultElement = contentTypesDocument.createElementNS("http://schemas.openxmlformats.org/package/2006/content-types", "Default"); | |||
defaultElement.setAttribute("Extension", "sigs"); | |||
defaultElement.setAttribute("ContentType", "application/vnd.openxmlformats-package.digital-signature-origin"); | |||
typesElement.appendChild(defaultElement); | |||
} | |||
writeDocumentNoClosing(contentTypesDocument, zipOutputStream, false); | |||
} else if ("_rels/.rels".equals(zipEntry.getName())) { | |||
Document relsDocument = loadDocumentNoClose(zipInputStream); | |||
Element nsElement = relsDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "http://schemas.openxmlformats.org/package/2006/relationships"); | |||
NodeList nodeList = XPathAPI.selectNodeList(relsDocument, "/tns:Relationships/tns:Relationship[@Target='_xmlsignatures/origin.sigs']", | |||
nsElement); | |||
if (0 == nodeList.getLength()) { | |||
Element relationshipElement = relsDocument.createElementNS("http://schemas.openxmlformats.org/package/2006/relationships", "Relationship"); | |||
relationshipElement.setAttribute("Id", "rel-id-" + UUID.randomUUID().toString()); | |||
relationshipElement.setAttribute("Type", "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin"); | |||
relationshipElement.setAttribute("Target", "_xmlsignatures/origin.sigs"); | |||
relsDocument.getDocumentElement().appendChild(relationshipElement); | |||
} | |||
writeDocumentNoClosing(relsDocument, zipOutputStream, false); | |||
} else if ("_xmlsignatures/_rels/origin.sigs.rels".equals(zipEntry.getName())) { | |||
hasOriginSigsRels = true; | |||
Document originSignRelsDocument = loadDocumentNoClose(zipInputStream); | |||
Element relationshipElement = originSignRelsDocument.createElementNS("http://schemas.openxmlformats.org/package/2006/relationships", | |||
"Relationship"); | |||
String relationshipId = "rel-" + UUID.randomUUID().toString(); | |||
relationshipElement.setAttribute("Id", relationshipId); | |||
relationshipElement.setAttribute("Type", "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature"); | |||
String target = FilenameUtils.getName(signatureZipEntryName); | |||
LOG.debug("target: " + target); | |||
relationshipElement.setAttribute("Target", target); | |||
originSignRelsDocument.getDocumentElement().appendChild(relationshipElement); | |||
writeDocumentNoClosing(originSignRelsDocument, zipOutputStream, false); | |||
} else { | |||
IOUtils.copy(zipInputStream, zipOutputStream); | |||
} | |||
} | |||
if (false == hasOriginSigsRels) { | |||
/* | |||
* Add signature relationships document. | |||
*/ | |||
addOriginSigsRels(signatureZipEntryName, zipOutputStream); | |||
addOriginSigs(zipOutputStream); | |||
} | |||
/* | |||
* Return. | |||
*/ | |||
zipInputStream.close(); | |||
return zipOutputStream; | |||
} | |||
private void addOriginSigs(ZipOutputStream zipOutputStream) throws IOException { | |||
zipOutputStream.putNextEntry(new ZipEntry("_xmlsignatures/origin.sigs")); | |||
} | |||
private void addOriginSigsRels(String signatureZipEntryName, ZipOutputStream zipOutputStream) throws ParserConfigurationException, IOException, | |||
TransformerConfigurationException, TransformerFactoryConfigurationError, TransformerException { | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document originSignRelsDocument = documentBuilder.newDocument(); | |||
Element relationshipsElement = originSignRelsDocument.createElementNS("http://schemas.openxmlformats.org/package/2006/relationships", "Relationships"); | |||
relationshipsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", "http://schemas.openxmlformats.org/package/2006/relationships"); | |||
originSignRelsDocument.appendChild(relationshipsElement); | |||
Element relationshipElement = originSignRelsDocument.createElementNS("http://schemas.openxmlformats.org/package/2006/relationships", "Relationship"); | |||
String relationshipId = "rel-" + UUID.randomUUID().toString(); | |||
relationshipElement.setAttribute("Id", relationshipId); | |||
relationshipElement.setAttribute("Type", "http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/signature"); | |||
String target = FilenameUtils.getName(signatureZipEntryName); | |||
LOG.debug("target: " + target); | |||
relationshipElement.setAttribute("Target", target); | |||
relationshipsElement.appendChild(relationshipElement); | |||
zipOutputStream.putNextEntry(new ZipEntry("_xmlsignatures/_rels/origin.sigs.rels")); | |||
writeDocumentNoClosing(originSignRelsDocument, zipOutputStream, false); | |||
} | |||
@Override | |||
protected OutputStream getSignedDocumentOutputStream() { | |||
LOG.debug("get signed document output stream"); | |||
/* | |||
* Create each time a new object; we want an empty output stream to | |||
* start with. | |||
*/ | |||
OutputStream signedDocumentOutputStream = new OOXMLSignedDocumentOutputStream(); | |||
return signedDocumentOutputStream; | |||
} | |||
} |
@@ -1,44 +0,0 @@ | |||
/* ==================================================================== | |||
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.ooxml.signature.service.signer.ooxml; | |||
import java.security.Provider; | |||
import javax.xml.crypto.dsig.XMLSignatureFactory; | |||
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; | |||
/** | |||
* Creates {@link XMLSignatureFactory} and {@link KeyInfoFactory} instances | |||
* as used by the ooxml signature service. | |||
*/ | |||
final class CryptoFactoryFactory { | |||
private static final Provider _provider = new org.jcp.xml.dsig.internal.dom.XMLDSigRI(); | |||
private CryptoFactoryFactory() { | |||
// no instances of this class | |||
} | |||
public static XMLSignatureFactory getSignatureFactory() { | |||
return XMLSignatureFactory.getInstance("DOM", _provider); | |||
} | |||
public static KeyInfoFactory getKeyInfoFactory() { | |||
return KeyInfoFactory.getInstance("DOM", _provider); | |||
} | |||
} |
@@ -1,54 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.security.Provider; | |||
import java.security.Security; | |||
/** | |||
* Security Provider for Office OpenXML. | |||
*/ | |||
public class OOXMLProvider extends Provider { | |||
private static final long serialVersionUID = 1L; | |||
public static final String NAME = "OOXMLProvider"; | |||
private OOXMLProvider() { | |||
super(NAME, 1.0, "OOXML Security Provider"); | |||
put("TransformService." + RelationshipTransformService.TRANSFORM_URI, RelationshipTransformService.class.getName()); | |||
put("TransformService." + RelationshipTransformService.TRANSFORM_URI + " MechanismType", "DOM"); | |||
} | |||
/** | |||
* Installs this security provider. | |||
*/ | |||
public static void install() { | |||
Provider provider = Security.getProvider(NAME); | |||
if (null == provider) { | |||
Security.addProvider(new OOXMLProvider()); | |||
} | |||
} | |||
} |
@@ -1,371 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URL; | |||
import java.security.InvalidAlgorithmParameterException; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.util.Calendar; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.TimeZone; | |||
import java.util.UUID; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipInputStream; | |||
import javax.xml.crypto.XMLStructure; | |||
import javax.xml.crypto.dom.DOMStructure; | |||
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 javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import javax.xml.transform.TransformerException; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.ooxml.signature.service.signer.NoCloseInputStream; | |||
import org.apache.poi.ooxml.signature.service.signer.SignatureAspect; | |||
import org.apache.xml.security.utils.Constants; | |||
import org.apache.xpath.XPathAPI; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
/** | |||
* Office OpenXML Signature Aspect implementation. | |||
*/ | |||
final class OOXMLSignatureAspect implements SignatureAspect { | |||
private static final Log LOG = LogFactory.getLog(OOXMLSignatureAspect.class); | |||
private final AbstractOOXMLSignatureService _signatureService; | |||
public OOXMLSignatureAspect(AbstractOOXMLSignatureService signatureService) { | |||
_signatureService = signatureService; | |||
} | |||
public void preSign(XMLSignatureFactory signatureFactory, Document document, String signatureId, List<Reference> references, List<XMLObject> objects) | |||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { | |||
LOG.debug("pre sign"); | |||
addManifestObject(signatureFactory, document, signatureId, references, objects); | |||
addSignatureInfo(signatureFactory, document, signatureId, references, objects); | |||
} | |||
private void addManifestObject(XMLSignatureFactory signatureFactory, Document document, 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, document, signatureId, objectContent); | |||
objects.add(signatureFactory.newXMLObject(objectContent, objectId, null, null)); | |||
DigestMethod digestMethod = signatureFactory.newDigestMethod(DigestMethod.SHA1, 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 LinkedList<Reference>(); | |||
try { | |||
addRelationshipsReferences(signatureFactory, manifestReferences); | |||
} catch (Exception e) { | |||
throw new RuntimeException("error: " + e.getMessage(), e); | |||
} | |||
/* | |||
* Word | |||
*/ | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.theme+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml", manifestReferences); | |||
/* | |||
* Powerpoint | |||
*/ | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.presentationml.slide+xml", manifestReferences); | |||
addParts(signatureFactory, "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml", manifestReferences); | |||
Manifest manifest = signatureFactory.newManifest(manifestReferences); | |||
return manifest; | |||
} | |||
private static void addSignatureTime(XMLSignatureFactory signatureFactory, Document document, String signatureId, List<XMLStructure> objectContent) { | |||
/* | |||
* SignatureTime | |||
*/ | |||
Element signatureTimeElement = document.createElementNS("http://schemas.openxmlformats.org/package/2006/digital-signature", "mdssi:SignatureTime"); | |||
signatureTimeElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", "http://schemas.openxmlformats.org/package/2006/digital-signature"); | |||
Element formatElement = document.createElementNS("http://schemas.openxmlformats.org/package/2006/digital-signature", "mdssi:Format"); | |||
formatElement.setTextContent("YYYY-MM-DDThh:mm:ssTZD"); | |||
signatureTimeElement.appendChild(formatElement); | |||
Element valueElement = document.createElementNS("http://schemas.openxmlformats.org/package/2006/digital-signature", "mdssi:Value"); | |||
String now = formatTimestampAsISO8601(System.currentTimeMillis()); | |||
LOG.debug("now: " + now); | |||
valueElement.setTextContent(now); | |||
signatureTimeElement.appendChild(valueElement); | |||
List<XMLStructure> signatureTimeContent = new LinkedList<XMLStructure>(); | |||
signatureTimeContent.add(new DOMStructure(signatureTimeElement)); | |||
SignatureProperty signatureTimeSignatureProperty = signatureFactory.newSignatureProperty(signatureTimeContent, "#" + signatureId, "idSignatureTime"); | |||
List<SignatureProperty> signaturePropertyContent = new LinkedList<SignatureProperty>(); | |||
signaturePropertyContent.add(signatureTimeSignatureProperty); | |||
SignatureProperties signatureProperties = signatureFactory.newSignatureProperties(signaturePropertyContent, "id-signature-time-" | |||
+ UUID.randomUUID().toString()); | |||
objectContent.add(signatureProperties); | |||
} | |||
/** | |||
* @return text formatted "YYYY-MM-DDThh:mm:ssTZD" | |||
*/ | |||
static String formatTimestampAsISO8601(long ts) { | |||
Calendar c = Calendar.getInstance(); | |||
c.setTimeInMillis(ts); | |||
c.setTimeZone(TimeZone.getTimeZone("UTC")); | |||
char[] buf = "yyyy-mm-ddThh:mm:ssZ".toCharArray(); | |||
itoa(buf, 0, 4, c.get(Calendar.YEAR)); | |||
itoa(buf, 5, 2, c.get(Calendar.MONTH)+1); | |||
itoa(buf, 8, 2, c.get(Calendar.DAY_OF_MONTH)); | |||
itoa(buf, 11, 2, c.get(Calendar.HOUR_OF_DAY)); | |||
itoa(buf, 14, 2, c.get(Calendar.MINUTE)); | |||
itoa(buf, 17, 2, c.get(Calendar.SECOND)); | |||
return new String(buf); | |||
} | |||
private static void itoa(char[] buf, int start, int len, int value) { | |||
int acc = value; | |||
int i=start+len-1; | |||
while (i>=start) { | |||
int d = acc % 10; | |||
acc /= 10; | |||
buf[i] = (char) ('0' + d); | |||
i--; | |||
} | |||
} | |||
private void addSignatureInfo(XMLSignatureFactory signatureFactory, Document document, String signatureId, List<Reference> references, | |||
List<XMLObject> objects) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { | |||
List<XMLStructure> objectContent = new LinkedList<XMLStructure>(); | |||
Element signatureInfoElement = document.createElementNS("http://schemas.microsoft.com/office/2006/digsig", "SignatureInfoV1"); | |||
signatureInfoElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns", "http://schemas.microsoft.com/office/2006/digsig"); | |||
Element manifestHashAlgorithmElement = document.createElementNS("http://schemas.microsoft.com/office/2006/digsig", "ManifestHashAlgorithm"); | |||
manifestHashAlgorithmElement.setTextContent("http://www.w3.org/2000/09/xmldsig#sha1"); | |||
signatureInfoElement.appendChild(manifestHashAlgorithmElement); | |||
List<XMLStructure> signatureInfoContent = new LinkedList<XMLStructure>(); | |||
signatureInfoContent.add(new DOMStructure(signatureInfoElement)); | |||
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(DigestMethod.SHA1, null); | |||
Reference reference = signatureFactory.newReference("#" + objectId, digestMethod, null, "http://www.w3.org/2000/09/xmldsig#Object", null); | |||
references.add(reference); | |||
} | |||
private void addRelationshipsReferences(XMLSignatureFactory signatureFactory, List<Reference> manifestReferences) throws IOException, | |||
ParserConfigurationException, SAXException, NoSuchAlgorithmException, | |||
InvalidAlgorithmParameterException { | |||
URL ooxmlUrl = _signatureService.getOfficeOpenXMLDocumentURL(); | |||
InputStream inputStream = ooxmlUrl.openStream(); | |||
ZipInputStream zipInputStream = new ZipInputStream(inputStream); | |||
ZipEntry zipEntry; | |||
while (null != (zipEntry = zipInputStream.getNextEntry())) { | |||
if (false == zipEntry.getName().endsWith(".rels")) { | |||
continue; | |||
} | |||
Document relsDocument = loadDocumentNoClose(zipInputStream); | |||
addRelationshipsReference(signatureFactory, zipEntry.getName(), relsDocument, manifestReferences); | |||
} | |||
} | |||
private void addRelationshipsReference(XMLSignatureFactory signatureFactory, String zipEntryName, Document relsDocument, | |||
List<Reference> manifestReferences) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { | |||
LOG.debug("relationships: " + zipEntryName); | |||
RelationshipTransformParameterSpec parameterSpec = new RelationshipTransformParameterSpec(); | |||
NodeList nodeList = relsDocument.getDocumentElement().getChildNodes(); | |||
for (int nodeIdx = 0; nodeIdx < nodeList.getLength(); nodeIdx++) { | |||
Node node = nodeList.item(nodeIdx); | |||
if (node.getNodeType() != Node.ELEMENT_NODE) { | |||
continue; | |||
} | |||
Element element = (Element) node; | |||
String relationshipType = element.getAttribute("Type"); | |||
/* | |||
* We skip some relationship types. | |||
*/ | |||
if ("http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties".equals(relationshipType)) { | |||
continue; | |||
} | |||
if ("http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties".equals(relationshipType)) { | |||
continue; | |||
} | |||
if ("http://schemas.openxmlformats.org/package/2006/relationships/digital-signature/origin".equals(relationshipType)) { | |||
continue; | |||
} | |||
if ("http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail".equals(relationshipType)) { | |||
continue; | |||
} | |||
if ("http://schemas.openxmlformats.org/officeDocument/2006/relationships/presProps".equals(relationshipType)) { | |||
continue; | |||
} | |||
if ("http://schemas.openxmlformats.org/officeDocument/2006/relationships/viewProps".equals(relationshipType)) { | |||
continue; | |||
} | |||
String relationshipId = element.getAttribute("Id"); | |||
parameterSpec.addRelationshipReference(relationshipId); | |||
} | |||
List<Transform> transforms = new LinkedList<Transform>(); | |||
transforms.add(signatureFactory.newTransform(RelationshipTransformService.TRANSFORM_URI, parameterSpec)); | |||
transforms.add(signatureFactory.newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", (TransformParameterSpec) null)); | |||
DigestMethod digestMethod = signatureFactory.newDigestMethod(DigestMethod.SHA1, null); | |||
Reference reference = signatureFactory.newReference("/" + zipEntryName + "?ContentType=application/vnd.openxmlformats-package.relationships+xml", | |||
digestMethod, transforms, null, null); | |||
manifestReferences.add(reference); | |||
} | |||
private void addParts(XMLSignatureFactory signatureFactory, String contentType, List<Reference> references) throws NoSuchAlgorithmException, | |||
InvalidAlgorithmParameterException { | |||
List<String> documentResourceNames; | |||
try { | |||
documentResourceNames = getResourceNames(_signatureService.getOfficeOpenXMLDocumentURL(), contentType); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} | |||
DigestMethod digestMethod = signatureFactory.newDigestMethod(DigestMethod.SHA1, null); | |||
for (String documentResourceName : documentResourceNames) { | |||
LOG.debug("document resource: " + documentResourceName); | |||
Reference reference = signatureFactory.newReference("/" + documentResourceName + "?ContentType=" + contentType, digestMethod); | |||
references.add(reference); | |||
} | |||
} | |||
private List<String> getResourceNames(URL url, String contentType) throws IOException, ParserConfigurationException, SAXException, TransformerException { | |||
List<String> signatureResourceNames = new LinkedList<String>(); | |||
if (null == url) { | |||
throw new RuntimeException("OOXML URL is null"); | |||
} | |||
InputStream inputStream = url.openStream(); | |||
ZipInputStream zipInputStream = new ZipInputStream(inputStream); | |||
ZipEntry zipEntry; | |||
while (null != (zipEntry = zipInputStream.getNextEntry())) { | |||
if (false == "[Content_Types].xml".equals(zipEntry.getName())) { | |||
continue; | |||
} | |||
Document contentTypesDocument = loadDocument(zipInputStream); | |||
Element nsElement = contentTypesDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "http://schemas.openxmlformats.org/package/2006/content-types"); | |||
NodeList nodeList = XPathAPI.selectNodeList(contentTypesDocument, "/tns:Types/tns:Override[@ContentType='" + contentType + "']/@PartName", | |||
nsElement); | |||
for (int nodeIdx = 0; nodeIdx < nodeList.getLength(); nodeIdx++) { | |||
String partName = nodeList.item(nodeIdx).getTextContent(); | |||
LOG.debug("part name: " + partName); | |||
partName = partName.substring(1); // remove '/' | |||
signatureResourceNames.add(partName); | |||
} | |||
break; | |||
} | |||
return signatureResourceNames; | |||
} | |||
protected Document loadDocument(String zipEntryName) throws IOException, ParserConfigurationException, SAXException { | |||
Document document = findDocument(zipEntryName); | |||
if (null != document) { | |||
return document; | |||
} | |||
throw new RuntimeException("ZIP entry not found: " + zipEntryName); | |||
} | |||
protected Document findDocument(String zipEntryName) throws IOException, ParserConfigurationException, SAXException { | |||
URL ooxmlUrl = _signatureService.getOfficeOpenXMLDocumentURL(); | |||
InputStream inputStream = ooxmlUrl.openStream(); | |||
ZipInputStream zipInputStream = new ZipInputStream(inputStream); | |||
ZipEntry zipEntry; | |||
while (null != (zipEntry = zipInputStream.getNextEntry())) { | |||
if (false == zipEntryName.equals(zipEntry.getName())) { | |||
continue; | |||
} | |||
Document document = loadDocument(zipInputStream); | |||
return document; | |||
} | |||
return null; | |||
} | |||
private Document loadDocumentNoClose(InputStream documentInputStream) throws ParserConfigurationException, SAXException, IOException { | |||
NoCloseInputStream noCloseInputStream = new NoCloseInputStream(documentInputStream); | |||
InputSource inputSource = new InputSource(noCloseInputStream); | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.parse(inputSource); | |||
return document; | |||
} | |||
private 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; | |||
} | |||
} |
@@ -1,206 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.net.URL; | |||
import java.security.cert.X509Certificate; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.zip.ZipEntry; | |||
import java.util.zip.ZipInputStream; | |||
import javax.xml.crypto.MarshalException; | |||
import javax.xml.crypto.dsig.XMLSignature; | |||
import javax.xml.crypto.dsig.XMLSignatureException; | |||
import javax.xml.crypto.dsig.XMLSignatureFactory; | |||
import javax.xml.crypto.dsig.dom.DOMValidateContext; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import javax.xml.parsers.ParserConfigurationException; | |||
import javax.xml.transform.TransformerException; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.POIXMLDocument; | |||
import org.apache.poi.ooxml.signature.service.signer.KeyInfoKeySelector; | |||
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.PackageRelationship; | |||
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; | |||
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; | |||
import org.apache.poi.openxml4j.opc.PackagingURIHelper; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
/** | |||
* Signature verifier util class for Office Open XML file format. | |||
*/ | |||
public final class OOXMLSignatureVerifier { | |||
private static final Log LOG = LogFactory.getLog(OOXMLSignatureVerifier.class); | |||
private OOXMLSignatureVerifier() { | |||
// no instances of this class; | |||
} | |||
/** | |||
* @return <code>true</code> if the file referred by the given URL is an OOXML document. | |||
*/ | |||
public static boolean isOOXML(URL url) throws IOException { | |||
ZipInputStream zipInputStream = new ZipInputStream(url.openStream()); | |||
ZipEntry zipEntry; | |||
while (null != (zipEntry = zipInputStream.getNextEntry())) { | |||
if (false == "[Content_Types].xml".equals(zipEntry.getName())) { | |||
continue; | |||
} | |||
if (zipEntry.getSize() > 0) { | |||
return true; | |||
} | |||
} | |||
return false; | |||
} | |||
public static List<X509Certificate> getSigners(URL url) throws IOException, ParserConfigurationException, SAXException, TransformerException, | |||
MarshalException, XMLSignatureException, InvalidFormatException { | |||
List<X509Certificate> signers = new LinkedList<X509Certificate>(); | |||
List<PackagePart> signatureParts = getSignatureParts(url); | |||
if (signatureParts.isEmpty()) { | |||
LOG.debug("no signature resources"); | |||
} | |||
for (PackagePart signaturePart : signatureParts) { | |||
Document signatureDocument = loadDocument(signaturePart); | |||
if (null == signatureDocument) { | |||
continue; | |||
} | |||
NodeList signatureNodeList = signatureDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); | |||
if (0 == signatureNodeList.getLength()) { | |||
return null; | |||
} | |||
Node signatureNode = signatureNodeList.item(0); | |||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector(); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, signatureNode); | |||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE); | |||
OOXMLURIDereferencer dereferencer = new OOXMLURIDereferencer(url); | |||
domValidateContext.setURIDereferencer(dereferencer); | |||
XMLSignatureFactory xmlSignatureFactory = CryptoFactoryFactory.getSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
if (false == validity) { | |||
continue; | |||
} | |||
// TODO: check what has been signed. | |||
X509Certificate signer = keySelector.getCertificate(); | |||
signers.add(signer); | |||
} | |||
return signers; | |||
} | |||
public static boolean verifySignature(URL url) throws InvalidFormatException, IOException, ParserConfigurationException, SAXException, MarshalException, | |||
XMLSignatureException { | |||
PackagePart signaturePart = getSignaturePart(url); | |||
if (signaturePart == null) { | |||
LOG.info(url + " does not contain a signature"); | |||
return false; | |||
} | |||
LOG.debug("signature resource name: " + signaturePart.getPartName()); | |||
OOXMLProvider.install(); | |||
Document signatureDocument = loadDocument(signaturePart); | |||
LOG.debug("signature loaded"); | |||
NodeList signatureNodeList = signatureDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); | |||
Node signatureNode = signatureNodeList.item(0); | |||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector(); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, signatureNode); | |||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE); | |||
OOXMLURIDereferencer dereferencer = new OOXMLURIDereferencer(url); | |||
domValidateContext.setURIDereferencer(dereferencer); | |||
XMLSignatureFactory xmlSignatureFactory = CryptoFactoryFactory.getSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
return xmlSignature.validate(domValidateContext); | |||
} | |||
private static PackagePart getSignaturePart(URL url) throws IOException, InvalidFormatException { | |||
List<PackagePart> packageParts = getSignatureParts(url); | |||
if (packageParts.isEmpty()) { | |||
return null; | |||
} else { | |||
return packageParts.get(0); | |||
} | |||
} | |||
private static List<PackagePart> getSignatureParts(URL url) throws IOException, InvalidFormatException { | |||
List<PackagePart> packageParts = new LinkedList<PackagePart>(); | |||
OPCPackage pkg = POIXMLDocument.openPackage(url.getPath()); | |||
PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN); | |||
for (PackageRelationship rel : sigOrigRels) { | |||
PackagePartName relName = PackagingURIHelper.createPartName(rel.getTargetURI()); | |||
PackagePart sigPart = pkg.getPart(relName); | |||
if (LOG.isDebugEnabled()) { | |||
LOG.debug("Digital Signature Origin part = " + sigPart); | |||
} | |||
PackageRelationshipCollection sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE); | |||
for (PackageRelationship sigRel : sigRels) { | |||
PackagePartName sigRelName = PackagingURIHelper.createPartName(sigRel.getTargetURI()); | |||
PackagePart sigRelPart = pkg.getPart(sigRelName); | |||
if (LOG.isDebugEnabled()) { | |||
LOG.debug("XML Signature part = " + sigRelPart); | |||
} | |||
packageParts.add(sigRelPart); | |||
} | |||
} | |||
return packageParts; | |||
} | |||
private static Document loadDocument(PackagePart part) throws ParserConfigurationException, SAXException, IOException { | |||
InputStream documentInputStream = part.getInputStream(); | |||
return loadDocument(documentInputStream); | |||
} | |||
private 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; | |||
} | |||
} |
@@ -1,110 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.UnsupportedEncodingException; | |||
import java.net.URL; | |||
import java.net.URLDecoder; | |||
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.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.POIXMLDocument; | |||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
/** | |||
* JSR105 URI dereferencer for Office Open XML documents. | |||
*/ | |||
public class OOXMLURIDereferencer implements URIDereferencer { | |||
private static final Log LOG = LogFactory.getLog(OOXMLURIDereferencer.class); | |||
private final URL ooxmlUrl; | |||
private final URIDereferencer baseUriDereferencer; | |||
public OOXMLURIDereferencer(URL ooxmlUrl) { | |||
if (null == ooxmlUrl) { | |||
throw new IllegalArgumentException("ooxmlUrl is null"); | |||
} | |||
this.ooxmlUrl = ooxmlUrl; | |||
XMLSignatureFactory xmlSignatureFactory = CryptoFactoryFactory.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"); | |||
} | |||
String uri = uriReference.getURI(); | |||
try { | |||
uri = URLDecoder.decode(uri, "UTF-8"); | |||
} catch (UnsupportedEncodingException e) { | |||
LOG.warn("could not URL decode the uri: " + uri); | |||
} | |||
LOG.debug("dereference: " + uri); | |||
try { | |||
InputStream dataInputStream = findDataInputStream(uri); | |||
if (null == dataInputStream) { | |||
LOG.debug("cannot resolve, delegating to base DOM URI dereferencer: " + uri); | |||
return this.baseUriDereferencer.dereference(uriReference, context); | |||
} | |||
return new OctetStreamData(dataInputStream, uri, null); | |||
} catch (IOException e) { | |||
throw new URIReferenceException("I/O error: " + e.getMessage(), e); | |||
} catch (InvalidFormatException e) { | |||
throw new URIReferenceException("Invalid format error: " + e.getMessage(), e); | |||
} | |||
} | |||
private InputStream findDataInputStream(String uri) throws IOException, InvalidFormatException { | |||
if (-1 != uri.indexOf("?")) { | |||
uri = uri.substring(0, uri.indexOf("?")); | |||
} | |||
OPCPackage pkg = POIXMLDocument.openPackage(this.ooxmlUrl.getPath()); | |||
for (PackagePart part : pkg.getParts()) { | |||
if (uri.equals(part.getPartName().getURI().toString())) { | |||
LOG.debug("Part name: " + part.getPartName()); | |||
return part.getInputStream(); | |||
} | |||
} | |||
LOG.debug("No part found for URI: " + uri); | |||
return null; | |||
} | |||
} |
@@ -1,41 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.util.Comparator; | |||
import org.w3c.dom.Element; | |||
/** | |||
* Comparator for Relationship DOM elements. | |||
*/ | |||
public class RelationshipComparator implements Comparator<Element> { | |||
public int compare(Element element1, Element element2) { | |||
String id1 = element1.getAttribute("Id"); | |||
String id2 = element2.getAttribute("Id"); | |||
return id1.compareTo(id2); | |||
} | |||
} |
@@ -1,58 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import javax.xml.crypto.dsig.spec.TransformParameterSpec; | |||
/** | |||
* Relationship Transform parameter specification class. | |||
*/ | |||
public class RelationshipTransformParameterSpec implements TransformParameterSpec { | |||
private final List<String> sourceIds; | |||
/** | |||
* Main constructor. | |||
*/ | |||
public RelationshipTransformParameterSpec() { | |||
this.sourceIds = new LinkedList<String>(); | |||
} | |||
/** | |||
* Adds a relationship reference for the given source identifier. | |||
* | |||
* @param sourceId | |||
*/ | |||
public void addRelationshipReference(String sourceId) { | |||
this.sourceIds.add(sourceId); | |||
} | |||
List<String> getSourceIds() { | |||
return this.sourceIds; | |||
} | |||
} |
@@ -1,274 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.StringWriter; | |||
import java.security.InvalidAlgorithmParameterException; | |||
import java.security.spec.AlgorithmParameterSpec; | |||
import java.util.Collections; | |||
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 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.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.xml.security.utils.Constants; | |||
import org.apache.xpath.XPathAPI; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
/** | |||
* 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 Log LOG = LogFactory.getLog(RelationshipTransformService.class); | |||
public RelationshipTransformService() { | |||
super(); | |||
LOG.debug("constructor"); | |||
this.sourceIds = new LinkedList<String>(); | |||
} | |||
@Override | |||
public void init(TransformParameterSpec params) throws InvalidAlgorithmParameterException { | |||
LOG.debug("init(params)"); | |||
if (false == params instanceof RelationshipTransformParameterSpec) { | |||
throw new InvalidAlgorithmParameterException(); | |||
} | |||
RelationshipTransformParameterSpec relParams = (RelationshipTransformParameterSpec) params; | |||
for (String sourceId : relParams.getSourceIds()) { | |||
this.sourceIds.add(sourceId); | |||
} | |||
} | |||
@Override | |||
public void init(XMLStructure parent, XMLCryptoContext context) throws InvalidAlgorithmParameterException { | |||
LOG.debug("init(parent,context)"); | |||
LOG.debug("parent java type: " + parent.getClass().getName()); | |||
DOMStructure domParent = (DOMStructure) parent; | |||
Node parentNode = domParent.getNode(); | |||
try { | |||
LOG.debug("parent: " + toString(parentNode)); | |||
} catch (TransformerException e) { | |||
throw new InvalidAlgorithmParameterException(); | |||
} | |||
Element nsElement = parentNode.getOwnerDocument().createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", "http://schemas.openxmlformats.org/package/2006/digital-signature"); | |||
NodeList nodeList; | |||
try { | |||
nodeList = XPathAPI.selectNodeList(parentNode, "mdssi:RelationshipReference/@SourceId", nsElement); | |||
} catch (TransformerException e) { | |||
LOG.error("transformer exception: " + e.getMessage(), e); | |||
throw new InvalidAlgorithmParameterException(); | |||
} | |||
if (0 == nodeList.getLength()) { | |||
LOG.warn("no RelationshipReference/@SourceId parameters present"); | |||
} | |||
for (int nodeIdx = 0; nodeIdx < nodeList.getLength(); nodeIdx++) { | |||
Node node = nodeList.item(nodeIdx); | |||
String sourceId = node.getTextContent(); | |||
LOG.debug("sourceId: " + sourceId); | |||
this.sourceIds.add(sourceId); | |||
} | |||
} | |||
@Override | |||
public void marshalParams(XMLStructure parent, XMLCryptoContext context) throws MarshalException { | |||
LOG.debug("marshallParams(parent,context)"); | |||
DOMStructure domParent = (DOMStructure) parent; | |||
Node parentNode = domParent.getNode(); | |||
Element parentElement = (Element) parentNode; | |||
parentElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", "http://schemas.openxmlformats.org/package/2006/digital-signature"); | |||
Document document = parentNode.getOwnerDocument(); | |||
for (String sourceId : this.sourceIds) { | |||
Element relationshipReferenceElement = document.createElementNS("http://schemas.openxmlformats.org/package/2006/digital-signature", | |||
"mdssi:RelationshipReference"); | |||
relationshipReferenceElement.setAttribute("SourceId", sourceId); | |||
parentElement.appendChild(relationshipReferenceElement); | |||
} | |||
} | |||
public AlgorithmParameterSpec getParameterSpec() { | |||
LOG.debug("getParameterSpec"); | |||
return null; | |||
} | |||
public Data transform(Data data, XMLCryptoContext context) throws TransformException { | |||
LOG.debug("transform(data,context)"); | |||
LOG.debug("data java type: " + data.getClass().getName()); | |||
OctetStreamData octetStreamData = (OctetStreamData) data; | |||
LOG.debug("URI: " + octetStreamData.getURI()); | |||
InputStream octetStream = octetStreamData.getOctetStream(); | |||
Document relationshipsDocument; | |||
try { | |||
relationshipsDocument = loadDocument(octetStream); | |||
} catch (Exception e) { | |||
throw new TransformException(e.getMessage(), e); | |||
} | |||
try { | |||
LOG.debug("relationships document: " + toString(relationshipsDocument)); | |||
} catch (TransformerException e) { | |||
throw new TransformException(e.getMessage(), e); | |||
} | |||
Element nsElement = relationshipsDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "http://schemas.openxmlformats.org/package/2006/relationships"); | |||
Element relationshipsElement = relationshipsDocument.getDocumentElement(); | |||
NodeList childNodes = relationshipsElement.getChildNodes(); | |||
for (int nodeIdx = 0; nodeIdx < childNodes.getLength(); nodeIdx++) { | |||
Node childNode = childNodes.item(nodeIdx); | |||
if (Node.ELEMENT_NODE != childNode.getNodeType()) { | |||
LOG.debug("removing node"); | |||
relationshipsElement.removeChild(childNode); | |||
nodeIdx--; | |||
continue; | |||
} | |||
Element childElement = (Element) childNode; | |||
String idAttribute = childElement.getAttribute("Id"); | |||
LOG.debug("Relationship id attribute: " + idAttribute); | |||
if (false == this.sourceIds.contains(idAttribute)) { | |||
LOG.debug("removing element: " + idAttribute); | |||
relationshipsElement.removeChild(childNode); | |||
nodeIdx--; | |||
} | |||
/* | |||
* See: ISO/IEC 29500-2:2008(E) - 13.2.4.24 Relationships Transform | |||
* Algorithm. | |||
*/ | |||
if (null == childElement.getAttributeNode("TargetMode")) { | |||
childElement.setAttribute("TargetMode", "Internal"); | |||
} | |||
} | |||
LOG.debug("# Relationship elements: " + relationshipsElement.getElementsByTagName("*").getLength()); | |||
sortRelationshipElements(relationshipsElement); | |||
try { | |||
return toOctetStreamData(relationshipsDocument); | |||
} catch (TransformerException e) { | |||
throw new TransformException(e.getMessage(), e); | |||
} | |||
} | |||
private void sortRelationshipElements(Element relationshipsElement) { | |||
List<Element> relationshipElements = new LinkedList<Element>(); | |||
NodeList relationshipNodes = relationshipsElement.getElementsByTagName("*"); | |||
int nodeCount = relationshipNodes.getLength(); | |||
for (int nodeIdx = 0; nodeIdx < nodeCount; nodeIdx++) { | |||
Node relationshipNode = relationshipNodes.item(0); | |||
Element relationshipElement = (Element) relationshipNode; | |||
LOG.debug("unsorted Id: " + relationshipElement.getAttribute("Id")); | |||
relationshipElements.add(relationshipElement); | |||
relationshipsElement.removeChild(relationshipNode); | |||
} | |||
Collections.sort(relationshipElements, new RelationshipComparator()); | |||
for (Element relationshipElement : relationshipElements) { | |||
LOG.debug("sorted Id: " + relationshipElement.getAttribute("Id")); | |||
relationshipsElement.appendChild(relationshipElement); | |||
} | |||
} | |||
private 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(); | |||
} | |||
private OctetStreamData toOctetStreamData(Node node) throws TransformerException { | |||
Source source = new DOMSource(node); | |||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | |||
Result result = new StreamResult(outputStream); | |||
TransformerFactory transformerFactory = TransformerFactory.newInstance(); | |||
Transformer transformer = transformerFactory.newTransformer(); | |||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); | |||
transformer.transform(source, result); | |||
LOG.debug("result: " + new String(outputStream.toByteArray())); | |||
return new OctetStreamData(new ByteArrayInputStream(outputStream.toByteArray())); | |||
} | |||
private 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; | |||
} | |||
public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException { | |||
LOG.debug("transform(data,context,os)"); | |||
return null; | |||
} | |||
public boolean isFeatureSupported(String feature) { | |||
LOG.debug("isFeatureSupported(feature)"); | |||
return false; | |||
} | |||
} |
@@ -1,28 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
/** | |||
* This package contains implementation classes for the Office Open XML Signature Service. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer.ooxml; | |||
@@ -1,28 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
/** | |||
* This package contains implementation classes for the Signature Service SPI. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
@@ -1,56 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.spi; | |||
import java.security.cert.X509Certificate; | |||
import java.util.List; | |||
/** | |||
* Interface for authentication service components. | |||
*/ | |||
public interface AuthenticationService { | |||
/** | |||
* Validates the given certificate chain. After the client has | |||
* verified the authentication signature, it will invoke this method on your | |||
* authentication service component. The implementation of this method | |||
* should validate the given certificate chain. This validation could be | |||
* based on PKI validation, or could be based on simply trusting the | |||
* incoming public key. The actual implementation is very dependent on your | |||
* type of application. This method should only be used for certificate | |||
* validation. | |||
* | |||
* <p> | |||
* Check out <a href="http://code.google.com/p/jtrust/">jTrust</a> for an | |||
* implementation of a PKI validation framework. | |||
* </p> | |||
* | |||
* @param certificateChain | |||
* the X509 authentication certificate chain of the citizen. | |||
* @throws SecurityException | |||
* in case the certificate chain is invalid/not accepted. | |||
*/ | |||
void validateCertificateChain(List<X509Certificate> certificateChain) throws SecurityException; | |||
} |
@@ -1,54 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.spi; | |||
import java.io.Serializable; | |||
/** | |||
* Digest Information data transfer class. | |||
*/ | |||
public class DigestInfo implements Serializable { | |||
private static final long serialVersionUID = 1L; | |||
/** | |||
* Main constructor. | |||
* | |||
* @param digestValue | |||
* @param digestAlgo | |||
* @param description | |||
*/ | |||
public DigestInfo(byte[] digestValue, String digestAlgo, String description) { | |||
this.digestValue = digestValue; | |||
this.digestAlgo = digestAlgo; | |||
this.description = description; | |||
} | |||
public final byte[] digestValue; | |||
public final String description; | |||
public final String digestAlgo; | |||
} |
@@ -1,64 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.spi; | |||
/** | |||
* Insecure Client Environment Exception. | |||
*/ | |||
public class InsecureClientEnvironmentException extends Exception { | |||
private static final long serialVersionUID = 1L; | |||
private final boolean warnOnly; | |||
/** | |||
* Default constructor. | |||
*/ | |||
public InsecureClientEnvironmentException() { | |||
this(false); | |||
} | |||
/** | |||
* Main constructor. | |||
* | |||
* @param warnOnly | |||
* only makes that the citizen is warned about a possible | |||
* insecure enviroment. | |||
*/ | |||
public InsecureClientEnvironmentException(boolean warnOnly) { | |||
this.warnOnly = warnOnly; | |||
} | |||
/** | |||
* If set the eID Applet will only give a warning on case the server-side | |||
* marks the client environment as being insecure. Else the eID Applet will | |||
* abort the requested eID operation. | |||
* | |||
* @return | |||
*/ | |||
public boolean isWarnOnly() { | |||
return this.warnOnly; | |||
} | |||
} |
@@ -1,73 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.spi; | |||
import java.util.List; | |||
/** | |||
* Interface for security environment service components. Can be used by the eID | |||
* Applet Service to check the client environment security requirements. | |||
*/ | |||
public interface SecureClientEnvironmentService { | |||
/** | |||
* Checks whether the client environment is secure enough for this web | |||
* application. | |||
* | |||
* @param javaVersion | |||
* the version of the Java JRE on the client machine. | |||
* @param javaVendor | |||
* the vendor of the Java JRE on the client machine. | |||
* @param osName | |||
* the name of the operating system on the client machine. | |||
* @param osArch | |||
* the architecture of the client machine. | |||
* @param osVersion | |||
* the operating system version of the client machine. | |||
* @param userAgent | |||
* the user agent, i.e. browser, used on the client machine. | |||
* @param navigatorAppName | |||
* the optional navigator application name (browser) | |||
* @param navigatorAppVersion | |||
* the optional navigator application version (browser version) | |||
* @param navigatorUserAgent | |||
* the optional optional navigator user agent name. | |||
* @param remoteAddress | |||
* the address of the client machine. | |||
* @param sslKeySize | |||
* the key size of the SSL session used between server and | |||
* client. | |||
* @param sslCipherSuite | |||
* the cipher suite of the SSL session used between server and | |||
* client. | |||
* @param readerList | |||
* the list of smart card readers present on the client machine. | |||
* @throws InsecureClientEnvironmentException | |||
* if the client env is found not to be secure enough. | |||
*/ | |||
void checkSecureClientEnvironment(String javaVersion, String javaVendor, String osName, String osArch, String osVersion, String userAgent, | |||
String navigatorAppName, String navigatorAppVersion, String navigatorUserAgent, String remoteAddress, int sslKeySize, | |||
String sslCipherSuite, List<String> readerList) throws InsecureClientEnvironmentException; | |||
} |
@@ -1,77 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.spi; | |||
import java.security.NoSuchAlgorithmException; | |||
import java.security.cert.X509Certificate; | |||
import java.util.List; | |||
/** | |||
* Interface for signature service component. | |||
*/ | |||
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 | |||
* @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. | |||
* @return the digest to be signed. | |||
* @throws NoSuchAlgorithmException | |||
*/ | |||
DigestInfo preSign(List<DigestInfo> digestInfos, List<X509Certificate> signingCertificateChain) throws NoSuchAlgorithmException; | |||
/** | |||
* Post-sign callback method. Received the signature value. Depending on the | |||
* configuration the signing certificate chain is also obtained. | |||
* | |||
* <p> | |||
* TODO: service must be able to throw some exception on failure. | |||
* </p> | |||
* | |||
* @param signatureValue | |||
* @param signingCertificateChain | |||
* the optional chain of signing certificates. | |||
*/ | |||
void postSign(byte[] signatureValue, List<X509Certificate> signingCertificateChain); | |||
} |
@@ -1,28 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
/** | |||
* This package contains the service provider interfaces. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.spi; | |||
@@ -1,36 +0,0 @@ | |||
/* ==================================================================== | |||
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.ooxml.signature.service.signer; | |||
import org.apache.poi.ooxml.signature.service.signer.ooxml.TestOOXMLSignatureAspect; | |||
import junit.framework.Test; | |||
import junit.framework.TestSuite; | |||
public final class AllOOXMLSignatureTests { | |||
public static Test suite() { | |||
TestSuite result = new TestSuite(AllOOXMLSignatureTests.class.getName()); | |||
result.addTestSuite(TestAbstractOOXMLSignatureService.class); | |||
result.addTestSuite(TestAbstractXmlSignatureService.class); | |||
result.addTestSuite(TestOOXMLSignatureAspect.class); | |||
result.addTestSuite(TestOOXMLSignatureVerifier.class); | |||
return result; | |||
} | |||
} |
@@ -1,212 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.StringWriter; | |||
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.CertificateException; | |||
import java.security.cert.CertificateFactory; | |||
import java.security.cert.X509Certificate; | |||
import java.security.spec.RSAKeyGenParameterSpec; | |||
import java.util.Calendar; | |||
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.util.HexRead; | |||
import org.bouncycastle.asn1.ASN1InputStream; | |||
import org.bouncycastle.asn1.ASN1Sequence; | |||
import org.bouncycastle.asn1.DERIA5String; | |||
import org.bouncycastle.asn1.DERSequence; | |||
import org.bouncycastle.asn1.x509.AuthorityInformationAccess; | |||
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; | |||
import org.bouncycastle.asn1.x509.BasicConstraints; | |||
import org.bouncycastle.asn1.x509.DistributionPoint; | |||
import org.bouncycastle.asn1.x509.DistributionPointName; | |||
import org.bouncycastle.asn1.x509.GeneralName; | |||
import org.bouncycastle.asn1.x509.GeneralNames; | |||
import org.bouncycastle.asn1.x509.KeyUsage; | |||
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; | |||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; | |||
import org.bouncycastle.asn1.x509.X509Extensions; | |||
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; | |||
import org.bouncycastle.jce.X509Principal; | |||
import org.bouncycastle.x509.X509V3CertificateGenerator; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Node; | |||
import org.xml.sax.InputSource; | |||
import org.xml.sax.SAXException; | |||
final class PkiTestUtils { | |||
public static final byte[] SHA1_DIGEST_INFO_PREFIX = | |||
HexRead.readFromString( "30 1f 30 07 06 05 2b 0e 03 02 1a 04 14"); | |||
private PkiTestUtils() { | |||
// no instances of this class | |||
} | |||
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 SubjectKeyIdentifier createSubjectKeyId(PublicKey publicKey) throws IOException { | |||
ByteArrayInputStream bais = new ByteArrayInputStream(publicKey.getEncoded()); | |||
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(bais).readObject()); | |||
return new SubjectKeyIdentifier(info); | |||
} | |||
private static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey publicKey) throws IOException { | |||
ByteArrayInputStream bais = new ByteArrayInputStream(publicKey.getEncoded()); | |||
SubjectPublicKeyInfo info = new SubjectPublicKeyInfo((ASN1Sequence) new ASN1InputStream(bais).readObject()); | |||
return new AuthorityKeyIdentifier(info); | |||
} | |||
public static X509Certificate generateCertificate(PublicKey subjectPublicKey, String subjectDn, | |||
X509Certificate issuerCertificate, PrivateKey issuerPrivateKey, boolean caFlag, int pathLength, String crlUri, | |||
String ocspUri, KeyUsage keyUsage) throws IOException, InvalidKeyException, IllegalStateException, | |||
NoSuchAlgorithmException, SignatureException, CertificateException { | |||
Date notBefore = makeDate(2010, 1, 1); | |||
Date notAfter = makeDate(2011, 1, 1); | |||
String signatureAlgorithm = "SHA1withRSA"; | |||
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator(); | |||
certificateGenerator.reset(); | |||
certificateGenerator.setPublicKey(subjectPublicKey); | |||
certificateGenerator.setSignatureAlgorithm(signatureAlgorithm); | |||
certificateGenerator.setNotBefore(notBefore); | |||
certificateGenerator.setNotAfter(notAfter); | |||
X509Principal issuerDN; | |||
if (null != issuerCertificate) { | |||
issuerDN = new X509Principal(issuerCertificate.getSubjectX500Principal().toString()); | |||
} else { | |||
issuerDN = new X509Principal(subjectDn); | |||
} | |||
certificateGenerator.setIssuerDN(issuerDN); | |||
certificateGenerator.setSubjectDN(new X509Principal(subjectDn)); | |||
certificateGenerator.setSerialNumber(new BigInteger(128, new SecureRandom())); | |||
certificateGenerator.addExtension(X509Extensions.SubjectKeyIdentifier, false, createSubjectKeyId(subjectPublicKey)); | |||
PublicKey issuerPublicKey; | |||
issuerPublicKey = subjectPublicKey; | |||
certificateGenerator.addExtension(X509Extensions.AuthorityKeyIdentifier, false, createAuthorityKeyId(issuerPublicKey)); | |||
if (caFlag) { | |||
if (-1 == pathLength) { | |||
certificateGenerator.addExtension(X509Extensions.BasicConstraints, false, new BasicConstraints(true)); | |||
} else { | |||
certificateGenerator.addExtension(X509Extensions.BasicConstraints, false, new BasicConstraints(pathLength)); | |||
} | |||
} | |||
if (null != crlUri) { | |||
GeneralName gn = new GeneralName(GeneralName.uniformResourceIdentifier, new DERIA5String(crlUri)); | |||
GeneralNames gns = new GeneralNames(new DERSequence(gn)); | |||
DistributionPointName dpn = new DistributionPointName(0, gns); | |||
DistributionPoint distp = new DistributionPoint(dpn, null, null); | |||
certificateGenerator.addExtension(X509Extensions.CRLDistributionPoints, false, new DERSequence(distp)); | |||
} | |||
if (null != ocspUri) { | |||
GeneralName ocspName = new GeneralName(GeneralName.uniformResourceIdentifier, ocspUri); | |||
AuthorityInformationAccess authorityInformationAccess = new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName); | |||
certificateGenerator.addExtension(X509Extensions.AuthorityInfoAccess.getId(), 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; | |||
} | |||
private static Date makeDate(int year, int month, int day) { | |||
Calendar c = Calendar.getInstance(); | |||
c.set(year, month, day, 0, 0, 0); | |||
c.set(Calendar.MILLISECOND, 0); | |||
return c.getTime(); | |||
} | |||
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(); | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.io.Serializable; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import org.apache.poi.ooxml.signature.service.signer.TemporaryDataStorage; | |||
class TemporaryTestDataStorage implements TemporaryDataStorage { | |||
private ByteArrayOutputStream outputStream; | |||
private Map<String, Serializable> attributes; | |||
public TemporaryTestDataStorage() { | |||
this.outputStream = new ByteArrayOutputStream(); | |||
this.attributes = new HashMap<String, Serializable>(); | |||
} | |||
public InputStream getTempInputStream() { | |||
byte[] data = this.outputStream.toByteArray(); | |||
ByteArrayInputStream inputStream = new ByteArrayInputStream(data); | |||
return inputStream; | |||
} | |||
public OutputStream getTempOutputStream() { | |||
return this.outputStream; | |||
} | |||
public Serializable getAttribute(String attributeName) { | |||
return this.attributes.get(attributeName); | |||
} | |||
public void setAttribute(String attributeName, Serializable attributeValue) { | |||
this.attributes.put(attributeName, attributeValue); | |||
} | |||
} |
@@ -1,209 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.File; | |||
import java.io.OutputStream; | |||
import java.net.URL; | |||
import java.security.KeyPair; | |||
import java.security.cert.X509Certificate; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import javax.crypto.Cipher; | |||
import junit.framework.TestCase; | |||
import org.apache.commons.io.FileUtils; | |||
import org.apache.commons.io.FilenameUtils; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang.ArrayUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.ooxml.signature.service.signer.ooxml.AbstractOOXMLSignatureService; | |||
import org.apache.poi.ooxml.signature.service.signer.ooxml.OOXMLProvider; | |||
import org.apache.poi.ooxml.signature.service.signer.ooxml.OOXMLSignatureVerifier; | |||
import org.apache.poi.ooxml.signature.service.spi.DigestInfo; | |||
import org.bouncycastle.asn1.x509.KeyUsage; | |||
public class TestAbstractOOXMLSignatureService extends TestCase { | |||
private static final Log LOG = LogFactory.getLog(TestAbstractOOXMLSignatureService.class); | |||
static { | |||
OOXMLProvider.install(); | |||
} | |||
private static class OOXMLTestSignatureService extends AbstractOOXMLSignatureService { | |||
private final URL _ooxmlUrl; | |||
private final TemporaryTestDataStorage _temporaryDataStorage; | |||
private final ByteArrayOutputStream _signedOOXMLOutputStream; | |||
public OOXMLTestSignatureService(URL ooxmlUrl) { | |||
_temporaryDataStorage = new TemporaryTestDataStorage(); | |||
_signedOOXMLOutputStream = new ByteArrayOutputStream(); | |||
_ooxmlUrl = ooxmlUrl; | |||
} | |||
@Override | |||
protected URL getOfficeOpenXMLDocumentURL() { | |||
return _ooxmlUrl; | |||
} | |||
@Override | |||
protected OutputStream getSignedOfficeOpenXMLDocumentOutputStream() { | |||
return _signedOOXMLOutputStream; | |||
} | |||
public byte[] getSignedOfficeOpenXMLDocumentData() { | |||
return _signedOOXMLOutputStream.toByteArray(); | |||
} | |||
@Override | |||
protected TemporaryDataStorage getTemporaryDataStorage() { | |||
return _temporaryDataStorage; | |||
} | |||
} | |||
public void testPreSign() throws Exception { | |||
// setup | |||
URL ooxmlUrl = TestAbstractOOXMLSignatureService.class.getResource("/hello-world-unsigned.docx"); | |||
assertNotNull(ooxmlUrl); | |||
OOXMLTestSignatureService signatureService = new OOXMLTestSignatureService(ooxmlUrl); | |||
// operate | |||
DigestInfo digestInfo = signatureService.preSign(null, null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
LOG.debug("digest algo: " + digestInfo.digestAlgo); | |||
LOG.debug("digest description: " + digestInfo.description); | |||
assertEquals("Office OpenXML Document", digestInfo.description); | |||
assertNotNull(digestInfo.digestAlgo); | |||
assertNotNull(digestInfo.digestValue); | |||
TemporaryDataStorage temporaryDataStorage = signatureService.getTemporaryDataStorage(); | |||
String preSignResult = IOUtils.toString(temporaryDataStorage.getTempInputStream()); | |||
LOG.debug("pre-sign result: " + preSignResult); | |||
File tmpFile = File.createTempFile("ooxml-pre-sign-", ".xml"); | |||
FileUtils.writeStringToFile(tmpFile, preSignResult); | |||
LOG.debug("tmp pre-sign file: " + tmpFile.getAbsolutePath()); | |||
} | |||
public void testPostSign() throws Exception { | |||
sign("/hello-world-unsigned.docx"); | |||
} | |||
public void testSignOffice2010() throws Exception { | |||
sign("/hello-world-office-2010-technical-preview-unsigned.docx"); | |||
} | |||
public void testSignTwice() throws Exception { | |||
sign("/hello-world-signed.docx", 2); | |||
} | |||
public void testSignTwiceHere() throws Exception { | |||
File tmpFile = sign("/hello-world-unsigned.docx", 1); | |||
sign(tmpFile.toURI().toURL(), "CN=Test2", 2); | |||
} | |||
public void testSignPowerpoint() throws Exception { | |||
sign("/hello-world-unsigned.pptx"); | |||
} | |||
public void testSignSpreadsheet() throws Exception { | |||
sign("/hello-world-unsigned.xlsx"); | |||
} | |||
private void sign(String documentResourceName) throws Exception { | |||
sign(documentResourceName, 1); | |||
} | |||
private File sign(String documentResourceName, int signerCount) throws Exception { | |||
URL ooxmlUrl = TestAbstractOOXMLSignatureService.class.getResource(documentResourceName); | |||
return sign(ooxmlUrl, signerCount); | |||
} | |||
private File sign(URL ooxmlUrl, int signerCount) throws Exception { | |||
return sign(ooxmlUrl, "CN=Test", signerCount); | |||
} | |||
private File sign(URL ooxmlUrl, String signerDn, int signerCount) throws Exception { | |||
// setup | |||
assertNotNull(ooxmlUrl); | |||
OOXMLTestSignatureService signatureService = new OOXMLTestSignatureService(ooxmlUrl); | |||
// operate | |||
DigestInfo digestInfo = signatureService.preSign(null, null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
LOG.debug("digest algo: " + digestInfo.digestAlgo); | |||
LOG.debug("digest description: " + digestInfo.description); | |||
assertEquals("Office OpenXML Document", digestInfo.description); | |||
assertNotNull(digestInfo.digestAlgo); | |||
assertNotNull(digestInfo.digestValue); | |||
TemporaryDataStorage temporaryDataStorage = signatureService.getTemporaryDataStorage(); | |||
String preSignResult = IOUtils.toString(temporaryDataStorage.getTempInputStream()); | |||
LOG.debug("pre-sign result: " + preSignResult); | |||
File tmpFile = File.createTempFile("ooxml-pre-sign-", ".xml"); | |||
FileUtils.writeStringToFile(tmpFile, preSignResult); | |||
LOG.debug("tmp pre-sign file: " + tmpFile.getAbsolutePath()); | |||
// setup: key material, signature value | |||
KeyPair keyPair = PkiTestUtils.generateKeyPair(); | |||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); | |||
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); | |||
byte[] digestInfoValue = ArrayUtils.addAll(PkiTestUtils.SHA1_DIGEST_INFO_PREFIX, digestInfo.digestValue); | |||
byte[] signatureValue = cipher.doFinal(digestInfoValue); | |||
X509Certificate certificate = PkiTestUtils.generateCertificate(keyPair.getPublic(), signerDn, null, keyPair.getPrivate(), true, 0, | |||
null, null, new KeyUsage(KeyUsage.nonRepudiation)); | |||
// operate: postSign | |||
signatureService.postSign(signatureValue, Collections.singletonList(certificate)); | |||
// verify: signature | |||
byte[] signedOOXMLData = signatureService.getSignedOfficeOpenXMLDocumentData(); | |||
assertNotNull(signedOOXMLData); | |||
LOG.debug("signed OOXML size: " + signedOOXMLData.length); | |||
String extension = FilenameUtils.getExtension(ooxmlUrl.getFile()); | |||
tmpFile = File.createTempFile("ooxml-signed-", "." + extension); | |||
FileUtils.writeByteArrayToFile(tmpFile, signedOOXMLData); | |||
LOG.debug("signed OOXML file: " + tmpFile.getAbsolutePath()); | |||
List<X509Certificate> signers = OOXMLSignatureVerifier.getSigners(tmpFile.toURI().toURL()); | |||
assertEquals(signerCount, signers.size()); | |||
// assertEquals(certificate, signers.get(0)); | |||
LOG.debug("signed OOXML file: " + tmpFile.getAbsolutePath()); | |||
return tmpFile; | |||
} | |||
} |
@@ -1,554 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.security.KeyPair; | |||
import java.security.MessageDigest; | |||
import java.security.cert.X509Certificate; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import javax.crypto.Cipher; | |||
import javax.xml.crypto.Data; | |||
import javax.xml.crypto.KeySelector; | |||
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.dom.DOMCryptoContext; | |||
import javax.xml.crypto.dsig.CanonicalizationMethod; | |||
import javax.xml.crypto.dsig.DigestMethod; | |||
import javax.xml.crypto.dsig.Reference; | |||
import javax.xml.crypto.dsig.SignatureMethod; | |||
import javax.xml.crypto.dsig.SignedInfo; | |||
import javax.xml.crypto.dsig.XMLSignContext; | |||
import javax.xml.crypto.dsig.XMLSignature; | |||
import javax.xml.crypto.dsig.XMLSignatureFactory; | |||
import javax.xml.crypto.dsig.dom.DOMSignContext; | |||
import javax.xml.crypto.dsig.dom.DOMValidateContext; | |||
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; | |||
import javax.xml.parsers.DocumentBuilder; | |||
import javax.xml.parsers.DocumentBuilderFactory; | |||
import junit.framework.TestCase; | |||
import org.apache.commons.lang.ArrayUtils; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.ooxml.signature.service.spi.DigestInfo; | |||
import org.apache.xml.security.utils.Constants; | |||
import org.apache.xpath.XPathAPI; | |||
import org.bouncycastle.asn1.x509.KeyUsage; | |||
import org.jcp.xml.dsig.internal.dom.DOMReference; | |||
import org.jcp.xml.dsig.internal.dom.DOMXMLSignature; | |||
import org.w3c.dom.Document; | |||
import org.w3c.dom.Element; | |||
import org.w3c.dom.Node; | |||
import org.w3c.dom.NodeList; | |||
public final class TestAbstractXmlSignatureService extends TestCase { | |||
private static final Log LOG = LogFactory.getLog(TestAbstractXmlSignatureService.class); | |||
private static class XmlSignatureTestService extends AbstractXmlSignatureService { | |||
private Document envelopingDocument; | |||
private List<String> referenceUris; | |||
private TemporaryTestDataStorage temporaryDataStorage; | |||
private String signatureDescription; | |||
private ByteArrayOutputStream signedDocumentOutputStream; | |||
private URIDereferencer uriDereferencer; | |||
public XmlSignatureTestService() { | |||
super(); | |||
this.referenceUris = new LinkedList<String>(); | |||
this.temporaryDataStorage = new TemporaryTestDataStorage(); | |||
this.signedDocumentOutputStream = new ByteArrayOutputStream(); | |||
} | |||
public byte[] getSignedDocumentData() { | |||
return this.signedDocumentOutputStream.toByteArray(); | |||
} | |||
public void setEnvelopingDocument(Document envelopingDocument) { | |||
this.envelopingDocument = envelopingDocument; | |||
} | |||
@Override | |||
protected Document getEnvelopingDocument() { | |||
return this.envelopingDocument; | |||
} | |||
@Override | |||
protected String getSignatureDescription() { | |||
return this.signatureDescription; | |||
} | |||
public void setSignatureDescription(String signatureDescription) { | |||
this.signatureDescription = signatureDescription; | |||
} | |||
@Override | |||
protected List<String> getReferenceUris() { | |||
return this.referenceUris; | |||
} | |||
public void addReferenceUri(String referenceUri) { | |||
this.referenceUris.add(referenceUri); | |||
} | |||
@Override | |||
protected OutputStream getSignedDocumentOutputStream() { | |||
return this.signedDocumentOutputStream; | |||
} | |||
@Override | |||
protected TemporaryDataStorage getTemporaryDataStorage() { | |||
return this.temporaryDataStorage; | |||
} | |||
public String getFilesDigestAlgorithm() { | |||
return null; | |||
} | |||
@Override | |||
protected URIDereferencer getURIDereferencer() { | |||
return this.uriDereferencer; | |||
} | |||
public void setUriDereferencer(URIDereferencer uriDereferencer) { | |||
this.uriDereferencer = uriDereferencer; | |||
} | |||
} | |||
private XMLSignatureFactory getXMLSignatureFactory() { | |||
return XMLSignatureFactory.getInstance("DOM", new org.jcp.xml.dsig.internal.dom.XMLDSigRI()); | |||
} | |||
public void testSignEnvelopingDocument() throws Exception { | |||
// setup | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.newDocument(); | |||
Element rootElement = document.createElementNS("urn:test", "tns:root"); | |||
rootElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "urn:test"); | |||
document.appendChild(rootElement); | |||
Element dataElement = document.createElementNS("urn:test", "tns:data"); | |||
dataElement.setAttributeNS(null, "Id", "id-1234"); | |||
dataElement.setTextContent("data to be signed"); | |||
rootElement.appendChild(dataElement); | |||
XmlSignatureTestService testedInstance = new XmlSignatureTestService(); | |||
testedInstance.setEnvelopingDocument(document); | |||
testedInstance.addReferenceUri("#id-1234"); | |||
testedInstance.setSignatureDescription("test-signature-description"); | |||
// operate | |||
DigestInfo digestInfo = testedInstance.preSign(null, null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
LOG.debug("digest info description: " + digestInfo.description); | |||
assertEquals("test-signature-description", digestInfo.description); | |||
assertNotNull(digestInfo.digestValue); | |||
LOG.debug("digest algo: " + digestInfo.digestAlgo); | |||
assertEquals("SHA-1", digestInfo.digestAlgo); | |||
TemporaryTestDataStorage temporaryDataStorage = (TemporaryTestDataStorage) testedInstance.getTemporaryDataStorage(); | |||
assertNotNull(temporaryDataStorage); | |||
InputStream tempInputStream = temporaryDataStorage.getTempInputStream(); | |||
assertNotNull(tempInputStream); | |||
Document tmpDocument = PkiTestUtils.loadDocument(tempInputStream); | |||
LOG.debug("tmp document: " + PkiTestUtils.toString(tmpDocument)); | |||
Element nsElement = tmpDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
Node digestValueNode = XPathAPI.selectSingleNode(tmpDocument, "//ds:DigestValue", nsElement); | |||
assertNotNull(digestValueNode); | |||
String digestValueTextContent = digestValueNode.getTextContent(); | |||
LOG.debug("digest value text content: " + digestValueTextContent); | |||
assertTrue(digestValueTextContent.length() > 0); | |||
/* | |||
* Sign the received XML signature digest value. | |||
*/ | |||
KeyPair keyPair = PkiTestUtils.generateKeyPair(); | |||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); | |||
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); | |||
byte[] digestInfoValue = ArrayUtils.addAll(PkiTestUtils.SHA1_DIGEST_INFO_PREFIX, digestInfo.digestValue); | |||
byte[] signatureValue = cipher.doFinal(digestInfoValue); | |||
X509Certificate certificate = PkiTestUtils.generateCertificate(keyPair.getPublic(), "CN=Test", null, keyPair.getPrivate(), true, | |||
0, null, null, new KeyUsage(KeyUsage.nonRepudiation)); | |||
/* | |||
* Operate: postSign | |||
*/ | |||
testedInstance.postSign(signatureValue, Collections.singletonList(certificate)); | |||
byte[] signedDocumentData = testedInstance.getSignedDocumentData(); | |||
assertNotNull(signedDocumentData); | |||
Document signedDocument = PkiTestUtils.loadDocument(new ByteArrayInputStream(signedDocumentData)); | |||
LOG.debug("signed document: " + PkiTestUtils.toString(signedDocument)); | |||
NodeList signatureNodeList = signedDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); | |||
assertEquals(1, signatureNodeList.getLength()); | |||
Node signatureNode = signatureNodeList.item(0); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(KeySelector.singletonKeySelector(keyPair.getPublic()), signatureNode); | |||
XMLSignatureFactory xmlSignatureFactory = getXMLSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
assertTrue(validity); | |||
} | |||
public static class UriTestDereferencer implements URIDereferencer { | |||
private final Map<String, byte[]> resources; | |||
public UriTestDereferencer() { | |||
this.resources = new HashMap<String, byte[]>(); | |||
} | |||
public void addResource(String uri, byte[] data) { | |||
this.resources.put(uri, data); | |||
} | |||
public Data dereference(URIReference uriReference, XMLCryptoContext xmlCryptoContext) throws URIReferenceException { | |||
String uri = uriReference.getURI(); | |||
byte[] data = this.resources.get(uri); | |||
if (null == data) { | |||
return null; | |||
} | |||
return new OctetStreamData(new ByteArrayInputStream(data)); | |||
} | |||
} | |||
public void testSignExternalUri() throws Exception { | |||
// setup | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.newDocument(); | |||
XmlSignatureTestService testedInstance = new XmlSignatureTestService(); | |||
testedInstance.setEnvelopingDocument(document); | |||
testedInstance.addReferenceUri("external-uri"); | |||
testedInstance.setSignatureDescription("test-signature-description"); | |||
UriTestDereferencer uriDereferencer = new UriTestDereferencer(); | |||
uriDereferencer.addResource("external-uri", "hello world".getBytes()); | |||
testedInstance.setUriDereferencer(uriDereferencer); | |||
// operate | |||
DigestInfo digestInfo = testedInstance.preSign(null, null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
LOG.debug("digest info description: " + digestInfo.description); | |||
assertEquals("test-signature-description", digestInfo.description); | |||
assertNotNull(digestInfo.digestValue); | |||
LOG.debug("digest algo: " + digestInfo.digestAlgo); | |||
assertEquals("SHA-1", digestInfo.digestAlgo); | |||
TemporaryTestDataStorage temporaryDataStorage = (TemporaryTestDataStorage) testedInstance.getTemporaryDataStorage(); | |||
assertNotNull(temporaryDataStorage); | |||
InputStream tempInputStream = temporaryDataStorage.getTempInputStream(); | |||
assertNotNull(tempInputStream); | |||
Document tmpDocument = PkiTestUtils.loadDocument(tempInputStream); | |||
LOG.debug("tmp document: " + PkiTestUtils.toString(tmpDocument)); | |||
Element nsElement = tmpDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
Node digestValueNode = XPathAPI.selectSingleNode(tmpDocument, "//ds:DigestValue", nsElement); | |||
assertNotNull(digestValueNode); | |||
String digestValueTextContent = digestValueNode.getTextContent(); | |||
LOG.debug("digest value text content: " + digestValueTextContent); | |||
assertTrue(digestValueTextContent.length() > 0); | |||
/* | |||
* Sign the received XML signature digest value. | |||
*/ | |||
KeyPair keyPair = PkiTestUtils.generateKeyPair(); | |||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); | |||
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); | |||
byte[] digestInfoValue = ArrayUtils.addAll(PkiTestUtils.SHA1_DIGEST_INFO_PREFIX, digestInfo.digestValue); | |||
byte[] signatureValue = cipher.doFinal(digestInfoValue); | |||
X509Certificate certificate = PkiTestUtils.generateCertificate(keyPair.getPublic(), "CN=Test", null, keyPair.getPrivate(), true, | |||
0, null, null, new KeyUsage(KeyUsage.nonRepudiation)); | |||
/* | |||
* Operate: postSign | |||
*/ | |||
testedInstance.postSign(signatureValue, Collections.singletonList(certificate)); | |||
byte[] signedDocumentData = testedInstance.getSignedDocumentData(); | |||
assertNotNull(signedDocumentData); | |||
Document signedDocument = PkiTestUtils.loadDocument(new ByteArrayInputStream(signedDocumentData)); | |||
LOG.debug("signed document: " + PkiTestUtils.toString(signedDocument)); | |||
NodeList signatureNodeList = signedDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); | |||
assertEquals(1, signatureNodeList.getLength()); | |||
Node signatureNode = signatureNodeList.item(0); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(KeySelector.singletonKeySelector(keyPair.getPublic()), signatureNode); | |||
domValidateContext.setURIDereferencer(uriDereferencer); | |||
XMLSignatureFactory xmlSignatureFactory = getXMLSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
assertTrue(validity); | |||
} | |||
public void testSignEnvelopingDocumentWithExternalDigestInfo() throws Exception { | |||
// setup | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.newDocument(); | |||
Element rootElement = document.createElementNS("urn:test", "tns:root"); | |||
rootElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "urn:test"); | |||
document.appendChild(rootElement); | |||
XmlSignatureTestService testedInstance = new XmlSignatureTestService(); | |||
testedInstance.setEnvelopingDocument(document); | |||
testedInstance.setSignatureDescription("test-signature-description"); | |||
byte[] refData = "hello world".getBytes(); | |||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); | |||
messageDigest.update(refData); | |||
byte[] digestValue = messageDigest.digest(); | |||
DigestInfo refDigestInfo = new DigestInfo(digestValue, "SHA-1", "urn:test:ref"); | |||
// operate | |||
DigestInfo digestInfo = testedInstance.preSign(Collections.singletonList(refDigestInfo), null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
LOG.debug("digest info description: " + digestInfo.description); | |||
assertEquals("test-signature-description", digestInfo.description); | |||
assertNotNull(digestInfo.digestValue); | |||
LOG.debug("digest algo: " + digestInfo.digestAlgo); | |||
assertEquals("SHA-1", digestInfo.digestAlgo); | |||
TemporaryTestDataStorage temporaryDataStorage = (TemporaryTestDataStorage) testedInstance.getTemporaryDataStorage(); | |||
assertNotNull(temporaryDataStorage); | |||
InputStream tempInputStream = temporaryDataStorage.getTempInputStream(); | |||
assertNotNull(tempInputStream); | |||
Document tmpDocument = PkiTestUtils.loadDocument(tempInputStream); | |||
LOG.debug("tmp document: " + PkiTestUtils.toString(tmpDocument)); | |||
Element nsElement = tmpDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
Node digestValueNode = XPathAPI.selectSingleNode(tmpDocument, "//ds:DigestValue", nsElement); | |||
assertNotNull(digestValueNode); | |||
String digestValueTextContent = digestValueNode.getTextContent(); | |||
LOG.debug("digest value text content: " + digestValueTextContent); | |||
assertTrue(digestValueTextContent.length() > 0); | |||
/* | |||
* Sign the received XML signature digest value. | |||
*/ | |||
KeyPair keyPair = PkiTestUtils.generateKeyPair(); | |||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); | |||
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); | |||
byte[] digestInfoValue = ArrayUtils.addAll(PkiTestUtils.SHA1_DIGEST_INFO_PREFIX, digestInfo.digestValue); | |||
byte[] signatureValue = cipher.doFinal(digestInfoValue); | |||
X509Certificate certificate = PkiTestUtils.generateCertificate(keyPair.getPublic(), "CN=Test", null, keyPair.getPrivate(), true, | |||
0, null, null, new KeyUsage(KeyUsage.nonRepudiation)); | |||
/* | |||
* Operate: postSign | |||
*/ | |||
testedInstance.postSign(signatureValue, Collections.singletonList(certificate)); | |||
byte[] signedDocumentData = testedInstance.getSignedDocumentData(); | |||
assertNotNull(signedDocumentData); | |||
Document signedDocument = PkiTestUtils.loadDocument(new ByteArrayInputStream(signedDocumentData)); | |||
LOG.debug("signed document: " + PkiTestUtils.toString(signedDocument)); | |||
NodeList signatureNodeList = signedDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); | |||
assertEquals(1, signatureNodeList.getLength()); | |||
Node signatureNode = signatureNodeList.item(0); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(KeySelector.singletonKeySelector(keyPair.getPublic()), signatureNode); | |||
URIDereferencer dereferencer = new URITest2Dereferencer(); | |||
domValidateContext.setURIDereferencer(dereferencer); | |||
XMLSignatureFactory xmlSignatureFactory = getXMLSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
assertTrue(validity); | |||
} | |||
public void testSignExternalDigestInfo() throws Exception { | |||
// setup | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.newDocument(); | |||
XmlSignatureTestService testedInstance = new XmlSignatureTestService(); | |||
testedInstance.setEnvelopingDocument(document); | |||
testedInstance.setSignatureDescription("test-signature-description"); | |||
byte[] refData = "hello world".getBytes(); | |||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); | |||
messageDigest.update(refData); | |||
byte[] digestValue = messageDigest.digest(); | |||
DigestInfo refDigestInfo = new DigestInfo(digestValue, "SHA-1", "urn:test:ref"); | |||
// operate | |||
DigestInfo digestInfo = testedInstance.preSign(Collections.singletonList(refDigestInfo), null); | |||
// verify | |||
assertNotNull(digestInfo); | |||
LOG.debug("digest info description: " + digestInfo.description); | |||
assertEquals("test-signature-description", digestInfo.description); | |||
assertNotNull(digestInfo.digestValue); | |||
LOG.debug("digest algo: " + digestInfo.digestAlgo); | |||
assertEquals("SHA-1", digestInfo.digestAlgo); | |||
TemporaryTestDataStorage temporaryDataStorage = (TemporaryTestDataStorage) testedInstance.getTemporaryDataStorage(); | |||
assertNotNull(temporaryDataStorage); | |||
InputStream tempInputStream = temporaryDataStorage.getTempInputStream(); | |||
assertNotNull(tempInputStream); | |||
Document tmpDocument = PkiTestUtils.loadDocument(tempInputStream); | |||
LOG.debug("tmp document: " + PkiTestUtils.toString(tmpDocument)); | |||
Element nsElement = tmpDocument.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
Node digestValueNode = XPathAPI.selectSingleNode(tmpDocument, "//ds:DigestValue", nsElement); | |||
assertNotNull(digestValueNode); | |||
String digestValueTextContent = digestValueNode.getTextContent(); | |||
LOG.debug("digest value text content: " + digestValueTextContent); | |||
assertTrue(digestValueTextContent.length() > 0); | |||
/* | |||
* Sign the received XML signature digest value. | |||
*/ | |||
KeyPair keyPair = PkiTestUtils.generateKeyPair(); | |||
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); | |||
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate()); | |||
byte[] digestInfoValue = ArrayUtils.addAll(PkiTestUtils.SHA1_DIGEST_INFO_PREFIX, digestInfo.digestValue); | |||
byte[] signatureValue = cipher.doFinal(digestInfoValue); | |||
X509Certificate certificate = PkiTestUtils.generateCertificate(keyPair.getPublic(), "CN=Test", null, keyPair.getPrivate(), true, | |||
0, null, null, new KeyUsage(KeyUsage.nonRepudiation)); | |||
/* | |||
* Operate: postSign | |||
*/ | |||
testedInstance.postSign(signatureValue, Collections.singletonList(certificate)); | |||
byte[] signedDocumentData = testedInstance.getSignedDocumentData(); | |||
assertNotNull(signedDocumentData); | |||
Document signedDocument = PkiTestUtils.loadDocument(new ByteArrayInputStream(signedDocumentData)); | |||
LOG.debug("signed document: " + PkiTestUtils.toString(signedDocument)); | |||
NodeList signatureNodeList = signedDocument.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); | |||
assertEquals(1, signatureNodeList.getLength()); | |||
Node signatureNode = signatureNodeList.item(0); | |||
DOMValidateContext domValidateContext = new DOMValidateContext(KeySelector.singletonKeySelector(keyPair.getPublic()), signatureNode); | |||
URIDereferencer dereferencer = new URITest2Dereferencer(); | |||
domValidateContext.setURIDereferencer(dereferencer); | |||
XMLSignatureFactory xmlSignatureFactory = getXMLSignatureFactory(); | |||
XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); | |||
boolean validity = xmlSignature.validate(domValidateContext); | |||
assertTrue(validity); | |||
} | |||
private static class URITest2Dereferencer implements URIDereferencer { | |||
private static final Log LOG = LogFactory.getLog(URITest2Dereferencer.class); | |||
public Data dereference(URIReference uriReference, XMLCryptoContext context) throws URIReferenceException { | |||
LOG.debug("dereference: " + uriReference.getURI()); | |||
return new OctetStreamData(new ByteArrayInputStream("hello world".getBytes())); | |||
} | |||
} | |||
public void testJsr105Signature() throws Exception { | |||
KeyPair keyPair = PkiTestUtils.generateKeyPair(); | |||
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); | |||
documentBuilderFactory.setNamespaceAware(true); | |||
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); | |||
Document document = documentBuilder.newDocument(); | |||
Element rootElement = document.createElementNS("urn:test", "tns:root"); | |||
rootElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:tns", "urn:test"); | |||
document.appendChild(rootElement); | |||
Element dataElement = document.createElementNS("urn:test", "tns:data"); | |||
dataElement.setAttributeNS(null, "Id", "id-1234"); | |||
dataElement.setTextContent("data to be signed"); | |||
rootElement.appendChild(dataElement); | |||
XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance("DOM", new org.jcp.xml.dsig.internal.dom.XMLDSigRI()); | |||
XMLSignContext signContext = new DOMSignContext(keyPair.getPrivate(), document.getDocumentElement()); | |||
signContext.putNamespacePrefix(javax.xml.crypto.dsig.XMLSignature.XMLNS, "ds"); | |||
DigestMethod digestMethod = signatureFactory.newDigestMethod(DigestMethod.SHA1, null); | |||
Reference reference = signatureFactory.newReference("#id-1234", digestMethod); | |||
DOMReference domReference = (DOMReference) reference; | |||
assertNull(domReference.getCalculatedDigestValue()); | |||
assertNull(domReference.getDigestValue()); | |||
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(SignatureMethod.RSA_SHA1, null); | |||
CanonicalizationMethod canonicalizationMethod = signatureFactory.newCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS, | |||
(C14NMethodParameterSpec) null); | |||
SignedInfo signedInfo = signatureFactory.newSignedInfo(canonicalizationMethod, signatureMethod, Collections.singletonList(reference)); | |||
javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory.newXMLSignature(signedInfo, null); | |||
DOMXMLSignature domXmlSignature = (DOMXMLSignature) xmlSignature; | |||
domXmlSignature.marshal(document.getDocumentElement(), "ds", (DOMCryptoContext) signContext); | |||
domReference.digest(signContext); | |||
// xmlSignature.sign(signContext); | |||
// LOG.debug("signed document: " + toString(document)); | |||
Element nsElement = document.createElement("ns"); | |||
nsElement.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:ds", Constants.SignatureSpecNS); | |||
Node digestValueNode = XPathAPI.selectSingleNode(document, "//ds:DigestValue", nsElement); | |||
assertNotNull(digestValueNode); | |||
String digestValueTextContent = digestValueNode.getTextContent(); | |||
LOG.debug("digest value text content: " + digestValueTextContent); | |||
assertTrue(digestValueTextContent.length() > 0); | |||
} | |||
} |
@@ -1,238 +0,0 @@ | |||
/* ==================================================================== | |||
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. | |||
==================================================================== */ | |||
/* | |||
* Based on the eID Applet Project code. | |||
* Original Copyright (C) 2008-2009 FedICT. | |||
*/ | |||
package org.apache.poi.ooxml.signature.service.signer; | |||
import java.io.InputStream; | |||
import java.net.URL; | |||
import java.security.cert.X509Certificate; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.logging.Level; | |||
import junit.framework.TestCase; | |||
import org.apache.commons.logging.Log; | |||
import org.apache.commons.logging.LogFactory; | |||
import org.apache.poi.POIXMLDocument; | |||
import org.apache.poi.ooxml.signature.service.signer.ooxml.OOXMLProvider; | |||
import org.apache.poi.ooxml.signature.service.signer.ooxml.OOXMLSignatureVerifier; | |||
import org.apache.poi.openxml4j.opc.OPCPackage; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.signature.PackageDigitalSignatureManager; | |||
public class TestOOXMLSignatureVerifier extends TestCase { | |||
private static final Log LOG = LogFactory.getLog(TestOOXMLSignatureVerifier.class); | |||
static { | |||
OOXMLProvider.install(); | |||
} | |||
public void testIsOOXMLDocument() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-unsigned.docx"); | |||
// operate | |||
boolean result = OOXMLSignatureVerifier.isOOXML(url); | |||
// verify | |||
assertTrue(result); | |||
} | |||
public void testPOI() throws Exception { | |||
// setup | |||
InputStream inputStream = TestOOXMLSignatureVerifier.class.getResourceAsStream("/hello-world-unsigned.docx"); | |||
// operate | |||
boolean result = POIXMLDocument.hasOOXMLHeader(inputStream); | |||
// verify | |||
assertTrue(result); | |||
} | |||
public void testOPC() throws Exception { | |||
// setup | |||
InputStream inputStream = TestOOXMLSignatureVerifier.class.getResourceAsStream("/hello-world-signed.docx"); | |||
// operate | |||
OPCPackage opcPackage = OPCPackage.open(inputStream); | |||
ArrayList<PackagePart> parts = opcPackage.getParts(); | |||
for (PackagePart part : parts) { | |||
LOG.debug("part name: " + part.getPartName().getName()); | |||
LOG.debug("part content type: " + part.getContentType()); | |||
} | |||
ArrayList<PackagePart> signatureParts = opcPackage.getPartsByContentType("application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"); | |||
assertFalse(signatureParts.isEmpty()); | |||
PackagePart signaturePart = signatureParts.get(0); | |||
LOG.debug("signature part class type: " + signaturePart.getClass().getName()); | |||
PackageDigitalSignatureManager packageDigitalSignatureManager = new PackageDigitalSignatureManager(); | |||
// yeah... POI implementation still missing | |||
} | |||
public void testGetSignerUnsigned() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-unsigned.docx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertTrue(result.isEmpty()); | |||
} | |||
public void testGetSignerOffice2010Unsigned() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-office-2010-technical-preview-unsigned.docx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertTrue(result.isEmpty()); | |||
} | |||
public void testGetSignerUnsignedPowerpoint() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-unsigned.pptx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertTrue(result.isEmpty()); | |||
} | |||
public void testGetSignerUnsignedExcel() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-unsigned.xlsx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertTrue(result.isEmpty()); | |||
} | |||
public void testGetSigner() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-signed.docx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertEquals(1, result.size()); | |||
X509Certificate signer = result.get(0); | |||
LOG.debug("signer: " + signer.getSubjectX500Principal()); | |||
} | |||
public void testOffice2010TechnicalPreview() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-office-2010-technical-preview.docx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertEquals(1, result.size()); | |||
X509Certificate signer = result.get(0); | |||
LOG.debug("signer: " + signer.getSubjectX500Principal()); | |||
} | |||
public void testGetSignerPowerpoint() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-signed.pptx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertEquals(1, result.size()); | |||
X509Certificate signer = result.get(0); | |||
LOG.debug("signer: " + signer.getSubjectX500Principal()); | |||
} | |||
public void testGetSignerExcel() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-signed.xlsx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertEquals(1, result.size()); | |||
X509Certificate signer = result.get(0); | |||
LOG.debug("signer: " + signer.getSubjectX500Principal()); | |||
} | |||
public void testGetSigners() throws Exception { | |||
// setup | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-signed-twice.docx"); | |||
// operate | |||
List<X509Certificate> result = OOXMLSignatureVerifier.getSigners(url); | |||
// verify | |||
assertNotNull(result); | |||
assertEquals(2, result.size()); | |||
X509Certificate signer1 = result.get(0); | |||
X509Certificate signer2 = result.get(1); | |||
LOG.debug("signer 1: " + signer1.getSubjectX500Principal()); | |||
LOG.debug("signer 2: " + signer2.getSubjectX500Principal()); | |||
} | |||
public void testVerifySignature() throws Exception { | |||
java.util.logging.Logger logger = java.util.logging.Logger.getLogger("org.jcp.xml.dsig.internal.dom"); | |||
logger.log(Level.FINE, "test"); | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/hello-world-signed.docx"); | |||
boolean validity = OOXMLSignatureVerifier.verifySignature(url); | |||
assertTrue(validity); | |||
} | |||
public void testTamperedFile() throws Exception { | |||
java.util.logging.Logger logger = java.util.logging.Logger.getLogger("org.jcp.xml.dsig.internal.dom"); | |||
logger.log(Level.FINE, "test"); | |||
URL url = TestOOXMLSignatureVerifier.class.getResource("/invalidsig.docx"); | |||
boolean validity = OOXMLSignatureVerifier.verifySignature(url); | |||
assertFalse(validity); | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
/* ==================================================================== | |||
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.ooxml.signature.service.signer.ooxml; | |||
import java.util.Calendar; | |||
import java.util.TimeZone; | |||
import junit.framework.TestCase; | |||
public final class TestOOXMLSignatureAspect extends TestCase { | |||
private static final TimeZone TIME_ZONE_UTC = TimeZone.getTimeZone("UTC"); | |||
public void testFormatTimestampAsISO8601() { | |||
assertEquals("2010-06-05T04:03:02Z", OOXMLSignatureAspect.formatTimestampAsISO8601(makeTimestamp(2010, 6, 5, 4, 3, 2))); | |||
} | |||
private static long makeTimestamp(int year, int month, int day, int hour, int minute, int second) { | |||
Calendar c = Calendar.getInstance(); | |||
c.setTimeZone(TIME_ZONE_UTC); | |||
c.set(year, month-1, day, hour, minute, second); | |||
c.set(Calendar.MILLISECOND, 0); | |||
return c.getTimeInMillis(); | |||
} | |||
} |