poi-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kiwiwi...@apache.org
Subject svn commit: r1800207 - in /poi: site/src/documentation/content/xdocs/ trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/ trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/ trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/ trunk/src/ooxml/...
Date Wed, 28 Jun 2017 21:38:23 GMT
Author: kiwiwings
Date: Wed Jun 28 21:38:23 2017
New Revision: 1800207

URL: http://svn.apache.org/viewvc?rev=1800207&view=rev
Log:
Bug 61182 - Invalid signature created for streamed xslx file

Removed:
    poi/trunk/src/ooxml/java/org/apache/poi/util/XmlSort.java
Modified:
    poi/site/src/documentation/content/xdocs/encryption.xml
    poi/site/src/documentation/content/xdocs/status.xml
    poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
    poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
    poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java

Modified: poi/site/src/documentation/content/xdocs/encryption.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/encryption.xml?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/encryption.xml (original)
+++ poi/site/src/documentation/content/xdocs/encryption.xml Wed Jun 28 21:38:23 2017
@@ -31,9 +31,9 @@
 
    <body>
     <section><title>Overview</title>
-	<p>Apache POI contains support for reading few variants of encrypted office files: </p>
-	<ul>
-		<li>Binary formats (.xls, .ppt, .doc, ...)<br/>
+    <p>Apache POI contains support for reading few variants of encrypted office files: </p>
+    <ul>
+        <li>Binary formats (.xls, .ppt, .doc, ...)<br/>
         encryption is format-dependent and needs to be implemented per format differently.<br/>
         Use <link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
         Biff8EncryptionKey</link>.<link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html#setCurrentUserPassword(java.lang.String)">setCurrentUserPassword</link>(String password)
@@ -41,7 +41,7 @@
         Setting a null password before saving removes the password protection.<br/>
         The password is set in a thread local variable. Do not forget to reset it to null after text extraction.
         </li>
-		<li>XML-based formats (.xlsx, .pptx, .docx, ...)<br/>
+        <li>XML-based formats (.xlsx, .pptx, .docx, ...)<br/>
         use the same encryption logic over all formats. When encrypted, the zipped files will be
         stored within an OLE file in the EncryptedPackage stream.<br/>
         If you plan to use POI to actually generate encrypted documents, be aware not to use anything less than
@@ -54,13 +54,13 @@
         <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JDK7</link>,
         <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JDK8</link>).
         </li>
-	</ul>
+    </ul>
 
-	<p>Some "write-protected" files are encrypted with the built-in password "VelvetSweatshop", POI can read that files too.</p>
-    </section>	
+    <p>Some "write-protected" files are encrypted with the built-in password "VelvetSweatshop", POI can read that files too.</p>
+    </section>
 
     <section><title>Supported feature matrix</title>
-    
+
     <table>
         <tr>
             <th>Encryption</th>
@@ -87,7 +87,7 @@
             <td class="feature-yes">Yes (since 3.17)</td>
         </tr>
         <tr>
-        	<th/>
+            <th/>
             <th>XSSF</th>
             <th>XSLF</th>
             <th>XWPF</th>
@@ -117,22 +117,22 @@
             <td class="feature-yes">Yes</td>
         </tr>
     </table>
-    
+
     <p>*) the xor encryption is flawed and works only for very small files - see <link href="https://bz.apache.org/bugzilla/show_bug.cgi?id=59857">#59857</link>.
     </p>
 
     <p>**) the <link href="https://msdn.microsoft.com/en-us/library/cc313071(v=office.12).aspx">MS-OFFCRYPTO</link>
     documentation only mentions the RC4 (without CryptoAPI) encryption as a "in place" encryption, but
-    apparently there's also a container based method with that key generation logic. 
+    apparently there's also a container based method with that key generation logic.
     </p>
     </section>
 
     <section><title>Binary formats</title>
-    	<p>As mentioned above, use
+        <p>As mentioned above, use
         <link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
         Biff8EncryptionKey</link>.<link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html#setCurrentUserPassword(java.lang.String)">setCurrentUserPassword</link>(String password)
         to specify the password.</p>
-        
+
         <source>
 // XOR/RC4 decryption for xls
 Biff8EncryptionKey.setCurrentUserPassword("pass");
@@ -140,7 +140,7 @@ NPOIFSFileSystem fs = new NPOIFSFileSyst
 HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true);
 Biff8EncryptionKey.setCurrentUserPassword(null);
         </source>
-        
+
         <source>
 // RC4 CryptoApi support ppt - decryption
 Biff8EncryptionKey.setCurrentUserPassword("pass");
@@ -166,10 +166,10 @@ os.close();
     </section>
 
     <section><title>XML-based formats - Decryption</title>
-	<p>XML-based formats are stored in OLE-package stream "EncryptedPackage". Use org.apache.poi.poifs.crypt.Decryptor
-	to decode file:</p>
+    <p>XML-based formats are stored in OLE-package stream "EncryptedPackage". Use org.apache.poi.poifs.crypt.Decryptor
+    to decode file:</p>
 
-	<source>
+    <source>
 EncryptionInfo info = new EncryptionInfo(filesystem);
 Decryptor d = Decryptor.getInstance(info);
 
@@ -185,11 +185,11 @@ try {
 } catch (GeneralSecurityException ex) {
     throw new RuntimeException("Unable to process encrypted document", ex);
 }
-	</source>
+    </source>
 
-	<p>If you want to read file encrypted with build-in password, use Decryptor.DEFAULT_PASSWORD.</p>
+    <p>If you want to read file encrypted with build-in password, use Decryptor.DEFAULT_PASSWORD.</p>
      </section>
-     
+
      <section><title>XML-based formats - Encryption</title>
      <p>Encrypting a file is similar to the above decryption process. Basically you'll need to choose between
      <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/EncryptionMode.html">binaryRC4, standard and agile encryption</link>,
@@ -213,10 +213,10 @@ opc.close();
 // Write out the encrypted version
 FileOutputStream fos = new FileOutputStream("...");
 fs.writeFilesystem(fos);
-fos.close();     
+fos.close();
      </source>
      </section>
-     
+
      <section><title>XML-based formats - Signing (XML Signature)</title>
      <p>An Office document can be digital signed by a <link href="https://en.wikipedia.org/wiki/XML_Signature">XML Signature</link>
      to protect it from unauthorized modifications, i.e. modifications without having the original certificate.
@@ -231,17 +231,17 @@ fos.close();
      <ul>
      <li>BouncyCastle bcpkix and bcprov (tested against 1.53)</li>
      <li>Apache Santuario "xmlsec" (tested against 2.0.6)</li>
-     <li>and slf4j-api (tested against 1.7.12)</li>     
+     <li>and slf4j-api (tested against 1.7.12)</li>
      </ul>
      <p>Depending on the <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/SignatureConfig.html">configuration</link>
      and the activated <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/facets/package-summary.html">facets</link>
      various <link href="https://en.wikipedia.org/wiki/XAdES">XAdES levels</link> are supported - the support for higher levels (XAdES-T+)
      depend on supporting services and although the code is adopted, the integration is not well tested ... please support us on
-     integration (testing) with timestamp and revocation (OCSP) services. 
+     integration (testing) with timestamp and revocation (OCSP) services.
      </p>
      <p>Further test examples can be found in the corresponding <link href="https://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java?view=markup">test class</link>.</p>
      </section>
-     
+
      <section><title>Validating a signed office document</title>
 
      <source>
@@ -254,9 +254,10 @@ boolean isValid = si.verifySignature();
 ...
      </source>
      </section>
-     
+
      <section><title>Signing an office document</title>
-     
+
+     <section><title>Signing a file</title>
      <source>
 // loading the keystore - pkcs12 is used here, but of course jks &amp; co are also valid
 // the keystore needs to contain a private key and it's certificate having a
@@ -292,6 +293,38 @@ pkg.close();
      </source>
      </section>
 
+     <section><title>Signing a stream - in-memory</title>
+     <p>When saving a OOXML document, POI creates missing relations on the fly. Therefore calling the signing method before
+     would result in an invalid signature. Instead of trying to fix all save invocations, the user is asked to save the stream
+     before in a intermediate byte array (stream) and process this stream instead.</p>
+
+     <source>
+// load the key and setup SignatureConfig ... - see "Signing a file"
+
+SignatureInfo si = new SignatureInfo();
+si.setSignatureConfig(signatureConfig);
+
+// populate sample object
+XSSFWorkbook wb = new XSSFWorkbook();
+wb.createSheet().createRow(1).createCell(1).setCellValue("Test");
+ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+wb.write(bos);
+wb.close();
+
+// process the
+OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()));
+
+signatureConfig.setOpcPackage(pkg);
+si.confirmSignature();
+bos.reset();
+pkg.save(bos);
+pkg.close();
+
+// bos now contains the signed ooxml document
+     </source>
+     </section>
+     </section>
+
      <section><title>Encrypting temporary files created when unzipping an OOXML document</title>
        <p>For security-conscious environments where data at rest must be stored encrypted,
        the creation of plaintext temporary files is a grey area.</p>
@@ -304,13 +337,137 @@ pkg.close();
        and other <link href="https://svn.apache.org/viewvc?view=revision&amp;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>&lt;custom temp directory&gt;</em>
+-Xbootclasspath/p:<em>&lt;preload dir, which contains /org/apache/xml/security/utils/UnsyncBufferedOutputStream.class&gt;</em>
+-Dorg.apache.poi.util.POILogger=org.apache.poi.util.CommonsLogger
+-Djava.util.logging.config.file=<em>&lt;a dir containing ...&gt;</em>/logging.properties
+       </source>
+
+       <section><title>UnsyncBufferedOutputStream:</title>
+       <source>
+package org.apache.xml.security.utils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class UnsyncBufferedOutputStream extends OutputStream {
+    static final int size = 8*1024;
+    static int filecnt = 0;
+
+    private int pointer = 0;
+    private final OutputStream out;
+    private final FileOutputStream out2;
+
+    private final byte[] buf;
+
+    public UnsyncBufferedOutputStream(OutputStream out) {
+        buf = new byte[size];
+        this.out = out;
+        synchronized(UnsyncBufferedOutputStream.class) {
+            try {
+                String tmpDir = System.getProperty("java.io.tmpdir");
+                if (tmpDir == null) {
+                    tmpDir = "build";
+                }
+                File f = new File(tmpDir, "unsync-"+filecnt+".xml");
+                out2 = new FileOutputStream(f);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            } finally {
+                filecnt++;
+            }
+        }
+    }
+
+    public void write(byte[] arg0) throws IOException {
+        write(arg0, 0, arg0.length);
+    }
+
+    public void write(byte[] arg0, int arg1, int len) throws IOException {
+        int newLen = pointer+len;
+        if (newLen > size) {
+            flushBuffer();
+            if (len > size) {
+                out.write(arg0, arg1,len);
+                out2.write(arg0, arg1,len);
+                return;
+            }
+            newLen = len;
+        }
+        System.arraycopy(arg0, arg1, buf, pointer, len);
+        pointer = newLen;
+    }
+
+    private void flushBuffer() throws IOException {
+        if (pointer > 0) {
+            out.write(buf, 0, pointer);
+            out2.write(buf, 0, pointer);
+        }
+        pointer = 0;
+
+    }
+
+    public void write(int arg0) throws IOException {
+        if (pointer >= size) {
+            flushBuffer();
+        }
+        buf[pointer++] = (byte)arg0;
+
+    }
+
+    public void flush() throws IOException {
+        flushBuffer();
+        out.flush();
+        out2.flush();
+    }
+
+    public void close() throws IOException {
+        flush();
+        out.close();
+        out2.close();
+    }
+
+}
+</source>
+</section>
+
+     <section><title>logging.properties</title>
+     <source>
+handlers = org.slf4j.bridge.SLF4JBridgeHandler
+.level=ALL
+org.slf4j.bridge.SLF4JBridgeHandler.level=ALL
+     </source>
+     </section>
+     </section>
   </body>
 
   <footer>
     <legal>
       Copyright (c) @year@ The Apache Software Foundation. All rights reserved.
       <br />
-      Apache POI, POI, Apache, the Apache feather logo, and the Apache 
+      Apache POI, POI, Apache, the Apache feather logo, and the Apache
       POI project logo are trademarks of The Apache Software Foundation.
     </legal>
   </footer>

Modified: poi/site/src/documentation/content/xdocs/status.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/status.xml?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/status.xml (original)
+++ poi/site/src/documentation/content/xdocs/status.xml Wed Jun 28 21:38:23 2017
@@ -56,8 +56,11 @@
           when referring to both H??F and X??F formats.
     -->
 
-    <!-- release version="3.17-beta2" date="2017-09-??">
-    </release -->
+    <release version="3.17-beta2" date="2017-09-??">
+    	<actions>
+	        <action dev="PD" type="fix" fixes-bug="61182" module="OPC">Invalid signature created for streamed xslx file</action>
+    	</actions>
+    </release>
 
     <release version="3.17-beta1" date="2017-07-01">
       <summary>

Modified: poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java Wed Jun 28 21:38:23 2017
@@ -74,8 +74,12 @@ public final class StreamHelper {
                     out.flush(); // only flush, don't close!
                 }
             });
+            // xmlContent.setXmlStandalone(true);
             trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
-            trans.setOutputProperty(OutputKeys.INDENT, "yes");
+            // don't indent xml documents, the indent will cause errors in calculating the xml signature
+            // because of different handling of linebreaks in Windows/Unix
+            // see https://stackoverflow.com/questions/36063375
+            trans.setOutputProperty(OutputKeys.INDENT, "no");
             trans.setOutputProperty(OutputKeys.STANDALONE, "yes");
             trans.transform(xmlSource, outputTarget);
         } catch (TransformerException e) {

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java Wed Jun 28 21:38:23 2017
@@ -40,24 +40,32 @@ public class SignatureMarshalListener im
         this.target.set(target);
     }
     
+    @Override
     public void handleEvent(Event e) {
-        if (!(e instanceof MutationEvent)) return;
+        if (!(e instanceof MutationEvent)) {
+            return;
+        }
         MutationEvent mutEvt = (MutationEvent)e;
         EventTarget et = mutEvt.getTarget();
-        if (!(et instanceof Element)) return;
+        if (!(et instanceof Element)) {
+            return;
+        }
         handleElement((Element)et);
     }
 
     public void handleElement(Element el) {
         EventTarget target = this.target.get();
-        String packageId = signatureConfig.getPackageSignatureId();
+
         if (el.hasAttribute("Id")) {
             el.setIdAttribute("Id", true);
         }
 
         setListener(target, this, false);
-        if (packageId.equals(el.getAttribute("Id"))) {
-            el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
+        if (OO_DIGSIG_NS.equals(el.getNamespaceURI())) {
+            String parentNS = el.getParentNode().getNamespaceURI();
+            if (!OO_DIGSIG_NS.equals(parentNS) && !el.hasAttributeNS(XML_NS, "mdssi")) {
+                el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
+            }
         }
         setPrefix(el);
         setListener(target, this, true);
@@ -86,6 +94,7 @@ public class SignatureMarshalListener im
         }
     }
     
+    @Override
     public void setSignatureConfig(SignatureConfig signatureConfig) {
         this.signatureConfig = signatureConfig;
     }

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java Wed Jun 28 21:38:23 2017
@@ -18,9 +18,9 @@
 /* ====================================================================
    This product contains an ASLv2 licensed version of the OOXML signer
    package from the eID Applet project
-   http://code.google.com/p/eid-applet/source/browse/trunk/README.txt  
+   http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
    Copyright (C) 2008-2014 FedICT.
-   ================================================================= */ 
+   ================================================================= */
 
 package org.apache.poi.poifs.crypt.dsig.facets;
 
@@ -29,6 +29,9 @@ import java.net.URISyntaxException;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -70,13 +73,13 @@ import com.microsoft.schemas.office.x200
 
 /**
  * Office OpenXML Signature Facet implementation.
- * 
- * @author fcorneli
+ *
  * @see <a href="http://msdn.microsoft.com/en-us/library/cc313071.aspx">[MS-OFFCRYPTO]: Office Document Cryptography Structure</a>
  */
 public class OOXMLSignatureFacet extends SignatureFacet {
 
     private static final POILogger LOG = POILogFactory.getLogger(OOXMLSignatureFacet.class);
+    private static final String ID_PACKAGE_OBJECT = "idPackageObject";
 
     @Override
     public void preSign(
@@ -98,17 +101,16 @@ public class OOXMLSignatureFacet extends
         List<Reference> manifestReferences = new ArrayList<Reference>();
         addManifestReferences(manifestReferences);
         Manifest manifest =  getSignatureFactory().newManifest(manifestReferences);
-        
-        String objectId = "idPackageObject"; // really has to be this value.
+
         List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
         objectContent.add(manifest);
 
         addSignatureTime(document, objectContent);
 
-        XMLObject xo = getSignatureFactory().newXMLObject(objectContent, objectId, null, null);
+        XMLObject xo = getSignatureFactory().newXMLObject(objectContent, ID_PACKAGE_OBJECT, null, null);
         objects.add(xo);
 
-        Reference reference = newReference("#" + objectId, null, XML_DIGSIG_NS+"Object", null, null);
+        Reference reference = newReference("#"+ID_PACKAGE_OBJECT, null, XML_DIGSIG_NS+"Object", null, null);
         references.add(reference);
     }
 
@@ -121,7 +123,7 @@ public class OOXMLSignatureFacet extends
 
         Set<String> digestedPartNames = new HashSet<String>();
         for (PackagePart pp : relsEntryNames) {
-            String baseUri = pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
+            final String baseUri = pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
 
             PackageRelationshipCollection prc;
             try {
@@ -130,11 +132,11 @@ public class OOXMLSignatureFacet extends
             } catch (InvalidFormatException e) {
                 throw new XMLSignatureException("Invalid relationship descriptor: "+pp.getPartName().getName(), e);
             }
-            
+
             RelationshipTransformParameterSpec parameterSpec = new RelationshipTransformParameterSpec();
             for (PackageRelationship relationship : prc) {
                 String relationshipType = relationship.getRelationshipType();
-                
+
                 /*
                  * ECMA-376 Part 2 - 3rd edition
                  * 13.2.4.16 Manifest Element
@@ -144,22 +146,20 @@ public class OOXMLSignatureFacet extends
                     continue;
                 }
 
-                if (!isSignedRelationship(relationshipType)) continue;
+                if (!isSignedRelationship(relationshipType)) {
+                    continue;
+                }
 
                 parameterSpec.addRelationshipReference(relationship.getId());
 
-                // TODO: find a better way ...
-                String partName = relationship.getTargetURI().toString();
-                if (!partName.startsWith(baseUri)) {
-                    partName = baseUri + partName;
-                }
-                try {
-                    partName = new URI(partName).normalize().getPath().replace('\\', '/');
-                    LOG.log(POILogger.DEBUG, "part name: " + partName);
-                } catch (URISyntaxException e) {
-                    throw new XMLSignatureException(e);
+                String partName = normalizePartName(relationship.getTargetURI(), baseUri);
+
+                // We only digest a part once.
+                if (digestedPartNames.contains(partName)) {
+                    continue;
                 }
-                
+                digestedPartNames.add(partName);
+
                 String contentType;
                 try {
                     PackagePartName relName = PackagingURIHelper.createPartName(partName);
@@ -168,32 +168,52 @@ public class OOXMLSignatureFacet extends
                 } catch (InvalidFormatException e) {
                     throw new XMLSignatureException(e);
                 }
-                
+
                 if (relationshipType.endsWith("customXml")
                     && !(contentType.equals("inkml+xml") || contentType.equals("text/xml"))) {
                     LOG.log(POILogger.DEBUG, "skipping customXml with content type: " + contentType);
                     continue;
                 }
-                
-                if (!digestedPartNames.contains(partName)) {
-                    // We only digest a part once.
-                    String uri = partName + "?ContentType=" + contentType;
-                    Reference reference = newReference(uri, null, null, null, null);
-                    manifestReferences.add(reference);
-                    digestedPartNames.add(partName);
-                }
+
+                String uri = partName + "?ContentType=" + contentType;
+                Reference reference = newReference(uri, null, null, null, null);
+                manifestReferences.add(reference);
             }
-            
+
             if (parameterSpec.hasSourceIds()) {
                 List<Transform> transforms = new ArrayList<Transform>();
                 transforms.add(newTransform(RelationshipTransformService.TRANSFORM_URI, parameterSpec));
                 transforms.add(newTransform(CanonicalizationMethod.INCLUSIVE));
-                String uri = pp.getPartName().getName()
+                String uri = normalizePartName(pp.getPartName().getURI(), baseUri)
                     + "?ContentType=application/vnd.openxmlformats-package.relationships+xml";
                 Reference reference = newReference(uri, transforms, null, null, null);
                 manifestReferences.add(reference);
             }
         }
+        
+        Collections.sort(manifestReferences, new Comparator<Reference>() {
+            public int compare(Reference o1, Reference o2) {
+                return o1.getURI().compareTo(o2.getURI());
+            }
+        });
+    }
+
+    /**
+     * Normalize a URI/part name
+     * TODO: find a better way ...
+     */
+    private static String normalizePartName(URI partName, String baseUri) throws XMLSignatureException {
+        String pn = partName.toASCIIString();
+        if (!pn.startsWith(baseUri)) {
+            pn = baseUri + pn;
+        }
+        try {
+            pn = new URI(pn).normalize().getPath().replace('\\', '/');
+            LOG.log(POILogger.DEBUG, "part name: " + pn);
+        } catch (URISyntaxException e) {
+            throw new XMLSignatureException(e);
+        }
+        return pn;
     }
 
 
@@ -236,7 +256,7 @@ public class OOXMLSignatureFacet extends
         ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
         Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
         n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
-        
+
         List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
         signatureInfoContent.add(new DOMStructure(n));
         SignatureProperty signatureInfoSignatureProperty = getSignatureFactory()
@@ -268,208 +288,33 @@ public class OOXMLSignatureFacet extends
 
     protected static boolean isSignedRelationship(String relationshipType) {
         LOG.log(POILogger.DEBUG, "relationship type: " + relationshipType);
-        for (String signedTypeExtension : signed) {
-            if (relationshipType.endsWith(signedTypeExtension)) {
-                return true;
-            }
-        }
-        if (relationshipType.endsWith("customXml")) {
-            LOG.log(POILogger.DEBUG, "customXml relationship type");
-            return true;
-        }
-        return false;
+        String rt = relationshipType.replaceFirst(".*/relationships/", "");
+        return (signed.contains(rt) || rt.endsWith("customXml"));
     }
-    
-    public static final String[] contentTypes = {
-        /*
-         * Word
-         */
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
-        "application/vnd.openxmlformats-officedocument.theme+xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml",
-        "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
-
-        /*
-         * Word 2010
-         */
-        "application/vnd.ms-word.stylesWithEffects+xml",
-
-        /*
-         * Excel
-         */
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
-        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
-
-        /*
-         * Powerpoint
-         */
-        "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml",
-        "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml",
-        "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml",
-        "application/vnd.openxmlformats-officedocument.presentationml.slide+xml",
-        "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml",
-
-        /*
-         * Powerpoint 2010
-         */
-        "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml",
-        "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"
-    };
 
     /**
      * Office 2010 list of signed types (extensions).
      */
-    public static final String[] signed = {
-        "powerPivotData", //
-        "activeXControlBinary", //
-        "attachedToolbars", //
-        "connectorXml", //
-        "downRev", //
-        "functionPrototypes", //
-        "graphicFrameDoc", //
-        "groupShapeXml", //
-        "ink", //
-        "keyMapCustomizations", //
-        "legacyDiagramText", //
-        "legacyDocTextInfo", //
-        "officeDocument", //
-        "pictureXml", //
-        "shapeXml", //
-        "smartTags", //
-        "ui/altText", //
-        "ui/buttonSize", //
-        "ui/controlID", //
-        "ui/description", //
-        "ui/enabled", //
-        "ui/extensibility", //
-        "ui/helperText", //
-        "ui/imageID", //
-        "ui/imageMso", //
-        "ui/keyTip", //
-        "ui/label", //
-        "ui/lcid", //
-        "ui/loud", //
-        "ui/pressed", //
-        "ui/progID", //
-        "ui/ribbonID", //
-        "ui/showImage", //
-        "ui/showLabel", //
-        "ui/supertip", //
-        "ui/target", //
-        "ui/text", //
-        "ui/title", //
-        "ui/tooltip", //
-        "ui/userCustomization", //
-        "ui/visible", //
-        "userXmlData", //
-        "vbaProject", //
-        "wordVbaData", //
-        "wsSortMap", //
-        "xlBinaryIndex", //
-        "xlExternalLinkPath/xlAlternateStartup", //
-        "xlExternalLinkPath/xlLibrary", //
-        "xlExternalLinkPath/xlPathMissing", //
-        "xlExternalLinkPath/xlStartup", //
-        "xlIntlMacrosheet", //
-        "xlMacrosheet", //
-        "customData", //
-        "diagramDrawing", //
-        "hdphoto", //
-        "inkXml", //
-        "media", //
-        "slicer", //
-        "slicerCache", //
-        "stylesWithEffects", //
-        "ui/extensibility", //
-        "chartColorStyle", //
-        "chartLayout", //
-        "chartStyle", //
-        "dictionary", //
-        "timeline", //
-        "timelineCache", //
-        "aFChunk", //
-        "attachedTemplate", //
-        "audio", //
-        "calcChain", //
-        "chart", //
-        "chartsheet", //
-        "chartUserShapes", //
-        "commentAuthors", //
-        "comments", //
-        "connections", //
-        "control", //
-        "customProperty", //
-        "customXml", //
-        "diagramColors", //
-        "diagramData", //
-        "diagramLayout", //
-        "diagramQuickStyle", //
-        "dialogsheet", //
-        "drawing", //
-        "endnotes", //
-        "externalLink", //
-        "externalLinkPath", //
-        "font", //
-        "fontTable", //
-        "footer", //
-        "footnotes", //
-        "glossaryDocument", //
-        "handoutMaster", //
-        "header", //
-        "hyperlink", //
-        "image", //
-        "mailMergeHeaderSource", //
-        "mailMergeRecipientData", //
-        "mailMergeSource", //
-        "notesMaster", //
-        "notesSlide", //
-        "numbering", //
-        "officeDocument", //
-        "oleObject", //
-        "package", //
-        "pivotCacheDefinition", //
-        "pivotCacheRecords", //
-        "pivotTable", //
-        "presProps", //
-        "printerSettings", //
-        "queryTable", //
-        "recipientData", //
-        "settings", //
-        "sharedStrings", //
-        "sheetMetadata", //
-        "slide", //
-        "slideLayout", //
-        "slideMaster", //
-        "slideUpdateInfo", //
-        "slideUpdateUrl", //
-        "styles", //
-        "table", //
-        "tableSingleCells", //
-        "tableStyles", //
-        "tags", //
-        "theme", //
-        "themeOverride", //
-        "transform", //
-        "video", //
-        "viewProps", //
-        "volatileDependencies", //
-        "webSettings", //
-        "worksheet", //
-        "xmlMaps", //
-        "ctrlProp", //
-        "customData", //
-        "diagram", //
-        "diagramColorsHeader", //
-        "diagramLayoutHeader", //
-        "diagramQuickStyleHeader", //
-        "documentParts", //
-        "slicer", //
-        "slicerCache", //
-        "vmlDrawing" //
-    };
+    private static final Set<String> signed = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
+        "activeXControlBinary","aFChunk","attachedTemplate","attachedToolbars","audio","calcChain","chart","chartColorStyle",
+        "chartLayout","chartsheet","chartStyle","chartUserShapes","commentAuthors","comments","connections","connectorXml",
+        "control","ctrlProp","customData","customData","customProperty","customXml","diagram","diagramColors",
+        "diagramColorsHeader","diagramData","diagramDrawing","diagramLayout","diagramLayoutHeader","diagramQuickStyle",
+        "diagramQuickStyleHeader","dialogsheet","dictionary","documentParts","downRev","drawing","endnotes","externalLink",
+        "externalLinkPath","font","fontTable","footer","footnotes","functionPrototypes","glossaryDocument","graphicFrameDoc",
+        "groupShapeXml","handoutMaster","hdphoto","header","hyperlink","image","ink","inkXml","keyMapCustomizations",
+        "legacyDiagramText","legacyDocTextInfo","mailMergeHeaderSource","mailMergeRecipientData","mailMergeSource","media",
+        "notesMaster","notesSlide","numbering","officeDocument","officeDocument","oleObject","package","pictureXml",
+        "pivotCacheDefinition","pivotCacheRecords","pivotTable","powerPivotData","presProps","printerSettings","queryTable",
+        "recipientData","settings","shapeXml","sharedStrings","sheetMetadata","slicer","slicer","slicerCache","slicerCache",
+        "slide","slideLayout","slideMaster","slideUpdateInfo","slideUpdateUrl","smartTags","styles","stylesWithEffects",
+        "table","tableSingleCells","tableStyles","tags","theme","themeOverride","timeline","timelineCache","transform",
+        "ui/altText","ui/buttonSize","ui/controlID","ui/description","ui/enabled","ui/extensibility","ui/extensibility",
+        "ui/helperText","ui/imageID","ui/imageMso","ui/keyTip","ui/label","ui/lcid","ui/loud","ui/pressed","ui/progID",
+        "ui/ribbonID","ui/showImage","ui/showLabel","ui/supertip","ui/target","ui/text","ui/title","ui/tooltip",
+        "ui/userCustomization","ui/visible","userXmlData","vbaProject","video","viewProps","vmlDrawing",
+        "volatileDependencies","webSettings","wordVbaData","worksheet","wsSortMap","xlBinaryIndex",
+        "xlExternalLinkPath/xlAlternateStartup","xlExternalLinkPath/xlLibrary","xlExternalLinkPath/xlPathMissing",
+        "xlExternalLinkPath/xlStartup","xlIntlMacrosheet","xlMacrosheet","xmlMaps"
+    )));
 }
\ No newline at end of file

Modified: poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java (original)
+++ poi/trunk/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java Wed Jun 28 21:38:23 2017
@@ -25,10 +25,9 @@
 package org.apache.poi.poifs.crypt.dsig.services;
 
 import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
+import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS;
+import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_NS;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.InvalidAlgorithmParameterException;
@@ -36,9 +35,8 @@ import java.security.Provider;
 import java.security.Security;
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
 import java.util.List;
+import java.util.TreeMap;
 
 import javax.xml.crypto.Data;
 import javax.xml.crypto.MarshalException;
@@ -50,23 +48,20 @@ import javax.xml.crypto.dsig.TransformEx
 import javax.xml.crypto.dsig.TransformService;
 import javax.xml.crypto.dsig.spec.TransformParameterSpec;
 
+import org.apache.jcp.xml.dsig.internal.dom.ApacheNodeSetData;
+import org.apache.poi.util.DocumentHelper;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
-import org.apache.poi.util.XmlSort;
-import org.apache.xmlbeans.XmlCursor;
+import org.apache.xml.security.signature.XMLSignatureInput;
 import org.apache.xmlbeans.XmlException;
 import org.apache.xmlbeans.XmlObject;
-import org.apache.xmlbeans.XmlOptions;
 import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTRelationshipReference;
 import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.RelationshipReferenceDocument;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationship;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationships;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.RelationshipsDocument;
-import org.openxmlformats.schemas.xpackage.x2006.relationships.STTargetMode;
 import org.w3.x2000.x09.xmldsig.TransformDocument;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * JSR105 implementation of the RelationshipTransform transformation.
@@ -89,7 +84,7 @@ public class RelationshipTransformServic
     public static class RelationshipTransformParameterSpec implements TransformParameterSpec {
         List<String> sourceIds = new ArrayList<String>();
         public void addRelationshipReference(String relationshipId) {
-            sourceIds.add(relationshipId);
+                sourceIds.add(relationshipId);
         }
         public boolean hasSourceIds() {
             return !sourceIds.isEmpty();
@@ -163,15 +158,13 @@ public class RelationshipTransformServic
         LOG.log(POILogger.DEBUG, "marshallParams(parent,context)");
         DOMStructure domParent = (DOMStructure) parent;
         Element parentNode = (Element)domParent.getNode();
-        // parentNode.setAttributeNS(XML_NS, "xmlns:mdssi", XML_DIGSIG_NS);
         Document doc = parentNode.getOwnerDocument();
         
         for (String sourceId : this.sourceIds) {
-            RelationshipReferenceDocument relRef = RelationshipReferenceDocument.Factory.newInstance();
-            relRef.addNewRelationshipReference().setSourceId(sourceId);
-            Node n = relRef.getRelationshipReference().getDomNode();
-            n = doc.importNode(n, true);
-            parentNode.appendChild(n);
+            Element el = doc.createElementNS(OO_DIGSIG_NS, "mdssi:RelationshipReference");
+            el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
+            el.setAttribute("SourceId", sourceId);
+            parentNode.appendChild(el);
         }
     }
     
@@ -180,6 +173,13 @@ public class RelationshipTransformServic
         return null;
     }
 
+    /**
+     * The relationships transform takes the XML document from the Relationships part
+     * and converts it to another XML document.
+     * 
+     * @see <a href="https://www.ecma-international.org/activities/Office%20Open%20XML%20Formats/Draft%20ECMA-376%203rd%20edition,%20March%202011/Office%20Open%20XML%20Part%202%20-%20Open%20Packaging%20Conventions.pdf">13.2.4.24 Relationships Transform Algorithm</a>
+     * @see <a href="https://stackoverflow.com/questions/36063375">XML Relationship Transform Algorithm</a>
+     */
     public Data transform(Data data, XMLCryptoContext context) throws TransformException {
         LOG.log(POILogger.DEBUG, "transform(data,context)");
         LOG.log(POILogger.DEBUG, "data java type: " + data.getClass().getName());
@@ -187,53 +187,40 @@ public class RelationshipTransformServic
         LOG.log(POILogger.DEBUG, "URI: " + octetStreamData.getURI());
         InputStream octetStream = octetStreamData.getOctetStream();
         
-        RelationshipsDocument relDoc;
+        Document doc;
         try {
-            relDoc = RelationshipsDocument.Factory.parse(octetStream, DEFAULT_XML_OPTIONS);
+            doc = DocumentHelper.readDocument(octetStream);
         } catch (Exception e) {
             throw new TransformException(e.getMessage(), e);
         }
-        LOG.log(POILogger.DEBUG, "relationships document", relDoc);
         
-        CTRelationships rels = relDoc.getRelationships();
-        List<CTRelationship> relList = rels.getRelationshipList();
-        Iterator<CTRelationship> relIter = rels.getRelationshipList().iterator();
-        while (relIter.hasNext()) {
-            CTRelationship rel = relIter.next();
-            /*
-             * See: ISO/IEC 29500-2:2008(E) - 13.2.4.24 Relationships Transform
-             * Algorithm.
-             */
-            if (!this.sourceIds.contains(rel.getId())) {
-                LOG.log(POILogger.DEBUG, "removing element: " + rel.getId());
-                relIter.remove();
-            } else {
-                if (!rel.isSetTargetMode()) {
-                    rel.setTargetMode(STTargetMode.INTERNAL);
+        // keep only those relationships which id is registered in the sourceIds
+        Element root = doc.getDocumentElement();
+        NodeList nl = root.getChildNodes();
+        TreeMap<String,Element> rsList = new TreeMap<String,Element>();
+        for (int i=nl.getLength()-1; i>=0; i--) {
+            Node n = nl.item(i);
+            if ("Relationship".equals(n.getLocalName())) {
+                Element el = (Element)n;
+                String id = el.getAttribute("Id");
+                if (sourceIds.contains(id)) {
+                    String targetMode = el.getAttribute("TargetMode");
+                    if ("".equals(targetMode)) {
+                        el.setAttribute("TargetMode", "Internal");
+                    }
+                    rsList.put(id, el);
                 }
             }
+            root.removeChild(n);
         }
-        
-        // TODO: remove non element nodes ???
-        LOG.log(POILogger.DEBUG, "# Relationship elements", relList.size());
-        
-        XmlSort.sort(rels, new Comparator<XmlCursor>(){
-            public int compare(XmlCursor c1, XmlCursor c2) {
-                String id1 = ((CTRelationship)c1.getObject()).getId();
-                String id2 = ((CTRelationship)c2.getObject()).getId();
-                return id1.compareTo(id2);
-            }
-        });
 
-        try {
-            ByteArrayOutputStream bos = new ByteArrayOutputStream();
-            XmlOptions xo = new XmlOptions();
-            xo.setSaveNoXmlDecl();
-            relDoc.save(bos, xo);
-            return new OctetStreamData(new ByteArrayInputStream(bos.toByteArray()));
-        } catch (IOException e) {
-            throw new TransformException(e.getMessage(), e);
+        for (Element el : rsList.values()) {
+            root.appendChild(el);
         }
+        
+        LOG.log(POILogger.DEBUG, "# Relationship elements: ", rsList.size());
+        
+        return new ApacheNodeSetData(new XMLSignatureInput(root));
     }
 
     public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException {

Modified: poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
URL: http://svn.apache.org/viewvc/poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java?rev=1800207&r1=1800206&r2=1800207&view=diff
==============================================================================
--- poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java (original)
+++ poi/trunk/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java Wed Jun 28 21:38:23 2017
@@ -72,6 +72,7 @@ import org.apache.poi.poifs.crypt.dsig.s
 import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
 import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
 import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
+import org.apache.poi.poifs.storage.RawDataUtil;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
 import org.apache.poi.util.DocumentHelper;
 import org.apache.poi.util.IOUtils;
@@ -119,6 +120,70 @@ public class TestSignatureInfo {
     }
     
     @Test
+    public void bug61182() throws Exception {
+        String pfxInput =
+            "H4sIAAAAAAAAAFXTfzzTeRwH8P2uGRmG6hKSmJh9a2HsuPy60VnHCEU6v86sieZH2Jr2qFl+s+ZHJ5tfUcfKb4uho/OjiFq1qTv5ceFyp0PqEK"+
+            "fH4+66++Pz+Dwer9fj8f7r9cRzEd4QMBTPRWxDIM14ZN47NfAWsJgL34Bx4at4Lvwdngvd9b8KqgbjQpGbMXzzgRGovytVFTBEzIXU47kQCd4U"+
+            "ofJPvHl8JwyTjRS55hbKoor3UJLDE1i/PcPKCBAIDATjQlKiK67XjVYdcnkZgD2txroiAUb8W9dtn57DvTsbM+3wIsdocXDEN7TdPKgaSl+tU1"+
+            "xq9oqiB5yMaZCPho8uUEbFU9U6u3N7lEMLTJGeA0RfX+5FMRrpXPFrbrlJ8uNUCE2H247P28Ckyfqlsy32yeKg/HTbH5JpqUDNw2B32+SaiRw7"+
+            "ofRMePUpaAoK7KYgmd5ZIc0rLLYjJBfOWCb28xlrGhbpJvdToFdqt5PXVjEz5YOJ6g7W0fskuKW9/iZP0yLEVpR9XkkHmb6tfpcE8YwCdWNCan"+
+            "LvAsco25JdF1j2/FLAMVU79HdOex07main90dy40511OZtTGZ+TdVd3lKZ7D3clEg9hLESHwSNnZ6239X4yLM4xYSElQ/hqSbwdmiozYG9PhF2"+
+            "Zf0XaZnxzTK0Iot+rJ3kYoxWTLE8DR9leV62Ywbtlg4mapYOxb3lT7fQ1x4EQ44flh2oFWSPLR8LMbsc6jzJsV6OZ3TrODjHEdw9W+8OD32vd8"+
+            "XQ6iCaIHcrSOn6qS0TKLr786234eeSAhvAQbEsVn7vrvc/487Be/O2e/+5Y5zRq2zAtz6pfcNyraJNDqMW1inNkgJ3t3VESbZ3pNzyl3KHILs0"+
+            "51dY6msDYSlWhw40TglXxj9rw95O6gFWIuN012W/vhS50jpKXcao4gc1aLaXtJXxirbRkpZ/0e7a0pD6TDa7+GxEdEEML3VGo9udD5YUKhU3y7"+
+            "SzWAgN6WIEIglq7LilvCjqIVLIfg8CvVGL9f5iSsCDf5hef4vMxbyvcjINuy06gZu+iPYOWNxjfrwKGYzoqqotK2aywgYVrPMh0JovfkDuN95n"+
+            "MdVlYHbN1Mnn4TxAwuv+u3AkBlDZvRUUCwoDMUGxeMNPhTaAgWl60xhhBgCBaEMgAACReMAav7n3x598IDYJ9GxGXRAwaPOT/kfO/1AgPqLQkp"+
+            "MiIVaHthnUS4v2y32e2BjdMPyIImUTBW3cV3R5tjVQm0MOm+D2C5+bBW9vHLjLR4lun4toQiY3Ls/v4bES/OJ4EmpZk5xhL9i5ClofYZNEsxFn"+
+            "An/q821Tg+Cq9Er4XYGQe8ogjjLJ2b7dUsJ3auFQFNUJF7Ke7yUL2EeYYxl6vz5l4q5u8704mRbFts1E1eWMp6WIy91GPrsVlRGvtuNERfrjfE"+
+            "YtzUI3Flcv65zJUbUBEzUnTS0fEYso2XyToAl8kb251mUY2o2lJzv5dp/1htmcjeeP2MjxC+3S45ljx7jd52Pv9XAat+ryiauFOF7YgztkoWWD"+
+            "h62tplPH1bzDV+d0NLdaE5AfVJ09HuUYTFS+iggtvT5Euyk+unj4N2XvzW91n+GNjtgWfKOHmkinUPvYRh70Jv+wlPJrVaT8mL7GxJLqDC9jbv"+
+            "Gznoiae6es+wQejnk3XjU366MrK/zXxngBYj9J6NnXc9mMiTFLX8WqQ8iTelTAFs2NJzPoDzrBUz4JFIEOa6Dja6dULc68g1jFDTeEHZyra7RZ"+
+            "2ElqGDEqcNRo3SNX6feMy9EF1GOyZK0Sa87KwjKw8aM68dpsIYjfLcTXaZ6atg0BKfMnl6axeUGEaIFSP7rzj9wjzumRbG3jgUVp2lX5AK/tsO"+
+            "7R4TQX/9/H6RiN34c9KldmPZZGANXzzTajZS9mR2OSvlJ+F4AgSko4htrMAKFTBu51/5SWNsO1vlRaaG48ZRJ+8PzuHQMdvS36gNpRPi7jhF1S"+
+            "H3B2ycI4y0VURv6SrqJNUY/X645ZFJQ+eBO+ptG7o8axf1dcqh2beiQk+GRTeZ37LVeUlaeo9vl1/+8tyBfyT2v5lFC5E19WdKIyCuZe7r99Px"+
+            "D/Od4Qj0TA92+DQnbCQTCMy/wwse9O4gsEebkkpPIP5GBV3Q0YBsj75XE0uSFQ1tCZSW8bNa9MUJZ/nPBfExohHlgGAAA=";
+        
+        Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+        cal.clear();
+        cal.set(2017, 6, 1);
+        
+        SignatureConfig signatureConfig = prepareConfig("test", "CN=Test", pfxInput);
+        signatureConfig.setExecutionTime(cal.getTime());
+
+        SignatureInfo si = new SignatureInfo();
+        si.setSignatureConfig(signatureConfig);
+
+        XSSFWorkbook wb1 = new XSSFWorkbook();
+        wb1.createSheet().createRow(1).createCell(1).setCellValue("Test");
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+        wb1.write(bos);
+        wb1.close();
+        
+        OPCPackage pkg1 = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()));
+        
+        signatureConfig.setOpcPackage(pkg1);
+        si.confirmSignature();
+        assertTrue(si.verifySignature());
+        bos.reset();
+        pkg1.save(bos);
+        pkg1.close();
+        
+        XSSFWorkbook wb2 = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()));
+        assertEquals("Test", wb2.getSheetAt(0).getRow(1).getCell(1).getStringCellValue());
+        OPCPackage pkg2 = wb2.getPackage();
+        signatureConfig.setOpcPackage(pkg2);
+        assertTrue(si.verifySignature());
+        String signExp =
+            "Lxp2LFa+0YWGOBL8zVdf7SWRQiNK/Tt85W+kmH1bunlua030BKbQc6yWIIk6gN6jCTtrJ1h2eMRbLwymygOUpM"+
+            "dd0MeQY3mMWRSO9qEW87SQvyDqBh71zXWW3ZYET+vJWr3BCNEtXCy8jZvgXqILBGk5vMJW/EYaUEhBcDGjCm0=";
+        String signAct = si.getSignatureParts().iterator().next().
+            getSignatureDocument().getSignature().getSignatureValue().getStringValue();
+        assertEquals(signExp, signAct);
+        pkg2.close();
+        wb2.close();
+    }
+    
+    @Test
     public void office2007prettyPrintedRels() throws Exception {
         OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ);
         try {
@@ -611,15 +676,21 @@ public class TestSignatureInfo {
             pkg.close();
         }
     }
-    
-    private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
-        initKeyPair(alias, signerDn);
+
+    private SignatureConfig prepareConfig(String alias, String signerDn, String pfxInput) throws Exception {
+        initKeyPair(alias, signerDn, pfxInput);
 
         SignatureConfig signatureConfig = new SignatureConfig();
         signatureConfig.setKey(keyPair.getPrivate());
         signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
         signatureConfig.setExecutionTime(cal.getTime());
         signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
+
+        return signatureConfig;
+    }
+    
+    private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
+        SignatureConfig signatureConfig = prepareConfig(alias, signerDn, null);
         signatureConfig.setOpcPackage(pkgCopy);
         
         SignatureInfo si = new SignatureInfo();
@@ -656,13 +727,21 @@ public class TestSignatureInfo {
     }
 
     private void initKeyPair(String alias, String subjectDN) throws Exception {
+        initKeyPair(alias, subjectDN, null);
+    }
+    
+    private void initKeyPair(String alias, String subjectDN, String pfxInput) throws Exception {
         final char password[] = "test".toCharArray();
         File file = new File("build/test.pfx");
 
         KeyStore keystore = KeyStore.getInstance("PKCS12");
 
-        if (file.exists()) {
-            FileInputStream fis = new FileInputStream(file);
+        if (pfxInput != null) {
+            InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput));
+            keystore.load(fis, password);
+            fis.close();
+        } else if (file.exists()) { 
+            InputStream fis = new FileInputStream(file);
             keystore.load(fis, password);
             fis.close();
         } else {
@@ -685,9 +764,12 @@ public class TestSignatureInfo {
                 , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage);
 
             keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
-            FileOutputStream fos = new FileOutputStream(file);
-            keystore.store(fos, password);
-            fos.close();
+            
+            if (pfxInput == null) {
+                FileOutputStream fos = new FileOutputStream(file);
+                keystore.store(fos, password);
+                fos.close();
+            }
         }
     }
 
@@ -701,8 +783,7 @@ public class TestSignatureInfo {
         // in the Sonar Maven runs where we are at a different source directory
         File buildDir = new File("build");
         if(!buildDir.exists()) {
-            assertTrue("Failed to create " + buildDir.getAbsolutePath(), 
-                    buildDir.mkdirs());
+            assertTrue("Failed to create " + buildDir.getAbsolutePath(), buildDir.mkdirs());
         }
         File tmpFile = new File(buildDir, "sigtest"+extension);
 



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org


Mime
View raw message