Digital Signatures (for docx, pptx, xlsx)

Digital Signatures (for docx, pptx, xlsx). 0

INTRODUCTION.. 1

SIGNATURES IN MICROSOFT OFFICE. 1

SIGNATURE SUPPORT IN OFFICE. 5

THE SPECIFICATION.. 5

SPECIFICATION.. 7

3.1 Core Generation. 7

3.2 Core Validation. 7

XAdES. 8

USAGE: INVISIBLE SIGNING.. 10

USAGE: VISIBLE SIGNATURE. 10

Inserting a signature line. 12

Signing the document. 12

USAGE: SIGNATURE VALIDATION.. 13

API SUMMARY. 13

configureSignature. 13

setVisualSignature. 14

sign. 14

miscellaneous. 15

LIMITATIONS. 16

Visible Signatures. 16

Certificate Validation (should a signature be trusted?). 16

X.590 certificate. 17

DSA and RSA algorithms. 17

KNOWN ISSUES WITH OFFICE. 17

Windows Explorer. 17

Word Online. 17

Word for Mac. 17

Word for Android. 18

TROUBLESHOOTING.. 18

Validation errors. 18

 

INTRODUCTION

This chapter explains how to use the docx4j  dig sig tool:

Dig Sig makes it easy to use docx4j to work with digital signatures:

·         programmatically sign docx, pptx, or xlsx files

o   with 1 or more signatures

o   with or without the signature being “visible” (docx only)

·         validate signatures

The implementation follows the Microsoft spec, so the signatures work as expected in Microsoft Office.

SIGNATURES IN MICROSOFT OFFICE

A signature can be visible (in Excel or Word) or invisible.

In either case, the signature also hashes the document (more on this below), so Office can detect whether its been “tampered” with (ie edited) after signing.  This is its primary purpose: to detect whether signed data has been altered

In Word, a “visible” signature looks like this:

This is inserted using the Signature Line dialog (Insert > Singature Line):

A user can click on the Signature Line to sign it.

First they’ll see a dialog:

When someone signs it, their signature appears there:

Signatures can also be “invisible”.  In this case, the document is hashed, but there is no signature on the document surface.   You add an invisible digital signature via File>Info>Protect Document:

The sign dialog is similar:

In Word 2013/2016, this dialog has some additional fields:

Either way, after a document has been signed, the Signature panel gives you visual confirmation that the signatures are vald/intact (that is, that the signed parts have not been altered):

 

You can click to get the details:

(The information given differs a little depending on whether the signature is visible or not)

You are alerted if the signature is no longer valid:

In the case of a visual signature, you will also see red text superimposed on it:

SIGNATURE SUPPORT IN OFFICE

Signatures are supported in Office 2007, 2010 and 2013, however, Powerpoint does not support visible signatures.

Signatures are not supported in Office for Mac (either 2011 or 2016). 

THE SPECIFICATION

Signing is part of the Open Packaging Conventions spec.

Docx4j Enterprise can sign documents following that spec; it can also validate signatures.

As noted in the spec:

Digital signatures do not protect data from being changed. However, consumers can detect whether signed data has been altered and notify the end-user, restrict the display of altered content, or take other actions.

It isn’t necessary to understand the full details in order to use Docx4j’s digital signature functionality.  However, it is useful to understand the role of XmlSignaturePart:

·         when you sign a document, you create an XmlSignaturePart

·         there is an XmlSignaturePart for each signature

·         the XmlSignaturePart is used for validation

An XmlSignaturePart looks something like:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#" xmlns:dssi="http://schemas.microsoft.com/office/2006/digsig" xmlns:mdssi="http://schemas.openxmlformats.org/package/2006/digital-signature" xmlns:xd="http://uri.etsi.org/01903/v1.3.2#" Id="idPackageSignature">

  <SignedInfo>

    <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>

    <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>

    <Reference Type="http://www.w3.org/2000/09/xmldsig#Object" URI="#idPackageObject">

      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>

      <DigestValue>z7n7di9KSx4VqtOfph4aLW2f2v8=</DigestValue>

    </Reference>

    <Reference Type="http://www.w3.org/2000/09/xmldsig#Object" URI="#idOfficeObject">

      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>

      <DigestValue>Kj6v0MJbJZSjEvWCJTqJ9Ka1wis=</DigestValue>

    </Reference>

    <Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#idSignedProperties">

      <Transforms>

        <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></Transform>

      </Transforms>

      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>

      <DigestValue>ErjZGNguSuMk9yR5GarA7POLx4Y=</DigestValue>

    </Reference>

  </SignedInfo>

<SignatureValue Id="idPackageSignature-signature-value">ejBOZSQY7fK4GcsNH2tfm2TlcwG+..qjO03wwp48BONaBD9GtG3P8jy2GbEsVM23ZaRaBRA==</SignatureValue>

 

  <KeyInfo> <!-- KeyInfoSignatureFacet -->

    <X509Data>

      <X509Certificate>MIIFGDCCBACgAwIBAgIQf9Hy8UJoFT4iDZtsk+..+JhxM8k4JSmlXwk98Oq87tkZ</X509Certificate>

    </X509Data>

  </KeyInfo>

  <Object Id="idPackageObject">

    <Manifest>

      <Reference URI="/word/document.xml?ContentType=application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml">

        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>

        <DigestValue>w8hL8o7msdLrxRjyoP0+OXvfOx8=</DigestValue>

      </Reference>

      :

    </Manifest>

    <SignatureProperties Id="id-signature-time-Wed Mar 02 11:11:48 EST 2016">

      <SignatureProperty Id="idSignatureTime" Target="#idPackageSignature">

        <mdssi:SignatureTime>

          <mdssi:Format>YYYY-MM-DDThh:mm:ssTZD</mdssi:Format>

          <mdssi:Value>2016-03-02T00:11:48Z</mdssi:Value>

        </mdssi:SignatureTime>

      </SignatureProperty>

    </SignatureProperties>

  </Object>

  <Object Id="idOfficeObject">

    <SignatureProperties>

      <SignatureProperty Id="idOfficeV1Details" Target="#idPackageSignature">

        <dssi:SignatureInfoV1>

          :

          <dssi:SignatureProviderDetails>0</dssi:SignatureProviderDetails>

          <dssi:SignatureType>1</dssi:SignatureType>

          <dssi:ManifestHashAlgorithm>http://www.w3.org/2000/09/xmldsig#sha1</dssi:ManifestHashAlgorithm>

        </dssi:SignatureInfoV1>

      </SignatureProperty>

    </SignatureProperties>

</Object>

 

  <Object>

    <xd:QualifyingProperties Target="#idPackageSignature">

 

      <xd:SignedProperties Id="idSignedProperties">

        <xd:SignedSignatureProperties>

          <xd:SigningTime>2016-02-02T11:11:48Z</xd:SigningTime>

          <xd:SigningCertificate>

            <xd:Cert>

              <xd:CertDigest>

                <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>

                <DigestValue>zk1lWOk+1aUEe7B+bZzgO84hQy8=</DigestValue>

              </xd:CertDigest>

              <xd:IssuerSerial>

                <X509IssuerName>[issuer]</X509IssuerName>

                <X509SerialNumber>16990207296591082732488088650</X509SerialNumber>

              </xd:IssuerSerial>

            </xd:Cert>

          </xd:SigningCertificate>

          <xd:SignaturePolicyIdentifier>

            <xd:SignaturePolicyImplied/>

          </xd:SignaturePolicyIdentifier>

          <xd:SignatureProductionPlace>

            <xd:City></xd:City>

            <xd:StateOrProvince></xd:StateOrProvince>

            <xd:PostalCode></xd:PostalCode>

            <xd:CountryName></xd:CountryName>

          </xd:SignatureProductionPlace>

          <xd:SignerRole>

            <xd:ClaimedRoles>

              <xd:ClaimedRole> </xd:ClaimedRole>

            </xd:ClaimedRoles>

          </xd:SignerRole>

        </xd:SignedSignatureProperties>

        <xd:SignedDataObjectProperties>

          <xd:CommitmentTypeIndication>

            <xd:CommitmentTypeId>

              <xd:Identifier>http://uri.etsi.org/01903/v1.2.2#ProofOfOrigin</xd:Identifier>

              <xd:Description>Created and approved this document</xd:Description>

            </xd:CommitmentTypeId>

            <xd:AllSignedDataObjects/>

          </xd:CommitmentTypeIndication>

        </xd:SignedDataObjectProperties>

      </xd:SignedProperties>

 

      <xd:UnsignedProperties>

        <xd:UnsignedSignatureProperties>

          <!-- Office 2013 XAdES-X-L, not implemented in Docx4j 3.3.0 -->

          <xd:CertificateValues>

            <xd:EncapsulatedX509Certificate>MIIE5 .. y6vFUg=</xd:EncapsulatedX509Certificate>

            <xd:EncapsulatedX509Certificate>MIIEZj ..s0AH8g=</xd:EncapsulatedX509Certificate>

          </xd:CertificateValues>

        </xd:UnsignedSignatureProperties>

      </xd:UnsignedProperties>

 

    </xd:QualifyingProperties>

  </Object>

</Signature>

SPECIFICATION

The Open Packaging Conventions spec describes how the package digital signature framework applies the W3C Recommendation “XML-Signature Syntax and Processing” with certain modifications.

The W3C Recommendation https://www.w3.org/TR/xmldsig-core/ explains how to generate a signature, and validate it.  It is not necessary to understand the information in this section in any detail, in order to use Docx4j to sign or validate.

The following is quoted from the spec:

3.1 Core Generation

The REQUIRED steps include the generation of Reference elements and the SignatureValue over SignedInfo.

3.1.1 Reference Generation

For each data object being signed:

1.       Apply the Transforms, as determined by the application, to the data object.

2.       Calculate the digest value over the resulting data object.

3.       Create a Reference element, including the (optional) identification of the data object, any (optional) transform elements, the digest algorithm and the DigestValue. (Note, it is the canonical form of these references that are signed in 3.1.2 and validated in 3.2.1 .)

3.1.2 Signature Generation

1.       Create SignedInfo element with SignatureMethodCanonicalizationMethod and Reference(s).

2.       Canonicalize and then calculate the SignatureValue over SignedInfo based on algorithms specified in SignedInfo.

3.       Construct the Signature element that includes SignedInfoObject(s) (if desired, encoding may be different than that used for signing), KeyInfo (if required), and SignatureValue.

3.2 Core Validation

The REQUIRED steps of core validation include (1) reference validation, the verification of the digest contained in each Reference in SignedInfo, and (2) the cryptographicsignature validation of the signature calculated over SignedInfo.

3.2.1 Reference Validation

1.       Canonicalize the SignedInfo element based on the CanonicalizationMethod in SignedInfo.

2.       For each Reference in SignedInfo:

1.       Obtain the data object to be digested. (For example, the signature application may dereference the URI and execute Transforms provided by the signer in theReference element, or it may obtain the content through other means such as a local cache.)

2.       Digest the resulting data object using the DigestMethod specified in its Reference specification.

3.       Compare the generated digest value against DigestValue in the SignedInfo Reference; if there is any mismatch, validation fails.

3.2.2 Signature Validation

1.       Obtain the keying information from KeyInfo or from an external source.

2.       Obtain the canonical form of the SignatureMethod using the CanonicalizationMethod and use the result (and previously obtained KeyInfo) to confirm the SignatureValue over the SignedInfo element.

Note that the Open Packaging Conventions modify/elaborate on these steps slightly.

In addition, the Signature panel in Office also checks certificate validity (ie in addition to reference and signature validation).

XAdES

XAdES v1.4.1[1] extends XMLDSIG into the domain of non-repudiation by defining XML formats for advanced electronic signatures that remain valid over long periods and are compliant with EU-DIR-ESIG.  XAdES incorporates additional useful information, including evidence as to validity even if the signer or verifying party later attempts to deny (repudiates) the validity of the signature.

XAdES includes the following seven forms:

XAdES-BES
(Base)

provides basic authentication and integrity protection: and satisfies the legal requirements for advanced electronic signatures, but does not provide non-repudiation of its existence.

Supported

XAdES-EPES

as above, but it has a SignaturePolicyIdentifier element.

In Office, the SignaturePolicyIdentifier element only has the default element (SignaturePolicyImplied).

Default

XAdES-T

with Time-Stamp: Includes time-stamp to provide protection against repudiation

Supported

XAdES-C

with complete validation data: Includes references to the set of data supporting the validation of the electronic signature (i.e. the references to the certification path and its associated revocation status information). This form is useful for those situations where such information is archived by an external source, like a trusted service provider.

Not currently supported

XAdES-X

with eXtended validation data: Includes time-stamp on the references to the validation data or on the ds:Signature element and the aforementioned validation data. This time-stamp counters the risk that any keys used in the certificate chain or in the revocation status information may be compromised.

Not currently supported

XAdES-X-L

with eXtended validation data incorporated for the long term: Includes the validation data for those situations where the validation data are not stored elsewhere for the long term.

CertificateValues:A verifier will have to prove that the certification path was valid, at the time of the validation of the signature, up to a trust point according to the naming constraints and the certificate policy constraints from the "Signature Validation Policy". It will be necessary to capture all the certificates from the certification path, starting with those from the signer and ending up with those of the certificate from one trusted root of the "Signature validation policy".

RevocationValues: When dealing with long term electronic signatures, all the revocation data used in the verification of such signatures must be stored and conveniently time-stamped

Could support (see CertificateTrustChecker further below)

 

(Office 2013 & 2016 write CertificateValues)

 

XAdES-A

with archiving validation data: It includes additional time-stamps for archiving signatures in a way that they are protected if the cryptographic data become weak.

(Not supported by Office)

 

XAdES- EPES is the default format for Office 2010 signatures.

Docx4j follows this.  The property com.plutext.dsig.XAdES.Level supports the following values:

0 -    XAdES Off (Create XML-DSig signatures)

1 -    Create XAdES-EPES signatures (default)

2 -    Create XAdES-T signatures

Regarding XAdES-T, quoting https://blogs.msdn.microsoft.com/david_leblanc/2010/05/30/office-2010-digital-signatures-and-xades/

The next level, XAdES-T is where you really get a lot of benefit. A serious problem with XML-DSig signatures is that they’re not good for the long term. You are typically given a certificate that’s good for 1-2 years, you sign something, and then open it 2 years later, and it’s invalid. We can’t replace pen and paper this way – real signatures have to be good for many years. The solution is a SignatureTimeStamp element. This uses RFC 3161 to record a timestamp of the signature value. Assuming that we can trust the timestamp server, we now have external proof of the signing time. This means that a future expiration doesn’t apply, and a revocation may not apply.

To create an XAdES-T signature, you need to do 2 things:

1.       Set docx4j property com.plutext.dsig.XAdES.Level=2 in docx4j.properties

2.       Set your time stamp server:

signatureHelper.getSignatureConfig().setTspUrl(tspUrl);

and any other properties (username, proxy etc).

USAGE: INVISIBLE SIGNING

In this scenario you sign the package with one or more signatures (each creating an XmlSignaturePart), but the signatures are not visible on the document surface. 

Basic usage (signature is not visible):

        SignatureHelper signing= new SignatureHelper(pkg);

        signing.configureSignature(fis, password);

        signing.sign();

        // then save your pkg

signing.configureSignature is used to provide the key to be used for signing the package.  Several method signatures are available:

        /**

         * Provide details of a PKCS12 key, in preparation for signing this package.

         */

        public SignatureDetail configureSignature(InputStream PKCS12stream, char[] password) throws Docx4JException  {

 

                KeyStore keystore = KeyStore.getInstance("PKCS12"); // a new keystore

                        keystore.load(PKCS12stream, password); // now containing one entry

 

                :

       

 

        /**

         * Provide details of a key to be used for signing this package

         *

         * @param pkEntry

         */

        public SignatureDetail configureSignature(KeyStore.PrivateKeyEntry pkEntry)

To use this latter method, load a keystore of your choice, then invoke its getEntry method.  For more details, see https://docs.oracle.com/javase/7/docs/api/java/security/KeyStore.html

 

USAGE: VISIBLE SIGNATURE

A visible signature is a “signature line” on the document surface, coupled with an XmlSignaturePart.

The two are tied together via a reference from the XmlSignaturePart to the signature line’s ID.

Before signing, the signature line will look something like:

The underlying XML:

      <w:r>

        <w:pict>

          <v:shapetype id="_x0000_t75" coordsize="21600,21600" ..>

            :

          </v:shapetype>

          <v:shape id="_x0000_i1025" type="#_x0000_t75" alt="Microsoft Office Signature Line..." style="width:192pt;height:96pt">

            <v:imagedata r:id="rId5" o:title=""/>

            <o:lock v:ext="edit" ungrouping="t" rotation="t" cropping="t" verticies="t" text="t" grouping="t"/>

            <o:signatureline v:ext="edit" id="{12846971-C688-4F86-B09E-338A31F4D41B}"

            o:suggestedsigner="John Doe"

            o:suggestedsigner2="Manager" issignatureline="t"/>

          </v:shape>

        </w:pict>

      </w:r>

Note:

·         the o:signatureline element

·         the rel id in the v:imagedata element, which is to an EMF image

You can sign the document (ie add the XmlSignaturePart) at the same time as inserting the signature line, or you can do it later (via docx4j or using Word).

If the signature line is already present in the docx, you can use docx4j to sign the document (ie add the XmlSignaturePart).

After signing, the signature line will look something like:

               

and the signature panel should say:

               

Visible signatures are currently only supported for Docx files.  (Excel 2010 has signature line support, but Docx4j does not currently have a high level API for creating an Excel signature line)

Inserting a signature line

 

        SignatureHelper helper= new SignatureHelper(pkg);

      

        CTSignatureLine sigLine = helper.createCTSignatureLine("John Doe", true);

        P p = helper.createDocxSignatureLineP(sigLine, imageRelId, null, true);

        pkg.getMainDocumentPart().addObject(p);

 

When you insert a signature line, Word stores an EMF image of the signature block.   Note that Docx4j does not currently support generating a custom signature block EMF image for a user to use to later sign.  However, this does not matter if signing is done later via the Office user interface, since Office generates that image itself on-the-fly.  For this reason, the sample code just uses a tiny 2 pixel image.

CTSignatureLine represents o:signatureline:

            <o:signatureline v:ext="edit" id="{12846971-C688-4F86-B09E-338A31F4D41B}"

            o:suggestedsigner="John Doe"

            o:suggestedsigner2="Manager" issignatureline="t"/>

 

helper.createCTSignatureLine sets o:suggestedsigner

You can set other attributes as you see fit (eg the signer’s title goes in o:suggestedsigner2).

Signing the document

To sign the document, you need a reference to the signature line object.   You’ll have this if you just inserted the signature line.  Otherwise, you’ll have to search the document contents to find the signature line (not explained here).

So continuing the example above:

        SignatureDetail details = helper.configureSignature(fis, password);

        details.setVisualSignature(sigLine,

                        validSigLnImg png,

                        signatureImage png);

        helper.sign();

validSigLnImg png is critical, its the signature block image (signature plus signature line) which appears on the document surface.

If the signature is invalidated (eg the document is altered), that image will be replaced with InvalidSigLnImg.  As a convenience, Docx4j can generate that image from validSigLnImg, provided you provide it inpng format.  Alternatively, you can provide SignatureDetail with your own image to be used if the document is invalid:

        /**

         * A base64 encoded image (Word uses EMF, but PNG tested ok with Word 2010),

         * displayed in the document if the signature is invalid; made up of the signature line plus

         * the signature, plus red text saying it is invalid.

         *

         * If null, can be generated from validSigLnImg, provided that is a png

         *

         * @param validSigLnImg

         */

        public void setInvalidSigLnImg(String invalidSigLnImg)  

In the Word UI, a user can sign visually with an image, or with text.  In the above example, the visual signature is configured with a signature image: signatureImage png is an image of the signature; it doesn’t appear on the document surface, but is stored in the XmlSignaturePart.

If you wanted to sign instead with text, you’d use:

public void setVisualSignature(CTSignatureLine signatureLine, byte[] validSigLnImg,

                               String signatureText)

 

USAGE: SIGNATURE VALIDATION

 

Validate existing signatures:

        SignatureHelper signing= new SignatureHelper(pkg);

        List<XmlSignaturePart> parts = signing.getSignaturesStatus(null);

       

        for (XmlSignaturePart sp : parts) {       

                System.out.println(sp.getPartName().getName() + " ---> " + sp.getSignatureStatus(null));

        }

API SUMMARY

Mostly, you interact with com.plutext.dsig.SignatureHelper

Some configuration settings are in SignatureConfig.  You can access that from SignatureHelper:

        public SignatureConfig getSignatureConfig()

 

configureSignature

 

        /** 

         * Provide details of a PKCS12 key, in preparation for signing this package

         *

         * @param privateKeyIS

         * @param password

         * @throws Docx4JException

         */

        public SignatureDetail configureSignature(InputStream PKCS12stream, String password) throws Docx4JException

       

        /**

         * Provide details of a PKCS12 key, in preparation for signing this package.

         *

         * @param privateKeyIS

         * @param password

         * @param signatureLine

         * @throws Docx4JException

         */

        public SignatureDetail configureSignature(InputStream PKCS12stream, char[] password) throws Docx4JException 

 

        /**

         * Provide details of a key to be used for signing this package

         *

         * @param pkEntry

         */

        public SignatureDetail configureSignature(KeyStore.PrivateKeyEntry pkEntry)

 

setVisualSignature

 

 

        /**

         * Set the extra info required to visibly sign this package

         * with a textual signature

         * (associating it with a signature line on the document surface)

         *

         * @param signatureDetail

         * @param signatureLine

         * @param validSigLnImg   the image (Word uses EMF, but PNG tested ok with Word 2010

         * and MUST be used since we can't manipulate an EMF, yet),

         * displayed in the document; should be the signature line plus the signature.

         * @param signatureText

         */

        public void setVisualSignature(SignatureDetail signatureDetail, CTSignatureLine signatureLine,

                        byte[] validSigLnImg, String signatureText);

 

 

        /**

         * Set the extra info required to visibly sign this package

         * with a graphical signature

         * (associating it with a signature line on the document surface)

         *

         * @param signatureDetail

         * @param signatureLine

         * @param validSigLnImg   the image (Word uses EMF, but PNG tested ok with Word 2010

         * and MUST be used since we can't manipulate an EMF, yet),

         * displayed in the document; should be the signature line plus the signature.

         * @param signatureImage  the signature itself (not actually displayed), but stored

         */

        public void setVisualSignature(SignatureDetail signatureDetail, CTSignatureLine signatureLine,

                        byte[] validSigLnImg, byte[] signatureImage)

sign

 

       

        /**

         * Sign it, using the key provided via configureSignature

         */

        public void sign() throws Docx4JException

 

 

miscellaneous

 

        /**

         * Signed if SignatureOriginPart has rels to one or more XmlSignatureParts

         * @return

         */

        public boolean isPackageSigned()

 

    /**

     * Set the signature status for each signature. This mimics what you see in the Word signatures pane,

     * and checks the validity of the signature (hash), whether it is complete or partial,

     * and the certificate used to sign with.

     *

     * @param zippedOfficeFile a docx/pptx/xlsx file (not docx4j package), to ensure the input has not been modified in docx4j

     * @param certificateTrustChecker

     * @return

     * @throws SignaturesNotPresentException

     * @throws DigitalSignatureException

     */

    public static List<XmlSignaturePart> getSignaturesStatus(InputStream zippedOfficeFile, CertificateTrustChecker certificateTrustChecker)

                throws SignaturesNotPresentException, DigitalSignatureException {

    public List<XmlSignaturePart> getSignaturesStatus(CertificateTrustChecker certificateTrustChecker) throws SignaturesNotPresentException, DigitalSignatureException

 

You can then iterate through the XmlSignaturePart objects, invoking getSignatureStatus(), which returns a SignatureStatus:

 

public enum SignatureStatus {

       

        UNKNOWN,

        VALID,

        INVALID,

        VALID_PARTIAL /* the signed parts are valid, but not all required parts are signed */,

        RECOVERABLE /* the signature is valid, but the certificate is not trusted */,

        RECOVERABLE_PARTIAL

 

}

 

    /**

     * Set the signature status for each signature, without checking the validity of the

     * certificate itself. This is a subset of what you see in the Word signatures pane,

     * since it checks the validity of the signature (hash), and whether it is complete or partial,

     * BUT NOT the certificate used to sign with.

     *

     * @param zippedOfficeFile a docx/pptx/xlsx file (not docx4j package), to ensure the input has not been modified in docx4j

     * @param certificateTrustChecker

     * @return

     * @throws SignaturesNotPresentException

     * @throws DigitalSignatureException

     */

    public static List<XmlSignaturePart> getSignaturesStatusTrusted(InputStream zippedOfficeFile) throws SignaturesNotPresentException, DigitalSignatureException

 

 

       

        /**

         * Remove all signature parts from this package

         * @param pkg

         */

        public void removeSignatureParts(OpcPackage pkg)

 

LIMITATIONS

Visible Signatures

1.       Visible signatures are currently only supported for Docx files.  (Excel 2010 has signature line support, but Docx4j does not currently have a high level API for creating an Excel signature line; please contact Plutext if you need this)

2.       Docx4j does not currently support generating a custom signature block EMF image for a user to use to later sign.  However, this does not matter if signing is done later via the Office user interface, since Office generates that image itself on-th-fly.  It may matter if signing is done via some third party user interface.

Where Docx4j is used to sign the document with a visible signature, the image provided for the signed signature block will appear on the document surface.

Certificate Validation (should a signature be trusted?)

It is possible to examine the signer to determine whether it is to be trusted (based on trusted certificate authorities).  This is called "certificate validation", and Word’s signature implementation does this.

Docx4j’s SignatureHelper contains:

    /**

     * Set the signature status for each signature. This mimics what you see in the Word signatures

     * pane, and checks the validity of the signature (hash), whether it is complete or partial,

     * and the certificate used to sign with.

     *

     * @param certificateTrustChecker pass null to use the default implementation

     * @return

     * @throws SignaturesNotPresentException

     * @throws DigitalSignatureException

     */

    public List<XmlSignaturePart> getSignaturesStatus(CertificateTrustChecker certificateTrustChecker) throws SignaturesNotPresentException, DigitalSignatureException {

 

You can then invoke getSignatureStatus(signatureConfig) on an XmlSignaturePart to get its status.

The details of how check the validity of a certificate in Java depends on your environment (including not just whether you use Oracle or IBM java, but if Oracle java, which version, and what certificates you want to trust). 

Because of this variation, the API accepts an object implementing:

public interface CertificateTrustChecker {

       

       CertificateTrustCheckResult isTrusted(X509Certificate signer) throws DigitalSignatureException, CertPathValidatorException, CertPathBuilderException;

 

}

An implementation of this is provided in DefaultCertificateTrustChecker, however it requires Java 8.

If you pass a null CertificateTrustChecker, the certificate will not be checked, and providing the signature is valid, the resulting SignatureStatus will be RECOVERABLE or RECOVERABLE_PARTIAL.

X.590 certificate

The X.509 certificate for validating the signature is located in the Digital Signature XML Signature part itself.

In contrast, the Open Packaging Conventions also supports:

·         placing it in a Digital Signature Certificate part

·         storing the certificate as a separate part in the package, or

·         not including it in the package if certificate data is known or can be obtained from a local or remote certificate store.

DSA and RSA algorithms

The Open Packaging Convention spec / Microsoft Office supports both DSA and RSA algorithms

In Docx4j, currently only RSA is supported for signing the digest (see SignatureInfo class).

KNOWN ISSUES WITH OFFICE

Windows Explorer

If you have the signed docx open in the preview pane in Windows Explorer, Word may be unable to open that docx.  So either turn off the preview pane, or preview some other file.

This seems to affect 64 bit versions of Word on Windows 7, 8 and 10.

Word Online

Word Online can’t open signed documents (even if signed in eg Word 2013):

(the error message is misleading)

Word for Mac

Word 2011 for Mac is silent as to whether signatures are present.

Word 2016 for Mac detects that a docx is signed, but doesn’t give any information about the signature.

Word for Android

Word 1.0.1 (16.0.4027.1006) can’t open digitally signed files.

This is fixed in Word 1.0.1 (16.06828.1002), which can open them (but can’t validate them).

TROUBLESHOOTING

Validation errors

If signature validation is failing, it is helpful to understand where.

To see, turn on slf4j debug-level logging for: org.apache.jcp.xml.dsig

The following VM arguments can also provide insight:

-Dcom.sun.net.ssl.checkRevocation=false

-Dcom.sun.security.enableCRLDP=false

-Djava.security.debug=certpath

-Djavax.net.debug=all



[1] http://uri.etsi.org/01903/v1.4.1/ts_101903v010401p.pdf