merge trunk

git-svn-id: https://svn.apache.org/repos/asf/poi/branches/hemf@1849036 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.gradle b/build.gradle
index 4a0f9a6..63e1d34 100644
--- a/build.gradle
+++ b/build.gradle
@@ -91,7 +91,7 @@
     // See https://github.com/melix/japicmp-gradle-plugin 
     apply plugin: 'me.champeau.gradle.japicmp'
         
-    version = '4.0.1-SNAPSHOT'
+    version = '4.0.2-SNAPSHOT'
     ext {
         japicmpversion = '4.0.0'
     }
@@ -233,6 +233,11 @@
         compile 'org.bouncycastle:bcpkix-jdk15on:1.60'
         compile 'com.github.virtuald:curvesapi:1.05'
 
+        // compile only, don't add it to our dist as it blows up the size
+        compile 'org.apache.xmlgraphics:batik-all:1.10'
+        compile 'xml-apis:xml-apis-ext:1.3.04'
+        compile 'org.apache.xmlgraphics:xmlgraphics-commons:2.3'
+
         // for ooxml-lite, should we move this somewhere else?
         compile 'junit:junit:4.12'
 
diff --git a/build.xml b/build.xml
index ce3c83c..d7032dc 100644
--- a/build.xml
+++ b/build.xml
@@ -217,6 +217,14 @@
     <property name="dsig.sl4j-api.jar" location="${compile.lib}/slf4j-api-1.7.25.jar"/>
     <property name="dsig.sl4j-api.url" value="${repository.m2}/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar"/>
 
+    <!-- svg/batik libs - not part of the distribution -->
+    <property name="svg.batik-all.url" value="${repository.m2}/maven2/org/apache/xmlgraphics/batik-all/1.10/batik-all-1.10.jar"/>
+    <property name="svg.batik-all.jar" value="${compile.lib}/batik-all-1.10.jar"/>
+    <property name="svg.xml-apis-ext.url" value="${repository.m2}/maven2/xml-apis/xml-apis-ext/1.3.04/xml-apis-ext-1.3.04.jar"/>
+    <property name="svg.xml-apis-ext.jar" value="${compile.lib}/xml-apis-ext-1.3.04.jar"/>
+    <property name="svg.xmlgraphics-commons.url" value="${repository.m2}/maven2/org/apache/xmlgraphics/xmlgraphics-commons/2.3/xmlgraphics-commons-2.3.jar"/>
+    <property name="svg.xmlgraphics-commons.jar" value="${compile.lib}/xmlgraphics-commons-2.3.jar"/>
+
     <!-- jars in the ooxml-lib directory, see the fetch-ooxml-jars target-->
     <property name="ooxml.curvesapi.jar" location="${ooxml.lib}/curvesapi-1.05.jar"/>
     <property name="ooxml.curvesapi.url"
@@ -395,6 +403,12 @@
         <pathelement location="${scratchpad.output.dir}" unless:true="${scratchpad.ignore}"/>
     </path>
 
+    <path id="batik.classpath">
+        <pathelement location="${svg.batik-all.jar}"/>
+        <pathelement location="${svg.xml-apis-ext.jar}"/>
+        <pathelement location="${svg.xmlgraphics-commons.jar}"/>
+    </path>
+
     <path id="ooxml-lite.classpath">
         <path refid="ooxml.base.classpath"/>
         <!-- instead of ooxml-xsds.jar use the filtered classes-->
@@ -402,11 +416,13 @@
         <pathelement location="${ooxml.output.dir}"/>
         <pathelement location="${ooxml.output.test.dir}"/>
         <pathelement location="${main.output.test.dir}"/>
+        <path refid="batik.classpath"/>
     </path>
 
     <path id="ooxml.classpath">
         <pathelement location="${ooxml.xsds.jar}"/>
         <path refid="ooxml.base.classpath"/>
+        <path refid="batik.classpath"/>
     </path>
 
     <path id="ooxml.lite.verify.classpath">
@@ -777,6 +793,9 @@
                     <available file="${ooxml.test.reflections.jar}"/>
                     <available file="${ooxml.test.guava.jar}"/>
                     <available file="${ooxml.test.javassist.jar}"/>
+                    <available file="${svg.xml-apis-ext.jar}"/>
+                    <available file="${svg.batik-all.jar}"/>
+                    <available file="${svg.xmlgraphics-commons.jar}"/>
                 </and>
                 <isset property="disconnected"/>
             </or>
@@ -791,6 +810,9 @@
         <downloadfile src="${ooxml.test.reflections.url}" dest="${ooxml.test.reflections.jar}"/>
         <downloadfile src="${ooxml.test.guava.url}" dest="${ooxml.test.guava.jar}"/>
         <downloadfile src="${ooxml.test.javassist.url}" dest="${ooxml.test.javassist.jar}"/>
+        <downloadfile src="${svg.batik-all.url}" dest="${svg.batik-all.jar}"/>
+        <downloadfile src="${svg.xml-apis-ext.url}" dest="${svg.xml-apis-ext.jar}"/>
+        <downloadfile src="${svg.xmlgraphics-commons.url}" dest="${svg.xmlgraphics-commons.jar}"/>
     </target>
     <target name="check-svn-jars">
         <condition property="svn.jars.present">
@@ -932,6 +954,7 @@
             </xmlbean>
 
             <local name="loaderMethod"/>
+            <!-- the space between "public  static" is on purpose to prevent double execution -->
             <property name="loaderMethod"><![CDATA[
         private static java.lang.ref.SoftReference<org.apache.xmlbeans.SchemaTypeLoader> typeLoader;
 
@@ -944,7 +967,7 @@
             return stl;
         }
 
-        public static \2 newInstance\(\) \{]]></property>
+        public  static \2 newInstance\(\) \{]]></property>
 
             <replaceregexp byline="true" match="(\s*)public static ([^ ]+) newInstance\(\) \{" replace="${loaderMethod}">
                 <fileset dir="${xmlbean.sources.dir}" includes="**/*.java" excludes="**/impl/**"/>
diff --git a/jenkins/create_jobs.groovy b/jenkins/create_jobs.groovy
index 60795db..3a75bc0 100644
--- a/jenkins/create_jobs.groovy
+++ b/jenkins/create_jobs.groovy
@@ -70,9 +70,7 @@
                        '-Djava.locale.providers=JRE,CLDR'],
           skipcigame: true
         ],
-        [ name: 'POI-DSL-IBM-JDK', jdk: 'IBMJDK', trigger: triggerSundays,
-          // some OOXML tests fail with strange XML parsing errors and missing JCE unlimited strength requirements
-          disabled: true, skipcigame: true
+        [ name: 'POI-DSL-IBM-JDK', jdk: 'IBMJDK', trigger: triggerSundays, skipcigame: true
         ],
         [ name: 'POI-DSL-old-Xerces', trigger: triggerSundays,
           shell: "test -f ${xercesLib} || wget -O ${xercesLib} ${xercesUrl}\n",
diff --git a/sonar/ooxml/pom.xml b/sonar/ooxml/pom.xml
index dc4ffc8..6a6232a 100644
--- a/sonar/ooxml/pom.xml
+++ b/sonar/ooxml/pom.xml
@@ -185,5 +185,12 @@
             <version>1.19</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <!-- don't add it to the distribution -->
+            <groupId>org.apache.xmlgraphics</groupId>
+            <artifactId>batik-all</artifactId>
+            <version>1.10</version>
+        </dependency>
     </dependencies>
 </project>
diff --git a/sonar/pom.xml b/sonar/pom.xml
index 3fcaf93..c008c3a 100644
--- a/sonar/pom.xml
+++ b/sonar/pom.xml
@@ -268,9 +268,7 @@
                                 </goals>
                                 <configuration>
                                     <target>
-                                        <property name="xmlbean.sources.dir"
-                                                  location="${basedir}/target/generated-sources/xmlbeans"/>
-
+                                        <!-- the space between "public  static" is on purpose to prevent double execution -->
                                         <property name="loaderMethod"><![CDATA[
         private static java.lang.ref.SoftReference<org.apache.xmlbeans.SchemaTypeLoader> typeLoader;
 
@@ -283,25 +281,26 @@
             return stl;
         }
 
-        public static \2 newInstance\(\) \{]]></property>
+        public  static \2 newInstance\(\) \{]]></property>
+
+                                        <fileset id="xsrc" dir="${basedir}/target/generated-sources/xmlbeans" includes="**/*.java" excludes="**/impl/**"/>
 
                                         <replaceregexp byline="true"
                                                        match="(\s*)public static ([^ ]+) newInstance\(\) \{"
                                                        replace="${loaderMethod}">
-                                            <fileset dir="${xmlbean.sources.dir}" includes="**/*.java"
-                                                     excludes="**/impl/**"/>
+                                            <fileset refid="xsrc"/>
                                         </replaceregexp>
 
-                                        <replace dir="${xmlbean.sources.dir}" includes="**/*.java"
-                                                 excludes="**/impl/**">
+                                        <replace>
+                                            <fileset refid="xsrc"/>
                                             <replacetoken>org.apache.xmlbeans.XmlBeans.getContextTypeLoader
                                             </replacetoken>
                                             <replacevalue>getTypeLoader</replacevalue>
                                         </replace>
 
                                         <!-- remove deprecated warnings, as we prefer the array methods - see #56854 -->
-                                        <replace dir="${xmlbean.sources.dir}" includes="**/*.java"
-                                                 excludes="**/impl/**">
+                                        <replace>
+                                            <fileset refid="xsrc"/>
                                             <replacetoken><![CDATA[     * @deprecated
 ]]></replacetoken>
                                         </replace>
diff --git a/src/integrationtest/org/apache/poi/TestAllFiles.java b/src/integrationtest/org/apache/poi/TestAllFiles.java
index 40f19f9..a166eb5 100644
--- a/src/integrationtest/org/apache/poi/TestAllFiles.java
+++ b/src/integrationtest/org/apache/poi/TestAllFiles.java
@@ -211,7 +211,13 @@
         HANDLERS.put("spreadsheet/BigSSTRecord2CR7", new NullFileHandler());
         HANDLERS.put("spreadsheet/BigSSTRecordCR", new NullFileHandler());
         HANDLERS.put("spreadsheet/test_properties1", new NullFileHandler());
-        
+
+        // keystore files
+        HANDLERS.put(".pfx", new NullFileHandler());
+        HANDLERS.put(".pem", new NullFileHandler());
+        HANDLERS.put(".jks", new NullFileHandler());
+        HANDLERS.put(".pkcs12", new NullFileHandler());
+
         Map<String,String> passmap = new HashMap<>();
         passmap.put("slideshow/Password_Protected-hello.ppt", "hello");
         passmap.put("slideshow/Password_Protected-56-hello.ppt", "hello");
diff --git a/src/java/org/apache/poi/sl/usermodel/PictureData.java b/src/java/org/apache/poi/sl/usermodel/PictureData.java
index 60e6266..0285dd5 100644
--- a/src/java/org/apache/poi/sl/usermodel/PictureData.java
+++ b/src/java/org/apache/poi/sl/usermodel/PictureData.java
@@ -46,7 +46,10 @@
         /** WordPerfect graphics (.wpg) */
         WPG(-1,12,"image/x-wpg",".wpg"),
         /** Microsoft Windows Media Photo image (.wdp) */
-        WDP(-1,13,"image/vnd.ms-photo",".wdp");
+        WDP(-1,13,"image/vnd.ms-photo",".wdp"),
+        /** Scalable vector graphics (.svg) - supported by Office 2016 and higher */
+        SVG(-1, -1, "image/svg+xml", ".svg")
+        ;
         
         public final int nativeId, ooxmlId;
         public final String contentType,extension;
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java
new file mode 100644
index 0000000..707233b
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java
@@ -0,0 +1,62 @@
+/* ====================================================================
+   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.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.dsig;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.ooxml.POIXMLRelation;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+
+public class DSigRelation extends POIXMLRelation {
+    /**
+     * A map to lookup POIXMLRelation by its relation type
+     */
+    private static final Map<String, DSigRelation> _table = new HashMap<>();
+
+    public static final DSigRelation ORIGIN_SIGS = new DSigRelation(
+            ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART,
+            PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN,
+            "/_xmlsignatures/origin.sigs", null
+    );
+
+    public static final DSigRelation SIG = new DSigRelation(
+        ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART,
+            PackageRelationshipTypes.DIGITAL_SIGNATURE,
+        "/_xmlsignatures/sig#.xml", null
+    );
+
+    private DSigRelation(String type, String rel, String defaultName, Class<? extends POIXMLDocumentPart> cls) {
+        super(type, rel, defaultName, cls);
+        _table.put(rel, this);
+    }
+
+    /**
+     * Get POIXMLRelation by relation type
+     *
+     * @param rel relation type, for example,
+     *            <code>http://schemas.openxmlformats.org/officeDocument/2006/relationships/image</code>
+     * @return registered POIXMLRelation or null if not found
+     */
+    public static DSigRelation getInstance(String rel) {
+        return _table.get(rel);
+    }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
index 8e276f8..71a42ef 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
@@ -174,6 +174,13 @@
      */
     private boolean updateConfigOnValidate = false;
 
+    /**
+     * if true, the signature is added to the existing signatures
+     *
+     * @since POI 4.0.2
+     */
+    private boolean allowMultipleSignatures = false;
+
 
     /**
      * Inits and checks the config object.
@@ -1008,4 +1015,25 @@
     public void setUpdateConfigOnValidate(boolean updateConfigOnValidate) {
         this.updateConfigOnValidate = updateConfigOnValidate;
     }
+
+    /**
+     * @return true, if multiple signatures can be attached
+     *
+     * @since POI 4.0.2
+     */
+    public boolean isAllowMultipleSignatures() {
+        return allowMultipleSignatures;
+    }
+
+    /**
+     * Activate multiple signatures
+     *
+     * @param allowMultipleSignatures if true, the signature will be added,
+     *          otherwise all existing signatures will be replaced by the current
+     *
+     * @since POI 4.0.2
+     */
+    public void setAllowMultipleSignatures(boolean allowMultipleSignatures) {
+        this.allowMultipleSignatures = allowMultipleSignatures;
+    }
 }
\ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
index 20c347d..1cb6e3c 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
@@ -59,7 +59,6 @@
 import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData;
 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;
@@ -377,9 +376,8 @@
             xmlSignContext.setURIDereferencer(uriDereferencer);
         }
 
-        for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
-            xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue());
-        }
+        signatureConfig.getNamespacePrefixes().forEach(xmlSignContext::putNamespacePrefix);
+
         xmlSignContext.setDefaultNamespacePrefix("");
         // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS));
 
@@ -516,9 +514,7 @@
     protected void writeDocument(Document document) throws MarshalException {
         XmlOptions xo = new XmlOptions();
         Map<String,String> namespaceMap = new HashMap<>();
-        for(Map.Entry<String,String> entry : signatureConfig.getNamespacePrefixes().entrySet()){
-            namespaceMap.put(entry.getValue(), entry.getKey());
-        }
+        signatureConfig.getNamespacePrefixes().forEach((k,v) -> namespaceMap.put(v,k));
         xo.setSaveSuggestedPrefixes(namespaceMap);
         xo.setUseDefaultNamespace();
 
@@ -530,43 +526,58 @@
          */
         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);
-        }
+            final DSigRelation originDesc = DSigRelation.ORIGIN_SIGS;
+            PackagePartName originPartName = PackagingURIHelper.createPartName(originDesc.getFileName(0));
 
-        PackagePart sigPart = pkg.getPart(sigPartName);
-        if (sigPart == null) {
-            sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART);
-        }
+            PackagePart originPart = pkg.getPart(originPartName);
+            if (originPart == null) {
+                // touch empty marker file
+                originPart = pkg.createPart(originPartName, originDesc.getContentType());
+                pkg.addRelationship(originPartName, TargetMode.INTERNAL, originDesc.getRelation());
+            }
 
-        try {
-            OutputStream os = sigPart.getOutputStream();
-            SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);
-            sigDoc.save(os, xo);
-            os.close();
+            // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
+            final DSigRelation sigDesc = DSigRelation.SIG;
+            int nextSigIdx = pkg.getUnusedPartIndex(sigDesc.getDefaultFileName());
+
+            if (!signatureConfig.isAllowMultipleSignatures()) {
+                PackageRelationshipCollection prc = originPart.getRelationshipsByType(sigDesc.getRelation());
+                for (int i=2; i<nextSigIdx; i++) {
+                    PackagePartName pn = PackagingURIHelper.createPartName(sigDesc.getFileName(i));
+                    for (PackageRelationship rel : prc) {
+                        PackagePart pp = originPart.getRelatedPart(rel);
+                        if (pp.getPartName().equals(pn)) {
+                            originPart.removeRelationship(rel.getId());
+                            prc.removeRelationship(rel.getId());
+                            break;
+                        }
+                    }
+
+                    pkg.removePart(pkg.getPart(pn));
+                }
+                nextSigIdx = 1;
+            }
+
+
+            PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx));
+            PackagePart sigPart = pkg.getPart(sigPartName);
+            if (sigPart == null) {
+                sigPart = pkg.createPart(sigPartName, sigDesc.getContentType());
+                originPart.addRelationship(sigPartName, TargetMode.INTERNAL, sigDesc.getRelation());
+            } else {
+                sigPart.clear();
+            }
+
+            try (OutputStream os = sigPart.getOutputStream()) {
+                SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);
+                sigDoc.save(os, xo);
+            }
+
         } 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);
     }
 
     private Element getDsigElement(final Document document, final String localName) {
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
index 81e5b21..e68b7a7 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
@@ -185,9 +185,7 @@
         final Map<String,String> nsMap = new HashMap<>();
 
         {
-            for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
-                nsMap.put(me.getValue(), me.getKey());
-            }
+            signatureConfig.getNamespacePrefixes().forEach((k,v) -> nsMap.put(v,k));
             nsMap.put("dsss", MS_DIGSIG_NS);
             nsMap.put("ds", XML_DIGSIG_NS);
         }
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java
index b3bfe9e..e7797e9 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java
@@ -129,10 +129,8 @@
         DOMSignContext domSignContext = (nextSibling == null)
             ? new DOMSignContext(key, n)
             : new DOMSignContext(key, n, nextSibling);
-        for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
-            domSignContext.putNamespacePrefix(me.getKey(), me.getValue());
-        }
-        
+        signatureConfig.getNamespacePrefixes().forEach(domSignContext::putNamespacePrefix);
+
         DOMStructure domStructure = new DOMStructure(n);
         domKeyInfo.marshal(domStructure, domSignContext);
         
diff --git a/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java b/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java
new file mode 100644
index 0000000..2a03dda
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/xslf/draw/SVGImageRenderer.java
@@ -0,0 +1,136 @@
+/* ====================================================================
+   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.
+==================================================================== */
+
+package org.apache.poi.xslf.draw;
+
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.RenderingHints;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+
+import org.apache.batik.anim.dom.SAXSVGDocumentFactory;
+import org.apache.batik.bridge.BridgeContext;
+import org.apache.batik.bridge.DocumentLoader;
+import org.apache.batik.bridge.GVTBuilder;
+import org.apache.batik.bridge.UserAgent;
+import org.apache.batik.bridge.UserAgentAdapter;
+import org.apache.batik.ext.awt.RenderingHintsKeyExt;
+import org.apache.batik.ext.awt.image.renderable.ClipRable8Bit;
+import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.util.XMLResourceDescriptor;
+import org.apache.poi.sl.draw.ImageRenderer;
+import org.w3c.dom.Document;
+
+public class SVGImageRenderer implements ImageRenderer {
+    private final GVTBuilder builder = new GVTBuilder();
+    private final BridgeContext context;
+    private final SAXSVGDocumentFactory svgFact;
+    private GraphicsNode svgRoot;
+    private double alpha = 1.0;
+
+    public SVGImageRenderer() {
+        String parser = XMLResourceDescriptor.getXMLParserClassName();
+        // TOOO: tell the batik guys to use secure parsing feature
+        svgFact = new SAXSVGDocumentFactory(parser);
+
+        UserAgent agent = new UserAgentAdapter();
+        DocumentLoader loader = new DocumentLoader(agent);
+        context = new BridgeContext(agent, loader);
+        context.setDynamic(true);
+    }
+
+
+    @Override
+    public void loadImage(InputStream data, String contentType) throws IOException {
+        Document document = svgFact.createDocument("", data);
+        svgRoot = builder.build(context, document);
+    }
+
+    @Override
+    public void loadImage(byte[] data, String contentType) throws IOException {
+        loadImage(new ByteArrayInputStream(data), contentType);
+    }
+
+    @Override
+    public Dimension getDimension() {
+        Rectangle2D r = svgRoot.getPrimitiveBounds();
+        return new Dimension((int)Math.ceil(r.getWidth()), (int)Math.ceil(r.getHeight()));
+    }
+
+    @Override
+    public void setAlpha(double alpha) {
+        this.alpha = alpha;
+    }
+
+    @Override
+    public BufferedImage getImage() {
+        return getImage(getDimension());
+    }
+
+    @Override
+    public BufferedImage getImage(Dimension dim) {
+        BufferedImage bi = new BufferedImage((int)dim.getWidth(), (int)dim.getHeight(), BufferedImage.TYPE_INT_ARGB);
+        Graphics2D g2d = (Graphics2D) bi.getGraphics();
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
+        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
+        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+        g2d.setRenderingHint(RenderingHintsKeyExt.KEY_BUFFERED_IMAGE, new WeakReference(bi));
+        Dimension dimSVG = getDimension();
+
+        double scaleX = dim.getWidth() / dimSVG.getWidth();
+        double scaleY = dim.getHeight() / dimSVG.getHeight();
+        g2d.scale(scaleX, scaleY);
+
+        svgRoot.paint(g2d);
+        g2d.dispose();
+
+        return bi;
+    }
+
+    @Override
+    public boolean drawImage(Graphics2D graphics, Rectangle2D anchor) {
+        return drawImage(graphics, anchor, null);
+    }
+
+    @Override
+    public boolean drawImage(Graphics2D graphics, Rectangle2D anchor, Insets clip) {
+        if (clip == null) {
+            svgRoot.setClip(null);
+        } else {
+            Rectangle2D clippedRect = new Rectangle2D.Double(
+                anchor.getX()+clip.left,
+                anchor.getY()+clip.top,
+                anchor.getWidth()-(clip.left+clip.right),
+                anchor.getHeight()-(clip.top+clip.bottom)
+            );
+            svgRoot.setClip(new ClipRable8Bit(null, clippedRect));
+        }
+
+        svgRoot.paint(graphics);
+
+        return true;
+    }
+}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java
index 881f5fc..b95b8ed 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureData.java
@@ -218,6 +218,8 @@
             return PictureType.WDP;
         } else if (XSLFRelation.IMAGE_TIFF.getContentType().equals(ct)) {
             return PictureType.TIFF;
+        } else if (XSLFRelation.IMAGE_SVG.getContentType().equals(ct)) {
+            return PictureType.SVG;
         } else {
             return null;
         }
@@ -237,6 +239,7 @@
             case WPG: return XSLFRelation.IMAGE_WPG;
             case WDP: return XSLFRelation.IMAGE_WDP;
             case TIFF: return XSLFRelation.IMAGE_TIFF;
+            case SVG: return XSLFRelation.IMAGE_SVG;
             default: return null;
         }
     }
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
index 9f07b18..b8de623 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
@@ -19,18 +19,31 @@
 
 package org.apache.poi.xslf.usermodel;
 
+import static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS;
+
+import java.awt.Dimension;
 import java.awt.Insets;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.net.URI;
 
+import javax.imageio.ImageIO;
 import javax.xml.namespace.QName;
 
 import org.apache.poi.openxml4j.opc.PackagePart;
 import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.sl.usermodel.PictureData.PictureType;
 import org.apache.poi.sl.usermodel.PictureShape;
 import org.apache.poi.sl.usermodel.Placeholder;
 import org.apache.poi.util.Beta;
 import org.apache.poi.util.POILogFactory;
 import org.apache.poi.util.POILogger;
+import org.apache.poi.util.Units;
+import org.apache.poi.xslf.draw.SVGImageRenderer;
 import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlException;
 import org.apache.xmlbeans.XmlObject;
@@ -55,6 +68,11 @@
     implements PictureShape<XSLFShape,XSLFTextParagraph> {
     private static final POILogger LOG = POILogFactory.getLogger(XSLFPictureShape.class);
 
+    private static final String DML_NS = "http://schemas.microsoft.com/office/drawing/2010/main";
+    private static final String SVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
+    private static final String BITMAP_URI = "{28A0092B-C50C-407E-A947-70E740481C1C}";
+    private static final String SVG_URI = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}";
+
     private XSLFPictureData _data;
 
     /*package*/ XSLFPictureShape(CTPicture shape, XSLFSheet sheet) {
@@ -196,6 +214,97 @@
         return (r == null) ? null : new Insets(r.getT(), r.getL(), r.getB(), r.getR());
     }
 
+    /**
+     * Add a SVG image reference
+     * @param svgPic a previously imported svg image
+     */
+    public void setSvgImage(XSLFPictureData svgPic) {
+        CTBlip blip = getBlip();
+        CTOfficeArtExtensionList extLst = blip.isSetExtLst() ? blip.getExtLst() : blip.addNewExtLst();
+
+        final int bitmapId = getExt(extLst, BITMAP_URI);
+        CTOfficeArtExtension extBitmap;
+        if (bitmapId == -1) {
+            extBitmap = extLst.addNewExt();
+            extBitmap.setUri(BITMAP_URI);
+            XmlCursor cur = extBitmap.newCursor();
+            cur.toEndToken();
+            cur.beginElement(new QName(DML_NS, "useLocalDpi", "a14"));
+            cur.insertNamespace("a14", DML_NS);
+            cur.insertAttributeWithValue("val", "0");
+            cur.dispose();
+        }
+
+        final int svgId = getExt(extLst, SVG_URI);;
+        if (svgId != -1) {
+            extLst.removeExt(svgId);
+        }
+
+        String svgRelId = getSheet().getRelationId(svgPic);
+        if (svgRelId == null) {
+            svgRelId = getSheet().addRelation(null, XSLFRelation.IMAGE_SVG, svgPic).getRelationship().getId();
+        }
+
+        CTOfficeArtExtension svgBitmap = extLst.addNewExt();
+        svgBitmap.setUri(SVG_URI);
+        XmlCursor cur = svgBitmap.newCursor();
+        cur.toEndToken();
+        cur.beginElement(new QName(SVG_NS, "svgBlip", "asvg"));
+        cur.insertNamespace("asvg", SVG_NS);
+        cur.insertAttributeWithValue(new QName(CORE_PROPERTIES_ECMA376_NS, "embed", "rel"), svgRelId);
+        cur.dispose();
+    }
+
+    /**
+     * Convienence method for adding SVG images, which generates the preview image
+     * @param sheet the sheet to add
+     * @param svgPic the svg picture to add
+     * @param previewType the preview picture type or null (defaults to PNG) - currently only JPEG,GIF,PNG are allowed
+     * @param anchor the image anchor (for calculating the preview image size) or
+     *               null (the preview size is taken from the svg picture bounds)
+     */
+    public static XSLFPictureShape addSvgImage(XSLFSheet sheet, XSLFPictureData svgPic, PictureType previewType, Rectangle2D anchor) throws IOException {
+
+        SVGImageRenderer renderer = new SVGImageRenderer();
+        try (InputStream is = svgPic.getInputStream()) {
+            renderer.loadImage(is, svgPic.getType().contentType);
+        }
+
+        Dimension dim = renderer.getDimension();
+        Rectangle2D anc = (anchor != null) ? anchor
+            : new Rectangle2D.Double(0,0, Units.pixelToPoints((int)dim.getWidth()), Units.pixelToPoints((int)dim.getHeight()));
+
+        PictureType pt = (previewType != null) ? previewType : PictureType.PNG;
+        if (pt != PictureType.JPEG || pt != PictureType.GIF || pt != PictureType.PNG) {
+            pt = PictureType.PNG;
+        }
+
+        BufferedImage thmBI = renderer.getImage(dim);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
+        // use extension instead of enum name, because of "jpeg"
+        ImageIO.write(thmBI, pt.extension.substring(1), bos);
+
+        XSLFPictureData pngPic = sheet.getSlideShow().addPicture(new ByteArrayInputStream(bos.toByteArray()), pt);
+
+        XSLFPictureShape shape = sheet.createPicture(pngPic);
+        shape.setAnchor(anc);
+        shape.setSvgImage(svgPic);
+        return shape;
+    }
+
+
+    private int getExt(CTOfficeArtExtensionList extLst, String uri) {
+        final int size = extLst.sizeOfExtArray();
+        for (int i=0; i<size; i++) {
+            CTOfficeArtExtension ext = extLst.getExtArray(i);
+            if (uri.equals(ext.getUri())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+
     @Override
     void copy(XSLFShape sh){
         super.copy(sh);
@@ -219,11 +328,11 @@
             nvPr.unsetCustDataLst();
         }
         if(blip.isSetExtLst()) {
-
+            // TODO: check for SVG copying
             CTOfficeArtExtensionList extLst = blip.getExtLst();
             //noinspection deprecation
             for(CTOfficeArtExtension ext : extLst.getExtArray()){
-                String xpath = "declare namespace a14='http://schemas.microsoft.com/office/drawing/2010/main' $this//a14:imgProps/a14:imgLayer";
+                String xpath = "declare namespace a14='"+ DML_NS +"' $this//a14:imgProps/a14:imgLayer";
                 XmlObject[] obj = ext.selectPath(xpath);
                 if(obj != null && obj.length == 1){
                     XmlCursor c = obj[0].newCursor();
@@ -234,6 +343,5 @@
                 }
             }
         }
-
     }
 }
\ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java
index 0ea5e34..9b9fc9c 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFRelation.java
@@ -16,6 +16,8 @@
 ==================================================================== */
 package org.apache.poi.xslf.usermodel;
 
+import static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.IMAGE_PART;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -159,80 +161,87 @@
 
     public static final XSLFRelation IMAGE_EMF = new XSLFRelation(
             PictureType.EMF.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.emf",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_WMF = new XSLFRelation(
             PictureType.WMF.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.wmf",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_PICT = new XSLFRelation(
             PictureType.PICT.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.pict",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_JPEG = new XSLFRelation(
             PictureType.JPEG.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.jpeg",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_PNG = new XSLFRelation(
             PictureType.PNG.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.png",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_DIB = new XSLFRelation(
             PictureType.DIB.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.dib",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_GIF = new XSLFRelation(
             PictureType.GIF.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.gif",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_TIFF = new XSLFRelation(
             PictureType.TIFF.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.tiff",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_EPS = new XSLFRelation(
             PictureType.EPS.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.eps",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_BMP = new XSLFRelation(
             PictureType.BMP.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.bmp",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_WPG = new XSLFRelation(
             PictureType.WPG.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.wpg",
             XSLFPictureData.class
     );
     public static final XSLFRelation IMAGE_WDP = new XSLFRelation(
             PictureType.WDP.contentType,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             "/ppt/media/image#.wdp",
             XSLFPictureData.class
     );
 
+    public static final XSLFRelation IMAGE_SVG = new XSLFRelation(
+            PictureType.SVG.contentType,
+            IMAGE_PART,
+            "/ppt/media/image#.svg",
+            XSLFPictureData.class
+    );
+
     public static final XSLFRelation IMAGES = new XSLFRelation(
             null,
-            "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
+            IMAGE_PART,
             null,
             XSLFPictureData.class
     );
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
deleted file mode 100644
index 381c6c2..0000000
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/* ====================================================================
-   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.
-==================================================================== */
-package org.apache.poi.poifs.crypt;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.cert.CRLException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.Date;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Result;
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.CRLNumber;
-import org.bouncycastle.asn1.x509.CRLReason;
-import org.bouncycastle.asn1.x509.DistributionPoint;
-import org.bouncycastle.asn1.x509.DistributionPointName;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.Extensions;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.KeyUsage;
-import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
-import org.bouncycastle.cert.X509CRLHolder;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.X509ExtensionUtils;
-import org.bouncycastle.cert.X509v2CRLBuilder;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.ocsp.BasicOCSPResp;
-import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
-import org.bouncycastle.cert.ocsp.CertificateID;
-import org.bouncycastle.cert.ocsp.CertificateStatus;
-import org.bouncycastle.cert.ocsp.OCSPReq;
-import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
-import org.bouncycastle.cert.ocsp.OCSPResp;
-import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
-import org.bouncycastle.cert.ocsp.Req;
-import org.bouncycastle.cert.ocsp.RevokedStatus;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.DigestCalculator;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class PkiTestUtils {
-
-    private PkiTestUtils() {
-        super();
-    }
-
-    static KeyPair generateKeyPair() throws Exception {
-        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
-        SecureRandom random = new SecureRandom();
-        keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024,
-                RSAKeyGenParameterSpec.F4), random);
-        return keyPairGenerator.generateKeyPair();
-    }
-
-    static X509Certificate generateCertificate(PublicKey subjectPublicKey,
-            String subjectDn, Date notBefore, Date notAfter,
-            X509Certificate issuerCertificate, PrivateKey issuerPrivateKey,
-            boolean caFlag, int pathLength, String crlUri, String ocspUri,
-            KeyUsage keyUsage)
-    throws IOException, OperatorCreationException, CertificateException
-    {
-        String signatureAlgorithm = "SHA1withRSA";
-        X500Name issuerName;
-        if (issuerCertificate != null) {
-            issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer();
-        } else {
-            issuerName = new X500Name(subjectDn);
-        }
-        
-        RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
-        RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
-
-        SubjectPublicKeyInfo subjectPublicKeyInfo = 
-            SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
-
-        DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
-            .setProvider("BC").build().get(CertificateID.HASH_SHA1);
-        
-        X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
-              issuerName
-            , new BigInteger(128, new SecureRandom())
-            , notBefore
-            , notAfter
-            , new X500Name(subjectDn)
-            , subjectPublicKeyInfo
-        );
-
-        X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
-        SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
-        AuthorityKeyIdentifier autKeyId = (issuerCertificate != null) 
-            ? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded()))
-            : exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
-
-        certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
-        certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
-
-        if (caFlag) {
-            BasicConstraints bc;
-            
-            if (-1 == pathLength) {
-                bc = new BasicConstraints(true);
-            } else {
-                bc = new BasicConstraints(pathLength);
-            }
-            certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
-        }
-
-        if (null != crlUri) {
-            int uri = GeneralName.uniformResourceIdentifier;
-            DERIA5String crlUriDer = new DERIA5String(crlUri);
-            GeneralName gn = new GeneralName(uri, crlUriDer);
-
-            DERSequence gnDer = new DERSequence(gn);
-            GeneralNames gns = GeneralNames.getInstance(gnDer);
-            
-            DistributionPointName dpn = new DistributionPointName(0, gns);
-            DistributionPoint distp = new DistributionPoint(dpn, null, null);
-            DERSequence distpDer = new DERSequence(distp);
-            certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer);
-        }
-
-        if (null != ocspUri) {
-            int uri = GeneralName.uniformResourceIdentifier;
-            GeneralName ocspName = new GeneralName(uri, ocspUri);
-            
-            AuthorityInformationAccess authorityInformationAccess =
-                new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName);
-            
-            certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess);
-        }
-
-        if (null != keyUsage) {
-            certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
-        }
-
-        JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
-        signerBuilder.setProvider("BC");
-        
-        X509CertificateHolder certHolder =
-            certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
-
-        /*
-         * Next certificate factory trick is needed to make sure that the
-         * certificate delivered to the caller is provided by the default
-         * security provider instead of BouncyCastle. If we don't do this trick
-         * we might run into trouble when trying to use the CertPath validator.
-         */
-//        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
-//        certificate = (X509Certificate) certificateFactory
-//                .generateCertificate(new ByteArrayInputStream(certificate
-//                        .getEncoded()));
-        return new JcaX509CertificateConverter().getCertificate(certHolder);
-    }
-
-    static Document loadDocument(InputStream documentInputStream)
-            throws ParserConfigurationException, SAXException, IOException {
-        InputSource inputSource = new InputSource(documentInputStream);
-        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
-                .newInstance();
-        documentBuilderFactory.setNamespaceAware(true);
-        DocumentBuilder documentBuilder = documentBuilderFactory
-                .newDocumentBuilder();
-        return documentBuilder.parse(inputSource);
-    }
-
-    static String toString(Node dom) throws TransformerException {
-        Source source = new DOMSource(dom);
-        StringWriter stringWriter = new StringWriter();
-        Result result = new StreamResult(stringWriter);
-        TransformerFactory transformerFactory = TransformerFactory
-                .newInstance();
-        Transformer transformer = transformerFactory.newTransformer();
-        /*
-         * We have to omit the ?xml declaration if we want to embed the
-         * document.
-         */
-        transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
-        transformer.transform(source, result);
-        return stringWriter.getBuffer().toString();
-    }
-
-    public static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey)
-    throws CertificateEncodingException, IOException, CRLException, OperatorCreationException {
-        
-        X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
-        X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
-        crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
-        JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
-
-        CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
-        
-        crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
-        X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
-        return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
-    }
-
-    public static OCSPResp createOcspResp(X509Certificate certificate,
-            boolean revoked, X509Certificate issuerCertificate,
-            X509Certificate ocspResponderCertificate,
-            PrivateKey ocspResponderPrivateKey, String signatureAlgorithm,
-            long nonceTimeinMillis)
-            throws Exception {
-        DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
-            .setProvider("BC").build().get(CertificateID.HASH_SHA1);
-        X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
-        CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
-        
-        // request
-        //create a nonce to avoid replay attack
-        BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
-        DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
-        Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
-        Extensions exts = new Extensions(ext);
-        
-        OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
-        ocspReqBuilder.addRequest(certId);
-        ocspReqBuilder.setRequestExtensions(exts);
-        OCSPReq ocspReq = ocspReqBuilder.build();
-
-        
-        SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
-            (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
-        
-        BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
-        basicOCSPRespBuilder.setResponseExtensions(exts);
-
-        // request processing
-        Req[] requestList = ocspReq.getRequestList();
-        for (Req ocspRequest : requestList) {
-            CertificateID certificateID = ocspRequest.getCertID();
-            CertificateStatus certificateStatus = CertificateStatus.GOOD;
-            if (revoked) {
-                certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn);
-            }
-            basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
-        }
-
-        // basic response generation
-        X509CertificateHolder[] chain = null;
-        if (!ocspResponderCertificate.equals(issuerCertificate)) {
-            // TODO: HorribleProxy can't convert array input params yet
-            chain = new X509CertificateHolder[] {
-                new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
-                issuerHolder
-            };
-        }
-        
-        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
-            .setProvider("BC").build(ocspResponderPrivateKey);
-        BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
-
-        
-        OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
-
-        return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
-    }
-}
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
index 4b9cff9..0b3525e 100644
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
+++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
@@ -29,6 +29,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -36,19 +37,30 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
+import java.math.BigInteger;
 import java.net.ConnectException;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.SocketTimeoutException;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.security.Key;
 import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.KeyStore;
 import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.cert.CRLException;
 import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
 import java.security.cert.X509CRL;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAKeyGenParameterSpec;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
@@ -56,7 +68,9 @@
 import java.util.Iterator;
 import java.util.List;
 
+import javax.xml.crypto.MarshalException;
 import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.XMLSignatureException;
 import javax.xml.crypto.dsig.dom.DOMSignContext;
 
 import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
@@ -90,8 +104,53 @@
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.apache.xmlbeans.SystemProperties;
 import org.apache.xmlbeans.XmlObject;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLNumber;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509ExtensionUtils;
+import org.bouncycastle.cert.X509v2CRLBuilder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.ocsp.BasicOCSPResp;
+import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.CertificateID;
+import org.bouncycastle.cert.ocsp.CertificateStatus;
+import org.bouncycastle.cert.ocsp.OCSPReq;
+import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
 import org.bouncycastle.cert.ocsp.OCSPResp;
+import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.Req;
+import org.bouncycastle.cert.ocsp.RevokedStatus;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
 import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
 import org.junit.AfterClass;
@@ -408,7 +467,7 @@
         try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) {
 
             initKeyPair("Test", "CN=Test");
-            final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate());
+            final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
 
             // setup
             SignatureConfig signatureConfig = new SignatureConfig();
@@ -481,7 +540,7 @@
 
             final RevocationData revocationData = new RevocationData();
             revocationData.addCRL(crl);
-            OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false,
+            OCSPResp ocspResp = createOcspResp(x509, false,
                     x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis());
             revocationData.addOCSP(ocspResp.getEncoded());
 
@@ -721,18 +780,84 @@
     
     @Test
     public void testMultiSign() throws Exception {
-        initKeyPair("KeyA", "CN=KeyA");
-        //KeyPair keyPairA = keyPair;
-        //X509Certificate x509A = x509;
-        initKeyPair("KeyB", "CN=KeyB");
-        //KeyPair keyPairB = keyPair;
-        //X509Certificate x509B = x509;
-        
-        File tpl = copy(testdata.getFile("bug58630.xlsx"));
+        cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+        cal.clear();
+        cal.setTimeZone(LocaleUtil.TIMEZONE_UTC);
+        cal.set(2018, Calendar.DECEMBER, 14);
+
+        // test signing with separate opened packages
+        File tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
         try (OPCPackage pkg = OPCPackage.open(tpl)) {
-            //SignatureConfig signatureConfig = new SignatureConfig();
-            assertNotNull(pkg);
+            signPkg63011(pkg, "bug63011_key1.pem", true);
         }
+
+        try (OPCPackage pkg = OPCPackage.open(tpl)) {
+            signPkg63011(pkg, "bug63011_key2.pem", true);
+        }
+
+        verifyPkg63011(tpl, true);
+
+        // test signing with single opened package
+        tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
+        try (OPCPackage pkg = OPCPackage.open(tpl)) {
+            signPkg63011(pkg, "bug63011_key1.pem", true);
+            signPkg63011(pkg, "bug63011_key2.pem", true);
+        }
+
+        verifyPkg63011(tpl, true);
+
+        try (OPCPackage pkg = OPCPackage.open(tpl)) {
+            signPkg63011(pkg, "bug63011_key1.pem", true);
+            signPkg63011(pkg, "bug63011_key2.pem", false);
+        }
+
+        verifyPkg63011(tpl, false);
+    }
+
+    private void verifyPkg63011(File tpl, boolean multi) throws InvalidFormatException, IOException {
+        try (OPCPackage pkg = OPCPackage.open(tpl, PackageAccess.READ)) {
+            SignatureConfig sic = new SignatureConfig();
+            sic.setOpcPackage(pkg);
+            SignatureInfo si = new SignatureInfo();
+            si.setSignatureConfig(sic);
+            List<X509Certificate> result = new ArrayList<>();
+            for (SignaturePart sp : si.getSignatureParts()) {
+                if (sp.validate()) {
+                    result.add(sp.getSigner());
+                }
+            }
+
+            assertNotNull(result);
+
+            if (multi) {
+                assertEquals(2, result.size());
+                assertEquals("CN=Muj Klic", result.get(0).getSubjectDN().toString());
+                assertEquals("CN=My Second key", result.get(1).getSubjectDN().toString());
+            } else {
+                assertEquals(1, result.size());
+                assertEquals("CN=My Second key", result.get(0).getSubjectDN().toString());
+            }
+
+            assertTrue(si.verifySignature());
+            pkg.revert();
+        }
+    }
+
+    private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi)
+    throws IOException, CertificateException, XMLSignatureException, MarshalException {
+        assertNotNull(pkg);
+        initKeyFromPEM(testdata.getFile(pemFile));
+
+        SignatureConfig config = new SignatureConfig();
+        config.setKey(keyPair.getPrivate());
+        config.setSigningCertificateChain(Collections.singletonList(x509));
+        config.setExecutionTime(cal.getTime());
+        config.setAllowMultipleSignatures(multi);
+        config.setOpcPackage(pkg);
+
+        SignatureInfo si = new SignatureInfo();
+        si.setSignatureConfig(config);
+        si.confirmSignature();
     }
 
     @Test
@@ -829,14 +954,14 @@
             x509 = (X509Certificate)keystore.getCertificate(alias);
             keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
         } else {
-            keyPair = PkiTestUtils.generateKeyPair();
+            keyPair = generateKeyPair();
             Date notBefore = cal.getTime();
             Calendar cal2 = (Calendar)cal.clone();
             cal2.add(Calendar.YEAR, 1);
             Date notAfter = cal2.getTime();
             KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
             
-            x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN
+            x509 = generateCertificate(keyPair.getPublic(), subjectDN
                 , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage);
 
             keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
@@ -849,6 +974,27 @@
         }
     }
 
+    private void initKeyFromPEM(File pemFile) throws IOException, CertificateException {
+        // see https://stackoverflow.com/questions/11787571/how-to-read-pem-file-to-get-private-and-public-key
+        PrivateKey key = null;
+        x509 = null;
+
+        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pemFile), StandardCharsets.ISO_8859_1))) {
+            PEMParser parser = new PEMParser(br);
+            for (Object obj; (obj = parser.readObject()) != null; ) {
+                if (obj instanceof PrivateKeyInfo) {
+                    key = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)obj);
+                } else if (obj instanceof X509CertificateHolder) {
+                    x509 = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)obj);
+                }
+            }
+        }
+
+        if (key != null && x509 != null) {
+            keyPair = new KeyPair(x509.getPublicKey(), key);
+        }
+    }
+
     private static File copy(File input) throws IOException {
         String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1");
         if (extension == null || extension.isEmpty()) {
@@ -872,4 +1018,187 @@
         return tmpFile;
     }
 
+    private static KeyPair generateKeyPair() throws Exception {
+        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+        SecureRandom random = new SecureRandom();
+        keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024,
+                RSAKeyGenParameterSpec.F4), random);
+        return keyPairGenerator.generateKeyPair();
+    }
+
+    private static X509Certificate generateCertificate(PublicKey subjectPublicKey,
+                                               String subjectDn, Date notBefore, Date notAfter,
+                                               X509Certificate issuerCertificate, PrivateKey issuerPrivateKey,
+                                               boolean caFlag, int pathLength, String crlUri, String ocspUri,
+                                               KeyUsage keyUsage)
+            throws IOException, OperatorCreationException, CertificateException
+    {
+        String signatureAlgorithm = "SHA1withRSA";
+        X500Name issuerName;
+        if (issuerCertificate != null) {
+            issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer();
+        } else {
+            issuerName = new X500Name(subjectDn);
+        }
+
+        RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
+        RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
+
+        SubjectPublicKeyInfo subjectPublicKeyInfo =
+                SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
+
+        DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
+                .setProvider("BC").build().get(CertificateID.HASH_SHA1);
+
+        X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
+                issuerName
+                , new BigInteger(128, new SecureRandom())
+                , notBefore
+                , notAfter
+                , new X500Name(subjectDn)
+                , subjectPublicKeyInfo
+        );
+
+        X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
+        SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
+        AuthorityKeyIdentifier autKeyId = (issuerCertificate != null)
+                ? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded()))
+                : exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
+
+        certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
+        certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
+
+        if (caFlag) {
+            BasicConstraints bc;
+
+            if (-1 == pathLength) {
+                bc = new BasicConstraints(true);
+            } else {
+                bc = new BasicConstraints(pathLength);
+            }
+            certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
+        }
+
+        if (null != crlUri) {
+            int uri = GeneralName.uniformResourceIdentifier;
+            DERIA5String crlUriDer = new DERIA5String(crlUri);
+            GeneralName gn = new GeneralName(uri, crlUriDer);
+
+            DERSequence gnDer = new DERSequence(gn);
+            GeneralNames gns = GeneralNames.getInstance(gnDer);
+
+            DistributionPointName dpn = new DistributionPointName(0, gns);
+            DistributionPoint distp = new DistributionPoint(dpn, null, null);
+            DERSequence distpDer = new DERSequence(distp);
+            certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer);
+        }
+
+        if (null != ocspUri) {
+            int uri = GeneralName.uniformResourceIdentifier;
+            GeneralName ocspName = new GeneralName(uri, ocspUri);
+
+            AuthorityInformationAccess authorityInformationAccess =
+                    new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName);
+
+            certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess);
+        }
+
+        if (null != keyUsage) {
+            certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
+        }
+
+        JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
+        signerBuilder.setProvider("BC");
+
+        X509CertificateHolder certHolder =
+                certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
+
+        /*
+         * Next certificate factory trick is needed to make sure that the
+         * certificate delivered to the caller is provided by the default
+         * security provider instead of BouncyCastle. If we don't do this trick
+         * we might run into trouble when trying to use the CertPath validator.
+         */
+//        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+//        certificate = (X509Certificate) certificateFactory
+//                .generateCertificate(new ByteArrayInputStream(certificate
+//                        .getEncoded()));
+        return new JcaX509CertificateConverter().getCertificate(certHolder);
+    }
+
+    private static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey)
+            throws CertificateEncodingException, IOException, CRLException, OperatorCreationException {
+
+        X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
+        X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
+        crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
+        JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
+
+        CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
+
+        crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
+        X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
+        return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
+    }
+
+    private static OCSPResp createOcspResp(X509Certificate certificate,
+                                          boolean revoked, X509Certificate issuerCertificate,
+                                          X509Certificate ocspResponderCertificate,
+                                          PrivateKey ocspResponderPrivateKey, String signatureAlgorithm,
+                                          long nonceTimeinMillis)
+            throws Exception {
+        DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
+                .setProvider("BC").build().get(CertificateID.HASH_SHA1);
+        X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
+        CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
+
+        // request
+        //create a nonce to avoid replay attack
+        BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
+        DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
+        Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
+        Extensions exts = new Extensions(ext);
+
+        OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
+        ocspReqBuilder.addRequest(certId);
+        ocspReqBuilder.setRequestExtensions(exts);
+        OCSPReq ocspReq = ocspReqBuilder.build();
+
+
+        SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
+                (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
+
+        BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
+        basicOCSPRespBuilder.setResponseExtensions(exts);
+
+        // request processing
+        Req[] requestList = ocspReq.getRequestList();
+        for (Req ocspRequest : requestList) {
+            CertificateID certificateID = ocspRequest.getCertID();
+            CertificateStatus certificateStatus = CertificateStatus.GOOD;
+            if (revoked) {
+                certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn);
+            }
+            basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
+        }
+
+        // basic response generation
+        X509CertificateHolder[] chain = null;
+        if (!ocspResponderCertificate.equals(issuerCertificate)) {
+            // TODO: HorribleProxy can't convert array input params yet
+            chain = new X509CertificateHolder[] {
+                    new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
+                    issuerHolder
+            };
+        }
+
+        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
+                .setProvider("BC").build(ocspResponderPrivateKey);
+        BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
+
+
+        OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
+
+        return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
+    }
 }
diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java
index 7805bfd..1c1fdf5 100644
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFPictureShape.java
@@ -16,14 +16,17 @@
 ==================================================================== */
 package org.apache.poi.xslf.usermodel;
 
+import static org.apache.poi.POIDataSamples.TEST_PROPERTY;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
+import java.awt.geom.Rectangle2D;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
@@ -247,4 +250,24 @@
         
         slideShow.close();
     }
+
+
+    @Test
+    public void renderSvgImage() throws Exception {
+        String dataDirName = System.getProperty(TEST_PROPERTY);
+        final String SVG_FILE = (dataDirName != null ? "../" : "") + "src/documentation/resources/images/project-header.svg";
+        XMLSlideShow ppt = new XMLSlideShow();
+        XSLFSlide slide = ppt.createSlide();
+
+        XSLFPictureData svgPic = ppt.addPicture(new File(dataDirName, SVG_FILE), PictureType.SVG);
+        XSLFPictureShape shape = XSLFPictureShape.addSvgImage(slide, svgPic, PictureType.JPEG, null);
+
+        Rectangle2D anchor = shape.getAnchor();
+        anchor.setRect(100, 100, anchor.getWidth(), anchor.getHeight());
+        shape.setAnchor(anchor);
+
+//        try (FileOutputStream fos = new FileOutputStream("svgtest.pptx")) {
+//            ppt.write(fos);
+//        }
+    }
 }
\ No newline at end of file
diff --git a/test-data/xmldsign/bug63011_key1.pem b/test-data/xmldsign/bug63011_key1.pem
new file mode 100644
index 0000000..dac2122
--- /dev/null
+++ b/test-data/xmldsign/bug63011_key1.pem
@@ -0,0 +1,45 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCr+sR6OR6R49ni
+/w/jjCSOewGvOjzJelKaGoT/cYmvx6JFrFwy3PV7FZEhRPQCKvE+cCOYAwBFTC7K
+uN0h/cen7tVF/idVWlzOTU/3Tdy/J7sb2KfToHvjxKkeMUl1SHvf1et+3HQHQSII
+Vf8A3cu0cL91sYSd32ZZYm2TeKxbTc1GO/R9OzvSOxCQaIVbAiMQvY04EQZb0F7/
+eBRj8UAdMbsTRVYuOSWrXX2DL5U3/nq/c8F0ozeyUOEbzWacZ83OcDEFC0hCrGyy
+ptKtEQBWhbgSZ9BRc5nZ9q97VoDOLwrb24zBI45AsRBvwRAhf5DLV5cOw6X8MdQd
+p0pKcN9HAgMBAAECggEAVcPyNe3EZAcYQw7mMplSJcgMOAG4DNY22Wk+SFGr04Cg
+WVSyih8NQPupa8kCUw5tTrEH3ygn+2cZsrlsdiYkaog9zfEIVpWA0NVXesJWwvGi
+aymp0G0pO5Z4rHjx5E5okGETVynDp1aBDV0tlZYGn47WvG/x7fVaClt+v9ufQMyD
+snUEf2lpWhUtoUhlOnDZr791QPLUkVi0roLSD0M2vPM1cbV8EO4SdhOOyTDCNFvo
+IAcysKV0UBN/TJOyB7FyDw1cJtJ9Ja/X9YoLcW+0fTZNhlYq6VrowCQyEchlb8Pz
+bRhUJc6oGffpaxtrMk8pmTSScpSTV5PtB3E4t6mYYQKBgQDVPX+GdnuNyyNdvF3g
+SplJZdyAjW21mZ4gdYLxyGHIsgTrz4SKJ49HZoaDdwwpQRZWnQC7gxCXbJ2IHwMF
++Qz7pp7eoRhQ3eBqQxfIypOeRSC6n3OIOYxIJlcV3O/PTGwu2eJhxck1ZG/j90DW
+NDo9hQYLFpD2xGiaWd611HncsQKBgQDOdzBZF/1PbUh/zz1nwAVPT85IM1vwU85k
+LIQUY/P+LFUvSa7gk3sabn6Zop5Cv0z+I1j5NW1lobLR4u02jqIb/dqeJ+XOt27n
+UMiYI/6WBA6Mkrf9j10BpFuSBWYotpkr8PZcog1v+FnODcOsG6Nl7fiJjkpPD6AF
+MCIHip4ZdwKBgC4fKhkMQXcOy+x3VJqxp+v/My0+6c7QlioRIKxpGfVNw9C5RsKX
+Ad+ApnGC60d1A37iYIkuNQV7gasygfXlw1Ae3tfqYhcDlomFT3ynjDw8WXLkEBoT
+0Gq+mDFrYxckQXX0vIlHPVjmC2l0TjrGex5ZSlBVpi8pljJkY85SUbVRAoGBAICq
+BCYxbfl2aAzGEEU8g9KWMD4MS4osH92LZE/0rhPCet96MpHfNoMVQq3pimicIJXr
+X0IGSoNgTjjACwXJwzpga0HOKUc2RtW+IRO2squ4IXz23dQU6GfijfIkjTJoAHJC
+urSlhGw3v3dKWptBqgUWVKEcXDCC0z0Ibtx2ROonAoGAWcRq3xpkbeR4K4yd+K9w
+4CBi8sdlz48tmZDSVigjq27Jk/6Ttk5K6x8M1rhUWYZ+ak1Y2NVV24oqnIdtIZrE
+BdgvzJCJN3Yx3FMYzUVqONFfGyppi0E7WvSYX0qlXKeZDcjgp1ORCOhiQL3ufE1+
+LdXLunLfjJzNnvX7XoTAyyE=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICojCCAYqgAwIBAgIEXA5r3jANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhN
+dWogS2xpYzAeFw0xODEyMTAxMzM2MzBaFw0xOTEyMTAxMzM2MzBaMBMxETAPBgNV
+BAMMCE11aiBLbGljMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/rE
+ejkekePZ4v8P44wkjnsBrzo8yXpSmhqE/3GJr8eiRaxcMtz1exWRIUT0AirxPnAj
+mAMARUwuyrjdIf3Hp+7VRf4nVVpczk1P903cvye7G9in06B748SpHjFJdUh739Xr
+ftx0B0EiCFX/AN3LtHC/dbGEnd9mWWJtk3isW03NRjv0fTs70jsQkGiFWwIjEL2N
+OBEGW9Be/3gUY/FAHTG7E0VWLjklq119gy+VN/56v3PBdKM3slDhG81mnGfNznAx
+BQtIQqxssqbSrREAVoW4EmfQUXOZ2fave1aAzi8K29uMwSOOQLEQb8EQIX+Qy1eX
+DsOl/DHUHadKSnDfRwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAJ5hi+ZinJ4QOA
+hC2sPuawdj6k+C3+CXNpRV5eMagBJZYPMsUfEPrP7ZjRiBpQVQyx6rktXqObrmTw
+e12yMor5cc8kGyQ6GmYoEoCKFS/S08fK5j5bwwy8KWfyH8tRGsEHeowPw3eIZCv7
+gowUhb3SOsh3osAtafSe9aS0rGNTGBdSTFnJEiew8zpWbdIFKySYxU8nmHNpIPXh
+O/EuAYMwbcF3BhM20Gm5hxqrgSWe7S+q3KqSJbs+k95j0jr8xoNzwUd8NzI40Uu1
+L1ejyqvGHjxhQooIej1Ea/MSp7v5ifpBWSp3yxlOjAnZPSEewCutMyHylIulS0sK
+2JQfgcdd
+-----END CERTIFICATE-----
diff --git a/test-data/xmldsign/bug63011_key2.pem b/test-data/xmldsign/bug63011_key2.pem
new file mode 100644
index 0000000..18de501
--- /dev/null
+++ b/test-data/xmldsign/bug63011_key2.pem
@@ -0,0 +1,45 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDMcC/+xW5l/R8i
+M9IKNdebiCQxkn/gfSjXD7oddrg42POCuPElhaGlHXPI+DCvNQ1+lbCTDJ7rk8F2
+DbefV80uwzMkMKnn0i3GW2K8LIGrv74ACYIzueHK+Mrm3N90/Xb6IqSR430SYH9r
+RbIfCqCVa0LJbVS2Pw2vBmvJHE/DVoSjKXlO73EbN4G4lJnj80NaZp6I0DDsoVup
+htKJWLtYwB3CDpI0oDst8/Beq7esDyyHHWf3v0TOtH19lRWSoD9yEmg+bRMKYfnu
+T5rWj8tF+cRjh+SIyBjuIn7n4qwnAs62CQKFLjp/3PjOEbaD/aCX+6xMWvAtk0UT
+KIDwYsejAgMBAAECggEAX0h2hibconpMFXPTlGCt4dadD+G46hdLfpjc5Lidehlb
+vXSXKCbVRRCqE2PhxPdUF8iKqK68Lw4JoA0apRCWGBJwdpCbz+k83nNfXFbeBP9z
+/a5w2czr+N7mKp2NJIix+DlHPJurgnIpUQUF1MPuPlXkpd7oGZzfstCqUex5HJAU
+N03lL/Z5/hgq93sLh1ZgPWhwAOYnps0rsOnFYXlXW7WVXvL1xpyBidZstOw+BdVC
+7BQNvKVa4KGC/PdgW7K4327JeMjRsX1nDUzZbORM5wihLE5idIHmDbQUm4G8JZul
+tLeV2Ew28L91jcc55Pbe40tBeMSqgSuU2r6/dU4uCQKBgQDlUCI8vW1Q7J92m+zU
+Y8lHu5mS4nlwXJEpuUoCuD2falAZW40+sG3fqZ1tEiCEpEYSYKMP0vbFrNLQ68av
+HWhjXWCQFvuUfAK8BsMusRIBQ/ZJduSa5vSgsQA+ZVl/Pnj9PyMN70nhueHcXjnM
+SNo5X3BQMNERaec7Lr0mRjDGHwKBgQDkOvcbdt4gCwpAeARzCTh15uw+q8KWPz77
+hPbaO4GJIMZZmRULhsrgBNZAsREasO4ZKJLI+wtukCXqgHAGzdSl6aK1fDLgFYqP
+Hp8UvgvIdF5yV7oAma9gRbK47sQlyBSFvQUyAuvA7wcviQ43/TIa173iBS07WfpA
+KlZu6+Ol/QKBgQDMJfglDRtKJS6eIJjKSQADzZ8eZmNoxfAyLhQWscGir7oZqSjo
++5cFvPI7DR1IGRuM9t2Uk+M//uk8N/uNIOgzNglmnh9hhLnGfVq8scVuvPuBUciZ
+oy179bha1E3F+28pPlFN8Y9b1umeD4DzPpSQ6UeLDLrD/v1t8eFRNsHrTwKBgE7v
+4gW6wCrfBqWznP5YoxGMVAt9BqlGqLb/jw195ViTYGce3juFXGfM5HmthFfx9/f1
+o4cl5RdRffu0foqr6C+WNjOFCGeeq7TCh4z6CkNDlGMB2pBYl2K52I3D702N/SMg
+dEqO3hF12stjIOrWhNzp33/sAG/1t+s3eXuV1L/VAoGAe0d/g5LcACFbl0sDRcSV
+WYsKHyR2dieALKf47mhzQtKmY506zM22n52s9sUT92j4jf/4dnP5OciJniSppgfW
+V64/856DuMNPoIssmNh3tQwjm8I0/t+iDGJJSHnQthG0AwD2NRe/D8Uty4P7ED64
+0gEVrDJEgT60MrQt15f+1sM=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXA/YwjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1N
+eSBTZWNvbmQga2V5MB4XDTE4MTIxMTE1MzMyMloXDTE5MTIxMTE1MzMyMlowGDEW
+MBQGA1UEAwwNTXkgU2Vjb25kIGtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMxwL/7FbmX9HyIz0go115uIJDGSf+B9KNcPuh12uDjY84K48SWFoaUd
+c8j4MK81DX6VsJMMnuuTwXYNt59XzS7DMyQwqefSLcZbYrwsgau/vgAJgjO54cr4
+yubc33T9dvoipJHjfRJgf2tFsh8KoJVrQsltVLY/Da8Ga8kcT8NWhKMpeU7vcRs3
+gbiUmePzQ1pmnojQMOyhW6mG0olYu1jAHcIOkjSgOy3z8F6rt6wPLIcdZ/e/RM60
+fX2VFZKgP3ISaD5tEwph+e5PmtaPy0X5xGOH5IjIGO4ifufirCcCzrYJAoUuOn/c
++M4RtoP9oJf7rExa8C2TRRMogPBix6MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+n3rvm1DatfrrFdhNOfNwlYMF12ND1BIEFYYI13vIkZ5wmb+gNYZu32FqdTpX//qV
+VkNLXJj/DrR+rKWH2jZwabaYyv5XmJl9Y0/LqN3ExiqijYsPgpWEKCzhfyv+Qe5B
+Qzx8WCJjGh0UMQ3P8/FRYsUPf5AFMdz9jZK0uYCFFgkb4LDp23MbNYG4eRBB+/+R
+Arq/poWd+eb5G79+rnrGGCtqhVuxrMP8xkPy4Rkg8zr56CxQSHX7qXehYc9x/dTj
+CyHPrOUAw4W/pYShYtHsqORQivPvwnwQQ4upK6s2l5pbPwyEzV45clIPU+3tjvn7
+67DLhc66Sz4/XkWeEXeeMg==
+-----END CERTIFICATE-----