You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@poi.apache.org by ki...@apache.org on 2021/11/07 23:48:15 UTC

svn commit: r1894820 - in /poi: site/src/documentation/content/xdocs/ trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/ trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/ trunk/poi-ooxml/src/test/java/org/apache/poi/poif...

Author: kiwiwings
Date: Sun Nov  7 23:48:15 2021
New Revision: 1894820

URL: http://svn.apache.org/viewvc?rev=1894820&view=rev
Log:
#65672 - Digitial Signature - set commitment type and purpose

Added:
    poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/DummyKeystore.java   (with props)
Modified:
    poi/site/src/documentation/content/xdocs/changes.xml
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
    poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java
    poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java

Modified: poi/site/src/documentation/content/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/poi/site/src/documentation/content/xdocs/changes.xml?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/site/src/documentation/content/xdocs/changes.xml (original)
+++ poi/site/src/documentation/content/xdocs/changes.xml Sun Nov  7 23:48:15 2021
@@ -73,6 +73,7 @@
     <release version="5.2.0" date="2022-03-??">
         <actions>
             <action type="add" fixes-bug="65668" context="OOXML">upgrade to xmlsec 2.3.0 - make secure validation configurable</action>
+            <action type="add" fixes-bug="65672" context="OOXML">Digitial Signature - set commitment type and purpose</action>
         </actions>
     </release>
 

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java Sun Nov  7 23:48:15 2021
@@ -219,6 +219,8 @@ public class SignatureConfig {
      */
     private boolean secureValidation = true;
 
+    private String commitmentType = "Created and approved this document";
+
 
     public SignatureConfig() {
         // OOo doesn't like ds namespaces so per default prefixing is off.
@@ -1117,4 +1119,21 @@ public class SignatureConfig {
     public void setSecureValidation(boolean secureValidation) {
         this.secureValidation = secureValidation;
     }
+
+    public String getCommitmentType() {
+        return commitmentType;
+    }
+
+    /**
+     * Set the commitmentType, which is usually one of ...
+     * <ul>
+     *     <li>"Created and approved this document"
+     *     <li>"Approved this document"
+     *     <li>"Created this document"
+     *     <li>... or any other important sounding statement
+     * </ul>
+     */
+    public void setCommitmentType(String commitmentType) {
+        this.commitmentType = commitmentType;
+    }
 }
\ No newline at end of file

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java Sun Nov  7 23:48:15 2021
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -43,7 +44,7 @@ import org.w3c.dom.traversal.NodeIterato
  */
 public class SignatureMarshalDefaultListener implements SignatureMarshalListener {
     private static final String OBJECT_TAG = "Object";
-    private final Set<String> IGNORE_NS = new HashSet<>(Arrays.asList(null, XML_NS, XML_DIGSIG_NS));
+    private static final Set<String> IGNORE_NS = new HashSet<>(Arrays.asList(null, XML_NS, XML_DIGSIG_NS));
 
     @Override
     public void handleElement(SignatureInfo signatureInfo, Document doc, EventTarget target, EventListener parentListener) {
@@ -52,15 +53,23 @@ public class SignatureMarshalDefaultList
 
         final DocumentTraversal traversal = (DocumentTraversal) doc;
         final Map<String, String> prefixCfg = signatureInfo.getSignatureConfig().getNamespacePrefixes();
-
         final Map<String, String> prefixUsed = new HashMap<>();
 
-        NodeList nl = doc.getElementsByTagName(OBJECT_TAG);
-        final int objLen = nl.getLength();
-        for (int i=0; i<objLen; i++) {
-            final Element objNode = (Element)nl.item(i);
-            getAllNamespaces(traversal, objNode, prefixCfg, prefixUsed);
-            prefixUsed.forEach((ns, prefix) -> objNode.setAttributeNS(XML_NS, "xmlns:"+prefix, ns));
+        forEachElement(doc.getElementsByTagName(OBJECT_TAG), (o) -> {
+           forEachElement(o.getChildNodes(), (c) -> {
+               getAllNamespaces(traversal, c, prefixCfg, prefixUsed);
+               prefixUsed.forEach((ns, prefix) -> c.setAttributeNS(XML_NS, "xmlns:"+prefix, ns));
+           });
+        });
+    }
+
+    private static void forEachElement(NodeList nl, Consumer<Element> consumer) {
+        int len = nl.getLength();
+        for(int i=0; i<len; i++) {
+            Node n = nl.item(i);
+            if (n instanceof Element) {
+                consumer.accept((Element)n);
+            }
         }
     }
 

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java Sun Nov  7 23:48:15 2021
@@ -180,6 +180,7 @@ public class SignaturePart {
         m.put("//dsss:SignatureComments", signatureConfig::setSignatureDescription);
         m.put("//xd:QualifyingProperties//xd:SignedSignatureProperties//ds:DigestMethod/@Algorithm", signatureConfig::setXadesDigestAlgo);
         m.put("//ds:CanonicalizationMethod", signatureConfig::setCanonicalizationMethod);
+        m.put("//xd:CommitmentTypeId/xd:Description", signatureConfig::setCommitmentType);
 
         for (Map.Entry<String,Consumer<String>> me : m.entrySet()) {
             String val = (String)xpath.compile(me.getKey()).evaluate(doc, XPathConstants.STRING);

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java Sun Nov  7 23:48:15 2021
@@ -30,7 +30,6 @@ import static org.apache.poi.poifs.crypt
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Base64;
 import java.util.Collections;
 import java.util.Comparator;
@@ -260,25 +259,8 @@ public class OOXMLSignatureFacet impleme
 
         List<XMLStructure> objectContent = new ArrayList<>();
 
-        SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
-        CTSignatureInfoV1 ctSigV1 = sigV1.addNewSignatureInfoV1();
-        if (signatureConfig.getDigestAlgo() != HashAlgorithm.sha1) {
-            ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
-        }
-
-        String desc = signatureConfig.getSignatureDescription();
-        if (desc != null) {
-            ctSigV1.setSignatureComments(desc);
-        }
-
-        byte[] image = signatureConfig.getSignatureImage();
-        if (image != null) {
-            ctSigV1.setSetupID(signatureConfig.getSignatureImageSetupId().toString());
-            ctSigV1.setSignatureImage(image);
-            ctSigV1.setSignatureType(2);
-        }
-
-        Element n = (Element)document.importNode(ctSigV1.getDomNode(), true);
+        SignatureInfoV1Document sigV1 = createSignatureInfoV1(signatureInfo);
+        Element n = (Element)document.importNode(sigV1.getSignatureInfoV1().getDomNode(), true);
         n.setAttributeNS(XML_NS, XMLConstants.XMLNS_ATTRIBUTE, MS_DIGSIG_NS);
 
         List<XMLStructure> signatureInfoContent = new ArrayList<>();
@@ -321,6 +303,35 @@ public class OOXMLSignatureFacet impleme
         }
     }
 
+    /**
+     * Create SignatureInfoV1 element. This method can be easily extended by subclasses, to fill its other elements.
+     */
+    protected SignatureInfoV1Document createSignatureInfoV1(SignatureInfo signatureInfo) {
+        SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
+
+        SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
+        CTSignatureInfoV1 ctSigV1 = sigV1.addNewSignatureInfoV1();
+        if (signatureConfig.getDigestAlgo() != HashAlgorithm.sha1) {
+            ctSigV1.setManifestHashAlgorithm(signatureConfig.getDigestMethodUri());
+        }
+
+        String desc = signatureConfig.getSignatureDescription();
+        if (desc != null) {
+            ctSigV1.setSignatureComments(desc);
+        }
+
+        byte[] image = signatureConfig.getSignatureImage();
+        if (image == null) {
+            ctSigV1.setSignatureType(1);
+        } else {
+            ctSigV1.setSetupID(signatureConfig.getSignatureImageSetupId().toString());
+            ctSigV1.setSignatureImage(image);
+            ctSigV1.setSignatureType(2);
+        }
+
+        return sigV1;
+    }
+
     protected static String getRelationshipReferenceURI(String zipEntryName) {
         return "/"
             + zipEntryName

Modified: poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java (original)
+++ poi/trunk/poi-ooxml/src/main/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java Sun Nov  7 23:48:15 2021
@@ -63,6 +63,7 @@ import org.etsi.uri.x01903.v13.AnyType;
 import org.etsi.uri.x01903.v13.CertIDListType;
 import org.etsi.uri.x01903.v13.CertIDType;
 import org.etsi.uri.x01903.v13.ClaimedRolesListType;
+import org.etsi.uri.x01903.v13.CommitmentTypeIndicationType;
 import org.etsi.uri.x01903.v13.DataObjectFormatType;
 import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
 import org.etsi.uri.x01903.v13.ObjectIdentifierType;
@@ -70,6 +71,7 @@ import org.etsi.uri.x01903.v13.Qualifyin
 import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
 import org.etsi.uri.x01903.v13.SignaturePolicyIdType;
 import org.etsi.uri.x01903.v13.SignaturePolicyIdentifierType;
+import org.etsi.uri.x01903.v13.SignedDataObjectPropertiesType;
 import org.etsi.uri.x01903.v13.SignedPropertiesType;
 import org.etsi.uri.x01903.v13.SignedSignaturePropertiesType;
 import org.etsi.uri.x01903.v13.SignerRoleType;
@@ -114,9 +116,22 @@ public class XAdESSignatureFacet impleme
         QualifyingPropertiesType qualifyingProperties = qualDoc.addNewQualifyingProperties();
         qualifyingProperties.setTarget("#" + signatureConfig.getPackageSignatureId());
 
+        createSignedProperties(signatureInfo, qualifyingProperties);
+
+        // add XAdES ds:Object
+        objects.add(addXadesObject(signatureInfo, document, qualifyingProperties));
+
+        // add XAdES ds:Reference
+        references.add(addXadesReference(signatureInfo));
+    }
+
+    /**
+     * Create the signedProperties. Subclasses can easily extend the signed properties.
+     */
+    protected SignedPropertiesType createSignedProperties(SignatureInfo signatureInfo, QualifyingPropertiesType qualifyingProperties) {
         // SignedProperties
         SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties();
-        signedProperties.setId(signatureConfig.getXadesSignatureId());
+        signedProperties.setId(signatureInfo.getSignatureConfig().getXadesSignatureId());
 
         // SignedSignatureProperties
         SignedSignaturePropertiesType signedSignatureProperties = signedProperties.addNewSignedSignatureProperties();
@@ -134,16 +149,14 @@ public class XAdESSignatureFacet impleme
         addPolicy(signatureInfo, signedSignatureProperties);
 
         // DataObjectFormat
-        addMimeTypes(signedProperties);
+        addMimeTypes(signatureInfo, signedProperties);
 
-        // add XAdES ds:Object
-        objects.add(addXadesObject(signatureInfo, document, qualifyingProperties));
+        addCommitmentType(signatureInfo, signedProperties);
 
-        // add XAdES ds:Reference
-        references.add(addXadesReference(signatureInfo));
+        return signedProperties;
     }
 
-    private void addSigningTime(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
+    protected void addSigningTime(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
         SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
         Calendar xmlGregorianCalendar = Calendar.getInstance(TimeZone.getTimeZone("Z"), Locale.ROOT);
         xmlGregorianCalendar.setTime(signatureConfig.getExecutionTime());
@@ -151,7 +164,7 @@ public class XAdESSignatureFacet impleme
         signedSignatureProperties.setSigningTime(xmlGregorianCalendar);
     }
 
-    private void addCertificate(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
+    protected void addCertificate(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
         SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
         List<X509Certificate> chain = signatureConfig.getSigningCertificateChain();
         if (chain == null || chain.isEmpty()) {
@@ -162,7 +175,7 @@ public class XAdESSignatureFacet impleme
         setCertID(certId, signatureConfig, signatureConfig.isXadesIssuerNameNoReverseOrder(), chain.get(0));
     }
 
-    private void addXadesRole(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
+    protected void addXadesRole(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
         SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
         String role = signatureConfig.getXadesRole();
         if (role == null || role.isEmpty()) {
@@ -178,7 +191,7 @@ public class XAdESSignatureFacet impleme
         insertXChild(claimedRole, roleString);
     }
 
-    private void addPolicy(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
+    protected void addPolicy(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) {
         SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
         SignaturePolicyService policyService = signatureConfig.getSignaturePolicyService();
         if (policyService == null) {
@@ -214,7 +227,7 @@ public class XAdESSignatureFacet impleme
         insertXChild(sigPolicyQualifier, spUriElement);
     }
 
-    private void addMimeTypes(SignedPropertiesType signedProperties) {
+    protected void addMimeTypes(SignatureInfo signatureInfo, SignedPropertiesType signedProperties) {
         if (dataObjectFormatMimeTypes.isEmpty()) {
             return;
         }
@@ -232,14 +245,40 @@ public class XAdESSignatureFacet impleme
         });
     }
 
-    private XMLObject addXadesObject(SignatureInfo signatureInfo, Document document, QualifyingPropertiesType qualifyingProperties) {
+    protected XMLObject addXadesObject(SignatureInfo signatureInfo, Document document, QualifyingPropertiesType qualifyingProperties) {
         Element qualDocEl = importNode(document, qualifyingProperties);
         List<XMLStructure> xadesObjectContent = singletonList(new DOMStructure(qualDocEl));
-        XMLObject xo = signatureInfo.getSignatureFactory().newXMLObject(xadesObjectContent, null, null, null);
-        return xo;
+        return signatureInfo.getSignatureFactory().newXMLObject(xadesObjectContent, null, null, null);
     }
 
-    private Reference addXadesReference(SignatureInfo signatureInfo) throws XMLSignatureException {
+    protected void addCommitmentType(SignatureInfo signatureInfo, SignedPropertiesType signedProperties) {
+        SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
+        String desc = signatureConfig.getSignatureDescription();
+        String commit = signatureConfig.getCommitmentType();
+        if (desc == null && commit == null) {
+            return;
+        }
+
+        SignedDataObjectPropertiesType dopt = signedProperties.isSetSignedDataObjectProperties()
+            ? signedProperties.getSignedDataObjectProperties()
+            : signedProperties.addNewSignedDataObjectProperties();
+
+        CommitmentTypeIndicationType cti = dopt.addNewCommitmentTypeIndication();
+        if (commit != null) {
+            ObjectIdentifierType ctid = cti.addNewCommitmentTypeId();
+            ctid.addNewIdentifier().setStringValue("http://uri.etsi.org/01903/v1.2.2#ProofOfOrigin");
+            ctid.setDescription(signatureConfig.getCommitmentType());
+        }
+        if (desc != null) {
+            cti.addNewAllSignedDataObjects();
+            AnyType ctq = cti.addNewCommitmentTypeQualifiers().addNewCommitmentTypeQualifier();
+            ctq.set(XmlString.Factory.newValue(desc));
+        }
+    }
+
+
+
+    protected Reference addXadesReference(SignatureInfo signatureInfo) throws XMLSignatureException {
         SignatureConfig signatureConfig = signatureInfo.getSignatureConfig();
         List<Transform> transforms = singletonList(newTransform(signatureInfo, CanonicalizationMethod.INCLUSIVE));
         return newReference(signatureInfo, "#"+signatureConfig.getXadesSignatureId(), transforms, XADES_TYPE);

Added: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/DummyKeystore.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/DummyKeystore.java?rev=1894820&view=auto
==============================================================================
--- poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/DummyKeystore.java (added)
+++ poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/DummyKeystore.java Sun Nov  7 23:48:15 2021
@@ -0,0 +1,355 @@
+/* ====================================================================
+   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 java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+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.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.poi.poifs.crypt.CryptoFunctions;
+import org.apache.poi.poifs.storage.RawDataUtil;
+import org.apache.poi.util.LocaleUtil;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLNumber;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+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.OCSPException;
+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.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;
+
+public class DummyKeystore {
+    public static class KeyCertPair {
+        private final PrivateKey key;
+        private final List<X509Certificate> x509chain;
+
+        public KeyCertPair(PrivateKey key, Certificate[] x509chain) {
+            this.key = key;
+            this.x509chain = Stream.of(x509chain).map(X509Certificate.class::cast).collect(Collectors.toList());
+        }
+
+        public PrivateKey getKey() {
+            return key;
+        }
+
+        public X509Certificate getX509() {
+            return x509chain.get(0);
+        }
+
+        public List<X509Certificate> getX509Chain() {
+            return x509chain;
+        }
+    }
+
+    private static final SecureRandom RANDOM = new SecureRandom();
+    private static final String DUMMY_ALIAS = "Test";
+    private static final String DUMMY_PASS = "test";
+
+    private final KeyStore keystore;
+
+    public DummyKeystore(String storePass) throws GeneralSecurityException, IOException {
+        this((File)null, storePass);
+    }
+
+    public DummyKeystore(File storeFile, String storePass) throws GeneralSecurityException, IOException {
+        CryptoFunctions.registerBouncyCastle();
+        keystore = KeyStore.getInstance("PKCS12");
+        try (InputStream fis = storeFile != null && storeFile.exists() ? new FileInputStream(storeFile) : null) {
+            keystore.load(fis, storePass.toCharArray());
+        }
+    }
+
+    public DummyKeystore(String pfxInput, String storePass) throws GeneralSecurityException, IOException {
+        CryptoFunctions.registerBouncyCastle();
+        keystore = KeyStore.getInstance("PKCS12");
+        try (InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput))) {
+            keystore.load(fis, storePass.toCharArray());
+        }
+    }
+
+    /**
+     * Create dummy key
+     * @return the alias of the dummy key
+     */
+    public KeyCertPair createDummyKey() throws GeneralSecurityException, IOException, OperatorCreationException {
+        return addEntry(DUMMY_ALIAS, DUMMY_PASS, 2048, 24);
+    }
+
+    public KeyCertPair addEntryFromPEM(File pemFile, String keyPass) throws IOException, CertificateException, KeyStoreException {
+        // see https://stackoverflow.com/questions/11787571/how-to-read-pem-file-to-get-private-and-public-key
+        PrivateKey key = null;
+        X509Certificate 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) {
+            throw new IOException("Please add private key and certificate in the PEM file.");
+        }
+
+        String alias = x509.getSubjectDN().getName();
+        keystore.setKeyEntry(alias, key, keyPass.toCharArray(), new Certificate[]{x509});
+
+        return new KeyCertPair(key, new Certificate[]{x509});
+    }
+
+
+    /**
+     * Add an entry with password, keySize and expiry values. Ignore if alias is already in keystore
+     * @param keySize multiple of 1024, e.g. 1024, 2048
+     */
+    public KeyCertPair addEntry(String keyAlias, String keyPass, int keySize, int expiryInMonths) throws GeneralSecurityException, IOException, OperatorCreationException {
+        if (!keystore.isKeyEntry(keyAlias)) {
+            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+            keyPairGenerator.initialize(new RSAKeyGenParameterSpec(keySize, RSAKeyGenParameterSpec.F4), RANDOM);
+            KeyPair pair = keyPairGenerator.generateKeyPair();
+
+            Date notBefore = new Date();
+            Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+            cal.add(Calendar.MONTH, expiryInMonths);
+            Date notAfter = cal.getTime();
+            KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
+
+            X509Certificate x509 = generateCertificate(pair.getPublic(), notBefore, notAfter, pair.getPrivate(), keyUsage);
+            keystore.setKeyEntry(keyAlias, pair.getPrivate(), keyPass.toCharArray(), new Certificate[]{x509});
+            return new KeyCertPair(pair.getPrivate(), new X509Certificate[]{x509});
+        } else {
+            return new KeyCertPair(getKey(keyAlias, keyPass), keystore.getCertificateChain(keyAlias));
+        }
+    }
+
+    public KeyCertPair getKeyPair(String keyAlias, String keyPass) throws GeneralSecurityException {
+        return new KeyCertPair(getKey(keyAlias, keyPass), keystore.getCertificateChain(keyAlias));
+    }
+
+    public PrivateKey getKey(String keyAlias, String keyPass) throws GeneralSecurityException {
+        return (PrivateKey)keystore.getKey(keyAlias, keyPass.toCharArray());
+    }
+
+    public X509Certificate getFirstX509(String alias) throws KeyStoreException {
+        return (X509Certificate)keystore.getCertificate(alias);
+    }
+
+    public void save(File storeFile, String storePass) throws IOException, GeneralSecurityException {
+        try (FileOutputStream fos = new FileOutputStream(storeFile)) {
+            keystore.store(fos, storePass.toCharArray());
+        }
+    }
+
+    public X509CRL generateCrl(KeyCertPair certPair)
+        throws GeneralSecurityException, IOException, OperatorCreationException {
+
+        PrivateKey issuerPrivateKey = certPair.getKey();
+        X509Certificate issuer = certPair.getX509();
+
+        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 X509Certificate generateCertificate(PublicKey subjectPublicKey,
+        Date notBefore, Date notAfter,
+        PrivateKey issuerPrivateKey,
+        KeyUsage keyUsage)
+        throws IOException, OperatorCreationException, CertificateException {
+        final String signatureAlgorithm = "SHA1withRSA";
+        final String subjectDn = "CN=Test";
+        X500Name 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 = exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
+
+        certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
+        certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
+
+        BasicConstraints bc = new BasicConstraints(0);
+        certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
+
+        if (null != keyUsage) {
+            certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
+        }
+
+        JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
+        signerBuilder.setProvider("BC");
+
+        X509CertificateHolder certHolder =
+            certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
+
+        return new JcaX509CertificateConverter().getCertificate(certHolder);
+    }
+
+    public OCSPResp createOcspResp(KeyCertPair certPair, long nonceTimeinMillis)
+    throws OperatorCreationException, OCSPException, CertificateEncodingException, IOException {
+        X509Certificate certificate = certPair.getX509();
+        X509Certificate issuerCertificate = certPair.getX509();
+        X509Certificate ocspResponderCertificate = certPair.getX509();
+        PrivateKey ocspResponderPrivateKey = certPair.getKey();
+
+
+        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;
+            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);
+    }
+
+}

Propchange: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/DummyKeystore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java
URL: http://svn.apache.org/viewvc/poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java?rev=1894820&r1=1894819&r2=1894820&view=diff
==============================================================================
--- poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java (original)
+++ poi/trunk/poi-ooxml/src/test/java/org/apache/poi/poifs/crypt/dsig/TestSignatureInfo.java Sun Nov  7 23:48:15 2021
@@ -30,42 +30,23 @@ import static org.junit.jupiter.api.Asse
 import static org.junit.jupiter.api.Assumptions.assumeFalse;
 import static org.junit.jupiter.api.Assumptions.assumeTrue;
 
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 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.GeneralSecurityException;
 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.Arrays;
 import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 import java.util.function.BiFunction;
@@ -89,8 +70,8 @@ import org.apache.poi.openxml4j.exceptio
 import org.apache.poi.openxml4j.opc.OPCPackage;
 import org.apache.poi.openxml4j.opc.PackageAccess;
 import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
-import org.apache.poi.poifs.crypt.CryptoFunctions;
 import org.apache.poi.poifs.crypt.HashAlgorithm;
+import org.apache.poi.poifs.crypt.dsig.DummyKeystore.KeyCertPair;
 import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;
 import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
 import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
@@ -102,7 +83,6 @@ import org.apache.poi.poifs.crypt.dsig.s
 import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
 import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
 import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
-import org.apache.poi.poifs.storage.RawDataUtil;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
 import org.apache.poi.util.ConditionalExecution.DisabledOnJreEx;
 import org.apache.poi.util.IOUtils;
@@ -121,43 +101,7 @@ import org.apache.xmlbeans.SystemPropert
 import org.apache.xmlbeans.XmlCursor;
 import org.apache.xmlbeans.XmlException;
 import org.apache.xmlbeans.XmlObject;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.CRLNumber;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.Extensions;
-import org.bouncycastle.asn1.x509.KeyUsage;
-import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-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.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.EncapsulatedPKIDataType;
 import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
@@ -180,15 +124,7 @@ import org.w3c.dom.Document;
 class TestSignatureInfo {
     private static final Logger LOG = LogManager.getLogger(TestSignatureInfo.class);
     private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance();
-
-    private static Calendar cal;
-    private KeyPair keyPair;
-    private X509Certificate x509;
-
-    @BeforeAll
-    public static void setUpClass() {
-        POITestCase.setImageIOCacheDir();
-    }
+    private static final String STORE_PASS = "test";
 
     @AfterAll
     public static void removeUserLocale() {
@@ -196,16 +132,13 @@ class TestSignatureInfo {
     }
 
     @BeforeAll
-    public static void initBouncy() {
-        CryptoFunctions.registerBouncyCastle();
+    public static void initXmlsec() {
+        POITestCase.setImageIOCacheDir();
 
         // Set cal to now ... only set to fixed date for debugging ...
         LocaleUtil.resetUserLocale();
         LocaleUtil.resetUserTimeZone();
 
-        cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
-        assertNotNull(cal);
-
         // don't run this test when we are using older Xerces as it triggers an XML Parser backwards compatibility issue
         // in the xmlsec jar file
         String additionalJar = System.getProperty("additionaljar");
@@ -460,12 +393,15 @@ class TestSignatureInfo {
     @Test
     @DisabledOnJreEx("1.8.0_292")
     void testSignSpreadsheetWithSignatureInfo() throws Exception {
-        initKeyPair();
         String testFile = "hello-world-unsigned.xlsx";
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
+
+
         try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
             SignatureConfig sic = new SignatureConfig();
-            sic.setKey(keyPair.getPrivate());
-            sic.setSigningCertificateChain(Collections.singletonList(x509));
+            sic.setKey(certPair.getKey());
+            sic.setSigningCertificateChain(certPair.getX509Chain());
             SignatureInfo si = new SignatureInfo();
             si.setOpcPackage(pkg);
             si.setSignatureConfig(sic);
@@ -490,23 +426,24 @@ class TestSignatureInfo {
 
         final String execTimestr;
 
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
 
         try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) {
 
-            initKeyPair();
-            final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
+            final X509CRL crl = ks.generateCrl(certPair);
 
             // setup
             SignatureConfig signatureConfig = new SignatureConfig();
-            signatureConfig.setKey(keyPair.getPrivate());
+            signatureConfig.setKey(certPair.getKey());
 
             /*
              * We need at least 2 certificates for the XAdES-C complete certificate
              * refs construction.
              */
             List<X509Certificate> certificateChain = new ArrayList<>();
-            certificateChain.add(x509);
-            certificateChain.add(x509);
+            certificateChain.add(certPair.getX509());
+            certificateChain.add(certPair.getX509());
             signatureConfig.setSigningCertificateChain(certificateChain);
 
             signatureConfig.addSignatureFacet(new OOXMLSignatureFacet());
@@ -558,7 +495,8 @@ class TestSignatureInfo {
 
             final RevocationData revocationData = new RevocationData();
             revocationData.addCRL(crl);
-            OCSPResp ocspResp = createOcspResp(x509, x509, x509, keyPair.getPrivate(), cal.getTimeInMillis());
+            Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+            OCSPResp ocspResp = ks.createOcspResp(certPair, cal.getTimeInMillis());
             revocationData.addOCSP(ocspResp.getEncoded());
 
             RevocationDataService revocationDataService = revocationChain -> revocationData;
@@ -686,27 +624,15 @@ class TestSignatureInfo {
     void testCertChain() throws Exception {
         final boolean isIBM = System.getProperty("java.vendor").contains("IBM");
 
-        KeyStore keystore = KeyStore.getInstance("PKCS12");
-        String password = "test";
-        try (InputStream is = testdata.openResourceAsStream("chaintest.pfx")) {
-            keystore.load(is, password.toCharArray());
-        }
-
-        Key key = keystore.getKey("poitest", password.toCharArray());
-        Certificate[] chainList = keystore.getCertificateChain("poitest");
-        List<X509Certificate> certChain = new ArrayList<>();
-        for (Certificate c : chainList) {
-            certChain.add((X509Certificate)c);
-        }
-        x509 = certChain.get(0);
-        keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
+        DummyKeystore ks = new DummyKeystore(testdata.getFile("chaintest.pfx"), STORE_PASS);
+        KeyCertPair certPair = ks.getKeyPair("poitest", "test");
 
         String testFile = "hello-world-unsigned.xlsx";
         try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
 
             SignatureConfig signatureConfig = new SignatureConfig();
-            signatureConfig.setKey(keyPair.getPrivate());
-            signatureConfig.setSigningCertificateChain(certChain);
+            signatureConfig.setKey(certPair.getKey());
+            signatureConfig.setSigningCertificateChain(certPair.getX509Chain());
             Calendar oldCal = LocaleUtil.getLocaleCalendar(2007, 7, 1);
             signatureConfig.setExecutionTime(oldCal.getTime());
             signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
@@ -735,11 +661,12 @@ class TestSignatureInfo {
     @DisabledOnJreEx("1.8.0_292")
     void testNonSha1() throws Exception {
         String testFile = "hello-world-unsigned.xlsx";
-        initKeyPair();
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
 
         SignatureConfig signatureConfig = new SignatureConfig();
-        signatureConfig.setKey(keyPair.getPrivate());
-        signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+        signatureConfig.setKey(certPair.getKey());
+        signatureConfig.setSigningCertificateChain(certPair.getX509Chain());
 
         HashAlgorithm[] testAlgo = {HashAlgorithm.sha224, HashAlgorithm.sha256
                 , HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160};
@@ -764,7 +691,8 @@ class TestSignatureInfo {
     @Test
     @DisabledOnJreEx("1.8.0_292")
     void bug65214() throws Exception {
-        initKeyPair();
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
 
         UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
         try (XWPFDocument doc = new XWPFDocument()) {
@@ -776,8 +704,8 @@ class TestSignatureInfo {
         }
 
         SignatureConfig signatureConfig = new SignatureConfig();
-        signatureConfig.setKey(keyPair.getPrivate());
-        signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+        signatureConfig.setKey(certPair.getKey());
+        signatureConfig.setSigningCertificateChain(certPair.getX509Chain());
         signatureConfig.setDigestAlgo(HashAlgorithm.sha256);
         try (OPCPackage pkg = OPCPackage.open(bos.toInputStream())) {
             SignatureInfo si = new SignatureInfo();
@@ -803,6 +731,9 @@ class TestSignatureInfo {
     @Test
     @DisabledOnJreEx("1.8.0_292")
     void bug58630() throws Exception {
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
+
         // test deletion of sheet 0 and signing
         File tpl = copy(testdata.getFile("bug58630.xlsx"));
         try (SXSSFWorkbook wb1 = new SXSSFWorkbook((XSSFWorkbook)WorkbookFactory.create(tpl), 10)) {
@@ -812,10 +743,9 @@ class TestSignatureInfo {
             wb1.write(os);
 
             try (OPCPackage pkg = OPCPackage.open(os.toInputStream())) {
-                initKeyPair();
                 SignatureConfig signatureConfig = new SignatureConfig();
-                signatureConfig.setKey(keyPair.getPrivate());
-                signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+                signatureConfig.setKey(certPair.getKey());
+                signatureConfig.setSigningCertificateChain(certPair.getX509Chain());
 
                 SignatureInfo si = new SignatureInfo();
                 si.setOpcPackage(pkg);
@@ -828,7 +758,7 @@ class TestSignatureInfo {
 
     @Test
     void testMultiSign() throws Exception {
-        cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+        Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
         cal.clear();
         cal.setTimeZone(LocaleUtil.TIMEZONE_UTC);
         cal.set(2018, Calendar.DECEMBER, 14);
@@ -892,13 +822,16 @@ class TestSignatureInfo {
     }
 
     private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi)
-            throws IOException, CertificateException, XMLSignatureException, MarshalException {
+        throws IOException, GeneralSecurityException, XMLSignatureException, MarshalException {
         assertNotNull(pkg);
-        initKeyFromPEM(testdata.getFile(pemFile));
+
+        DummyKeystore ks = new DummyKeystore("test");
+        KeyCertPair certPair = ks.addEntryFromPEM(testdata.getFile(pemFile), "test");
 
         SignatureConfig config = new SignatureConfig();
-        config.setKey(keyPair.getPrivate());
-        config.setSigningCertificateChain(Collections.singletonList(x509));
+        config.setKey(certPair.getKey());
+        config.setSigningCertificateChain(certPair.getX509Chain());
+        Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
         config.setExecutionTime(cal.getTime());
         config.setAllowMultipleSignatures(multi);
 
@@ -939,7 +872,8 @@ class TestSignatureInfo {
 
     @Test
     void createXAdES_T_65623() throws Exception {
-        initKeyPair();
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
 
         UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
         try (XSSFWorkbook wb = new XSSFWorkbook()) {
@@ -949,12 +883,12 @@ class TestSignatureInfo {
 
         SignatureConfig signatureConfig = new SignatureConfig();
         signatureConfig.setDigestAlgo(HashAlgorithm.sha256);
-        signatureConfig.setKey(keyPair.getPrivate());
-        signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+        signatureConfig.setKey(certPair.getKey());
+        signatureConfig.setSigningCertificateChain(certPair.getX509Chain());
 
         // mock tsp
         // signatureConfig.setTspUrl("http://timestamp.digicert.com");
-        final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
+        final X509CRL crl = ks.generateCrl(certPair);
         TimeStampService tspService = (signatureInfo, data, revocationData) -> {
             revocationData.addCRL(crl);
             return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252);
@@ -1024,7 +958,8 @@ class TestSignatureInfo {
     @DisabledOnJreEx("1.8.0_292")
     @Tag("scratchpad.ignore")
     void testSignatureImage() throws Exception {
-        initKeyPair();
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
 
         List<Supplier<SignatureLine>> lines = Arrays.asList(XSSFSignatureLine::new, XWPFSignatureLine::new);
         for (Supplier<SignatureLine> sup : lines) {
@@ -1051,8 +986,8 @@ class TestSignatureInfo {
 
             try (OPCPackage pkg = OPCPackage.open(signDoc, PackageAccess.READ_WRITE)) {
                 SignatureConfig sic = new SignatureConfig();
-                sic.setKey(keyPair.getPrivate());
-                sic.setSigningCertificateChain(Collections.singletonList(x509));
+                sic.setKey(certPair.getKey());
+                sic.setSigningCertificateChain(certPair.getX509Chain());
 
                 line.updateSignatureConfig(sic);
 
@@ -1115,13 +1050,52 @@ class TestSignatureInfo {
         return xls;
     }
 
+    @Test
+    void commitmentType65672() throws Exception {
+        DummyKeystore ks = new DummyKeystore(STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();
+
+        UnsynchronizedByteArrayOutputStream bos = new UnsynchronizedByteArrayOutputStream();
+        try (XSSFWorkbook wb = new XSSFWorkbook()) {
+            wb.createSheet().createRow(0).createCell(0).setCellValue("test");
+            wb.write(bos);
+        }
+
+        String commitType = "POI Test commit";
+        try (OPCPackage pkg = OPCPackage.open(bos.toInputStream())) {
+            SignatureConfig sc = new SignatureConfig();
+            sc.setKey(certPair.getKey());
+            sc.setSigningCertificateChain(certPair.getX509Chain());
+            sc.setCommitmentType(commitType);
+            SignatureInfo si = new SignatureInfo();
+            si.setSignatureConfig(sc);
+            si.setOpcPackage(pkg);
+            si.confirmSignature();
+            bos.reset();
+            pkg.save(bos);
+        }
+
+        try (OPCPackage pkg = OPCPackage.open(bos.toInputStream())) {
+            SignatureInfo si = new SignatureInfo();
+            SignatureConfig sc = new SignatureConfig();
+            sc.setUpdateConfigOnValidate(true);
+            si.setSignatureConfig(sc);
+            si.setOpcPackage(pkg);
+            si.verifySignature();
+            assertEquals(commitType, sc.getCommitmentType());
+        }
+    }
+
 
     private SignatureConfig prepareConfig(String pfxInput) throws Exception {
-        initKeyPair(pfxInput);
+        DummyKeystore ks = (pfxInput == null) ? new DummyKeystore(STORE_PASS) : new DummyKeystore(pfxInput, STORE_PASS);
+        KeyCertPair certPair = ks.createDummyKey();;
 
         SignatureConfig signatureConfig = new SignatureConfig();
-        signatureConfig.setKey(keyPair.getPrivate());
-        signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
+        signatureConfig.setKey(certPair.getKey());
+        signatureConfig.setSigningCertificateChain(certPair.getX509Chain());
+
+        Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
         signatureConfig.setExecutionTime(cal.getTime());
         signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
 
@@ -1164,77 +1138,6 @@ class TestSignatureInfo {
         assertEquals(signerCount, result.size());
     }
 
-    private void initKeyPair() throws Exception {
-        initKeyPair(null);
-    }
-
-    private void initKeyPair(String pfxInput) throws Exception {
-        final String alias = "Test";
-        final char[] password = "test".toCharArray();
-        File file = new File("build/test.pfx");
-        assertTrue(file.getParentFile().exists() || file.getParentFile().mkdir());
-
-        KeyStore keystore = KeyStore.getInstance("PKCS12");
-
-        if (pfxInput != null) {
-            try (InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput))) {
-                keystore.load(fis, password);
-            }
-        } else if (file.exists()) {
-            try (InputStream fis = new FileInputStream(file)) {
-                keystore.load(fis, password);
-            } catch (IOException e) {
-				throw new IOException("Failed when reading file " + file, e);
-			}
-        } else {
-            keystore.load(null, password);
-        }
-
-        if (keystore.isKeyEntry(alias)) {
-            Key key = keystore.getKey(alias, password);
-            x509 = (X509Certificate)keystore.getCertificate(alias);
-            keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
-        } else {
-            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 = generateCertificate(keyPair.getPublic(), notBefore, notAfter, keyPair.getPrivate(), keyUsage);
-
-            keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
-
-            if (pfxInput == null) {
-                try (FileOutputStream fos = new FileOutputStream(file)) {
-                    keystore.store(fos, password);
-                }
-            }
-        }
-    }
-
-    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.isEmpty()) {
@@ -1257,145 +1160,4 @@ class TestSignatureInfo {
 
         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,
-                                                       Date notBefore, Date notAfter,
-                                                       PrivateKey issuerPrivateKey,
-                                                       KeyUsage keyUsage)
-            throws IOException, OperatorCreationException, CertificateException {
-        final String signatureAlgorithm = "SHA1withRSA";
-        final String subjectDn = "CN=Test";
-        X500Name 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 = exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
-
-        certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
-        certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
-
-        BasicConstraints bc = new BasicConstraints(0);
-        certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
-
-        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,
-                                           X509Certificate issuerCertificate,
-                                           X509Certificate ocspResponderCertificate,
-                                           PrivateKey ocspResponderPrivateKey,
-                                           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;
-            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);
-    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org
For additional commands, e-mail: commits-help@poi.apache.org