]> source.dussan.org Git - poi.git/commitdiff
+svn:eol-style=native
authorJaven O'Neal <onealj@apache.org>
Sat, 2 Jul 2016 05:01:43 +0000 (05:01 +0000)
committerJaven O'Neal <onealj@apache.org>
Sat, 2 Jul 2016 05:01:43 +0000 (05:01 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1751022 13f79535-47bb-0310-9956-ffa450edef68

src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java

index f9afb7869e72d3a293b77f2879d6f4761105f5a0..c2fb8be2c4d4f2bb6acd7798a417fbd9388df7e0 100644 (file)
@@ -1,50 +1,50 @@
-/* ====================================================================\r
-   Licensed to the Apache Software Foundation (ASF) under one or more\r
-   contributor license agreements.  See the NOTICE file distributed with\r
-   this work for additional information regarding copyright ownership.\r
-   The ASF licenses this file to You under the Apache License, Version 2.0\r
-   (the "License"); you may not use this file except in compliance with\r
-   the License.  You may obtain a copy of the License at\r
-\r
-       http://www.apache.org/licenses/LICENSE-2.0\r
-\r
-   Unless required by applicable law or agreed to in writing, software\r
-   distributed under the License is distributed on an "AS IS" BASIS,\r
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
-   See the License for the specific language governing permissions and\r
-   limitations under the License.\r
-==================================================================== */\r
-\r
-/* ====================================================================\r
-   This product contains an ASLv2 licensed version of the OOXML signer\r
-   package from the eID Applet project\r
-   http://code.google.com/p/eid-applet/source/browse/trunk/README.txt  \r
-   Copyright (C) 2008-2014 FedICT.\r
-   ================================================================= */ \r
-\r
-package org.apache.poi.poifs.crypt.dsig;\r
-\r
+/* ====================================================================
+   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.
+==================================================================== */
+
+/* ====================================================================
+   This product contains an ASLv2 licensed version of the OOXML signer
+   package from the eID Applet project
+   http://code.google.com/p/eid-applet/source/browse/trunk/README.txt  
+   Copyright (C) 2008-2014 FedICT.
+   ================================================================= */ 
+
+package org.apache.poi.poifs.crypt.dsig;
+
 import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
 import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_DIGSIG_NS;
 
 import javax.crypto.Cipher;
 import javax.xml.crypto.MarshalException;
 import javax.xml.crypto.URIDereferencer;
-import javax.xml.crypto.XMLStructure;\r
-import javax.xml.crypto.dsig.CanonicalizationMethod;\r
-import javax.xml.crypto.dsig.Manifest;\r
-import javax.xml.crypto.dsig.Reference;\r
-import javax.xml.crypto.dsig.SignatureMethod;\r
-import javax.xml.crypto.dsig.SignedInfo;\r
-import javax.xml.crypto.dsig.XMLObject;\r
-import javax.xml.crypto.dsig.XMLSignContext;\r
-import javax.xml.crypto.dsig.XMLSignature;\r
-import javax.xml.crypto.dsig.XMLSignatureException;\r
-import javax.xml.crypto.dsig.XMLSignatureFactory;\r
-import javax.xml.crypto.dsig.XMLValidateContext;\r
-import javax.xml.crypto.dsig.dom.DOMSignContext;\r
-import javax.xml.crypto.dsig.dom.DOMValidateContext;\r
-import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;\r
+import javax.xml.crypto.XMLStructure;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.Manifest;
+import javax.xml.crypto.dsig.Reference;
+import javax.xml.crypto.dsig.SignatureMethod;
+import javax.xml.crypto.dsig.SignedInfo;
+import javax.xml.crypto.dsig.XMLObject;
+import javax.xml.crypto.dsig.XMLSignContext;
+import javax.xml.crypto.dsig.XMLSignature;
+import javax.xml.crypto.dsig.XMLSignatureException;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+import javax.xml.crypto.dsig.XMLValidateContext;
+import javax.xml.crypto.dsig.dom.DOMSignContext;
+import javax.xml.crypto.dsig.dom.DOMValidateContext;
+import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
 import javax.xml.xpath.XPath;
 import javax.xml.xpath.XPathConstants;
 import javax.xml.xpath.XPathFactory;
@@ -67,97 +67,97 @@ import java.util.NoSuchElementException;
 
 import org.apache.jcp.xml.dsig.internal.dom.DOMReference;
 import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
-import org.apache.poi.EncryptedDocumentException;\r
-import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
-import org.apache.poi.openxml4j.opc.ContentTypes;\r
-import org.apache.poi.openxml4j.opc.OPCPackage;\r
-import org.apache.poi.openxml4j.opc.PackagePart;\r
-import org.apache.poi.openxml4j.opc.PackagePartName;\r
-import org.apache.poi.openxml4j.opc.PackageRelationship;\r
-import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;\r
-import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
-import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
-import org.apache.poi.openxml4j.opc.TargetMode;\r
-import org.apache.poi.poifs.crypt.ChainingMode;\r
-import org.apache.poi.poifs.crypt.CipherAlgorithm;\r
-import org.apache.poi.poifs.crypt.CryptoFunctions;\r
-import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;\r
-import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;\r
-import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;\r
-import org.apache.poi.util.DocumentHelper;\r
-import org.apache.poi.util.POILogFactory;\r
-import org.apache.poi.util.POILogger;\r
-import org.apache.xml.security.Init;\r
-import org.apache.xml.security.utils.Base64;\r
-import org.apache.xmlbeans.XmlException;\r
-import org.apache.xmlbeans.XmlOptions;\r
-import org.w3.x2000.x09.xmldsig.SignatureDocument;\r
-import org.w3c.dom.Document;\r
-import org.w3c.dom.Element;\r
-import org.w3c.dom.NodeList;\r
-import org.w3c.dom.events.EventListener;\r
-import org.w3c.dom.events.EventTarget;\r
-\r
-\r
-/**\r
- * <p>This class is the default entry point for XML signatures and can be used for\r
- * validating an existing signed office document and signing a office document.</p>\r
- * \r
- * <p><b>Validating a signed office document</b></p>\r
- * \r
- * <pre>\r
- * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ);\r
- * SignatureConfig sic = new SignatureConfig();\r
- * sic.setOpcPackage(pkg);\r
- * SignatureInfo si = new SignatureInfo();\r
- * si.setSignatureConfig(sic);\r
- * boolean isValid = si.validate();\r
- * ...\r
- * </pre>\r
- * \r
- * <p><b>Signing an office document</b></p>\r
- * \r
- * <pre>\r
- * // loading the keystore - pkcs12 is used here, but of course jks &amp; co are also valid\r
- * // the keystore needs to contain a private key and it's certificate having a\r
- * // 'digitalSignature' key usage\r
- * char password[] = "test".toCharArray();\r
- * File file = new File("test.pfx");\r
- * KeyStore keystore = KeyStore.getInstance("PKCS12");\r
- * FileInputStream fis = new FileInputStream(file);\r
- * keystore.load(fis, password);\r
- * fis.close();\r
- * \r
- * // extracting private key and certificate\r
- * String alias = "xyz"; // alias of the keystore entry\r
- * Key key = keystore.getKey(alias, password);\r
- * X509Certificate x509 = (X509Certificate)keystore.getCertificate(alias);\r
- * \r
- * // filling the SignatureConfig entries (minimum fields, more options are available ...)\r
- * SignatureConfig signatureConfig = new SignatureConfig();\r
- * signatureConfig.setKey(keyPair.getPrivate());\r
- * signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));\r
- * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ_WRITE);\r
- * signatureConfig.setOpcPackage(pkg);\r
- * \r
- * // adding the signature document to the package\r
- * SignatureInfo si = new SignatureInfo();\r
- * si.setSignatureConfig(signatureConfig);\r
- * si.confirmSignature();\r
- * // optionally verify the generated signature\r
- * boolean b = si.verifySignature();\r
- * assert (b);\r
- * // write the changes back to disc\r
- * pkg.close();\r
- * </pre>\r
- * \r
- * <p><b>Implementation notes:</b></p>\r
- * \r
- * <p>Although there's a XML signature implementation in the Oracle JDKs 6 and higher,\r
- * compatibility with IBM JDKs is also in focus (... but maybe not thoroughly tested ...).\r
- * Therefore we are using the Apache Santuario libs (xmlsec) instead of the built-in classes,\r
- * as the compatibility seems to be provided there.</p>\r
- * \r
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;
+import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.poifs.crypt.ChainingMode;
+import org.apache.poi.poifs.crypt.CipherAlgorithm;
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
+import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
+import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
+import org.apache.poi.util.DocumentHelper;
+import org.apache.poi.util.POILogFactory;
+import org.apache.poi.util.POILogger;
+import org.apache.xml.security.Init;
+import org.apache.xml.security.utils.Base64;
+import org.apache.xmlbeans.XmlException;
+import org.apache.xmlbeans.XmlOptions;
+import org.w3.x2000.x09.xmldsig.SignatureDocument;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.events.EventListener;
+import org.w3c.dom.events.EventTarget;
+
+
+/**
+ * <p>This class is the default entry point for XML signatures and can be used for
+ * validating an existing signed office document and signing a office document.</p>
+ * 
+ * <p><b>Validating a signed office document</b></p>
+ * 
+ * <pre>
+ * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ);
+ * SignatureConfig sic = new SignatureConfig();
+ * sic.setOpcPackage(pkg);
+ * SignatureInfo si = new SignatureInfo();
+ * si.setSignatureConfig(sic);
+ * boolean isValid = si.validate();
+ * ...
+ * </pre>
+ * 
+ * <p><b>Signing an office document</b></p>
+ * 
+ * <pre>
+ * // 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();
+ * </pre>
+ * 
+ * <p><b>Implementation notes:</b></p>
+ * 
+ * <p>Although there's a XML signature implementation in the Oracle JDKs 6 and higher,
+ * compatibility with IBM JDKs is also in focus (... but maybe not thoroughly tested ...).
+ * Therefore we are using the Apache Santuario libs (xmlsec) instead of the built-in classes,
+ * as the compatibility seems to be provided there.</p>
+ * 
  * <p>To use SignatureInfo and its sibling classes, you'll need to have the following libs
  * in the classpath:</p>
  * <ul>
@@ -165,517 +165,517 @@ import org.w3c.dom.events.EventTarget;
  * <li>Apache Santuario "xmlsec" (tested against 2.0.5)</li>
  * <li>and slf4j-api (tested against 1.7.12)</li>
  * </ul>
- */\r
-public class SignatureInfo implements SignatureConfigurable {\r
-\r
-    private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);\r
-    private static boolean isInitialized = false;\r
-    \r
-    private SignatureConfig signatureConfig;\r
-\r
-    public class SignaturePart {\r
-        private final PackagePart signaturePart;\r
-        private X509Certificate signer;\r
-        private List<X509Certificate> certChain;\r
-        \r
-        private SignaturePart(PackagePart signaturePart) {\r
-            this.signaturePart = signaturePart;\r
-        }\r
-        \r
-        /**\r
-         * @return the package part containing the signature\r
-         */\r
-        public PackagePart getPackagePart() {\r
-            return signaturePart;\r
-        }\r
-        \r
-        /**\r
-         * @return the signer certificate\r
-         */\r
-        public X509Certificate getSigner() {\r
-            return signer;\r
-        }\r
-        \r
-        /**\r
-         * @return the certificate chain of the signer\r
-         */\r
-        public List<X509Certificate> getCertChain() {\r
-            return certChain;\r
-        }\r
-        \r
-        /**\r
-         * Helper method for examining the xml signature\r
-         *\r
-         * @return the xml signature document\r
-         * @throws IOException if the xml signature doesn't exist or can't be read\r
-         * @throws XmlException if the xml signature is malformed\r
-         */\r
-        public SignatureDocument getSignatureDocument() throws IOException, XmlException {\r
-            // TODO: check for XXE\r
-            return SignatureDocument.Factory.parse(signaturePart.getInputStream(), DEFAULT_XML_OPTIONS);\r
-        }\r
-        \r
-        /**\r
-         * @return true, when the xml signature is valid, false otherwise\r
-         * \r
-         * @throws EncryptedDocumentException if the signature can't be extracted or if its malformed\r
-         */\r
-        @SuppressWarnings("unchecked")\r
-        public boolean validate() {\r
-            KeyInfoKeySelector keySelector = new KeyInfoKeySelector();\r
-            try {\r
-                Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());\r
-                XPath xpath = XPathFactory.newInstance().newXPath();\r
-                NodeList nl = (NodeList)xpath.compile("//*[@Id]").evaluate(doc, XPathConstants.NODESET);\r
-                for (int i=0; i<nl.getLength(); i++) {\r
-                    ((Element)nl.item(i)).setIdAttribute("Id", true);\r
-                }\r
-                \r
-                DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);\r
-                domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);\r
-                domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());\r
-                brokenJvmWorkaround(domValidateContext);\r
-    \r
-                XMLSignatureFactory xmlSignatureFactory = signatureConfig.getSignatureFactory();\r
-                XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);\r
-                \r
-                // TODO: replace with property when xml-sec patch is applied\r
-                for (Reference ref : (List<Reference>)xmlSignature.getSignedInfo().getReferences()) {\r
-                    SignatureFacet.brokenJvmWorkaround(ref);\r
-                }\r
-                for (XMLObject xo : (List<XMLObject>)xmlSignature.getObjects()) {\r
-                    for (XMLStructure xs : (List<XMLStructure>)xo.getContent()) {\r
-                        if (xs instanceof Manifest) {\r
-                           for (Reference ref : (List<Reference>)((Manifest)xs).getReferences()) {\r
-                               SignatureFacet.brokenJvmWorkaround(ref);\r
-                           }\r
-                        }\r
-                    }\r
-                }\r
-                \r
-                boolean valid = xmlSignature.validate(domValidateContext);\r
-\r
-                if (valid) {\r
-                    signer = keySelector.getSigner();\r
-                    certChain = keySelector.getCertChain();\r
-                }\r
-                \r
-                return valid;\r
-            } catch (Exception e) {\r
-                String s = "error in marshalling and validating the signature";\r
-                LOG.log(POILogger.ERROR, s, e);\r
-                throw new EncryptedDocumentException(s, e);\r
-            }\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * Constructor initializes xml signature environment, if it hasn't been initialized before\r
-     */\r
-    public SignatureInfo() {\r
-        initXmlProvider();        \r
-    }\r
-    \r
-    /**\r
-     * @return the signature config\r
-     */\r
-    public SignatureConfig getSignatureConfig() {\r
-        return signatureConfig;\r
-    }\r
-\r
-    /**\r
-     * @param signatureConfig the signature config, needs to be set before a SignatureInfo object is used\r
-     */\r
-    public void setSignatureConfig(SignatureConfig signatureConfig) {\r
-        this.signatureConfig = signatureConfig;\r
-    }\r
-\r
-    /**\r
-     * @return true, if first signature part is valid\r
-     */\r
-    public boolean verifySignature() {\r
-        // http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html\r
-        for (SignaturePart sp : getSignatureParts()){\r
-            // only validate first part\r
-            return sp.validate();\r
-        }\r
-        return false;\r
-    }\r
-\r
-    /**\r
-     * add the xml signature to the document\r
-     *\r
-     * @throws XMLSignatureException\r
-     * @throws MarshalException\r
-     */\r
-    public void confirmSignature() throws XMLSignatureException, MarshalException {\r
-        Document document = DocumentHelper.createDocument();\r
-        \r
-        // operate\r
-        DigestInfo digestInfo = preSign(document, null);\r
-\r
-        // setup: key material, signature value\r
-        byte[] signatureValue = signDigest(digestInfo.digestValue);\r
-        \r
-        // operate: postSign\r
-        postSign(document, signatureValue);\r
-    }\r
-\r
-    /**\r
-     * Sign (encrypt) the digest with the private key.\r
-     * Currently only rsa is supported.\r
-     *\r
-     * @param digest the hashed input\r
-     * @return the encrypted hash\r
-     */\r
-    public byte[] signDigest(byte digest[]) {\r
-        Cipher cipher = CryptoFunctions.getCipher(signatureConfig.getKey(), CipherAlgorithm.rsa\r
-            , ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");\r
-            \r
-        try {\r
-            ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();\r
-            digestInfoValueBuf.write(signatureConfig.getHashMagic());\r
-            digestInfoValueBuf.write(digest);\r
-            byte[] digestInfoValue = digestInfoValueBuf.toByteArray();\r
-            byte[] signatureValue = cipher.doFinal(digestInfoValue);\r
-            return signatureValue;\r
-        } catch (Exception e) {\r
-            throw new EncryptedDocumentException(e);\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * @return a signature part for each signature document.\r
-     * the parts can be validated independently.\r
-     */\r
-    public Iterable<SignaturePart> getSignatureParts() {\r
-        signatureConfig.init(true);\r
-        return new Iterable<SignaturePart>() {\r
-            public Iterator<SignaturePart> iterator() {\r
-                return new Iterator<SignaturePart>() {\r
-                    OPCPackage pkg = signatureConfig.getOpcPackage();\r
-                    Iterator<PackageRelationship> sigOrigRels = \r
-                        pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();\r
-                    Iterator<PackageRelationship> sigRels = null;\r
-                    PackagePart sigPart = null;\r
-                    \r
-                    public boolean hasNext() {\r
-                        while (sigRels == null || !sigRels.hasNext()) {\r
-                            if (!sigOrigRels.hasNext()) return false;\r
-                            sigPart = pkg.getPart(sigOrigRels.next());\r
-                            LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);\r
-                            try {\r
-                                sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();\r
-                            } catch (InvalidFormatException e) {\r
-                                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
-                            }\r
-                        }\r
-                        return true;\r
-                    }\r
-                    \r
-                    public SignaturePart next() {\r
-                        PackagePart sigRelPart = null;\r
-                        do {\r
-                            try {\r
-                                if (!hasNext()) throw new NoSuchElementException();\r
-                                sigRelPart = sigPart.getRelatedPart(sigRels.next()); \r
-                                LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);\r
-                            } catch (InvalidFormatException e) {\r
-                                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
-                            }\r
-                        } while (sigPart == null);\r
-                        return new SignaturePart(sigRelPart);\r
-                    }\r
-                    \r
-                    public void remove() {\r
-                        throw new UnsupportedOperationException();\r
-                    }\r
-                };\r
-            }\r
-        };\r
-    }\r
-    \r
-    /**\r
-     * Initialize the xml signing environment and the bouncycastle provider \r
-     */\r
-    protected static synchronized void initXmlProvider() {\r
-        if (isInitialized) return;\r
-        isInitialized = true;\r
-        \r
-        try {\r
-            Init.init();\r
-            RelationshipTransformService.registerDsigProvider();\r
-            CryptoFunctions.registerBouncyCastle();\r
-        } catch (Exception e) {\r
-            throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);\r
-        }\r
-    }\r
-    \r
-    /**\r
-     * Helper method for adding informations before the signing.\r
-     * Normally {@link #confirmSignature()} is sufficient to be used.\r
-     */\r
-    @SuppressWarnings("unchecked")\r
-    public DigestInfo preSign(Document document, List<DigestInfo> digestInfos)\r
-    throws XMLSignatureException, MarshalException {\r
-        signatureConfig.init(false);\r
-        \r
-        // it's necessary to explicitly set the mdssi namespace, but the sign() method has no\r
-        // normal way to interfere with, so we need to add the namespace under the hand ...\r
-        EventTarget target = (EventTarget)document;\r
-        EventListener creationListener = signatureConfig.getSignatureMarshalListener();\r
-        if (creationListener != null) {\r
-            if (creationListener instanceof SignatureMarshalListener) {\r
-                ((SignatureMarshalListener)creationListener).setEventTarget(target);\r
-            }\r
-            SignatureMarshalListener.setListener(target, creationListener, true);\r
-        }\r
-        \r
-        /*\r
-         * Signature context construction.\r
-         */\r
-        XMLSignContext xmlSignContext = new DOMSignContext(signatureConfig.getKey(), document);\r
-        URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer();\r
-        if (null != uriDereferencer) {\r
-            xmlSignContext.setURIDereferencer(uriDereferencer);\r
-        }\r
-\r
-        for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {\r
-            xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue());\r
-        }\r
-        xmlSignContext.setDefaultNamespacePrefix("");\r
-        // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS));\r
-        \r
-        brokenJvmWorkaround(xmlSignContext);\r
-        \r
-        XMLSignatureFactory signatureFactory = signatureConfig.getSignatureFactory();\r
-\r
-        /*\r
-         * Add ds:References that come from signing client local files.\r
-         */\r
-        List<Reference> references = new ArrayList<Reference>();\r
-        for (DigestInfo digestInfo : safe(digestInfos)) {\r
-            byte[] documentDigestValue = digestInfo.digestValue;\r
-\r
-            String uri = new File(digestInfo.description).getName();\r
-            Reference reference = SignatureFacet.newReference\r
-                (uri, null, null, null, documentDigestValue, signatureConfig);\r
-            references.add(reference);\r
-        }\r
-\r
-        /*\r
-         * Invoke the signature facets.\r
-         */\r
-        List<XMLObject> objects = new ArrayList<XMLObject>();\r
-        for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {\r
-            LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());\r
-            signatureFacet.preSign(document, references, objects);\r
-        }\r
-\r
-        /*\r
-         * ds:SignedInfo\r
-         */\r
-        SignedInfo signedInfo;\r
-        try {\r
-            SignatureMethod signatureMethod = signatureFactory.newSignatureMethod\r
-                (signatureConfig.getSignatureMethodUri(), null);\r
-            CanonicalizationMethod canonicalizationMethod = signatureFactory\r
-                .newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),\r
-                (C14NMethodParameterSpec) null);\r
-            signedInfo = signatureFactory.newSignedInfo(\r
-                canonicalizationMethod, signatureMethod, references);\r
-        } catch (GeneralSecurityException e) {\r
-            throw new XMLSignatureException(e);\r
-        }\r
-\r
-        /*\r
-         * JSR105 ds:Signature creation\r
-         */\r
-        String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value";\r
-        javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory\r
-            .newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(),\r
-            signatureValueId);\r
-\r
-        /*\r
-         * ds:Signature Marshalling.\r
-         */\r
-        xmlSignature.sign(xmlSignContext);\r
-\r
-        /*\r
-         * Completion of undigested ds:References in the ds:Manifests.\r
-         */\r
-        for (XMLObject object : objects) {\r
-            LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());\r
-            List<XMLStructure> objectContentList = object.getContent();\r
-            for (XMLStructure objectContent : objectContentList) {\r
-                LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());\r
-                if (!(objectContent instanceof Manifest)) continue;\r
-                Manifest manifest = (Manifest) objectContent;\r
-                List<Reference> manifestReferences = manifest.getReferences();\r
-                for (Reference manifestReference : manifestReferences) {\r
-                    if (manifestReference.getDigestValue() != null) continue;\r
-\r
-                    DOMReference manifestDOMReference = (DOMReference)manifestReference;\r
-                    manifestDOMReference.digest(xmlSignContext);\r
-                }\r
-            }\r
-        }\r
-\r
-        /*\r
-         * Completion of undigested ds:References.\r
-         */\r
-        List<Reference> signedInfoReferences = signedInfo.getReferences();\r
-        for (Reference signedInfoReference : signedInfoReferences) {\r
-            DOMReference domReference = (DOMReference)signedInfoReference;\r
-\r
-            // ds:Reference with external digest value\r
-            if (domReference.getDigestValue() != null) continue;\r
-            \r
-            domReference.digest(xmlSignContext);\r
-        }\r
-\r
-        /*\r
-         * Calculation of XML signature digest value.\r
-         */\r
-        DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo;\r
-        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();\r
-        domSignedInfo.canonicalize(xmlSignContext, dataStream);\r
-        byte[] octets = dataStream.toByteArray();\r
-\r
-        /*\r
-         * TODO: we could be using DigestOutputStream here to optimize memory\r
-         * usage.\r
-         */\r
-\r
-        MessageDigest md = CryptoFunctions.getMessageDigest(signatureConfig.getDigestAlgo());\r
-        byte[] digestValue = md.digest(octets);\r
-        \r
-        \r
-        String description = signatureConfig.getSignatureDescription();\r
-        return new DigestInfo(digestValue, signatureConfig.getDigestAlgo(), description);\r
-    }\r
-\r
-    /**\r
-     * Helper method for adding informations after the signing.\r
-     * Normally {@link #confirmSignature()} is sufficient to be used.\r
-     */\r
-    public void postSign(Document document, byte[] signatureValue)\r
-    throws MarshalException {\r
-        LOG.log(POILogger.DEBUG, "postSign");\r
-\r
-        /*\r
-         * Check ds:Signature node.\r
-         */\r
-        String signatureId = signatureConfig.getPackageSignatureId();\r
-        if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {\r
-            throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);\r
-        }\r
-\r
-        /*\r
-         * Insert signature value into the ds:SignatureValue element\r
-         */\r
-        NodeList sigValNl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue");\r
-        if (sigValNl.getLength() != 1) {\r
-            throw new RuntimeException("preSign has to be called before postSign");\r
-        }\r
-        sigValNl.item(0).setTextContent(Base64.encode(signatureValue));\r
-\r
-        /*\r
-         * Allow signature facets to inject their own stuff.\r
-         */\r
-        for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {\r
-            signatureFacet.postSign(document);\r
-        }\r
-\r
-        writeDocument(document);\r
-    }\r
-\r
-    /**\r
-     * Write XML signature into the OPC package\r
-     *\r
-     * @param document the xml signature document\r
-     * @throws MarshalException\r
-     */\r
-    protected void writeDocument(Document document) throws MarshalException {\r
-        XmlOptions xo = new XmlOptions();\r
-        Map<String,String> namespaceMap = new HashMap<String,String>();\r
-        for(Map.Entry<String,String> entry : signatureConfig.getNamespacePrefixes().entrySet()){\r
-            namespaceMap.put(entry.getValue(), entry.getKey());\r
-        }        \r
-        xo.setSaveSuggestedPrefixes(namespaceMap);\r
-        xo.setUseDefaultNamespace();\r
-\r
-        LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");\r
-\r
-        /*\r
-         * Copy the original OOXML content to the signed OOXML package. During\r
-         * copying some files need to changed.\r
-         */\r
-        OPCPackage pkg = signatureConfig.getOpcPackage();\r
-\r
-        PackagePartName sigPartName, sigsPartName;\r
-        try {\r
-            // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>\r
-            sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");\r
-            // <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>\r
-            sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");\r
-        } catch (InvalidFormatException e) {\r
-            throw new MarshalException(e);\r
-        }\r
-        \r
-        PackagePart sigPart = pkg.getPart(sigPartName);\r
-        if (sigPart == null) {\r
-            sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART);\r
-        }\r
-        \r
-        try {\r
-            OutputStream os = sigPart.getOutputStream();\r
-            SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);\r
-            sigDoc.save(os, xo);\r
-            os.close();\r
-        } catch (Exception e) {\r
-            throw new MarshalException("Unable to write signature document", e);\r
-        }\r
-        \r
-        PackagePart sigsPart = pkg.getPart(sigsPartName);\r
-        if (sigsPart == null) {\r
-            // touch empty marker file\r
-            sigsPart = pkg.createPart(sigsPartName, ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART);\r
-        }\r
-        \r
-        PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);\r
-        for (PackageRelationship pr : relCol) {\r
-            pkg.removeRelationship(pr.getId());\r
-        }\r
-        pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);\r
-        \r
-        sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);\r
-    }\r
-    \r
-    /**\r
-     * Helper method for null lists, which are converted to empty lists\r
-     *\r
-     * @param other the reference to wrap, if null\r
-     * @return if other is null, an empty lists is returned, otherwise other is returned\r
-     */\r
-    private static <T> List<T> safe(List<T> other) {\r
-        List<T> emptyList = Collections.emptyList();\r
-        return other == null ? emptyList : other;\r
-    }\r
-\r
-    private void brokenJvmWorkaround(XMLSignContext context) {\r
-        // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012\r
-        Provider bcProv = Security.getProvider("BC");\r
-        if (bcProv != null) {\r
-            context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv);\r
-        }        \r
-    }\r
-\r
-    private void brokenJvmWorkaround(XMLValidateContext context) {\r
-        // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012\r
-        Provider bcProv = Security.getProvider("BC");\r
-        if (bcProv != null) {\r
-            context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv);\r
-        }        \r
-    }\r
-}\r
+ */
+public class SignatureInfo implements SignatureConfigurable {
+
+    private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
+    private static boolean isInitialized = false;
+    
+    private SignatureConfig signatureConfig;
+
+    public class SignaturePart {
+        private final PackagePart signaturePart;
+        private X509Certificate signer;
+        private List<X509Certificate> certChain;
+        
+        private SignaturePart(PackagePart signaturePart) {
+            this.signaturePart = signaturePart;
+        }
+        
+        /**
+         * @return the package part containing the signature
+         */
+        public PackagePart getPackagePart() {
+            return signaturePart;
+        }
+        
+        /**
+         * @return the signer certificate
+         */
+        public X509Certificate getSigner() {
+            return signer;
+        }
+        
+        /**
+         * @return the certificate chain of the signer
+         */
+        public List<X509Certificate> getCertChain() {
+            return certChain;
+        }
+        
+        /**
+         * Helper method for examining the xml signature
+         *
+         * @return the xml signature document
+         * @throws IOException if the xml signature doesn't exist or can't be read
+         * @throws XmlException if the xml signature is malformed
+         */
+        public SignatureDocument getSignatureDocument() throws IOException, XmlException {
+            // TODO: check for XXE
+            return SignatureDocument.Factory.parse(signaturePart.getInputStream(), DEFAULT_XML_OPTIONS);
+        }
+        
+        /**
+         * @return true, when the xml signature is valid, false otherwise
+         * 
+         * @throws EncryptedDocumentException if the signature can't be extracted or if its malformed
+         */
+        @SuppressWarnings("unchecked")
+        public boolean validate() {
+            KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
+            try {
+                Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());
+                XPath xpath = XPathFactory.newInstance().newXPath();
+                NodeList nl = (NodeList)xpath.compile("//*[@Id]").evaluate(doc, XPathConstants.NODESET);
+                for (int i=0; i<nl.getLength(); i++) {
+                    ((Element)nl.item(i)).setIdAttribute("Id", true);
+                }
+                
+                DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
+                domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);
+                domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());
+                brokenJvmWorkaround(domValidateContext);
+    
+                XMLSignatureFactory xmlSignatureFactory = signatureConfig.getSignatureFactory();
+                XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);
+                
+                // TODO: replace with property when xml-sec patch is applied
+                for (Reference ref : (List<Reference>)xmlSignature.getSignedInfo().getReferences()) {
+                    SignatureFacet.brokenJvmWorkaround(ref);
+                }
+                for (XMLObject xo : (List<XMLObject>)xmlSignature.getObjects()) {
+                    for (XMLStructure xs : (List<XMLStructure>)xo.getContent()) {
+                        if (xs instanceof Manifest) {
+                           for (Reference ref : (List<Reference>)((Manifest)xs).getReferences()) {
+                               SignatureFacet.brokenJvmWorkaround(ref);
+                           }
+                        }
+                    }
+                }
+                
+                boolean valid = xmlSignature.validate(domValidateContext);
+
+                if (valid) {
+                    signer = keySelector.getSigner();
+                    certChain = keySelector.getCertChain();
+                }
+                
+                return valid;
+            } catch (Exception e) {
+                String s = "error in marshalling and validating the signature";
+                LOG.log(POILogger.ERROR, s, e);
+                throw new EncryptedDocumentException(s, e);
+            }
+        }
+    }
+    
+    /**
+     * Constructor initializes xml signature environment, if it hasn't been initialized before
+     */
+    public SignatureInfo() {
+        initXmlProvider();        
+    }
+    
+    /**
+     * @return the signature config
+     */
+    public SignatureConfig getSignatureConfig() {
+        return signatureConfig;
+    }
+
+    /**
+     * @param signatureConfig the signature config, needs to be set before a SignatureInfo object is used
+     */
+    public void setSignatureConfig(SignatureConfig signatureConfig) {
+        this.signatureConfig = signatureConfig;
+    }
+
+    /**
+     * @return true, if first signature part is valid
+     */
+    public boolean verifySignature() {
+        // http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
+        for (SignaturePart sp : getSignatureParts()){
+            // only validate first part
+            return sp.validate();
+        }
+        return false;
+    }
+
+    /**
+     * add the xml signature to the document
+     *
+     * @throws XMLSignatureException
+     * @throws MarshalException
+     */
+    public void confirmSignature() throws XMLSignatureException, MarshalException {
+        Document document = DocumentHelper.createDocument();
+        
+        // operate
+        DigestInfo digestInfo = preSign(document, null);
+
+        // setup: key material, signature value
+        byte[] signatureValue = signDigest(digestInfo.digestValue);
+        
+        // operate: postSign
+        postSign(document, signatureValue);
+    }
+
+    /**
+     * Sign (encrypt) the digest with the private key.
+     * Currently only rsa is supported.
+     *
+     * @param digest the hashed input
+     * @return the encrypted hash
+     */
+    public byte[] signDigest(byte digest[]) {
+        Cipher cipher = CryptoFunctions.getCipher(signatureConfig.getKey(), CipherAlgorithm.rsa
+            , ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");
+            
+        try {
+            ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
+            digestInfoValueBuf.write(signatureConfig.getHashMagic());
+            digestInfoValueBuf.write(digest);
+            byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
+            byte[] signatureValue = cipher.doFinal(digestInfoValue);
+            return signatureValue;
+        } catch (Exception e) {
+            throw new EncryptedDocumentException(e);
+        }
+    }
+    
+    /**
+     * @return a signature part for each signature document.
+     * the parts can be validated independently.
+     */
+    public Iterable<SignaturePart> getSignatureParts() {
+        signatureConfig.init(true);
+        return new Iterable<SignaturePart>() {
+            public Iterator<SignaturePart> iterator() {
+                return new Iterator<SignaturePart>() {
+                    OPCPackage pkg = signatureConfig.getOpcPackage();
+                    Iterator<PackageRelationship> sigOrigRels = 
+                        pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();
+                    Iterator<PackageRelationship> sigRels = null;
+                    PackagePart sigPart = null;
+                    
+                    public boolean hasNext() {
+                        while (sigRels == null || !sigRels.hasNext()) {
+                            if (!sigOrigRels.hasNext()) return false;
+                            sigPart = pkg.getPart(sigOrigRels.next());
+                            LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);
+                            try {
+                                sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();
+                            } catch (InvalidFormatException e) {
+                                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
+                            }
+                        }
+                        return true;
+                    }
+                    
+                    public SignaturePart next() {
+                        PackagePart sigRelPart = null;
+                        do {
+                            try {
+                                if (!hasNext()) throw new NoSuchElementException();
+                                sigRelPart = sigPart.getRelatedPart(sigRels.next()); 
+                                LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);
+                            } catch (InvalidFormatException e) {
+                                LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
+                            }
+                        } while (sigPart == null);
+                        return new SignaturePart(sigRelPart);
+                    }
+                    
+                    public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+    
+    /**
+     * Initialize the xml signing environment and the bouncycastle provider 
+     */
+    protected static synchronized void initXmlProvider() {
+        if (isInitialized) return;
+        isInitialized = true;
+        
+        try {
+            Init.init();
+            RelationshipTransformService.registerDsigProvider();
+            CryptoFunctions.registerBouncyCastle();
+        } catch (Exception e) {
+            throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
+        }
+    }
+    
+    /**
+     * Helper method for adding informations before the signing.
+     * Normally {@link #confirmSignature()} is sufficient to be used.
+     */
+    @SuppressWarnings("unchecked")
+    public DigestInfo preSign(Document document, List<DigestInfo> digestInfos)
+    throws XMLSignatureException, MarshalException {
+        signatureConfig.init(false);
+        
+        // it's necessary to explicitly set the mdssi namespace, but the sign() method has no
+        // normal way to interfere with, so we need to add the namespace under the hand ...
+        EventTarget target = (EventTarget)document;
+        EventListener creationListener = signatureConfig.getSignatureMarshalListener();
+        if (creationListener != null) {
+            if (creationListener instanceof SignatureMarshalListener) {
+                ((SignatureMarshalListener)creationListener).setEventTarget(target);
+            }
+            SignatureMarshalListener.setListener(target, creationListener, true);
+        }
+        
+        /*
+         * Signature context construction.
+         */
+        XMLSignContext xmlSignContext = new DOMSignContext(signatureConfig.getKey(), document);
+        URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer();
+        if (null != uriDereferencer) {
+            xmlSignContext.setURIDereferencer(uriDereferencer);
+        }
+
+        for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
+            xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue());
+        }
+        xmlSignContext.setDefaultNamespacePrefix("");
+        // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS));
+        
+        brokenJvmWorkaround(xmlSignContext);
+        
+        XMLSignatureFactory signatureFactory = signatureConfig.getSignatureFactory();
+
+        /*
+         * Add ds:References that come from signing client local files.
+         */
+        List<Reference> references = new ArrayList<Reference>();
+        for (DigestInfo digestInfo : safe(digestInfos)) {
+            byte[] documentDigestValue = digestInfo.digestValue;
+
+            String uri = new File(digestInfo.description).getName();
+            Reference reference = SignatureFacet.newReference
+                (uri, null, null, null, documentDigestValue, signatureConfig);
+            references.add(reference);
+        }
+
+        /*
+         * Invoke the signature facets.
+         */
+        List<XMLObject> objects = new ArrayList<XMLObject>();
+        for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
+            LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());
+            signatureFacet.preSign(document, references, objects);
+        }
+
+        /*
+         * ds:SignedInfo
+         */
+        SignedInfo signedInfo;
+        try {
+            SignatureMethod signatureMethod = signatureFactory.newSignatureMethod
+                (signatureConfig.getSignatureMethodUri(), null);
+            CanonicalizationMethod canonicalizationMethod = signatureFactory
+                .newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),
+                (C14NMethodParameterSpec) null);
+            signedInfo = signatureFactory.newSignedInfo(
+                canonicalizationMethod, signatureMethod, references);
+        } catch (GeneralSecurityException e) {
+            throw new XMLSignatureException(e);
+        }
+
+        /*
+         * JSR105 ds:Signature creation
+         */
+        String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value";
+        javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
+            .newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(),
+            signatureValueId);
+
+        /*
+         * ds:Signature Marshalling.
+         */
+        xmlSignature.sign(xmlSignContext);
+
+        /*
+         * Completion of undigested ds:References in the ds:Manifests.
+         */
+        for (XMLObject object : objects) {
+            LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());
+            List<XMLStructure> objectContentList = object.getContent();
+            for (XMLStructure objectContent : objectContentList) {
+                LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());
+                if (!(objectContent instanceof Manifest)) continue;
+                Manifest manifest = (Manifest) objectContent;
+                List<Reference> manifestReferences = manifest.getReferences();
+                for (Reference manifestReference : manifestReferences) {
+                    if (manifestReference.getDigestValue() != null) continue;
+
+                    DOMReference manifestDOMReference = (DOMReference)manifestReference;
+                    manifestDOMReference.digest(xmlSignContext);
+                }
+            }
+        }
+
+        /*
+         * Completion of undigested ds:References.
+         */
+        List<Reference> signedInfoReferences = signedInfo.getReferences();
+        for (Reference signedInfoReference : signedInfoReferences) {
+            DOMReference domReference = (DOMReference)signedInfoReference;
+
+            // ds:Reference with external digest value
+            if (domReference.getDigestValue() != null) continue;
+            
+            domReference.digest(xmlSignContext);
+        }
+
+        /*
+         * Calculation of XML signature digest value.
+         */
+        DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo;
+        ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
+        domSignedInfo.canonicalize(xmlSignContext, dataStream);
+        byte[] octets = dataStream.toByteArray();
+
+        /*
+         * TODO: we could be using DigestOutputStream here to optimize memory
+         * usage.
+         */
+
+        MessageDigest md = CryptoFunctions.getMessageDigest(signatureConfig.getDigestAlgo());
+        byte[] digestValue = md.digest(octets);
+        
+        
+        String description = signatureConfig.getSignatureDescription();
+        return new DigestInfo(digestValue, signatureConfig.getDigestAlgo(), description);
+    }
+
+    /**
+     * Helper method for adding informations after the signing.
+     * Normally {@link #confirmSignature()} is sufficient to be used.
+     */
+    public void postSign(Document document, byte[] signatureValue)
+    throws MarshalException {
+        LOG.log(POILogger.DEBUG, "postSign");
+
+        /*
+         * Check ds:Signature node.
+         */
+        String signatureId = signatureConfig.getPackageSignatureId();
+        if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {
+            throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
+        }
+
+        /*
+         * Insert signature value into the ds:SignatureValue element
+         */
+        NodeList sigValNl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue");
+        if (sigValNl.getLength() != 1) {
+            throw new RuntimeException("preSign has to be called before postSign");
+        }
+        sigValNl.item(0).setTextContent(Base64.encode(signatureValue));
+
+        /*
+         * Allow signature facets to inject their own stuff.
+         */
+        for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
+            signatureFacet.postSign(document);
+        }
+
+        writeDocument(document);
+    }
+
+    /**
+     * Write XML signature into the OPC package
+     *
+     * @param document the xml signature document
+     * @throws MarshalException
+     */
+    protected void writeDocument(Document document) throws MarshalException {
+        XmlOptions xo = new XmlOptions();
+        Map<String,String> namespaceMap = new HashMap<String,String>();
+        for(Map.Entry<String,String> entry : signatureConfig.getNamespacePrefixes().entrySet()){
+            namespaceMap.put(entry.getValue(), entry.getKey());
+        }        
+        xo.setSaveSuggestedPrefixes(namespaceMap);
+        xo.setUseDefaultNamespace();
+
+        LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");
+
+        /*
+         * Copy the original OOXML content to the signed OOXML package. During
+         * copying some files need to changed.
+         */
+        OPCPackage pkg = signatureConfig.getOpcPackage();
+
+        PackagePartName sigPartName, sigsPartName;
+        try {
+            // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
+            sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");
+            // <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>
+            sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");
+        } catch (InvalidFormatException e) {
+            throw new MarshalException(e);
+        }
+        
+        PackagePart sigPart = pkg.getPart(sigPartName);
+        if (sigPart == null) {
+            sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART);
+        }
+        
+        try {
+            OutputStream os = sigPart.getOutputStream();
+            SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);
+            sigDoc.save(os, xo);
+            os.close();
+        } catch (Exception e) {
+            throw new MarshalException("Unable to write signature document", e);
+        }
+        
+        PackagePart sigsPart = pkg.getPart(sigsPartName);
+        if (sigsPart == null) {
+            // touch empty marker file
+            sigsPart = pkg.createPart(sigsPartName, ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART);
+        }
+        
+        PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
+        for (PackageRelationship pr : relCol) {
+            pkg.removeRelationship(pr.getId());
+        }
+        pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
+        
+        sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);
+    }
+    
+    /**
+     * Helper method for null lists, which are converted to empty lists
+     *
+     * @param other the reference to wrap, if null
+     * @return if other is null, an empty lists is returned, otherwise other is returned
+     */
+    private static <T> List<T> safe(List<T> other) {
+        List<T> emptyList = Collections.emptyList();
+        return other == null ? emptyList : other;
+    }
+
+    private void brokenJvmWorkaround(XMLSignContext context) {
+        // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012
+        Provider bcProv = Security.getProvider("BC");
+        if (bcProv != null) {
+            context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv);
+        }        
+    }
+
+    private void brokenJvmWorkaround(XMLValidateContext context) {
+        // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012
+        Provider bcProv = Security.getProvider("BC");
+        if (bcProv != null) {
+            context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv);
+        }        
+    }
+}