<?xml version="1.0" encoding="UTF-8"?>
<!--
   ====================================================================
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   ====================================================================
-->
<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.3//EN" "./dtd/document-v13.dtd">


<document>
  <header>
    <title>Apache POI - Encryption support</title>
    <authors>
      <person id="maxcom" name="Maxim Valyanskiy" email="maxcom@apache.org"/>
    </authors>
  </header>

   <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/>
        encryption is format-dependent and needs to be implemented per format differently.<br/>
        Use <link href="http://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
        Biff8EncryptionKey</link>.setCurrentUserPassword(String password)
        to specify the decryption password before opening the file or (where applicable) before saving.
        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/>
        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
        agile encryption, because <link href="http://blogs.msdn.com/b/david_leblanc/archive/2010/04/16/don-t-use-office-rc4-encryption-really-just-don-t-do-it.aspx">RC4 is not really secure</link>.
        Of course you'll need to make sure, that your clients can read the documents,
        i.e. the various free Excel, Powerpoint, Word viewers have limitations in the cipher or hashing parameters.<br/>
        If you want to use high encryption parameters, you need to install the "Java Cryptography Extension (JCE) Unlimited
        Strength Jurisdiction Policy Files" for your JRE version
        (Oracle <link href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html">JDK6</link>,
        <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>

	<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>
    
    <p></p>

    <table>
        <tr>
            <td></td>
            <td>HSSF</td>
            <td>HSLF</td>
            <td>HWPF</td>
            <td>XSSF</td>
            <td>XSLF</td>
            <td>XWPF</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/dd949802(v=office.12).aspx">XOR obfuscation</link></td>
            <td class="feature-partly">Read</td>
            <td class="feature-na">N/A</td>
            <td class="feature-no">No</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/dd909583(v=office.12).aspx">40-bit RC4 encryption</link></td>
            <td class="feature-partly">Read</td>
            <td class="feature-na">N/A</td>
            <td class="feature-no">No</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/dd910113(v=office.12).aspx">Office Binary Document RC4 CryptoAPI Encryption</link></td>
            <td class="feature-no">No</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-no">No</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/dd907466(v=office.12).aspx">Office Binary Document RC4 Encryption *)</link></td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/dd906131(v=office.12).aspx">ECMA-376 Standard Encryption</link></td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/dd906131(v=office.12).aspx">ECMA-376 Agile Encryption</link></td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
        </tr>
        <tr>
            <td><link href="http://msdn.microsoft.com/en-us/library/ms757845(v=vs.85).aspx">ECMA-376 XML Signature</link></td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-na">N/A</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
            <td class="feature-yes">Yes</td>
        </tr>
    </table>
    
    <p>*) the <link href="http://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. 
    </p>
    </section>

    <section><title>Binary formats</title>
    	<p>As mentioned above, use
        <link href="http://poi.apache.org/apidocs/org/apache/poi/hssf/record/crypto/Biff8EncryptionKey.html">
        Biff8EncryptionKey</link>.setCurrentUserPassword(String password)
        to specify the password.</p>
        
        <source>
// XOR/RC4 decryption for xls
Biff8EncryptionKey.setCurrentUserPassword("pass");
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File("file.xls"), true);
HSSFWorkbook hwb = new HSSFWorkbook(fs.getRoot(), true);
Biff8EncryptionKey.setCurrentUserPassword(null);
        </source>
        
        <source>
// RC4 CryptoApi support ppt - decryption
Biff8EncryptionKey.setCurrentUserPassword("pass");
NPOIFSFileSystem fs = new NPOIFSFileSystem(new File("file.ppt"), true);
HSLFSlideShow hss = new HSLFSlideShow(fs);
...
// Option 1: remove password
Biff8EncryptionKey.setCurrentUserPassword(null);
OutputStream os = new FileOutputStream("decrypted.ppt");
hss.write(os);
os.close();
...
// Option 2: change encryption settings (experimental)
// need to cache data (i.e. read all data) before changing the key size
PictureData picsExpected[] = hss.getPictures();
hss.getDocumentSummaryInformation();
EncryptionInfo ei = hss.getDocumentEncryptionAtom().getEncryptionInfo();
((CryptoAPIEncryptionHeader)ei.getHeader()).setKeySize(0x78);
OutputStream os = new FileOutputStream("file_120bit.ppt");
hss.write(os);
os.close();
        </source>
    </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>

	<source>
EncryptionInfo info = new EncryptionInfo(filesystem);
Decryptor d = Decryptor.getInstance(info);

try {
    if (!d.verifyPassword(password)) {
        throw new RuntimeException("Unable to process: document is encrypted");
    }

    InputStream dataStream = d.getDataStream(filesystem);

    // parse dataStream

} catch (GeneralSecurityException ex) {
    throw new RuntimeException("Unable to process encrypted document", ex);
}
	</source>

	<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="http://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/EncryptionMode.html">binaryRC4, standard and agile encryption</link>,
     the cryptoAPI mode is used internally and it's direct use would result in an incomplete file.
     Apart of the CipherMode, the EncryptionInfo class provides further parameters to specify the cipher and
     hashing algorithm to be used.</p>
     <source>
POIFSFileSystem fs = new POIFSFileSystem();
EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile);
// EncryptionInfo info = new EncryptionInfo(EncryptionMode.agile, CipherAlgorithm.aes192, HashAlgorithm.sha384, -1, -1, null);

Encryptor enc = info.getEncryptor();
enc.confirmPassword("foobaa");

OPCPackage opc = OPCPackage.open(new File("..."), PackageAccess.READ_WRITE);
OutputStream os = enc.getDataStream(fs);
opc.save(os);
opc.close();

FileOutputStream fos = new FileOutputStream("...");
fs.writeFilesystem(fos);
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="http://en.wikipedia.org/wiki/XML_Signature">XML Signature</link>
     to protect it from unauthorized modifications, i.e. modifications without having the original certificate.
     The current implementation is based on the <link href="http://eid-applet.googlecode.com">eID Applet</link> which
     is dual-licensed to <link href="https://code.google.com/p/eid-applet/source/browse/trunk/README.txt">ASF/POI</link>.
     Instead of using the internal <link href="http://www.jsourcecode.com/class.php?proj=jdk%5Copenjdk&amp;jar=openjdk-6-b14&amp;class=org.jcp.xml.dsig.internal.dom.DOMXMLSignatureFactory">JDK API</link>
     this version is based on <link href="http://santuario.apache.org">Apache Santuario</link>.</p>
     <p>The classes have been tested against the following libraries, which need to be included additionally to the
     <link href="overview.html#components">default dependencies</link>:</p>
     <ul>
     <li>BouncyCastle bcpkix and bcprov (tested against 1.51)</li>
     <li>Apache Santuario "xmlsec" (tested against 2.0.1)</li>
     <li>and slf4j-api (tested against 1.7.7)</li>     
     </ul>
     <p>Depending on the <link href="http://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/SignatureConfig.html">configuration</link>
     and the activated <link href="http://poi.apache.org/apidocs/org/apache/poi/poifs/crypt/dsig/facets/package-summary.html">facets</link>
     various <link href="http://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. 
     </p>
     <p>Further test examples can be found in the corresponding <link href="http://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>
OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ);
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
boolean isValid = si.verifySignature();
...
     </source>
     </section>
     
     <section><title>Signing an office document</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
// 'digitalSignature' key usage
char password[] = "test".toCharArray();
File file = new File("test.pfx");
KeyStore keystore = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream(file);
keystore.load(fis, password);
fis.close();

// extracting private key and certificate
String alias = "xyz"; // alias of the keystore entry
Key key = keystore.getKey(alias, password);
X509Certificate x509 = (X509Certificate)keystore.getCertificate(alias);

// filling the SignatureConfig entries (minimum fields, more options are available ...)
SignatureConfig signatureConfig = new SignatureConfig();
signatureConfig.setKey(keyPair.getPrivate());
signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ_WRITE);
signatureConfig.setOpcPackage(pkg);

// adding the signature document to the package
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(signatureConfig);
si.confirmSignature();
// optionally verify the generated signature
boolean b = si.verifySignature();
assert (b);
// write the changes back to disc
pkg.close();
     </source>
     </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 
      POI project logo are trademarks of The Apache Software Foundation.
    </legal>
  </footer>
</document>




