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