View | Details | Raw Unified | Return to bug 61182
Collapse All | Expand All

(-)src/documentation/content/xdocs/encryption.xml (-28 / +152 lines)
Lines 31-39 Link Here
31
31
32
   <body>
32
   <body>
33
    <section><title>Overview</title>
33
    <section><title>Overview</title>
34
	<p>Apache POI contains support for reading few variants of encrypted office files: </p>
34
    <p>Apache POI contains support for reading few variants of encrypted office files: </p>
35
	<ul>
35
    <ul>
36
		<li>Binary formats (.xls, .ppt, .doc, ...)<br/>
36
        <li>Binary formats (.xls, .ppt, .doc, ...)<br/>
37
        encryption is format-dependent and needs to be implemented per format differently.<br/>
37
        encryption is format-dependent and needs to be implemented per format differently.<br/>
38
        Use <link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
38
        Use <link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
39
        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)
39
        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)
Lines 41-47 Link Here
41
        Setting a null password before saving removes the password protection.<br/>
41
        Setting a null password before saving removes the password protection.<br/>
42
        The password is set in a thread local variable. Do not forget to reset it to null after text extraction.
42
        The password is set in a thread local variable. Do not forget to reset it to null after text extraction.
43
        </li>
43
        </li>
44
		<li>XML-based formats (.xlsx, .pptx, .docx, ...)<br/>
44
        <li>XML-based formats (.xlsx, .pptx, .docx, ...)<br/>
45
        use the same encryption logic over all formats. When encrypted, the zipped files will be
45
        use the same encryption logic over all formats. When encrypted, the zipped files will be
46
        stored within an OLE file in the EncryptedPackage stream.<br/>
46
        stored within an OLE file in the EncryptedPackage stream.<br/>
47
        If you plan to use POI to actually generate encrypted documents, be aware not to use anything less than
47
        If you plan to use POI to actually generate encrypted documents, be aware not to use anything less than
Lines 54-66 Link Here
54
        <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JDK7</link>,
54
        <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">JDK7</link>,
55
        <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JDK8</link>).
55
        <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">JDK8</link>).
56
        </li>
56
        </li>
57
	</ul>
57
    </ul>
58
58
59
	<p>Some "write-protected" files are encrypted with the built-in password "VelvetSweatshop", POI can read that files too.</p>
59
    <p>Some "write-protected" files are encrypted with the built-in password "VelvetSweatshop", POI can read that files too.</p>
60
    </section>	
60
    </section>
61
61
62
    <section><title>Supported feature matrix</title>
62
    <section><title>Supported feature matrix</title>
63
    
63
64
    <table>
64
    <table>
65
        <tr>
65
        <tr>
66
            <th>Encryption</th>
66
            <th>Encryption</th>
Lines 87-93 Link Here
87
            <td class="feature-yes">Yes (since 3.17)</td>
87
            <td class="feature-yes">Yes (since 3.17)</td>
88
        </tr>
88
        </tr>
89
        <tr>
89
        <tr>
90
        	<th/>
90
            <th/>
91
            <th>XSSF</th>
91
            <th>XSSF</th>
92
            <th>XSLF</th>
92
            <th>XSLF</th>
93
            <th>XWPF</th>
93
            <th>XWPF</th>
Lines 117-138 Link Here
117
            <td class="feature-yes">Yes</td>
117
            <td class="feature-yes">Yes</td>
118
        </tr>
118
        </tr>
119
    </table>
119
    </table>
120
    
120
121
    <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>.
121
    <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>.
122
    </p>
122
    </p>
123
123
124
    <p>**) the <link href="https://msdn.microsoft.com/en-us/library/cc313071(v=office.12).aspx">MS-OFFCRYPTO</link>
124
    <p>**) the <link href="https://msdn.microsoft.com/en-us/library/cc313071(v=office.12).aspx">MS-OFFCRYPTO</link>
125
    documentation only mentions the RC4 (without CryptoAPI) encryption as a "in place" encryption, but
125
    documentation only mentions the RC4 (without CryptoAPI) encryption as a "in place" encryption, but
126
    apparently there's also a container based method with that key generation logic. 
126
    apparently there's also a container based method with that key generation logic.
127
    </p>
127
    </p>
128
    </section>
128
    </section>
129
129
130
    <section><title>Binary formats</title>
130
    <section><title>Binary formats</title>
131
    	<p>As mentioned above, use
131
        <p>As mentioned above, use
132
        <link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
132
        <link href="https://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
133
        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)
133
        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)
134
        to specify the password.</p>
134
        to specify the password.</p>
135
        
135
136
        <source>
136
        <source>
137
// XOR/RC4 decryption for xls
137
// XOR/RC4 decryption for xls
138
Biff8EncryptionKey.setCurrentUserPassword("pass");
138
Biff8EncryptionKey.setCurrentUserPassword("pass");
Lines 140-146 Link Here
140
HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true);
140
HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true);
141
Biff8EncryptionKey.setCurrentUserPassword(null);
141
Biff8EncryptionKey.setCurrentUserPassword(null);
142
        </source>
142
        </source>
143
        
143
144
        <source>
144
        <source>
145
// RC4 CryptoApi support ppt - decryption
145
// RC4 CryptoApi support ppt - decryption
146
Biff8EncryptionKey.setCurrentUserPassword("pass");
146
Biff8EncryptionKey.setCurrentUserPassword("pass");
Lines 166-175 Link Here
166
    </section>
166
    </section>
167
167
168
    <section><title>XML-based formats - Decryption</title>
168
    <section><title>XML-based formats - Decryption</title>
169
	<p>XML-based formats are stored in OLE-package stream "EncryptedPackage". Use org.apache.poi.poifs.crypt.Decryptor
169
    <p>XML-based formats are stored in OLE-package stream "EncryptedPackage". Use org.apache.poi.poifs.crypt.Decryptor
170
	to decode file:</p>
170
    to decode file:</p>
171
171
172
	<source>
172
    <source>
173
EncryptionInfo info = new EncryptionInfo(filesystem);
173
EncryptionInfo info = new EncryptionInfo(filesystem);
174
Decryptor d = Decryptor.getInstance(info);
174
Decryptor d = Decryptor.getInstance(info);
175
175
Lines 185-195 Link Here
185
} catch (GeneralSecurityException ex) {
185
} catch (GeneralSecurityException ex) {
186
    throw new RuntimeException("Unable to process encrypted document", ex);
186
    throw new RuntimeException("Unable to process encrypted document", ex);
187
}
187
}
188
	</source>
188
    </source>
189
189
190
	<p>If you want to read file encrypted with build-in password, use Decryptor.DEFAULT_PASSWORD.</p>
190
    <p>If you want to read file encrypted with build-in password, use Decryptor.DEFAULT_PASSWORD.</p>
191
     </section>
191
     </section>
192
     
192
193
     <section><title>XML-based formats - Encryption</title>
193
     <section><title>XML-based formats - Encryption</title>
194
     <p>Encrypting a file is similar to the above decryption process. Basically you'll need to choose between
194
     <p>Encrypting a file is similar to the above decryption process. Basically you'll need to choose between
195
     <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/EncryptionMode.html">binaryRC4, standard and agile encryption</link>,
195
     <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/EncryptionMode.html">binaryRC4, standard and agile encryption</link>,
Lines 213-222 Link Here
213
// Write out the encrypted version
213
// Write out the encrypted version
214
FileOutputStream fos = new FileOutputStream("...");
214
FileOutputStream fos = new FileOutputStream("...");
215
fs.writeFilesystem(fos);
215
fs.writeFilesystem(fos);
216
fos.close();     
216
fos.close();
217
     </source>
217
     </source>
218
     </section>
218
     </section>
219
     
219
220
     <section><title>XML-based formats - Signing (XML Signature)</title>
220
     <section><title>XML-based formats - Signing (XML Signature)</title>
221
     <p>An Office document can be digital signed by a <link href="https://en.wikipedia.org/wiki/XML_Signature">XML Signature</link>
221
     <p>An Office document can be digital signed by a <link href="https://en.wikipedia.org/wiki/XML_Signature">XML Signature</link>
222
     to protect it from unauthorized modifications, i.e. modifications without having the original certificate.
222
     to protect it from unauthorized modifications, i.e. modifications without having the original certificate.
Lines 231-247 Link Here
231
     <ul>
231
     <ul>
232
     <li>BouncyCastle bcpkix and bcprov (tested against 1.53)</li>
232
     <li>BouncyCastle bcpkix and bcprov (tested against 1.53)</li>
233
     <li>Apache Santuario "xmlsec" (tested against 2.0.6)</li>
233
     <li>Apache Santuario "xmlsec" (tested against 2.0.6)</li>
234
     <li>and slf4j-api (tested against 1.7.12)</li>     
234
     <li>and slf4j-api (tested against 1.7.12)</li>
235
     </ul>
235
     </ul>
236
     <p>Depending on the <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/SignatureConfig.html">configuration</link>
236
     <p>Depending on the <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/SignatureConfig.html">configuration</link>
237
     and the activated <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/facets/package-summary.html">facets</link>
237
     and the activated <link href="https://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/facets/package-summary.html">facets</link>
238
     various <link href="https://en.wikipedia.org/wiki/XAdES">XAdES levels</link> are supported - the support for higher levels (XAdES-T+)
238
     various <link href="https://en.wikipedia.org/wiki/XAdES">XAdES levels</link> are supported - the support for higher levels (XAdES-T+)
239
     depend on supporting services and although the code is adopted, the integration is not well tested ... please support us on
239
     depend on supporting services and although the code is adopted, the integration is not well tested ... please support us on
240
     integration (testing) with timestamp and revocation (OCSP) services. 
240
     integration (testing) with timestamp and revocation (OCSP) services.
241
     </p>
241
     </p>
242
     <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>
242
     <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>
243
     </section>
243
     </section>
244
     
244
245
     <section><title>Validating a signed office document</title>
245
     <section><title>Validating a signed office document</title>
246
246
247
     <source>
247
     <source>
Lines 254-262 Link Here
254
...
254
...
255
     </source>
255
     </source>
256
     </section>
256
     </section>
257
     
257
258
     <section><title>Signing an office document</title>
258
     <section><title>Signing an office document</title>
259
     
259
260
     <source>
260
     <source>
261
// loading the keystore - pkcs12 is used here, but of course jks &amp; co are also valid
261
// loading the keystore - pkcs12 is used here, but of course jks &amp; co are also valid
262
// the keystore needs to contain a private key and it's certificate having a
262
// the keystore needs to contain a private key and it's certificate having a
Lines 304-309 Link Here
304
       and other <link href="https://svn.apache.org/viewvc?view=revision&amp;revision=1768744">files</link>
304
       and other <link href="https://svn.apache.org/viewvc?view=revision&amp;revision=1768744">files</link>
305
       that are needed for this example.</p>
305
       that are needed for this example.</p>
306
     </section>
306
     </section>
307
308
     <section><title>Debugging XML signature issues</title>
309
       <p>Finding the source of a XML signature problem can be sometimes a pain in the ... neck, because
310
       the hashing of the canonicalized form is more or less intransparent done in the background.</p>
311
312
       <!-- TODO: find original source -->
313
       <p>One of the tripping hazards are <link href="https://stackoverflow.com/questions/36063375">different
314
       linebreaks in Windows/Unix</link>, therefore use the non-indent form of the xmls.</p>
315
316
       <p>The next thing is to compare successful signed documents from Office vs. POIs generated signature,
317
       i.e. unzip both files and look for differences. Usually the package relations (*.rels) will be different,
318
       and the sig1.xml, core.xml and [Content_Types].xml due to different order of the references.</p>
319
320
       <p>The package relationsships (*.rels) will be specially handled, i.e. they will be filtered and only
321
       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>
322
323
       <p>To check the processed files in the canonicalized form, the below UnsyncBufferedOutputStream class needs
324
       to be injected/replaced. Put the .class file in separate directory and add the following JVM parameters:</p>
325
326
       <source>
327
-Djava.io.tmpdir=<em>&lt;custom temp directory&gt;</em>
328
-Xbootclasspath/p:<em>&lt;preload dir, which contains /org/apache/xml/security/utils/UnsyncBufferedOutputStream.class&gt;</em>
329
-Dorg.apache.poi.util.POILogger=org.apache.poi.util.CommonsLogger
330
-Djava.util.logging.config.file=<em>&lt;a dir containing ...&gt;</em>/logging.properties
331
       </source>
332
333
       <section><title>UnsyncBufferedOutputStream:</title>
334
       <source>
335
package org.apache.xml.security.utils;
336
337
import java.io.File;
338
import java.io.FileOutputStream;
339
import java.io.IOException;
340
import java.io.OutputStream;
341
342
public class UnsyncBufferedOutputStream extends OutputStream {
343
    static final int size = 8*1024;
344
    static int filecnt = 0;
345
346
    private int pointer = 0;
347
    private final OutputStream out;
348
    private final FileOutputStream out2;
349
350
    private final byte[] buf;
351
352
    public UnsyncBufferedOutputStream(OutputStream out) {
353
        buf = new byte[size];
354
        this.out = out;
355
        synchronized(UnsyncBufferedOutputStream.class) {
356
            try {
357
                String tmpDir = System.getProperty("java.io.tmpdir");
358
                if (tmpDir == null) {
359
                    tmpDir = "build";
360
                }
361
                File f = new File(tmpDir, "unsync-"+filecnt+".xml");
362
                out2 = new FileOutputStream(f);
363
            } catch (IOException e) {
364
                throw new RuntimeException(e);
365
            } finally {
366
                filecnt++;
367
            }
368
        }
369
    }
370
371
    public void write(byte[] arg0) throws IOException {
372
        write(arg0, 0, arg0.length);
373
    }
374
375
    public void write(byte[] arg0, int arg1, int len) throws IOException {
376
        int newLen = pointer+len;
377
        if (newLen > size) {
378
            flushBuffer();
379
            if (len > size) {
380
                out.write(arg0, arg1,len);
381
                out2.write(arg0, arg1,len);
382
                return;
383
            }
384
            newLen = len;
385
        }
386
        System.arraycopy(arg0, arg1, buf, pointer, len);
387
        pointer = newLen;
388
    }
389
390
    private void flushBuffer() throws IOException {
391
        if (pointer > 0) {
392
            out.write(buf, 0, pointer);
393
            out2.write(buf, 0, pointer);
394
        }
395
        pointer = 0;
396
397
    }
398
399
    public void write(int arg0) throws IOException {
400
        if (pointer >= size) {
401
            flushBuffer();
402
        }
403
        buf[pointer++] = (byte)arg0;
404
405
    }
406
407
    public void flush() throws IOException {
408
        flushBuffer();
409
        out.flush();
410
        out2.flush();
411
    }
412
413
    public void close() throws IOException {
414
        flush();
415
        out.close();
416
        out2.close();
417
    }
418
419
}
420
</source>
421
</section>
422
423
     <section><title>logging.properties</title>
424
     <source>
425
handlers = org.slf4j.bridge.SLF4JBridgeHandler
426
.level=ALL
427
org.slf4j.bridge.SLF4JBridgeHandler.level=ALL
428
     </source>
429
     </section>
430
     </section>
307
  </body>
431
  </body>
308
432
309
  <footer>
433
  <footer>
Lines 310-316 Link Here
310
    <legal>
434
    <legal>
311
      Copyright (c) @year@ The Apache Software Foundation. All rights reserved.
435
      Copyright (c) @year@ The Apache Software Foundation. All rights reserved.
312
      <br />
436
      <br />
313
      Apache POI, POI, Apache, the Apache feather logo, and the Apache 
437
      Apache POI, POI, Apache, the Apache feather logo, and the Apache
314
      POI project logo are trademarks of The Apache Software Foundation.
438
      POI project logo are trademarks of The Apache Software Foundation.
315
    </legal>
439
    </legal>
316
  </footer>
440
  </footer>
(-)src/ooxml/java/org/apache/poi/openxml4j/opc/StreamHelper.java (-1 / +5 lines)
Lines 74-81 Link Here
74
                    out.flush(); // only flush, don't close!
74
                    out.flush(); // only flush, don't close!
75
                }
75
                }
76
            });
76
            });
77
            // xmlContent.setXmlStandalone(true);
77
            trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
78
            trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
78
            trans.setOutputProperty(OutputKeys.INDENT, "yes");
79
            // don't indent xml documents, the indent will cause errors in calculating the xml signature
80
            // because of different handling of linebreaks in Windows/Unix
81
            // see https://stackoverflow.com/questions/36063375
82
            trans.setOutputProperty(OutputKeys.INDENT, "no");
79
            trans.setOutputProperty(OutputKeys.STANDALONE, "yes");
83
            trans.setOutputProperty(OutputKeys.STANDALONE, "yes");
80
            trans.transform(xmlSource, outputTarget);
84
            trans.transform(xmlSource, outputTarget);
81
        } catch (TransformerException e) {
85
        } catch (TransformerException e) {
(-)src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java (-5 / +14 lines)
Lines 40-63 Link Here
40
        this.target.set(target);
40
        this.target.set(target);
41
    }
41
    }
42
    
42
    
43
    @Override
43
    public void handleEvent(Event e) {
44
    public void handleEvent(Event e) {
44
        if (!(e instanceof MutationEvent)) return;
45
        if (!(e instanceof MutationEvent)) {
46
            return;
47
        }
45
        MutationEvent mutEvt = (MutationEvent)e;
48
        MutationEvent mutEvt = (MutationEvent)e;
46
        EventTarget et = mutEvt.getTarget();
49
        EventTarget et = mutEvt.getTarget();
47
        if (!(et instanceof Element)) return;
50
        if (!(et instanceof Element)) {
51
            return;
52
        }
48
        handleElement((Element)et);
53
        handleElement((Element)et);
49
    }
54
    }
50
55
51
    public void handleElement(Element el) {
56
    public void handleElement(Element el) {
52
        EventTarget target = this.target.get();
57
        EventTarget target = this.target.get();
53
        String packageId = signatureConfig.getPackageSignatureId();
58
54
        if (el.hasAttribute("Id")) {
59
        if (el.hasAttribute("Id")) {
55
            el.setIdAttribute("Id", true);
60
            el.setIdAttribute("Id", true);
56
        }
61
        }
57
62
58
        setListener(target, this, false);
63
        setListener(target, this, false);
59
        if (packageId.equals(el.getAttribute("Id"))) {
64
        if (OO_DIGSIG_NS.equals(el.getNamespaceURI())) {
60
            el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
65
            String parentNS = el.getParentNode().getNamespaceURI();
66
            if (!OO_DIGSIG_NS.equals(parentNS) && !el.hasAttributeNS(XML_NS, "mdssi")) {
67
                el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
68
            }
61
        }
69
        }
62
        setPrefix(el);
70
        setPrefix(el);
63
        setListener(target, this, true);
71
        setListener(target, this, true);
Lines 86-91 Link Here
86
        }
94
        }
87
    }
95
    }
88
    
96
    
97
    @Override
89
    public void setSignatureConfig(SignatureConfig signatureConfig) {
98
    public void setSignatureConfig(SignatureConfig signatureConfig) {
90
        this.signatureConfig = signatureConfig;
99
        this.signatureConfig = signatureConfig;
91
    }
100
    }
(-)src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java (-234 / +79 lines)
Lines 18-26 Link Here
18
/* ====================================================================
18
/* ====================================================================
19
   This product contains an ASLv2 licensed version of the OOXML signer
19
   This product contains an ASLv2 licensed version of the OOXML signer
20
   package from the eID Applet project
20
   package from the eID Applet project
21
   http://code.google.com/p/eid-applet/source/browse/trunk/README.txt  
21
   http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
22
   Copyright (C) 2008-2014 FedICT.
22
   Copyright (C) 2008-2014 FedICT.
23
   ================================================================= */ 
23
   ================================================================= */
24
24
25
package org.apache.poi.poifs.crypt.dsig.facets;
25
package org.apache.poi.poifs.crypt.dsig.facets;
26
26
Lines 29-34 Link Here
29
import java.text.DateFormat;
29
import java.text.DateFormat;
30
import java.text.SimpleDateFormat;
30
import java.text.SimpleDateFormat;
31
import java.util.ArrayList;
31
import java.util.ArrayList;
32
import java.util.Arrays;
33
import java.util.Collections;
34
import java.util.Comparator;
32
import java.util.HashSet;
35
import java.util.HashSet;
33
import java.util.List;
36
import java.util.List;
34
import java.util.Locale;
37
import java.util.Locale;
Lines 70-82 Link Here
70
73
71
/**
74
/**
72
 * Office OpenXML Signature Facet implementation.
75
 * Office OpenXML Signature Facet implementation.
73
 * 
76
 *
74
 * @author fcorneli
75
 * @see <a href="http://msdn.microsoft.com/en-us/library/cc313071.aspx">[MS-OFFCRYPTO]: Office Document Cryptography Structure</a>
77
 * @see <a href="http://msdn.microsoft.com/en-us/library/cc313071.aspx">[MS-OFFCRYPTO]: Office Document Cryptography Structure</a>
76
 */
78
 */
77
public class OOXMLSignatureFacet extends SignatureFacet {
79
public class OOXMLSignatureFacet extends SignatureFacet {
78
80
79
    private static final POILogger LOG = POILogFactory.getLogger(OOXMLSignatureFacet.class);
81
    private static final POILogger LOG = POILogFactory.getLogger(OOXMLSignatureFacet.class);
82
    private static final String ID_PACKAGE_OBJECT = "idPackageObject";
80
83
81
    @Override
84
    @Override
82
    public void preSign(
85
    public void preSign(
Lines 98-114 Link Here
98
        List<Reference> manifestReferences = new ArrayList<Reference>();
101
        List<Reference> manifestReferences = new ArrayList<Reference>();
99
        addManifestReferences(manifestReferences);
102
        addManifestReferences(manifestReferences);
100
        Manifest manifest =  getSignatureFactory().newManifest(manifestReferences);
103
        Manifest manifest =  getSignatureFactory().newManifest(manifestReferences);
101
        
104
102
        String objectId = "idPackageObject"; // really has to be this value.
103
        List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
105
        List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
104
        objectContent.add(manifest);
106
        objectContent.add(manifest);
105
107
106
        addSignatureTime(document, objectContent);
108
        addSignatureTime(document, objectContent);
107
109
108
        XMLObject xo = getSignatureFactory().newXMLObject(objectContent, objectId, null, null);
110
        XMLObject xo = getSignatureFactory().newXMLObject(objectContent, ID_PACKAGE_OBJECT, null, null);
109
        objects.add(xo);
111
        objects.add(xo);
110
112
111
        Reference reference = newReference("#" + objectId, null, XML_DIGSIG_NS+"Object", null, null);
113
        Reference reference = newReference("#"+ID_PACKAGE_OBJECT, null, XML_DIGSIG_NS+"Object", null, null);
112
        references.add(reference);
114
        references.add(reference);
113
    }
115
    }
114
116
Lines 121-127 Link Here
121
123
122
        Set<String> digestedPartNames = new HashSet<String>();
124
        Set<String> digestedPartNames = new HashSet<String>();
123
        for (PackagePart pp : relsEntryNames) {
125
        for (PackagePart pp : relsEntryNames) {
124
            String baseUri = pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
126
            final String baseUri = pp.getPartName().getName().replaceFirst("(.*)/_rels/.*", "$1");
125
127
126
            PackageRelationshipCollection prc;
128
            PackageRelationshipCollection prc;
127
            try {
129
            try {
Lines 130-140 Link Here
130
            } catch (InvalidFormatException e) {
132
            } catch (InvalidFormatException e) {
131
                throw new XMLSignatureException("Invalid relationship descriptor: "+pp.getPartName().getName(), e);
133
                throw new XMLSignatureException("Invalid relationship descriptor: "+pp.getPartName().getName(), e);
132
            }
134
            }
133
            
135
134
            RelationshipTransformParameterSpec parameterSpec = new RelationshipTransformParameterSpec();
136
            RelationshipTransformParameterSpec parameterSpec = new RelationshipTransformParameterSpec();
135
            for (PackageRelationship relationship : prc) {
137
            for (PackageRelationship relationship : prc) {
136
                String relationshipType = relationship.getRelationshipType();
138
                String relationshipType = relationship.getRelationshipType();
137
                
139
138
                /*
140
                /*
139
                 * ECMA-376 Part 2 - 3rd edition
141
                 * ECMA-376 Part 2 - 3rd edition
140
                 * 13.2.4.16 Manifest Element
142
                 * 13.2.4.16 Manifest Element
Lines 144-165 Link Here
144
                    continue;
146
                    continue;
145
                }
147
                }
146
148
147
                if (!isSignedRelationship(relationshipType)) continue;
149
                if (!isSignedRelationship(relationshipType)) {
150
                    continue;
151
                }
148
152
149
                parameterSpec.addRelationshipReference(relationship.getId());
153
                parameterSpec.addRelationshipReference(relationship.getId());
150
154
151
                // TODO: find a better way ...
155
                String partName = normalizePartName(relationship.getTargetURI(), baseUri);
152
                String partName = relationship.getTargetURI().toString();
156
153
                if (!partName.startsWith(baseUri)) {
157
                // We only digest a part once.
154
                    partName = baseUri + partName;
158
                if (digestedPartNames.contains(partName)) {
159
                    continue;
155
                }
160
                }
156
                try {
161
                digestedPartNames.add(partName);
157
                    partName = new URI(partName).normalize().getPath().replace('\\', '/');
162
158
                    LOG.log(POILogger.DEBUG, "part name: " + partName);
159
                } catch (URISyntaxException e) {
160
                    throw new XMLSignatureException(e);
161
                }
162
                
163
                String contentType;
163
                String contentType;
164
                try {
164
                try {
165
                    PackagePartName relName = PackagingURIHelper.createPartName(partName);
165
                    PackagePartName relName = PackagingURIHelper.createPartName(partName);
Lines 168-202 Link Here
168
                } catch (InvalidFormatException e) {
168
                } catch (InvalidFormatException e) {
169
                    throw new XMLSignatureException(e);
169
                    throw new XMLSignatureException(e);
170
                }
170
                }
171
                
171
172
                if (relationshipType.endsWith("customXml")
172
                if (relationshipType.endsWith("customXml")
173
                    && !(contentType.equals("inkml+xml") || contentType.equals("text/xml"))) {
173
                    && !(contentType.equals("inkml+xml") || contentType.equals("text/xml"))) {
174
                    LOG.log(POILogger.DEBUG, "skipping customXml with content type: " + contentType);
174
                    LOG.log(POILogger.DEBUG, "skipping customXml with content type: " + contentType);
175
                    continue;
175
                    continue;
176
                }
176
                }
177
                
177
178
                if (!digestedPartNames.contains(partName)) {
178
                String uri = partName + "?ContentType=" + contentType;
179
                    // We only digest a part once.
179
                Reference reference = newReference(uri, null, null, null, null);
180
                    String uri = partName + "?ContentType=" + contentType;
180
                manifestReferences.add(reference);
181
                    Reference reference = newReference(uri, null, null, null, null);
182
                    manifestReferences.add(reference);
183
                    digestedPartNames.add(partName);
184
                }
185
            }
181
            }
186
            
182
187
            if (parameterSpec.hasSourceIds()) {
183
            if (parameterSpec.hasSourceIds()) {
188
                List<Transform> transforms = new ArrayList<Transform>();
184
                List<Transform> transforms = new ArrayList<Transform>();
189
                transforms.add(newTransform(RelationshipTransformService.TRANSFORM_URI, parameterSpec));
185
                transforms.add(newTransform(RelationshipTransformService.TRANSFORM_URI, parameterSpec));
190
                transforms.add(newTransform(CanonicalizationMethod.INCLUSIVE));
186
                transforms.add(newTransform(CanonicalizationMethod.INCLUSIVE));
191
                String uri = pp.getPartName().getName()
187
                String uri = normalizePartName(pp.getPartName().getURI(), baseUri)
192
                    + "?ContentType=application/vnd.openxmlformats-package.relationships+xml";
188
                    + "?ContentType=application/vnd.openxmlformats-package.relationships+xml";
193
                Reference reference = newReference(uri, transforms, null, null, null);
189
                Reference reference = newReference(uri, transforms, null, null, null);
194
                manifestReferences.add(reference);
190
                manifestReferences.add(reference);
195
            }
191
            }
196
        }
192
        }
193
        
194
        Collections.sort(manifestReferences, new Comparator<Reference>() {
195
            public int compare(Reference o1, Reference o2) {
196
                return o1.getURI().compareTo(o2.getURI());
197
            }
198
        });
197
    }
199
    }
198
200
201
    /**
202
     * Normalize a URI/part name
203
     * TODO: find a better way ...
204
     */
205
    private static String normalizePartName(URI partName, String baseUri) throws XMLSignatureException {
206
        String pn = partName.toASCIIString();
207
        if (!pn.startsWith(baseUri)) {
208
            pn = baseUri + pn;
209
        }
210
        try {
211
            pn = new URI(pn).normalize().getPath().replace('\\', '/');
212
            LOG.log(POILogger.DEBUG, "part name: " + pn);
213
        } catch (URISyntaxException e) {
214
            throw new XMLSignatureException(e);
215
        }
216
        return pn;
217
    }
199
218
219
200
    protected void addSignatureTime(Document document, List<XMLStructure> objectContent) {
220
    protected void addSignatureTime(Document document, List<XMLStructure> objectContent) {
201
        /*
221
        /*
202
         * SignatureTime
222
         * SignatureTime
Lines 236-242 Link Here
236
        ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
256
        ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
237
        Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
257
        Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
238
        n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
258
        n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
239
        
259
240
        List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
260
        List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
241
        signatureInfoContent.add(new DOMStructure(n));
261
        signatureInfoContent.add(new DOMStructure(n));
242
        SignatureProperty signatureInfoSignatureProperty = getSignatureFactory()
262
        SignatureProperty signatureInfoSignatureProperty = getSignatureFactory()
Lines 268-475 Link Here
268
288
269
    protected static boolean isSignedRelationship(String relationshipType) {
289
    protected static boolean isSignedRelationship(String relationshipType) {
270
        LOG.log(POILogger.DEBUG, "relationship type: " + relationshipType);
290
        LOG.log(POILogger.DEBUG, "relationship type: " + relationshipType);
271
        for (String signedTypeExtension : signed) {
291
        String rt = relationshipType.replaceFirst(".*/relationships/", "");
272
            if (relationshipType.endsWith(signedTypeExtension)) {
292
        return (signed.contains(rt) || rt.endsWith("customXml"));
273
                return true;
274
            }
275
        }
276
        if (relationshipType.endsWith("customXml")) {
277
            LOG.log(POILogger.DEBUG, "customXml relationship type");
278
            return true;
279
        }
280
        return false;
281
    }
293
    }
282
    
283
    public static final String[] contentTypes = {
284
        /*
285
         * Word
286
         */
287
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml",
288
        "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml",
289
        "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml",
290
        "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml",
291
        "application/vnd.openxmlformats-officedocument.theme+xml",
292
        "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml",
293
        "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml",
294
294
295
        /*
296
         * Word 2010
297
         */
298
        "application/vnd.ms-word.stylesWithEffects+xml",
299
300
        /*
301
         * Excel
302
         */
303
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml",
304
        "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml",
305
        "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml",
306
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml",
307
308
        /*
309
         * Powerpoint
310
         */
311
        "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml",
312
        "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml",
313
        "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml",
314
        "application/vnd.openxmlformats-officedocument.presentationml.slide+xml",
315
        "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml",
316
317
        /*
318
         * Powerpoint 2010
319
         */
320
        "application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml",
321
        "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"
322
    };
323
324
    /**
295
    /**
325
     * Office 2010 list of signed types (extensions).
296
     * Office 2010 list of signed types (extensions).
326
     */
297
     */
327
    public static final String[] signed = {
298
    private static final Set<String> signed = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
328
        "powerPivotData", //
299
        "activeXControlBinary","aFChunk","attachedTemplate","attachedToolbars","audio","calcChain","chart","chartColorStyle",
329
        "activeXControlBinary", //
300
        "chartLayout","chartsheet","chartStyle","chartUserShapes","commentAuthors","comments","connections","connectorXml",
330
        "attachedToolbars", //
301
        "control","ctrlProp","customData","customData","customProperty","customXml","diagram","diagramColors",
331
        "connectorXml", //
302
        "diagramColorsHeader","diagramData","diagramDrawing","diagramLayout","diagramLayoutHeader","diagramQuickStyle",
332
        "downRev", //
303
        "diagramQuickStyleHeader","dialogsheet","dictionary","documentParts","downRev","drawing","endnotes","externalLink",
333
        "functionPrototypes", //
304
        "externalLinkPath","font","fontTable","footer","footnotes","functionPrototypes","glossaryDocument","graphicFrameDoc",
334
        "graphicFrameDoc", //
305
        "groupShapeXml","handoutMaster","hdphoto","header","hyperlink","image","ink","inkXml","keyMapCustomizations",
335
        "groupShapeXml", //
306
        "legacyDiagramText","legacyDocTextInfo","mailMergeHeaderSource","mailMergeRecipientData","mailMergeSource","media",
336
        "ink", //
307
        "notesMaster","notesSlide","numbering","officeDocument","officeDocument","oleObject","package","pictureXml",
337
        "keyMapCustomizations", //
308
        "pivotCacheDefinition","pivotCacheRecords","pivotTable","powerPivotData","presProps","printerSettings","queryTable",
338
        "legacyDiagramText", //
309
        "recipientData","settings","shapeXml","sharedStrings","sheetMetadata","slicer","slicer","slicerCache","slicerCache",
339
        "legacyDocTextInfo", //
310
        "slide","slideLayout","slideMaster","slideUpdateInfo","slideUpdateUrl","smartTags","styles","stylesWithEffects",
340
        "officeDocument", //
311
        "table","tableSingleCells","tableStyles","tags","theme","themeOverride","timeline","timelineCache","transform",
341
        "pictureXml", //
312
        "ui/altText","ui/buttonSize","ui/controlID","ui/description","ui/enabled","ui/extensibility","ui/extensibility",
342
        "shapeXml", //
313
        "ui/helperText","ui/imageID","ui/imageMso","ui/keyTip","ui/label","ui/lcid","ui/loud","ui/pressed","ui/progID",
343
        "smartTags", //
314
        "ui/ribbonID","ui/showImage","ui/showLabel","ui/supertip","ui/target","ui/text","ui/title","ui/tooltip",
344
        "ui/altText", //
315
        "ui/userCustomization","ui/visible","userXmlData","vbaProject","video","viewProps","vmlDrawing",
345
        "ui/buttonSize", //
316
        "volatileDependencies","webSettings","wordVbaData","worksheet","wsSortMap","xlBinaryIndex",
346
        "ui/controlID", //
317
        "xlExternalLinkPath/xlAlternateStartup","xlExternalLinkPath/xlLibrary","xlExternalLinkPath/xlPathMissing",
347
        "ui/description", //
318
        "xlExternalLinkPath/xlStartup","xlIntlMacrosheet","xlMacrosheet","xmlMaps"
348
        "ui/enabled", //
319
    )));
349
        "ui/extensibility", //
350
        "ui/helperText", //
351
        "ui/imageID", //
352
        "ui/imageMso", //
353
        "ui/keyTip", //
354
        "ui/label", //
355
        "ui/lcid", //
356
        "ui/loud", //
357
        "ui/pressed", //
358
        "ui/progID", //
359
        "ui/ribbonID", //
360
        "ui/showImage", //
361
        "ui/showLabel", //
362
        "ui/supertip", //
363
        "ui/target", //
364
        "ui/text", //
365
        "ui/title", //
366
        "ui/tooltip", //
367
        "ui/userCustomization", //
368
        "ui/visible", //
369
        "userXmlData", //
370
        "vbaProject", //
371
        "wordVbaData", //
372
        "wsSortMap", //
373
        "xlBinaryIndex", //
374
        "xlExternalLinkPath/xlAlternateStartup", //
375
        "xlExternalLinkPath/xlLibrary", //
376
        "xlExternalLinkPath/xlPathMissing", //
377
        "xlExternalLinkPath/xlStartup", //
378
        "xlIntlMacrosheet", //
379
        "xlMacrosheet", //
380
        "customData", //
381
        "diagramDrawing", //
382
        "hdphoto", //
383
        "inkXml", //
384
        "media", //
385
        "slicer", //
386
        "slicerCache", //
387
        "stylesWithEffects", //
388
        "ui/extensibility", //
389
        "chartColorStyle", //
390
        "chartLayout", //
391
        "chartStyle", //
392
        "dictionary", //
393
        "timeline", //
394
        "timelineCache", //
395
        "aFChunk", //
396
        "attachedTemplate", //
397
        "audio", //
398
        "calcChain", //
399
        "chart", //
400
        "chartsheet", //
401
        "chartUserShapes", //
402
        "commentAuthors", //
403
        "comments", //
404
        "connections", //
405
        "control", //
406
        "customProperty", //
407
        "customXml", //
408
        "diagramColors", //
409
        "diagramData", //
410
        "diagramLayout", //
411
        "diagramQuickStyle", //
412
        "dialogsheet", //
413
        "drawing", //
414
        "endnotes", //
415
        "externalLink", //
416
        "externalLinkPath", //
417
        "font", //
418
        "fontTable", //
419
        "footer", //
420
        "footnotes", //
421
        "glossaryDocument", //
422
        "handoutMaster", //
423
        "header", //
424
        "hyperlink", //
425
        "image", //
426
        "mailMergeHeaderSource", //
427
        "mailMergeRecipientData", //
428
        "mailMergeSource", //
429
        "notesMaster", //
430
        "notesSlide", //
431
        "numbering", //
432
        "officeDocument", //
433
        "oleObject", //
434
        "package", //
435
        "pivotCacheDefinition", //
436
        "pivotCacheRecords", //
437
        "pivotTable", //
438
        "presProps", //
439
        "printerSettings", //
440
        "queryTable", //
441
        "recipientData", //
442
        "settings", //
443
        "sharedStrings", //
444
        "sheetMetadata", //
445
        "slide", //
446
        "slideLayout", //
447
        "slideMaster", //
448
        "slideUpdateInfo", //
449
        "slideUpdateUrl", //
450
        "styles", //
451
        "table", //
452
        "tableSingleCells", //
453
        "tableStyles", //
454
        "tags", //
455
        "theme", //
456
        "themeOverride", //
457
        "transform", //
458
        "video", //
459
        "viewProps", //
460
        "volatileDependencies", //
461
        "webSettings", //
462
        "worksheet", //
463
        "xmlMaps", //
464
        "ctrlProp", //
465
        "customData", //
466
        "diagram", //
467
        "diagramColorsHeader", //
468
        "diagramLayoutHeader", //
469
        "diagramQuickStyleHeader", //
470
        "documentParts", //
471
        "slicer", //
472
        "slicerCache", //
473
        "vmlDrawing" //
474
    };
475
}
320
}
(-)src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java (-56 / +43 lines)
Lines 25-34 Link Here
25
package org.apache.poi.poifs.crypt.dsig.services;
25
package org.apache.poi.poifs.crypt.dsig.services;
26
26
27
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
27
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
28
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS;
29
import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_NS;
28
30
29
import java.io.ByteArrayInputStream;
30
import java.io.ByteArrayOutputStream;
31
import java.io.IOException;
32
import java.io.InputStream;
31
import java.io.InputStream;
33
import java.io.OutputStream;
32
import java.io.OutputStream;
34
import java.security.InvalidAlgorithmParameterException;
33
import java.security.InvalidAlgorithmParameterException;
Lines 36-44 Link Here
36
import java.security.Security;
35
import java.security.Security;
37
import java.security.spec.AlgorithmParameterSpec;
36
import java.security.spec.AlgorithmParameterSpec;
38
import java.util.ArrayList;
37
import java.util.ArrayList;
39
import java.util.Comparator;
40
import java.util.Iterator;
41
import java.util.List;
38
import java.util.List;
39
import java.util.TreeMap;
42
40
43
import javax.xml.crypto.Data;
41
import javax.xml.crypto.Data;
44
import javax.xml.crypto.MarshalException;
42
import javax.xml.crypto.MarshalException;
Lines 50-72 Link Here
50
import javax.xml.crypto.dsig.TransformService;
48
import javax.xml.crypto.dsig.TransformService;
51
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
49
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
52
50
51
import org.apache.jcp.xml.dsig.internal.dom.ApacheNodeSetData;
52
import org.apache.poi.util.DocumentHelper;
53
import org.apache.poi.util.POILogFactory;
53
import org.apache.poi.util.POILogFactory;
54
import org.apache.poi.util.POILogger;
54
import org.apache.poi.util.POILogger;
55
import org.apache.poi.util.XmlSort;
55
import org.apache.xml.security.signature.XMLSignatureInput;
56
import org.apache.xmlbeans.XmlCursor;
57
import org.apache.xmlbeans.XmlException;
56
import org.apache.xmlbeans.XmlException;
58
import org.apache.xmlbeans.XmlObject;
57
import org.apache.xmlbeans.XmlObject;
59
import org.apache.xmlbeans.XmlOptions;
60
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTRelationshipReference;
58
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTRelationshipReference;
61
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.RelationshipReferenceDocument;
59
import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.RelationshipReferenceDocument;
62
import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationship;
63
import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationships;
64
import org.openxmlformats.schemas.xpackage.x2006.relationships.RelationshipsDocument;
65
import org.openxmlformats.schemas.xpackage.x2006.relationships.STTargetMode;
66
import org.w3.x2000.x09.xmldsig.TransformDocument;
60
import org.w3.x2000.x09.xmldsig.TransformDocument;
67
import org.w3c.dom.Document;
61
import org.w3c.dom.Document;
68
import org.w3c.dom.Element;
62
import org.w3c.dom.Element;
69
import org.w3c.dom.Node;
63
import org.w3c.dom.Node;
64
import org.w3c.dom.NodeList;
70
65
71
/**
66
/**
72
 * JSR105 implementation of the RelationshipTransform transformation.
67
 * JSR105 implementation of the RelationshipTransform transformation.
Lines 89-95 Link Here
89
    public static class RelationshipTransformParameterSpec implements TransformParameterSpec {
84
    public static class RelationshipTransformParameterSpec implements TransformParameterSpec {
90
        List<String> sourceIds = new ArrayList<String>();
85
        List<String> sourceIds = new ArrayList<String>();
91
        public void addRelationshipReference(String relationshipId) {
86
        public void addRelationshipReference(String relationshipId) {
92
            sourceIds.add(relationshipId);
87
                sourceIds.add(relationshipId);
93
        }
88
        }
94
        public boolean hasSourceIds() {
89
        public boolean hasSourceIds() {
95
            return !sourceIds.isEmpty();
90
            return !sourceIds.isEmpty();
Lines 163-177 Link Here
163
        LOG.log(POILogger.DEBUG, "marshallParams(parent,context)");
158
        LOG.log(POILogger.DEBUG, "marshallParams(parent,context)");
164
        DOMStructure domParent = (DOMStructure) parent;
159
        DOMStructure domParent = (DOMStructure) parent;
165
        Element parentNode = (Element)domParent.getNode();
160
        Element parentNode = (Element)domParent.getNode();
166
        // parentNode.setAttributeNS(XML_NS, "xmlns:mdssi", XML_DIGSIG_NS);
167
        Document doc = parentNode.getOwnerDocument();
161
        Document doc = parentNode.getOwnerDocument();
168
        
162
        
169
        for (String sourceId : this.sourceIds) {
163
        for (String sourceId : this.sourceIds) {
170
            RelationshipReferenceDocument relRef = RelationshipReferenceDocument.Factory.newInstance();
164
            Element el = doc.createElementNS(OO_DIGSIG_NS, "mdssi:RelationshipReference");
171
            relRef.addNewRelationshipReference().setSourceId(sourceId);
165
            el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS);
172
            Node n = relRef.getRelationshipReference().getDomNode();
166
            el.setAttribute("SourceId", sourceId);
173
            n = doc.importNode(n, true);
167
            parentNode.appendChild(el);
174
            parentNode.appendChild(n);
175
        }
168
        }
176
    }
169
    }
177
    
170
    
Lines 180-185 Link Here
180
        return null;
173
        return null;
181
    }
174
    }
182
175
176
    /**
177
     * The relationships transform takes the XML document from the Relationships part
178
     * and converts it to another XML document.
179
     * 
180
     * @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>
181
     * @see <a href="https://stackoverflow.com/questions/36063375">XML Relationship Transform Algorithm</a>
182
     */
183
    public Data transform(Data data, XMLCryptoContext context) throws TransformException {
183
    public Data transform(Data data, XMLCryptoContext context) throws TransformException {
184
        LOG.log(POILogger.DEBUG, "transform(data,context)");
184
        LOG.log(POILogger.DEBUG, "transform(data,context)");
185
        LOG.log(POILogger.DEBUG, "data java type: " + data.getClass().getName());
185
        LOG.log(POILogger.DEBUG, "data java type: " + data.getClass().getName());
Lines 187-239 Link Here
187
        LOG.log(POILogger.DEBUG, "URI: " + octetStreamData.getURI());
187
        LOG.log(POILogger.DEBUG, "URI: " + octetStreamData.getURI());
188
        InputStream octetStream = octetStreamData.getOctetStream();
188
        InputStream octetStream = octetStreamData.getOctetStream();
189
        
189
        
190
        RelationshipsDocument relDoc;
190
        Document doc;
191
        try {
191
        try {
192
            relDoc = RelationshipsDocument.Factory.parse(octetStream, DEFAULT_XML_OPTIONS);
192
            doc = DocumentHelper.readDocument(octetStream);
193
        } catch (Exception e) {
193
        } catch (Exception e) {
194
            throw new TransformException(e.getMessage(), e);
194
            throw new TransformException(e.getMessage(), e);
195
        }
195
        }
196
        LOG.log(POILogger.DEBUG, "relationships document", relDoc);
197
        
196
        
198
        CTRelationships rels = relDoc.getRelationships();
197
        // keep only those relationships which id is registered in the sourceIds
199
        List<CTRelationship> relList = rels.getRelationshipList();
198
        Element root = doc.getDocumentElement();
200
        Iterator<CTRelationship> relIter = rels.getRelationshipList().iterator();
199
        NodeList nl = root.getChildNodes();
201
        while (relIter.hasNext()) {
200
        TreeMap<String,Element> rsList = new TreeMap<String,Element>();
202
            CTRelationship rel = relIter.next();
201
        for (int i=nl.getLength()-1; i>=0; i--) {
203
            /*
202
            Node n = nl.item(i);
204
             * See: ISO/IEC 29500-2:2008(E) - 13.2.4.24 Relationships Transform
203
            if ("Relationship".equals(n.getLocalName())) {
205
             * Algorithm.
204
                Element el = (Element)n;
206
             */
205
                String id = el.getAttribute("Id");
207
            if (!this.sourceIds.contains(rel.getId())) {
206
                if (sourceIds.contains(id)) {
208
                LOG.log(POILogger.DEBUG, "removing element: " + rel.getId());
207
                    String targetMode = el.getAttribute("TargetMode");
209
                relIter.remove();
208
                    if ("".equals(targetMode)) {
210
            } else {
209
                        el.setAttribute("TargetMode", "Internal");
211
                if (!rel.isSetTargetMode()) {
210
                    }
212
                    rel.setTargetMode(STTargetMode.INTERNAL);
211
                    rsList.put(id, el);
213
                }
212
                }
214
            }
213
            }
214
            root.removeChild(n);
215
        }
215
        }
216
217
        for (Element el : rsList.values()) {
218
            root.appendChild(el);
219
        }
216
        
220
        
217
        // TODO: remove non element nodes ???
221
        LOG.log(POILogger.DEBUG, "# Relationship elements: ", rsList.size());
218
        LOG.log(POILogger.DEBUG, "# Relationship elements", relList.size());
219
        
222
        
220
        XmlSort.sort(rels, new Comparator<XmlCursor>(){
223
        return new ApacheNodeSetData(new XMLSignatureInput(root));
221
            public int compare(XmlCursor c1, XmlCursor c2) {
222
                String id1 = ((CTRelationship)c1.getObject()).getId();
223
                String id2 = ((CTRelationship)c2.getObject()).getId();
224
                return id1.compareTo(id2);
225
            }
226
        });
227
228
        try {
229
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
230
            XmlOptions xo = new XmlOptions();
231
            xo.setSaveNoXmlDecl();
232
            relDoc.save(bos, xo);
233
            return new OctetStreamData(new ByteArrayInputStream(bos.toByteArray()));
234
        } catch (IOException e) {
235
            throw new TransformException(e.getMessage(), e);
236
        }
237
    }
224
    }
238
225
239
    public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException {
226
    public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException {
(-)src/ooxml/java/org/apache/poi/util/XmlSort.java (-89 lines)
Lines 1-88 Link Here
1
/* ====================================================================
2
   Licensed to the Apache Software Foundation (ASF) under one or more
3
   contributor license agreements.  See the NOTICE file distributed with
4
   this work for additional information regarding copyright ownership.
5
   The ASF licenses this file to You under the Apache License, Version 2.0
6
   (the "License"); you may not use this file except in compliance with
7
   the License.  You may obtain a copy of the License at
8
9
       http://www.apache.org/licenses/LICENSE-2.0
10
11
   Unless required by applicable law or agreed to in writing, software
12
   distributed under the License is distributed on an "AS IS" BASIS,
13
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
   See the License for the specific language governing permissions and
15
   limitations under the License.
16
==================================================================== */
17
18
package org.apache.poi.util;
19
20
import java.util.Comparator;
21
22
import org.apache.xmlbeans.XmlCursor;
23
import org.apache.xmlbeans.XmlObject;
24
25
public final class XmlSort {
26
    /**
27
     * Sorts the children of <code>element</code> according to the order indicated by the
28
     * comparator.
29
     * @param element the element whose content is to be sorted. Only element children are sorted,
30
     * attributes are not touched. When elements are reordered, all the text, comments and PIs
31
     * follow the element that they come immediately after.
32
     * @param comp a comparator that is to be used when comparing the <code>QName</code>s of two
33
     * elements. 
34
     * @throws IllegalArgumentException if the input <code>XmlObject</code> does not represent
35
     * an element
36
     */
37
    public static void sort(XmlObject element, Comparator<XmlCursor> comp) {
38
        XmlCursor headCursor = element.newCursor();
39
        if (!headCursor.isStart()) {
40
            throw new IllegalStateException("The element parameter must point to a STARTDOC");
41
        }
42
        // We use insertion sort to minimize the number of swaps, because each swap means
43
        // moving a part of the document
44
        /* headCursor points to the beginning of the list of the already sorted items and
45
           listCursor points to the beginning of the list of unsorted items
46
           At the beginning, headCursor points to the first element and listCursor points to the
47
           second element. The algorithm ends when listCursor cannot be moved to the "next"
48
           element in the unsorted list, i.e. the unsorted list becomes empty */
49
        boolean moved = headCursor.toFirstChild();
50
        if (!moved) {
51
            // Cursor was not moved, which means that the given element has no children and
52
            // therefore there is nothing to sort
53
            return;
54
        }
55
        XmlCursor listCursor = headCursor.newCursor();
56
        boolean moreElements = listCursor.toNextSibling();
57
        while (moreElements) {
58
            moved = false;
59
            // While we can move the head of the unsorted list, it means that there are still
60
            // items (elements) that need to be sorted
61
            while (headCursor.comparePosition(listCursor) < 0) {
62
                if (comp.compare(headCursor, listCursor) > 0) {
63
                    // We have found the position in the sorted list, insert the element and the
64
                    // text following the element in the current position
65
                    // Move the element
66
                    listCursor.moveXml(headCursor);
67
                    // Move the text following the element
68
                    while (!listCursor.isStart() && !listCursor.isEnd())
69
                        listCursor.moveXml(headCursor);
70
                    moreElements = listCursor.isStart();
71
                    moved = true;
72
                    break;
73
                }
74
                headCursor.toNextSibling();
75
            }
76
            if (!moved) {
77
                // Because during the move of a fragment of XML, the listCursor is also moved, in
78
                // case we didn't need to move XML (the new element to be inserted happened to
79
                // be the last one in order), we need to move this cursor
80
                moreElements = listCursor.toNextSibling();
81
            }
82
            // Reposition the head of the sorted list
83
            headCursor.toParent();
84
            headCursor.toFirstChild();
85
        }
86
    }
87
}
88
 
89
native
(-)src/ooxml/java/org/apache/poi/xssf/usermodel/XSSFDrawing.java (-1 / +3 lines)
Lines 72-77 Link Here
72
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
72
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObject;
73
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObjects;
73
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTOleObjects;
74
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
74
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet;
75
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STDvAspect;
75
76
76
/**
77
/**
77
 * Represents a SpreadsheetML drawing
78
 * Represents a SpreadsheetML drawing
Lines 426-432 Link Here
426
        CTOleObjects oo = cwb.isSetOleObjects() ? cwb.getOleObjects() : cwb.addNewOleObjects();
427
        CTOleObjects oo = cwb.isSetOleObjects() ? cwb.getOleObjects() : cwb.addNewOleObjects();
427
428
428
        CTOleObject ole1 = oo.addNewOleObject();
429
        CTOleObject ole1 = oo.addNewOleObject();
429
        ole1.setProgId("Package");
430
        ole1.setProgId("Packager Shell Object");
431
        ole1.setDvAspect(STDvAspect.DVASPECT_ICON);
430
        ole1.setShapeId(shapeId);
432
        ole1.setShapeId(shapeId);
431
        ole1.setId(olePR.getId());
433
        ole1.setId(olePR.getId());
432
434
(-)src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java (-2 / +39 lines)
Lines 119-124 Link Here
119
    }
119
    }
120
    
120
    
121
    @Test
121
    @Test
122
    public void bug61182() throws Exception {
123
        SignatureConfig signatureConfig = prepareConfig("test", "CN=Test");
124
125
        SignatureInfo si = new SignatureInfo();
126
        si.setSignatureConfig(signatureConfig);
127
128
        XSSFWorkbook wb1 = new XSSFWorkbook();
129
        wb1.createSheet().createRow(1).createCell(1).setCellValue("Test");
130
        ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
131
        wb1.write(bos);
132
        wb1.close();
133
        
134
        OPCPackage pkg1 = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()));
135
        
136
        signatureConfig.setOpcPackage(pkg1);
137
        si.confirmSignature();
138
        assertTrue(si.verifySignature());
139
        bos.reset();
140
        pkg1.save(bos);
141
        pkg1.close();
142
        
143
        XSSFWorkbook wb2 = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()));
144
        assertEquals("Test", wb2.getSheetAt(0).getRow(1).getCell(1).getStringCellValue());
145
        OPCPackage pkg2 = wb2.getPackage();
146
        signatureConfig.setOpcPackage(pkg2);
147
        assertTrue(si.verifySignature());
148
        pkg2.close();
149
        wb2.close();
150
    }
151
    
152
    @Test
122
    public void office2007prettyPrintedRels() throws Exception {
153
    public void office2007prettyPrintedRels() throws Exception {
123
        OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ);
154
        OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ);
124
        try {
155
        try {
Lines 611-618 Link Here
611
            pkg.close();
642
            pkg.close();
612
        }
643
        }
613
    }
644
    }
614
    
645
615
    private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
646
    private SignatureConfig prepareConfig(String alias, String signerDn) throws Exception {
616
        initKeyPair(alias, signerDn);
647
        initKeyPair(alias, signerDn);
617
648
618
        SignatureConfig signatureConfig = new SignatureConfig();
649
        SignatureConfig signatureConfig = new SignatureConfig();
Lines 620-625 Link Here
620
        signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
651
        signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
621
        signatureConfig.setExecutionTime(cal.getTime());
652
        signatureConfig.setExecutionTime(cal.getTime());
622
        signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
653
        signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
654
655
        return signatureConfig;
656
    }
657
    
658
    private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
659
        SignatureConfig signatureConfig = prepareConfig(alias, signerDn);
623
        signatureConfig.setOpcPackage(pkgCopy);
660
        signatureConfig.setOpcPackage(pkgCopy);
624
        
661
        
625
        SignatureInfo si = new SignatureInfo();
662
        SignatureInfo si = new SignatureInfo();

Return to bug 61182