ASF Bugzilla – Attachment 35075 Details for
Bug 61182
Invalid signature created for streamed xlsx file
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for XML Signatures / Unix linebreaks
signature-patch.diff (text/plain), 49.18 KB, created by
Andreas Beeker
on 2017-06-26 01:50:24 UTC
(
hide
)
Description:
Patch for XML Signatures / Unix linebreaks
Filename:
MIME Type:
Creator:
Andreas Beeker
Created:
2017-06-26 01:50:24 UTC
Size:
49.18 KB
patch
obsolete
>Index: src/documentation/content/xdocs/encryption.xml >=================================================================== >--- src/documentation/content/xdocs/encryption.xml (revision 1799756) >+++ src/documentation/content/xdocs/encryption.xml (working copy) >@@ -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 @@ > HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true); > Biff8EncryptionKey.setCurrentUserPassword(null); > </source> >- >+ > <source> > // RC4 CryptoApi support ppt - decryption > Biff8EncryptionKey.setCurrentUserPassword("pass"); >@@ -166,10 +166,10 @@ > </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 @@ > } 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 @@ > // 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 @@ > <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,9 @@ > ... > </source> > </section> >- >+ > <section><title>Signing an office document</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 >@@ -304,6 +304,130 @@ > 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> >@@ -310,7 +434,7 @@ > <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> >Index: src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java >=================================================================== >--- src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java (revision 1799756) >+++ src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java (working copy) >@@ -74,8 +74,12 @@ > 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) { >Index: src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java >=================================================================== >--- src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java (revision 1799756) >+++ src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java (working copy) >@@ -40,24 +40,32 @@ > 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 @@ > } > } > >+ @Override > public void setSignatureConfig(SignatureConfig signatureConfig) { > this.signatureConfig = signatureConfig; > } >Index: src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java >=================================================================== >--- src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java (revision 1799756) >+++ src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java (working copy) >@@ -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.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 @@ > > /** > * 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 @@ > 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 @@ > > 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 @@ > } 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 @@ > 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,35 +168,55 @@ > } 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; >+ } > >+ > protected void addSignatureTime(Document document, List<XMLStructure> objectContent) { > /* > * SignatureTime >@@ -236,7 +256,7 @@ > 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 @@ > > 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 >Index: src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java >=================================================================== >--- src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java (revision 1799756) >+++ src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java (working copy) >@@ -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.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.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 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 @@ > 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 @@ > 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 @@ > 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); > } >+ >+ for (Element el : rsList.values()) { >+ root.appendChild(el); >+ } > >- // TODO: remove non element nodes ??? >- LOG.log(POILogger.DEBUG, "# Relationship elements", relList.size()); >+ LOG.log(POILogger.DEBUG, "# Relationship elements: ", rsList.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); >- } >+ return new ApacheNodeSetData(new XMLSignatureInput(root)); > } > > public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException { >Index: src/ooxml/java/org/apache/poi/util/XmlSort.java >=================================================================== >--- src/ooxml/java/org/apache/poi/util/XmlSort.java (revision 1799756) >+++ src/ooxml/java/org/apache/poi/util/XmlSort.java (nonexistent) >@@ -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(); >- } >- } >-} >- >\ No newline at end of file > >Property changes on: src/ooxml/java/org/apache/poi/util/XmlSort.java >___________________________________________________________________ >Deleted: svn:eol-style >## -1 +0,0 ## >-native >\ No newline at end of property >Index: src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java >=================================================================== >--- src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java (revision 1799756) >+++ src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java (working copy) >@@ -72,6 +72,7 @@ > import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject; > import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObjects; > import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; >+import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDvAspect; > > /** > * Represents a SpreadsheetML drawing >@@ -426,7 +427,8 @@ > CTOleObjects oo = cwb.isSetOleObjects() ? cwb.getOleObjects() : cwb.addNewOleObjects(); > > CTOleObject ole1 = oo.addNewOleObject(); >- ole1.setProgId("Package"); >+ ole1.setProgId("Packager Shell Object"); >+ ole1.setDvAspect(STDvAspect.DVASPECT_ICON); > ole1.setShapeId(shapeId); > ole1.setId(olePR.getId()); > >Index: src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java >=================================================================== >--- src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java (revision 1799756) >+++ src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java (working copy) >@@ -119,6 +119,37 @@ > } > > @Test >+ public void bug61182() throws Exception { >+ SignatureConfig signatureConfig = prepareConfig("test", "CN=Test"); >+ >+ 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()); >+ pkg2.close(); >+ wb2.close(); >+ } >+ >+ @Test > public void office2007prettyPrintedRels() throws Exception { > OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ); > try { >@@ -611,8 +642,8 @@ > pkg.close(); > } > } >- >- private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception { >+ >+ private SignatureConfig prepareConfig(String alias, String signerDn) throws Exception { > initKeyPair(alias, signerDn); > > SignatureConfig signatureConfig = new SignatureConfig(); >@@ -620,6 +651,12 @@ > 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); > signatureConfig.setOpcPackage(pkgCopy); > > SignatureInfo si = new SignatureInfo();
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 61182
: 35075 |
35140