Browse Source

Bug 61182 - Invalid signature created for streamed xslx file

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1800207 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_3_17_FINAL
Andreas Beeker 7 years ago
parent
commit
c80988698f

+ 5
- 1
src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java View File

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

+ 14
- 5
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java View File

@@ -40,24 +40,32 @@ public class SignatureMarshalListener implements EventListener, SignatureConfigu
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 implements EventListener, SignatureConfigu
}
}
@Override
public void setSignatureConfig(SignatureConfig signatureConfig) {
this.signatureConfig = signatureConfig;
}

+ 79
- 234
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java View File

@@ -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.x2006.digsig.SignatureInfoV1Document;

/**
* 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 SignatureFacet {
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 SignatureFacet {

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 SignatureFacet {
} 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 SignatureFacet {
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 SignatureFacet {
} 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 SignatureFacet {
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 SignatureFacet {

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

+ 43
- 56
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java View File

@@ -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.TransformException;
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 RelationshipTransformService extends TransformService {
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 RelationshipTransformService extends TransformService {
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 RelationshipTransformService extends TransformService {
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 RelationshipTransformService extends TransformService {
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 {

+ 0
- 88
src/ooxml/java/org/apache/poi/util/XmlSort.java View File

@@ -1,88 +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.util;

import java.util.Comparator;

import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;

public final class XmlSort {
/**
* Sorts the children of <code>element</code> according to the order indicated by the
* comparator.
* @param element the element whose content is to be sorted. Only element children are sorted,
* attributes are not touched. When elements are reordered, all the text, comments and PIs
* follow the element that they come immediately after.
* @param comp a comparator that is to be used when comparing the <code>QName</code>s of two
* elements.
* @throws IllegalArgumentException if the input <code>XmlObject</code> does not represent
* an element
*/
public static void sort(XmlObject element, Comparator<XmlCursor> comp) {
XmlCursor headCursor = element.newCursor();
if (!headCursor.isStart()) {
throw new IllegalStateException("The element parameter must point to a STARTDOC");
}
// We use insertion sort to minimize the number of swaps, because each swap means
// moving a part of the document
/* headCursor points to the beginning of the list of the already sorted items and
listCursor points to the beginning of the list of unsorted items
At the beginning, headCursor points to the first element and listCursor points to the
second element. The algorithm ends when listCursor cannot be moved to the "next"
element in the unsorted list, i.e. the unsorted list becomes empty */
boolean moved = headCursor.toFirstChild();
if (!moved) {
// Cursor was not moved, which means that the given element has no children and
// therefore there is nothing to sort
return;
}
XmlCursor listCursor = headCursor.newCursor();
boolean moreElements = listCursor.toNextSibling();
while (moreElements) {
moved = false;
// While we can move the head of the unsorted list, it means that there are still
// items (elements) that need to be sorted
while (headCursor.comparePosition(listCursor) < 0) {
if (comp.compare(headCursor, listCursor) > 0) {
// We have found the position in the sorted list, insert the element and the
// text following the element in the current position
// Move the element
listCursor.moveXml(headCursor);
// Move the text following the element
while (!listCursor.isStart() && !listCursor.isEnd())
listCursor.moveXml(headCursor);
moreElements = listCursor.isStart();
moved = true;
break;
}
headCursor.toNextSibling();
}
if (!moved) {
// Because during the move of a fragment of XML, the listCursor is also moved, in
// case we didn't need to move XML (the new element to be inserted happened to
// be the last one in order), we need to move this cursor
moreElements = listCursor.toNextSibling();
}
// Reposition the head of the sorted list
headCursor.toParent();
headCursor.toFirstChild();
}
}
}

+ 91
- 10
src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java View File

@@ -72,6 +72,7 @@ import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
import org.apache.poi.poifs.storage.RawDataUtil;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.util.DocumentHelper;
import org.apache.poi.util.IOUtils;
@@ -118,6 +119,70 @@ public class TestSignatureInfo {
additionalJar == null || additionalJar.trim().length() == 0);
}
@Test
public void bug61182() throws Exception {
String pfxInput =
"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";
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);
@@ -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);


Loading…
Cancel
Save