Author: kiwiwings
Date: Wed Jun 28 21:38:23 2017
New Revision: 1800207
URL: http://svn.apache.org/viewvc?rev=1800207&view=rev
Log:
Bug 61182 - Invalid signature created for streamed xslx file
Removed:
poi/trunk/src/ooxml/java/org/apache/poi/util/XmlSort.java
Modified:
poi/site/src/documentation/content/xdocs/encryption.xml
poi/site/src/documentation/content/xdocs/status.xml
poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
Modified: poi/site/src/documentation/content/xdocs/encryption.xml
URL:
http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/encryption.xml?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/encryption.xml (original)
+++ poi/site/src/documentation/content/xdocs/encryption.xml Wed Jun 28 21:38:23
2017
@@ -31,9 +31,9 @@
<body>
<section><title>Overview</title>
- <p>Apache POI contains support for reading few variants of encrypted
office files: </p>
- <ul>
- <li>Binary formats (.xls, .ppt, .doc, ...)<br/>
+ <p>Apache POI contains support for reading few variants of encrypted
office files: </p>
+ <ul>
+ <li>Binary formats (.xls, .ppt, .doc, ...)<br/>
encryption is format-dependent and needs to be implemented per format
differently.<br/>
Use <link
href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
Biff8EncryptionKey</link>.<link
href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html#setCurrentUserPassword(java.lang.String)">setCurrentUserPassword</link>(String
password)
@@ -41,7 +41,7 @@
Setting a null password before saving removes the password
protection.<br/>
The password is set in a thread local variable. Do not forget to reset
it to null after text extraction.
</li>
- <li>XML-based formats (.xlsx, .pptx, .docx, ...)<br/>
+ <li>XML-based formats (.xlsx, .pptx, .docx, ...)<br/>
use the same encryption logic over all formats. When encrypted, the
zipped files will be
stored within an OLE file in the EncryptedPackage stream.<br/>
If you plan to use POI to actually generate encrypted documents, be
aware not to use anything less than
@@ -54,13 +54,13 @@
<link
href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JDK7</link>,
<link
href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JDK8</link>).
</li>
- </ul>
+ </ul>
- <p>Some "write-protected" files are encrypted with the built-in
password "VelvetSweatshop", POI can read that files too.</p>
- </section>
+ <p>Some "write-protected" files are encrypted with the built-in password
"VelvetSweatshop", POI can read that files too.</p>
+ </section>
<section><title>Supported feature matrix</title>
-
+
<table>
<tr>
<th>Encryption</th>
@@ -87,7 +87,7 @@
<td class="feature-yes">Yes (since 3.17)</td>
</tr>
<tr>
- <th/>
+ <th/>
<th>XSSF</th>
<th>XSLF</th>
<th>XWPF</th>
@@ -117,22 +117,22 @@
<td class="feature-yes">Yes</td>
</tr>
</table>
-
+
<p>*) the xor encryption is flawed and works only for very small files -
see <link
href="https://bz.apache.org/bugzilla/show_bug.cgi?id=59857">#59857</link>.
</p>
<p>**) the <link
href="https://msdn.microsoft.com/en-us/library/cc313071(v=office.12).aspx">MS-OFFCRYPTO</link>
documentation only mentions the RC4 (without CryptoAPI) encryption as a
"in place" encryption, but
- apparently there's also a container based method with that key generation
logic.
+ apparently there's also a container based method with that key generation
logic.
</p>
</section>
<section><title>Binary formats</title>
- <p>As mentioned above, use
+ <p>As mentioned above, use
<link
href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
Biff8EncryptionKey</link>.<link
href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html#setCurrentUserPassword(java.lang.String)">setCurrentUserPassword</link>(String
password)
to specify the password.</p>
-
+
<source>
// XOR/RC4 decryption for xls
Biff8EncryptionKey.setCurrentUserPassword("pass");
@@ -140,7 +140,7 @@ NPOIFSFileSystem fs = new NPOIFSFileSyst
HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true);
Biff8EncryptionKey.setCurrentUserPassword(null);
</source>
-
+
<source>
// RC4 CryptoApi support ppt - decryption
Biff8EncryptionKey.setCurrentUserPassword("pass");
@@ -166,10 +166,10 @@ os.close();
</section>
<section><title>XML-based formats - Decryption</title>
- <p>XML-based formats are stored in OLE-package stream
"EncryptedPackage". Use org.apache.poi.poifs.crypt.Decryptor
- to decode file:</p>
+ <p>XML-based formats are stored in OLE-package stream "EncryptedPackage".
Use org.apache.poi.poifs.crypt.Decryptor
+ to decode file:</p>
- <source>
+ <source>
EncryptionInfo info = new EncryptionInfo(filesystem);
Decryptor d = Decryptor.getInstance(info);
@@ -185,11 +185,11 @@ try {
} catch (GeneralSecurityException ex) {
throw new RuntimeException("Unable to process encrypted document", ex);
}
- </source>
+ </source>
- <p>If you want to read file encrypted with build-in password, use
Decryptor.DEFAULT_PASSWORD.</p>
+ <p>If you want to read file encrypted with build-in password, use
Decryptor.DEFAULT_PASSWORD.</p>
</section>
-
+
<section><title>XML-based formats - Encryption</title>
<p>Encrypting a file is similar to the above decryption process.
Basically you'll need to choose between
<link
href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/EncryptionMode.html">binaryRC4,
standard and agile encryption</link>,
@@ -213,10 +213,10 @@ opc.close();
// Write out the encrypted version
FileOutputStream fos = new FileOutputStream("...");
fs.writeFilesystem(fos);
-fos.close();
+fos.close();
</source>
</section>
-
+
<section><title>XML-based formats - Signing (XML Signature)</title>
<p>An Office document can be digital signed by a <link
href="https://en.wikipedia.org/wiki/XML_Signature">XML Signature</link>
to protect it from unauthorized modifications, i.e. modifications without
having the original certificate.
@@ -231,17 +231,17 @@ fos.close();
<ul>
<li>BouncyCastle bcpkix and bcprov (tested against 1.53)</li>
<li>Apache Santuario "xmlsec" (tested against 2.0.6)</li>
- <li>and slf4j-api (tested against 1.7.12)</li>
+ <li>and slf4j-api (tested against 1.7.12)</li>
</ul>
<p>Depending on the <link
href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/SignatureConfig.html">configuration</link>
and the activated <link
href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/facets/package-summary.html">facets</link>
various <link href="https://en.wikipedia.org/wiki/XAdES">XAdES
levels</link> are supported - the support for higher levels (XAdES-T+)
depend on supporting services and although the code is adopted, the
integration is not well tested ... please support us on
- integration (testing) with timestamp and revocation (OCSP) services.
+ integration (testing) with timestamp and revocation (OCSP) services.
</p>
<p>Further test examples can be found in the corresponding <link
href="https://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java?view=markup">test
class</link>.</p>
</section>
-
+
<section><title>Validating a signed office document</title>
<source>
@@ -254,9 +254,10 @@ boolean isValid = si.verifySignature();
...
</source>
</section>
-
+
<section><title>Signing an office document</title>
-
+
+ <section><title>Signing a file</title>
<source>
// loading the keystore - pkcs12 is used here, but of course jks & co are
also valid
// the keystore needs to contain a private key and it's certificate having a
@@ -292,6 +293,38 @@ pkg.close();
</source>
</section>
+ <section><title>Signing a stream - in-memory</title>
+ <p>When saving a OOXML document, POI creates missing relations on the
fly. Therefore calling the signing method before
+ would result in an invalid signature. Instead of trying to fix all save
invocations, the user is asked to save the stream
+ before in a intermediate byte array (stream) and process this stream
instead.</p>
+
+ <source>
+// load the key and setup SignatureConfig ... - see "Signing a file"
+
+SignatureInfo si = new SignatureInfo();
+si.setSignatureConfig(signatureConfig);
+
+// populate sample object
+XSSFWorkbook wb = new XSSFWorkbook();
+wb.createSheet().createRow(1).createCell(1).setCellValue("Test");
+ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+wb.write(bos);
+wb.close();
+
+// process the
+OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()));
+
+signatureConfig.setOpcPackage(pkg);
+si.confirmSignature();
+bos.reset();
+pkg.save(bos);
+pkg.close();
+
+// bos now contains the signed ooxml document
+ </source>
+ </section>
+ </section>
+
<section><title>Encrypting temporary files created when unzipping an
OOXML document</title>
<p>For security-conscious environments where data at rest must be
stored encrypted,
the creation of plaintext temporary files is a grey area.</p>
@@ -304,13 +337,137 @@ pkg.close();
and other <link
href="https://svn.apache.org/viewvc?view=revision&revision=1768744">files</link>
that are needed for this example.</p>
</section>
+
+ <section><title>Debugging XML signature issues</title>
+ <p>Finding the source of a XML signature problem can be sometimes a
pain in the ... neck, because
+ the hashing of the canonicalized form is more or less intransparent
done in the background.</p>
+
+ <!-- TODO: find original source -->
+ <p>One of the tripping hazards are <link
href="https://stackoverflow.com/questions/36063375">different
+ linebreaks in Windows/Unix</link>, therefore use the non-indent form of
the xmls.</p>
+
+ <p>The next thing is to compare successful signed documents from Office
vs. POIs generated signature,
+ i.e. unzip both files and look for differences. Usually the package
relations (*.rels) will be different,
+ and the sig1.xml, core.xml and [Content_Types].xml due to different
order of the references.</p>
+
+ <p>The package relationsships (*.rels) will be specially handled, i.e.
they will be filtered and only
+ a subset will be processed - see <link
href="https://www.ecma-international.org/activities/Office%20Open%20XML%20Formats/Draft%20ECMA-376%203rd%20edition,%20March%202011/Office%20Open%20XML%20Part%202%20-%20Open%20Packaging%20Conventions.pdf">13.2.4.24
Relationships Transform Algorithm</link>.</p>
+
+ <p>To check the processed files in the canonicalized form, the below
UnsyncBufferedOutputStream class needs
+ to be injected/replaced. Put the .class file in separate directory and
add the following JVM parameters:</p>
+
+ <source>
+-Djava.io.tmpdir=<em><custom temp directory></em>
+-Xbootclasspath/p:<em><preload dir, which contains
/org/apache/xml/security/utils/UnsyncBufferedOutputStream.class></em>
+-Dorg.apache.poi.util.POILogger=org.apache.poi.util.CommonsLogger
+-Djava.util.logging.config.file=<em><a dir containing
...></em>/logging.properties
+ </source>
+
+ <section><title>UnsyncBufferedOutputStream:</title>
+ <source>
+package org.apache.xml.security.utils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class UnsyncBufferedOutputStream extends OutputStream {
+ static final int size = 8*1024;
+ static int filecnt = 0;
+
+ private int pointer = 0;
+ private final OutputStream out;
+ private final FileOutputStream out2;
+
+ private final byte[] buf;
+
+ public UnsyncBufferedOutputStream(OutputStream out) {
+ buf = new byte[size];
+ this.out = out;
+ synchronized(UnsyncBufferedOutputStream.class) {
+ try {
+ String tmpDir = System.getProperty("java.io.tmpdir");
+ if (tmpDir == null) {
+ tmpDir = "build";
+ }
+ File f = new File(tmpDir, "unsync-"+filecnt+".xml");
+ out2 = new FileOutputStream(f);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ filecnt++;
+ }
+ }
+ }
+
+ public void write(byte[] arg0) throws IOException {
+ write(arg0, 0, arg0.length);
+ }
+
+ public void write(byte[] arg0, int arg1, int len) throws IOException {
+ int newLen = pointer+len;
+ if (newLen > size) {
+ flushBuffer();
+ if (len > size) {
+ out.write(arg0, arg1,len);
+ out2.write(arg0, arg1,len);
+ return;
+ }
+ newLen = len;
+ }
+ System.arraycopy(arg0, arg1, buf, pointer, len);
+ pointer = newLen;
+ }
+
+ private void flushBuffer() throws IOException {
+ if (pointer > 0) {
+ out.write(buf, 0, pointer);
+ out2.write(buf, 0, pointer);
+ }
+ pointer = 0;
+
+ }
+
+ public void write(int arg0) throws IOException {
+ if (pointer >= size) {
+ flushBuffer();
+ }
+ buf[pointer++] = (byte)arg0;
+
+ }
+
+ public void flush() throws IOException {
+ flushBuffer();
+ out.flush();
+ out2.flush();
+ }
+
+ public void close() throws IOException {
+ flush();
+ out.close();
+ out2.close();
+ }
+
+}
+</source>
+</section>
+
+ <section><title>logging.properties</title>
+ <source>
+handlers = org.slf4j.bridge.SLF4JBridgeHandler
+.level=ALL
+org.slf4j.bridge.SLF4JBridgeHandler.level=ALL
+ </source>
+ </section>
+ </section>
</body>
<footer>
<legal>
Copyright (c) @year@ The Apache Software Foundation. All rights reserved.
<br />
- Apache POI, POI, Apache, the Apache feather logo, and the Apache
+ Apache POI, POI, Apache, the Apache feather logo, and the Apache
POI project logo are trademarks of The Apache Software Foundation.
</legal>
</footer>
Modified: poi/site/src/documentation/content/xdocs/status.xml
URL:
http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Wed Jun 28 21:38:23 2017
@@ -56,8 +56,11 @@
when referring to both H??F and X??F formats.
-->
- <!-- release version="3.17-beta2" date="2017-09-??">
- </release -->
+ <release version="3.17-beta2" date="2017-09-??">
+ <actions>
+ <action dev="PD" type="fix" fixes-bug="61182"
module="OPC">Invalid signature created for streamed xslx file</action>
+ </actions>
+ </release>
<release version="3.17-beta1" date="2017-07-01">
<summary>
Modified:
poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
(original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java Wed
Jun 28 21:38:23 2017
@@ -74,8 +74,12 @@ public final class StreamHelper {
out.flush(); // only flush, don't close!
}
});
+ // xmlContent.setXmlStandalone(true);
trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- trans.setOutputProperty(OutputKeys.INDENT, "yes");
+ // don't indent xml documents, the indent will cause errors in
calculating the xml signature
+ // because of different handling of linebreaks in Windows/Unix
+ // see https://stackoverflow.com/questions/36063375
+ trans.setOutputProperty(OutputKeys.INDENT, "no");
trans.setOutputProperty(OutputKeys.STANDALONE, "yes");
trans.transform(xmlSource, outputTarget);
} catch (TransformerException e) {
Modified:
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
---
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
(original)
+++
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
Wed Jun 28 21:38:23 2017
@@ -40,24 +40,32 @@ public class SignatureMarshalListener im
this.target.set(target);
}
+ @Override
public void handleEvent(Event e) {
- if (!(e instanceof MutationEvent)) return;
+ if (!(e instanceof MutationEvent)) {
+ return;
+ }
MutationEvent mutEvt = (MutationEvent)e;
EventTarget et = mutEvt.getTarget();
- if (!(et instanceof Element)) return;
+ if (!(et instanceof Element)) {
+ return;
+ }
handleElement((Element)et);
}
public void handleElement(Element el) {
EventTarget target = this.target.get();
- String packageId = signatureConfig.getPackageSignatureId();
+
if (el.hasAttribute("Id")) {
el.setIdAttribute("Id", true);
}
setListener(target, this, false);
- if (packageId.equals(el.getAttribute("Id"))) {
- el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
+ if (OO_DIGSIG_NS.equals(el.getNamespaceURI())) {
+ String parentNS = el.getParentNode().getNamespaceURI();
+ if (!OO_DIGSIG_NS.equals(parentNS) && !el.hasAttributeNS(XML_NS,
"mdssi")) {
+ el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
+ }
}
setPrefix(el);
setListener(target, this, true);
@@ -86,6 +94,7 @@ public class SignatureMarshalListener im
}
}
+ @Override
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}
Modified:
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
---
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
(original)
+++
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
Wed Jun 28 21:38:23 2017
@@ -18,9 +18,9 @@
/* ====================================================================
This product contains an ASLv2 licensed version of the OOXML signer
package from the eID Applet project
- http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
+ http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
Copyright (C) 2008-2014 FedICT.
- ================================================================= */
+ ================================================================= */
package org.apache.poi.poifs.crypt.dsig.facets;
@@ -29,6 +29,9 @@ import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -70,13 +73,13 @@ import com.microsoft.schemas.office.x200
/**
* Office OpenXML Signature Facet implementation.
- *
- * @author fcorneli
+ *
* @see <a
href="http://msdn.microsoft.com/en-us/library/cc313071.aspx">[MS-OFFCRYPTO]:
Office Document Cryptography Structure</a>
*/
public class OOXMLSignatureFacet extends SignatureFacet {
private static final POILogger LOG =
POILogFactory.getLogger(OOXMLSignatureFacet.class);
+ private static final String ID_PACKAGE_OBJECT = "idPackageObject";
@Override
public void preSign(
@@ -98,17 +101,16 @@ public class OOXMLSignatureFacet extends
List<Reference> manifestReferences = new ArrayList<Reference>();
addManifestReferences(manifestReferences);
Manifest manifest =
getSignatureFactory().newManifest(manifestReferences);
-
- String objectId = "idPackageObject"; // really has to be this value.
+
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
objectContent.add(manifest);
addSignatureTime(document, objectContent);
- XMLObject xo = getSignatureFactory().newXMLObject(objectContent,
objectId, null, null);
+ XMLObject xo = getSignatureFactory().newXMLObject(objectContent,
ID_PACKAGE_OBJECT, null, null);
objects.add(xo);
- Reference reference = newReference("#" + objectId, null,
XML_DIGSIG_NS+"Object", null, null);
+ Reference reference = newReference("#"+ID_PACKAGE_OBJECT, null,
XML_DIGSIG_NS+"Object", null, null);
references.add(reference);
}
@@ -121,7 +123,7 @@ public class OOXMLSignatureFacet extends
Set<String> digestedPartNames = new HashSet<String>();
for (PackagePart pp : relsEntryNames) {
- String baseUri =
pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
+ final String baseUri =
pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
PackageRelationshipCollection prc;
try {
@@ -130,11 +132,11 @@ public class OOXMLSignatureFacet extends
} catch (InvalidFormatException e) {
throw new XMLSignatureException("Invalid relationship
descriptor: "+pp.getPartName().getName(), e);
}
-
+
RelationshipTransformParameterSpec parameterSpec = new
RelationshipTransformParameterSpec();
for (PackageRelationship relationship : prc) {
String relationshipType = relationship.getRelationshipType();
-
+
/*
* ECMA-376 Part 2 - 3rd edition
* 13.2.4.16 Manifest Element
@@ -144,22 +146,20 @@ public class OOXMLSignatureFacet extends
continue;
}
- if (!isSignedRelationship(relationshipType)) continue;
+ if (!isSignedRelationship(relationshipType)) {
+ continue;
+ }
parameterSpec.addRelationshipReference(relationship.getId());
- // TODO: find a better way ...
- String partName = relationship.getTargetURI().toString();
- if (!partName.startsWith(baseUri)) {
- partName = baseUri + partName;
- }
- try {
- partName = new
URI(partName).normalize().getPath().replace('\\', '/');
- LOG.log(POILogger.DEBUG, "part name: " + partName);
- } catch (URISyntaxException e) {
- throw new XMLSignatureException(e);
+ String partName =
normalizePartName(relationship.getTargetURI(), baseUri);
+
+ // We only digest a part once.
+ if (digestedPartNames.contains(partName)) {
+ continue;
}
-
+ digestedPartNames.add(partName);
+
String contentType;
try {
PackagePartName relName =
PackagingURIHelper.createPartName(partName);
@@ -168,32 +168,52 @@ public class OOXMLSignatureFacet extends
} catch (InvalidFormatException e) {
throw new XMLSignatureException(e);
}
-
+
if (relationshipType.endsWith("customXml")
&& !(contentType.equals("inkml+xml") ||
contentType.equals("text/xml"))) {
LOG.log(POILogger.DEBUG, "skipping customXml with content
type: " + contentType);
continue;
}
-
- if (!digestedPartNames.contains(partName)) {
- // We only digest a part once.
- String uri = partName + "?ContentType=" + contentType;
- Reference reference = newReference(uri, null, null, null,
null);
- manifestReferences.add(reference);
- digestedPartNames.add(partName);
- }
+
+ String uri = partName + "?ContentType=" + contentType;
+ Reference reference = newReference(uri, null, null, null,
null);
+ manifestReferences.add(reference);
}
-
+
if (parameterSpec.hasSourceIds()) {
List<Transform> transforms = new ArrayList<Transform>();
transforms.add(newTransform(RelationshipTransformService.TRANSFORM_URI,
parameterSpec));
transforms.add(newTransform(CanonicalizationMethod.INCLUSIVE));
- String uri = pp.getPartName().getName()
+ String uri = normalizePartName(pp.getPartName().getURI(),
baseUri)
+
"?ContentType=application/vnd.openxmlformats-package.relationships+xml";
Reference reference = newReference(uri, transforms, null,
null, null);
manifestReferences.add(reference);
}
}
+
+ Collections.sort(manifestReferences, new Comparator<Reference>() {
+ public int compare(Reference o1, Reference o2) {
+ return o1.getURI().compareTo(o2.getURI());
+ }
+ });
+ }
+
+ /**
+ * Normalize a URI/part name
+ * TODO: find a better way ...
+ */
+ private static String normalizePartName(URI partName, String baseUri)
throws XMLSignatureException {
+ String pn = partName.toASCIIString();
+ if (!pn.startsWith(baseUri)) {
+ pn = baseUri + pn;
+ }
+ try {
+ pn = new URI(pn).normalize().getPath().replace('\\', '/');
+ LOG.log(POILogger.DEBUG, "part name: " + pn);
+ } catch (URISyntaxException e) {
+ throw new XMLSignatureException(e);
+ }
+ return pn;
}
@@ -236,7 +256,7 @@ public class OOXMLSignatureFacet extends
ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
-
+
List<XMLStructure> signatureInfoContent = new
ArrayList<XMLStructure>();
signatureInfoContent.add(new DOMStructure(n));
SignatureProperty signatureInfoSignatureProperty =
getSignatureFactory()
@@ -268,208 +288,33 @@ public class OOXMLSignatureFacet extends
protected static boolean isSignedRelationship(String relationshipType) {
LOG.log(POILogger.DEBUG, "relationship type: " + relationshipType);
- for (String signedTypeExtension : signed) {
- if (relationshipType.endsWith(signedTypeExtension)) {
- return true;
- }
- }
- if (relationshipType.endsWith("customXml")) {
- LOG.log(POILogger.DEBUG, "customXml relationship type");
- return true;
- }
- return false;
+ String rt = relationshipType.replaceFirst(".*/relationships/", "");
+ return (signed.contains(rt) || rt.endsWith("customXml"));
}
-
- public static final String[] contentTypes = {
- /*
- * Word
- */
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml",
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
- "application/vnd.openxmlformats-officedocument.theme+xml",
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml",
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
-
- /*
- * Word 2010
- */
- "application/vnd.ms-word.stylesWithEffects+xml",
-
- /*
- * Excel
- */
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
-
- /*
- * Powerpoint
- */
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml",
-
"application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml",
-
"application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml",
-
"application/vnd.openxmlformats-officedocument.presentationml.slide+xml",
-
"application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml",
-
- /*
- * Powerpoint 2010
- */
-
"application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml",
-
"application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"
- };
/**
* Office 2010 list of signed types (extensions).
*/
- public static final String[] signed = {
- "powerPivotData", //
- "activeXControlBinary", //
- "attachedToolbars", //
- "connectorXml", //
- "downRev", //
- "functionPrototypes", //
- "graphicFrameDoc", //
- "groupShapeXml", //
- "ink", //
- "keyMapCustomizations", //
- "legacyDiagramText", //
- "legacyDocTextInfo", //
- "officeDocument", //
- "pictureXml", //
- "shapeXml", //
- "smartTags", //
- "ui/altText", //
- "ui/buttonSize", //
- "ui/controlID", //
- "ui/description", //
- "ui/enabled", //
- "ui/extensibility", //
- "ui/helperText", //
- "ui/imageID", //
- "ui/imageMso", //
- "ui/keyTip", //
- "ui/label", //
- "ui/lcid", //
- "ui/loud", //
- "ui/pressed", //
- "ui/progID", //
- "ui/ribbonID", //
- "ui/showImage", //
- "ui/showLabel", //
- "ui/supertip", //
- "ui/target", //
- "ui/text", //
- "ui/title", //
- "ui/tooltip", //
- "ui/userCustomization", //
- "ui/visible", //
- "userXmlData", //
- "vbaProject", //
- "wordVbaData", //
- "wsSortMap", //
- "xlBinaryIndex", //
- "xlExternalLinkPath/xlAlternateStartup", //
- "xlExternalLinkPath/xlLibrary", //
- "xlExternalLinkPath/xlPathMissing", //
- "xlExternalLinkPath/xlStartup", //
- "xlIntlMacrosheet", //
- "xlMacrosheet", //
- "customData", //
- "diagramDrawing", //
- "hdphoto", //
- "inkXml", //
- "media", //
- "slicer", //
- "slicerCache", //
- "stylesWithEffects", //
- "ui/extensibility", //
- "chartColorStyle", //
- "chartLayout", //
- "chartStyle", //
- "dictionary", //
- "timeline", //
- "timelineCache", //
- "aFChunk", //
- "attachedTemplate", //
- "audio", //
- "calcChain", //
- "chart", //
- "chartsheet", //
- "chartUserShapes", //
- "commentAuthors", //
- "comments", //
- "connections", //
- "control", //
- "customProperty", //
- "customXml", //
- "diagramColors", //
- "diagramData", //
- "diagramLayout", //
- "diagramQuickStyle", //
- "dialogsheet", //
- "drawing", //
- "endnotes", //
- "externalLink", //
- "externalLinkPath", //
- "font", //
- "fontTable", //
- "footer", //
- "footnotes", //
- "glossaryDocument", //
- "handoutMaster", //
- "header", //
- "hyperlink", //
- "image", //
- "mailMergeHeaderSource", //
- "mailMergeRecipientData", //
- "mailMergeSource", //
- "notesMaster", //
- "notesSlide", //
- "numbering", //
- "officeDocument", //
- "oleObject", //
- "package", //
- "pivotCacheDefinition", //
- "pivotCacheRecords", //
- "pivotTable", //
- "presProps", //
- "printerSettings", //
- "queryTable", //
- "recipientData", //
- "settings", //
- "sharedStrings", //
- "sheetMetadata", //
- "slide", //
- "slideLayout", //
- "slideMaster", //
- "slideUpdateInfo", //
- "slideUpdateUrl", //
- "styles", //
- "table", //
- "tableSingleCells", //
- "tableStyles", //
- "tags", //
- "theme", //
- "themeOverride", //
- "transform", //
- "video", //
- "viewProps", //
- "volatileDependencies", //
- "webSettings", //
- "worksheet", //
- "xmlMaps", //
- "ctrlProp", //
- "customData", //
- "diagram", //
- "diagramColorsHeader", //
- "diagramLayoutHeader", //
- "diagramQuickStyleHeader", //
- "documentParts", //
- "slicer", //
- "slicerCache", //
- "vmlDrawing" //
- };
+ private static final Set<String> signed = Collections.unmodifiableSet(new
HashSet<String>(Arrays.asList(
+
"activeXControlBinary","aFChunk","attachedTemplate","attachedToolbars","audio","calcChain","chart","chartColorStyle",
+
"chartLayout","chartsheet","chartStyle","chartUserShapes","commentAuthors","comments","connections","connectorXml",
+
"control","ctrlProp","customData","customData","customProperty","customXml","diagram","diagramColors",
+
"diagramColorsHeader","diagramData","diagramDrawing","diagramLayout","diagramLayoutHeader","diagramQuickStyle",
+
"diagramQuickStyleHeader","dialogsheet","dictionary","documentParts","downRev","drawing","endnotes","externalLink",
+
"externalLinkPath","font","fontTable","footer","footnotes","functionPrototypes","glossaryDocument","graphicFrameDoc",
+
"groupShapeXml","handoutMaster","hdphoto","header","hyperlink","image","ink","inkXml","keyMapCustomizations",
+
"legacyDiagramText","legacyDocTextInfo","mailMergeHeaderSource","mailMergeRecipientData","mailMergeSource","media",
+
"notesMaster","notesSlide","numbering","officeDocument","officeDocument","oleObject","package","pictureXml",
+
"pivotCacheDefinition","pivotCacheRecords","pivotTable","powerPivotData","presProps","printerSettings","queryTable",
+
"recipientData","settings","shapeXml","sharedStrings","sheetMetadata","slicer","slicer","slicerCache","slicerCache",
+
"slide","slideLayout","slideMaster","slideUpdateInfo","slideUpdateUrl","smartTags","styles","stylesWithEffects",
+
"table","tableSingleCells","tableStyles","tags","theme","themeOverride","timeline","timelineCache","transform",
+
"ui/altText","ui/buttonSize","ui/controlID","ui/description","ui/enabled","ui/extensibility","ui/extensibility",
+
"ui/helperText","ui/imageID","ui/imageMso","ui/keyTip","ui/label","ui/lcid","ui/loud","ui/pressed","ui/progID",
+
"ui/ribbonID","ui/showImage","ui/showLabel","ui/supertip","ui/target","ui/text","ui/title","ui/tooltip",
+
"ui/userCustomization","ui/visible","userXmlData","vbaProject","video","viewProps","vmlDrawing",
+
"volatileDependencies","webSettings","wordVbaData","worksheet","wsSortMap","xlBinaryIndex",
+
"xlExternalLinkPath/xlAlternateStartup","xlExternalLinkPath/xlLibrary","xlExternalLinkPath/xlPathMissing",
+
"xlExternalLinkPath/xlStartup","xlIntlMacrosheet","xlMacrosheet","xmlMaps"
+ )));
}
\ No newline at end of file
Modified:
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
---
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
(original)
+++
poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
Wed Jun 28 21:38:23 2017
@@ -25,10 +25,9 @@
package org.apache.poi.poifs.crypt.dsig.services;
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
+import static
org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS;
+import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_NS;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
@@ -36,9 +35,8 @@ import java.security.Provider;
import java.security.Security;
import java.security.spec.AlgorithmParameterSpec;
import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
import java.util.List;
+import java.util.TreeMap;
import javax.xml.crypto.Data;
import javax.xml.crypto.MarshalException;
@@ -50,23 +48,20 @@ import javax.xml.crypto.dsig.TransformEx
import javax.xml.crypto.dsig.TransformService;
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
+import org.apache.jcp.xml.dsig.internal.dom.ApacheNodeSetData;
+import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.POILogFactory;
import org.apache.poi.util.POILogger;
-import org.apache.poi.util.XmlSort;
-import org.apache.xmlbeans.XmlCursor;
+import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
-import org.apache.xmlbeans.XmlOptions;
import
org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTRelationshipReference;
import
org.openxmlformats.schemas.xpackage.x2006.digitalSignature.RelationshipReferenceDocument;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationship;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationships;
-import
org.openxmlformats.schemas.xpackage.x2006.relationships.RelationshipsDocument;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.STTargetMode;
import org.w3.x2000.x09.xmldsig.TransformDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
/**
* JSR105 implementation of the RelationshipTransform transformation.
@@ -89,7 +84,7 @@ public class RelationshipTransformServic
public static class RelationshipTransformParameterSpec implements
TransformParameterSpec {
List<String> sourceIds = new ArrayList<String>();
public void addRelationshipReference(String relationshipId) {
- sourceIds.add(relationshipId);
+ sourceIds.add(relationshipId);
}
public boolean hasSourceIds() {
return !sourceIds.isEmpty();
@@ -163,15 +158,13 @@ public class RelationshipTransformServic
LOG.log(POILogger.DEBUG, "marshallParams(parent,context)");
DOMStructure domParent = (DOMStructure) parent;
Element parentNode = (Element)domParent.getNode();
- // parentNode.setAttributeNS(XML_NS, "xmlns:mdssi", XML_DIGSIG_NS);
Document doc = parentNode.getOwnerDocument();
for (String sourceId : this.sourceIds) {
- RelationshipReferenceDocument relRef =
RelationshipReferenceDocument.Factory.newInstance();
- relRef.addNewRelationshipReference().setSourceId(sourceId);
- Node n = relRef.getRelationshipReference().getDomNode();
- n = doc.importNode(n, true);
- parentNode.appendChild(n);
+ Element el = doc.createElementNS(OO_DIGSIG_NS,
"mdssi:RelationshipReference");
+ el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
+ el.setAttribute("SourceId", sourceId);
+ parentNode.appendChild(el);
}
}
@@ -180,6 +173,13 @@ public class RelationshipTransformServic
return null;
}
+ /**
+ * The relationships transform takes the XML document from the
Relationships part
+ * and converts it to another XML document.
+ *
+ * @see <a
href="https://www.ecma-international.org/activities/Office%20Open%20XML%20Formats/Draft%20ECMA-376%203rd%20edition,%20March%202011/Office%20Open%20XML%20Part%202%20-%20Open%20Packaging%20Conventions.pdf">13.2.4.24
Relationships Transform Algorithm</a>
+ * @see <a href="https://stackoverflow.com/questions/36063375">XML
Relationship Transform Algorithm</a>
+ */
public Data transform(Data data, XMLCryptoContext context) throws
TransformException {
LOG.log(POILogger.DEBUG, "transform(data,context)");
LOG.log(POILogger.DEBUG, "data java type: " +
data.getClass().getName());
@@ -187,53 +187,40 @@ public class RelationshipTransformServic
LOG.log(POILogger.DEBUG, "URI: " + octetStreamData.getURI());
InputStream octetStream = octetStreamData.getOctetStream();
- RelationshipsDocument relDoc;
+ Document doc;
try {
- relDoc = RelationshipsDocument.Factory.parse(octetStream,
DEFAULT_XML_OPTIONS);
+ doc = DocumentHelper.readDocument(octetStream);
} catch (Exception e) {
throw new TransformException(e.getMessage(), e);
}
- LOG.log(POILogger.DEBUG, "relationships document", relDoc);
- CTRelationships rels = relDoc.getRelationships();
- List<CTRelationship> relList = rels.getRelationshipList();
- Iterator<CTRelationship> relIter =
rels.getRelationshipList().iterator();
- while (relIter.hasNext()) {
- CTRelationship rel = relIter.next();
- /*
- * See: ISO/IEC 29500-2:2008(E) - 13.2.4.24 Relationships Transform
- * Algorithm.
- */
- if (!this.sourceIds.contains(rel.getId())) {
- LOG.log(POILogger.DEBUG, "removing element: " + rel.getId());
- relIter.remove();
- } else {
- if (!rel.isSetTargetMode()) {
- rel.setTargetMode(STTargetMode.INTERNAL);
+ // keep only those relationships which id is registered in the
sourceIds
+ Element root = doc.getDocumentElement();
+ NodeList nl = root.getChildNodes();
+ TreeMap<String,Element> rsList = new TreeMap<String,Element>();
+ for (int i=nl.getLength()-1; i>=0; i--) {
+ Node n = nl.item(i);
+ if ("Relationship".equals(n.getLocalName())) {
+ Element el = (Element)n;
+ String id = el.getAttribute("Id");
+ if (sourceIds.contains(id)) {
+ String targetMode = el.getAttribute("TargetMode");
+ if ("".equals(targetMode)) {
+ el.setAttribute("TargetMode", "Internal");
+ }
+ rsList.put(id, el);
}
}
+ root.removeChild(n);
}
-
- // TODO: remove non element nodes ???
- LOG.log(POILogger.DEBUG, "# Relationship elements", relList.size());
-
- XmlSort.sort(rels, new Comparator<XmlCursor>(){
- public int compare(XmlCursor c1, XmlCursor c2) {
- String id1 = ((CTRelationship)c1.getObject()).getId();
- String id2 = ((CTRelationship)c2.getObject()).getId();
- return id1.compareTo(id2);
- }
- });
- try {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- XmlOptions xo = new XmlOptions();
- xo.setSaveNoXmlDecl();
- relDoc.save(bos, xo);
- return new OctetStreamData(new
ByteArrayInputStream(bos.toByteArray()));
- } catch (IOException e) {
- throw new TransformException(e.getMessage(), e);
+ for (Element el : rsList.values()) {
+ root.appendChild(el);
}
+
+ LOG.log(POILogger.DEBUG, "# Relationship elements: ", rsList.size());
+
+ return new ApacheNodeSetData(new XMLSignatureInput(root));
}
public Data transform(Data data, XMLCryptoContext context, OutputStream
os) throws TransformException {
Modified:
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
URL:
http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
---
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
(original)
+++
poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
Wed Jun 28 21:38:23 2017
@@ -72,6 +72,7 @@ import org.apache.poi.poifs.crypt.dsig.s
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
+import org.apache.poi.poifs.storage.RawDataUtil;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.IOUtils;
@@ -119,6 +120,70 @@ public class TestSignatureInfo {
}
@Test
+ public void bug61182() throws Exception {
+ String pfxInput =
+
"H4sIAAAAAAAAAFXTfzzTeRwH8P2uGRmG6hKSmJh9a2HsuPy60VnHCEU6v86sieZH2Jr2qFl+s+ZHJ5tfUcfKb4uho/OjiFq1qTv5ceFyp0PqEK"+
+
"fH4+66++Pz+Dwer9fj8f7r9cRzEd4QMBTPRWxDIM14ZN47NfAWsJgL34Bx4at4Lvwdngvd9b8KqgbjQpGbMXzzgRGovytVFTBEzIXU47kQCd4U"+
+
"ofJPvHl8JwyTjRS55hbKoor3UJLDE1i/PcPKCBAIDATjQlKiK67XjVYdcnkZgD2txroiAUb8W9dtn57DvTsbM+3wIsdocXDEN7TdPKgaSl+tU1"+
+
"xq9oqiB5yMaZCPho8uUEbFU9U6u3N7lEMLTJGeA0RfX+5FMRrpXPFrbrlJ8uNUCE2H247P28Ckyfqlsy32yeKg/HTbH5JpqUDNw2B32+SaiRw7"+
+
"ofRMePUpaAoK7KYgmd5ZIc0rLLYjJBfOWCb28xlrGhbpJvdToFdqt5PXVjEz5YOJ6g7W0fskuKW9/iZP0yLEVpR9XkkHmb6tfpcE8YwCdWNCan"+
+
"LvAsco25JdF1j2/FLAMVU79HdOex07main90dy40511OZtTGZ+TdVd3lKZ7D3clEg9hLESHwSNnZ6239X4yLM4xYSElQ/hqSbwdmiozYG9PhF2"+
+
"Zf0XaZnxzTK0Iot+rJ3kYoxWTLE8DR9leV62Ywbtlg4mapYOxb3lT7fQ1x4EQ44flh2oFWSPLR8LMbsc6jzJsV6OZ3TrODjHEdw9W+8OD32vd8"+
+
"XQ6iCaIHcrSOn6qS0TKLr786234eeSAhvAQbEsVn7vrvc/487Be/O2e/+5Y5zRq2zAtz6pfcNyraJNDqMW1inNkgJ3t3VESbZ3pNzyl3KHILs0"+
+
"51dY6msDYSlWhw40TglXxj9rw95O6gFWIuN012W/vhS50jpKXcao4gc1aLaXtJXxirbRkpZ/0e7a0pD6TDa7+GxEdEEML3VGo9udD5YUKhU3y7"+
+
"SzWAgN6WIEIglq7LilvCjqIVLIfg8CvVGL9f5iSsCDf5hef4vMxbyvcjINuy06gZu+iPYOWNxjfrwKGYzoqqotK2aywgYVrPMh0JovfkDuN95n"+
+
"MdVlYHbN1Mnn4TxAwuv+u3AkBlDZvRUUCwoDMUGxeMNPhTaAgWl60xhhBgCBaEMgAACReMAav7n3x598IDYJ9GxGXRAwaPOT/kfO/1AgPqLQkp"+
+
"MiIVaHthnUS4v2y32e2BjdMPyIImUTBW3cV3R5tjVQm0MOm+D2C5+bBW9vHLjLR4lun4toQiY3Ls/v4bES/OJ4EmpZk5xhL9i5ClofYZNEsxFn"+
+
"An/q821Tg+Cq9Er4XYGQe8ogjjLJ2b7dUsJ3auFQFNUJF7Ke7yUL2EeYYxl6vz5l4q5u8704mRbFts1E1eWMp6WIy91GPrsVlRGvtuNERfrjfE"+
+
"YtzUI3Flcv65zJUbUBEzUnTS0fEYso2XyToAl8kb251mUY2o2lJzv5dp/1htmcjeeP2MjxC+3S45ljx7jd52Pv9XAat+ryiauFOF7YgztkoWWD"+
+
"h62tplPH1bzDV+d0NLdaE5AfVJ09HuUYTFS+iggtvT5Euyk+unj4N2XvzW91n+GNjtgWfKOHmkinUPvYRh70Jv+wlPJrVaT8mL7GxJLqDC9jbv"+
+
"Gznoiae6es+wQejnk3XjU366MrK/zXxngBYj9J6NnXc9mMiTFLX8WqQ8iTelTAFs2NJzPoDzrBUz4JFIEOa6Dja6dULc68g1jFDTeEHZyra7RZ"+
+
"2ElqGDEqcNRo3SNX6feMy9EF1GOyZK0Sa87KwjKw8aM68dpsIYjfLcTXaZ6atg0BKfMnl6axeUGEaIFSP7rzj9wjzumRbG3jgUVp2lX5AK/tsO"+
+
"7R4TQX/9/H6RiN34c9KldmPZZGANXzzTajZS9mR2OSvlJ+F4AgSko4htrMAKFTBu51/5SWNsO1vlRaaG48ZRJ+8PzuHQMdvS36gNpRPi7jhF1S"+
+
"H3B2ycI4y0VURv6SrqJNUY/X645ZFJQ+eBO+ptG7o8axf1dcqh2beiQk+GRTeZ37LVeUlaeo9vl1/+8tyBfyT2v5lFC5E19WdKIyCuZe7r99Px"+
+
"D/Od4Qj0TA92+DQnbCQTCMy/wwse9O4gsEebkkpPIP5GBV3Q0YBsj75XE0uSFQ1tCZSW8bNa9MUJZ/nPBfExohHlgGAAA=";
+
+ Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+ cal.clear();
+ cal.set(2017, 6, 1);
+
+ SignatureConfig signatureConfig = prepareConfig("test", "CN=Test",
pfxInput);
+ signatureConfig.setExecutionTime(cal.getTime());
+
+ SignatureInfo si = new SignatureInfo();
+ si.setSignatureConfig(signatureConfig);
+
+ XSSFWorkbook wb1 = new XSSFWorkbook();
+ wb1.createSheet().createRow(1).createCell(1).setCellValue("Test");
+ ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+ wb1.write(bos);
+ wb1.close();
+
+ OPCPackage pkg1 = OPCPackage.open(new
ByteArrayInputStream(bos.toByteArray()));
+
+ signatureConfig.setOpcPackage(pkg1);
+ si.confirmSignature();
+ assertTrue(si.verifySignature());
+ bos.reset();
+ pkg1.save(bos);
+ pkg1.close();
+
+ XSSFWorkbook wb2 = new XSSFWorkbook(new
ByteArrayInputStream(bos.toByteArray()));
+ assertEquals("Test",
wb2.getSheetAt(0).getRow(1).getCell(1).getStringCellValue());
+ OPCPackage pkg2 = wb2.getPackage();
+ signatureConfig.setOpcPackage(pkg2);
+ assertTrue(si.verifySignature());
+ String signExp =
+
"Lxp2LFa+0YWGOBL8zVdf7SWRQiNK/Tt85W+kmH1bunlua030BKbQc6yWIIk6gN6jCTtrJ1h2eMRbLwymygOUpM"+
+
"dd0MeQY3mMWRSO9qEW87SQvyDqBh71zXWW3ZYET+vJWr3BCNEtXCy8jZvgXqILBGk5vMJW/EYaUEhBcDGjCm0=";
+ String signAct = si.getSignatureParts().iterator().next().
+
getSignatureDocument().getSignature().getSignatureValue().getStringValue();
+ assertEquals(signExp, signAct);
+ pkg2.close();
+ wb2.close();
+ }
+
+ @Test
public void office2007prettyPrintedRels() throws Exception {
OPCPackage pkg =
OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"),
PackageAccess.READ);
try {
@@ -611,15 +676,21 @@ public class TestSignatureInfo {
pkg.close();
}
}
-
- private void sign(OPCPackage pkgCopy, String alias, String signerDn, int
signerCount) throws Exception {
- initKeyPair(alias, signerDn);
+
+ private SignatureConfig prepareConfig(String alias, String signerDn,
String pfxInput) throws Exception {
+ initKeyPair(alias, signerDn, pfxInput);
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setKey(keyPair.getPrivate());
signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
signatureConfig.setExecutionTime(cal.getTime());
signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
+
+ return signatureConfig;
+ }
+
+ private void sign(OPCPackage pkgCopy, String alias, String signerDn, int
signerCount) throws Exception {
+ SignatureConfig signatureConfig = prepareConfig(alias, signerDn, null);
signatureConfig.setOpcPackage(pkgCopy);
SignatureInfo si = new SignatureInfo();
@@ -656,13 +727,21 @@ public class TestSignatureInfo {
}
private void initKeyPair(String alias, String subjectDN) throws Exception {
+ initKeyPair(alias, subjectDN, null);
+ }
+
+ private void initKeyPair(String alias, String subjectDN, String pfxInput)
throws Exception {
final char password[] = "test".toCharArray();
File file = new File("build/test.pfx");
KeyStore keystore = KeyStore.getInstance("PKCS12");
- if (file.exists()) {
- FileInputStream fis = new FileInputStream(file);
+ if (pfxInput != null) {
+ InputStream fis = new
ByteArrayInputStream(RawDataUtil.decompress(pfxInput));
+ keystore.load(fis, password);
+ fis.close();
+ } else if (file.exists()) {
+ InputStream fis = new FileInputStream(file);
keystore.load(fis, password);
fis.close();
} else {
@@ -685,9 +764,12 @@ public class TestSignatureInfo {
, notBefore, notAfter, null, keyPair.getPrivate(), true, 0,
null, null, keyUsage);
keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new
Certificate[]{x509});
- FileOutputStream fos = new FileOutputStream(file);
- keystore.store(fos, password);
- fos.close();
+
+ if (pfxInput == null) {
+ FileOutputStream fos = new FileOutputStream(file);
+ keystore.store(fos, password);
+ fos.close();
+ }
}
}
@@ -701,8 +783,7 @@ public class TestSignatureInfo {
// in the Sonar Maven runs where we are at a different source directory
File buildDir = new File("build");
if(!buildDir.exists()) {
- assertTrue("Failed to create " + buildDir.getAbsolutePath(),
- buildDir.mkdirs());
+ assertTrue("Failed to create " + buildDir.getAbsolutePath(),
buildDir.mkdirs());
}
File tmpFile = new File(buildDir, "sigtest"+extension);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]