diff --git a/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java b/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
index 860d71159..735408b41 100644
--- a/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
+++ b/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
@@ -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) {
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
index 3b21eb865..338174fce 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
@@ -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;
}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
index 5affefc07..d8164cdc6 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
@@ -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 [MS-OFFCRYPTO]: Office Document Cryptography Structure
*/
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 manifestReferences = new ArrayList();
addManifestReferences(manifestReferences);
Manifest manifest = getSignatureFactory().newManifest(manifestReferences);
-
- String objectId = "idPackageObject"; // really has to be this value.
+
List objectContent = new ArrayList();
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 digestedPartNames = new HashSet();
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;
+ String partName = normalizePartName(relationship.getTargetURI(), baseUri);
+
+ // We only digest a part once.
+ if (digestedPartNames.contains(partName)) {
+ continue;
}
- try {
- partName = new URI(partName).normalize().getPath().replace('\\', '/');
- LOG.log(POILogger.DEBUG, "part name: " + partName);
- } catch (URISyntaxException e) {
- throw new XMLSignatureException(e);
- }
-
+ 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 transforms = new ArrayList();
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() {
+ 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 signatureInfoContent = new ArrayList();
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 signed = Collections.unmodifiableSet(new HashSet(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
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
index 55518772f..ff0564bba 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
@@ -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 sourceIds = new ArrayList();
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 13.2.4.24 Relationships Transform Algorithm
+ * @see XML Relationship Transform Algorithm
+ */
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 relList = rels.getRelationshipList();
- Iterator 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 rsList = new TreeMap();
+ 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(){
- 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 {
diff --git a/src/ooxml/java/org/apache/poi/util/XmlSort.java b/src/ooxml/java/org/apache/poi/util/XmlSort.java
deleted file mode 100644
index 09f05fb46..000000000
--- a/src/ooxml/java/org/apache/poi/util/XmlSort.java
+++ /dev/null
@@ -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 element
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 QName
s of two
- * elements.
- * @throws IllegalArgumentException if the input XmlObject
does not represent
- * an element
- */
- public static void sort(XmlObject element, Comparator 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();
- }
- }
-}
-
\ No newline at end of file
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
index 3c172f385..93a58950c 100644
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
+++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
@@ -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);