aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java62
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java28
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java85
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java4
-rw-r--r--src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java6
-rw-r--r--src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java314
-rw-r--r--src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java357
-rw-r--r--test-data/xmldsign/bug63011_key1.pem45
-rw-r--r--test-data/xmldsign/bug63011_key2.pem45
9 files changed, 574 insertions, 372 deletions
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java
new file mode 100644
index 0000000000..707233b074
--- /dev/null
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java
@@ -0,0 +1,62 @@
+/* ====================================================================
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+==================================================================== */
+
+package org.apache.poi.poifs.crypt.dsig;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.poi.ooxml.POIXMLDocumentPart;
+import org.apache.poi.ooxml.POIXMLRelation;
+import org.apache.poi.openxml4j.opc.ContentTypes;
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
+
+public class DSigRelation extends POIXMLRelation {
+ /**
+ * A map to lookup POIXMLRelation by its relation type
+ */
+ private static final Map<String, DSigRelation> _table = new HashMap<>();
+
+ public static final DSigRelation ORIGIN_SIGS = new DSigRelation(
+ ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART,
+ PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN,
+ "/_xmlsignatures/origin.sigs", null
+ );
+
+ public static final DSigRelation SIG = new DSigRelation(
+ ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART,
+ PackageRelationshipTypes.DIGITAL_SIGNATURE,
+ "/_xmlsignatures/sig#.xml", null
+ );
+
+ private DSigRelation(String type, String rel, String defaultName, Class<? extends POIXMLDocumentPart> cls) {
+ super(type, rel, defaultName, cls);
+ _table.put(rel, this);
+ }
+
+ /**
+ * Get POIXMLRelation by relation type
+ *
+ * @param rel relation type, for example,
+ * <code>http://schemas.openxmlformats.org/officeDocument/2006/relationships/image</code>
+ * @return registered POIXMLRelation or null if not found
+ */
+ public static DSigRelation getInstance(String rel) {
+ return _table.get(rel);
+ }
+
+}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
index 8e276f896e..71a42ef9e1 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java
@@ -174,6 +174,13 @@ public class SignatureConfig {
*/
private boolean updateConfigOnValidate = false;
+ /**
+ * if true, the signature is added to the existing signatures
+ *
+ * @since POI 4.0.2
+ */
+ private boolean allowMultipleSignatures = false;
+
/**
* Inits and checks the config object.
@@ -1008,4 +1015,25 @@ public class SignatureConfig {
public void setUpdateConfigOnValidate(boolean updateConfigOnValidate) {
this.updateConfigOnValidate = updateConfigOnValidate;
}
+
+ /**
+ * @return true, if multiple signatures can be attached
+ *
+ * @since POI 4.0.2
+ */
+ public boolean isAllowMultipleSignatures() {
+ return allowMultipleSignatures;
+ }
+
+ /**
+ * Activate multiple signatures
+ *
+ * @param allowMultipleSignatures if true, the signature will be added,
+ * otherwise all existing signatures will be replaced by the current
+ *
+ * @since POI 4.0.2
+ */
+ public void setAllowMultipleSignatures(boolean allowMultipleSignatures) {
+ this.allowMultipleSignatures = allowMultipleSignatures;
+ }
} \ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
index 20c347d4d7..1cb6e3ca1d 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java
@@ -59,7 +59,6 @@ import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
-import org.apache.poi.openxml4j.opc.ContentTypes;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackagePartName;
@@ -377,9 +376,8 @@ public class SignatureInfo implements SignatureConfigurable {
xmlSignContext.setURIDereferencer(uriDereferencer);
}
- for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
- xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue());
- }
+ signatureConfig.getNamespacePrefixes().forEach(xmlSignContext::putNamespacePrefix);
+
xmlSignContext.setDefaultNamespacePrefix("");
// signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS));
@@ -516,9 +514,7 @@ public class SignatureInfo implements SignatureConfigurable {
protected void writeDocument(Document document) throws MarshalException {
XmlOptions xo = new XmlOptions();
Map<String,String> namespaceMap = new HashMap<>();
- for(Map.Entry<String,String> entry : signatureConfig.getNamespacePrefixes().entrySet()){
- namespaceMap.put(entry.getValue(), entry.getKey());
- }
+ signatureConfig.getNamespacePrefixes().forEach((k,v) -> namespaceMap.put(v,k));
xo.setSaveSuggestedPrefixes(namespaceMap);
xo.setUseDefaultNamespace();
@@ -530,43 +526,58 @@ public class SignatureInfo implements SignatureConfigurable {
*/
OPCPackage pkg = signatureConfig.getOpcPackage();
- PackagePartName sigPartName, sigsPartName;
try {
- // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
- sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");
// <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>
- sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");
- } catch (InvalidFormatException e) {
- throw new MarshalException(e);
- }
+ final DSigRelation originDesc = DSigRelation.ORIGIN_SIGS;
+ PackagePartName originPartName = PackagingURIHelper.createPartName(originDesc.getFileName(0));
+
+ PackagePart originPart = pkg.getPart(originPartName);
+ if (originPart == null) {
+ // touch empty marker file
+ originPart = pkg.createPart(originPartName, originDesc.getContentType());
+ pkg.addRelationship(originPartName, TargetMode.INTERNAL, originDesc.getRelation());
+ }
- PackagePart sigPart = pkg.getPart(sigPartName);
- if (sigPart == null) {
- sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART);
- }
+ // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
+ final DSigRelation sigDesc = DSigRelation.SIG;
+ int nextSigIdx = pkg.getUnusedPartIndex(sigDesc.getDefaultFileName());
+
+ if (!signatureConfig.isAllowMultipleSignatures()) {
+ PackageRelationshipCollection prc = originPart.getRelationshipsByType(sigDesc.getRelation());
+ for (int i=2; i<nextSigIdx; i++) {
+ PackagePartName pn = PackagingURIHelper.createPartName(sigDesc.getFileName(i));
+ for (PackageRelationship rel : prc) {
+ PackagePart pp = originPart.getRelatedPart(rel);
+ if (pp.getPartName().equals(pn)) {
+ originPart.removeRelationship(rel.getId());
+ prc.removeRelationship(rel.getId());
+ break;
+ }
+ }
- try {
- OutputStream os = sigPart.getOutputStream();
- SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);
- sigDoc.save(os, xo);
- os.close();
- } catch (Exception e) {
- throw new MarshalException("Unable to write signature document", e);
- }
+ pkg.removePart(pkg.getPart(pn));
+ }
+ nextSigIdx = 1;
+ }
- PackagePart sigsPart = pkg.getPart(sigsPartName);
- if (sigsPart == null) {
- // touch empty marker file
- sigsPart = pkg.createPart(sigsPartName, ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART);
- }
- PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
- for (PackageRelationship pr : relCol) {
- pkg.removeRelationship(pr.getId());
- }
- pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
+ PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx));
+ PackagePart sigPart = pkg.getPart(sigPartName);
+ if (sigPart == null) {
+ sigPart = pkg.createPart(sigPartName, sigDesc.getContentType());
+ originPart.addRelationship(sigPartName, TargetMode.INTERNAL, sigDesc.getRelation());
+ } else {
+ sigPart.clear();
+ }
+
+ try (OutputStream os = sigPart.getOutputStream()) {
+ SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);
+ sigDoc.save(os, xo);
+ }
- sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);
+ } catch (Exception e) {
+ throw new MarshalException("Unable to write signature document", e);
+ }
}
private Element getDsigElement(final Document document, final String localName) {
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
index 81e5b21971..e68b7a72d0 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java
@@ -185,9 +185,7 @@ public class SignaturePart {
final Map<String,String> nsMap = new HashMap<>();
{
- for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
- nsMap.put(me.getValue(), me.getKey());
- }
+ signatureConfig.getNamespacePrefixes().forEach((k,v) -> nsMap.put(v,k));
nsMap.put("dsss", MS_DIGSIG_NS);
nsMap.put("ds", XML_DIGSIG_NS);
}
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java
index b3bfe9ecfb..e7797e942d 100644
--- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java
+++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java
@@ -129,10 +129,8 @@ public class KeyInfoSignatureFacet extends SignatureFacet {
DOMSignContext domSignContext = (nextSibling == null)
? new DOMSignContext(key, n)
: new DOMSignContext(key, n, nextSibling);
- for (Map.Entry<String,String> me : signatureConfig.getNamespacePrefixes().entrySet()) {
- domSignContext.putNamespacePrefix(me.getKey(), me.getValue());
- }
-
+ signatureConfig.getNamespacePrefixes().forEach(domSignContext::putNamespacePrefix);
+
DOMStructure domStructure = new DOMStructure(n);
domKeyInfo.marshal(domStructure, domSignContext);
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
deleted file mode 100644
index 381c6c202d..0000000000
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java
+++ /dev/null
@@ -1,314 +0,0 @@
-/* ====================================================================
- Licensed to the Apache Software Foundation (ASF) under one or more
- contributor license agreements. See the NOTICE file distributed with
- this work for additional information regarding copyright ownership.
- The ASF licenses this file to You under the Apache License, Version 2.0
- (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-==================================================================== */
-package org.apache.poi.poifs.crypt;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.StringWriter;
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.cert.CRLException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAKeyGenParameterSpec;
-import java.util.Date;
-
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Result;
-import javax.xml.transform.Source;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-
-import org.bouncycastle.asn1.DERIA5String;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
-import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
-import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
-import org.bouncycastle.asn1.x509.BasicConstraints;
-import org.bouncycastle.asn1.x509.CRLNumber;
-import org.bouncycastle.asn1.x509.CRLReason;
-import org.bouncycastle.asn1.x509.DistributionPoint;
-import org.bouncycastle.asn1.x509.DistributionPointName;
-import org.bouncycastle.asn1.x509.Extension;
-import org.bouncycastle.asn1.x509.Extensions;
-import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.asn1.x509.GeneralNames;
-import org.bouncycastle.asn1.x509.KeyUsage;
-import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
-import org.bouncycastle.cert.X509CRLHolder;
-import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.X509ExtensionUtils;
-import org.bouncycastle.cert.X509v2CRLBuilder;
-import org.bouncycastle.cert.X509v3CertificateBuilder;
-import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
-import org.bouncycastle.cert.ocsp.BasicOCSPResp;
-import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
-import org.bouncycastle.cert.ocsp.CertificateID;
-import org.bouncycastle.cert.ocsp.CertificateStatus;
-import org.bouncycastle.cert.ocsp.OCSPReq;
-import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
-import org.bouncycastle.cert.ocsp.OCSPResp;
-import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
-import org.bouncycastle.cert.ocsp.Req;
-import org.bouncycastle.cert.ocsp.RevokedStatus;
-import org.bouncycastle.crypto.params.RSAKeyParameters;
-import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
-import org.bouncycastle.operator.ContentSigner;
-import org.bouncycastle.operator.DigestCalculator;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-
-public class PkiTestUtils {
-
- private PkiTestUtils() {
- super();
- }
-
- static KeyPair generateKeyPair() throws Exception {
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
- SecureRandom random = new SecureRandom();
- keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024,
- RSAKeyGenParameterSpec.F4), random);
- return keyPairGenerator.generateKeyPair();
- }
-
- static X509Certificate generateCertificate(PublicKey subjectPublicKey,
- String subjectDn, Date notBefore, Date notAfter,
- X509Certificate issuerCertificate, PrivateKey issuerPrivateKey,
- boolean caFlag, int pathLength, String crlUri, String ocspUri,
- KeyUsage keyUsage)
- throws IOException, OperatorCreationException, CertificateException
- {
- String signatureAlgorithm = "SHA1withRSA";
- X500Name issuerName;
- if (issuerCertificate != null) {
- issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer();
- } else {
- issuerName = new X500Name(subjectDn);
- }
-
- RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
- RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
-
- SubjectPublicKeyInfo subjectPublicKeyInfo =
- SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
-
- DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
- .setProvider("BC").build().get(CertificateID.HASH_SHA1);
-
- X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
- issuerName
- , new BigInteger(128, new SecureRandom())
- , notBefore
- , notAfter
- , new X500Name(subjectDn)
- , subjectPublicKeyInfo
- );
-
- X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
- SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
- AuthorityKeyIdentifier autKeyId = (issuerCertificate != null)
- ? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded()))
- : exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
-
- certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
- certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
-
- if (caFlag) {
- BasicConstraints bc;
-
- if (-1 == pathLength) {
- bc = new BasicConstraints(true);
- } else {
- bc = new BasicConstraints(pathLength);
- }
- certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
- }
-
- if (null != crlUri) {
- int uri = GeneralName.uniformResourceIdentifier;
- DERIA5String crlUriDer = new DERIA5String(crlUri);
- GeneralName gn = new GeneralName(uri, crlUriDer);
-
- DERSequence gnDer = new DERSequence(gn);
- GeneralNames gns = GeneralNames.getInstance(gnDer);
-
- DistributionPointName dpn = new DistributionPointName(0, gns);
- DistributionPoint distp = new DistributionPoint(dpn, null, null);
- DERSequence distpDer = new DERSequence(distp);
- certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer);
- }
-
- if (null != ocspUri) {
- int uri = GeneralName.uniformResourceIdentifier;
- GeneralName ocspName = new GeneralName(uri, ocspUri);
-
- AuthorityInformationAccess authorityInformationAccess =
- new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName);
-
- certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess);
- }
-
- if (null != keyUsage) {
- certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
- }
-
- JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
- signerBuilder.setProvider("BC");
-
- X509CertificateHolder certHolder =
- certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
-
- /*
- * Next certificate factory trick is needed to make sure that the
- * certificate delivered to the caller is provided by the default
- * security provider instead of BouncyCastle. If we don't do this trick
- * we might run into trouble when trying to use the CertPath validator.
- */
-// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
-// certificate = (X509Certificate) certificateFactory
-// .generateCertificate(new ByteArrayInputStream(certificate
-// .getEncoded()));
- return new JcaX509CertificateConverter().getCertificate(certHolder);
- }
-
- static Document loadDocument(InputStream documentInputStream)
- throws ParserConfigurationException, SAXException, IOException {
- InputSource inputSource = new InputSource(documentInputStream);
- DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory
- .newInstance();
- documentBuilderFactory.setNamespaceAware(true);
- DocumentBuilder documentBuilder = documentBuilderFactory
- .newDocumentBuilder();
- return documentBuilder.parse(inputSource);
- }
-
- static String toString(Node dom) throws TransformerException {
- Source source = new DOMSource(dom);
- StringWriter stringWriter = new StringWriter();
- Result result = new StreamResult(stringWriter);
- TransformerFactory transformerFactory = TransformerFactory
- .newInstance();
- Transformer transformer = transformerFactory.newTransformer();
- /*
- * We have to omit the ?xml declaration if we want to embed the
- * document.
- */
- transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
- transformer.transform(source, result);
- return stringWriter.getBuffer().toString();
- }
-
- public static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey)
- throws CertificateEncodingException, IOException, CRLException, OperatorCreationException {
-
- X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
- X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
- crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
- JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
-
- CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
-
- crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
- X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
- return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
- }
-
- public static OCSPResp createOcspResp(X509Certificate certificate,
- boolean revoked, X509Certificate issuerCertificate,
- X509Certificate ocspResponderCertificate,
- PrivateKey ocspResponderPrivateKey, String signatureAlgorithm,
- long nonceTimeinMillis)
- throws Exception {
- DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
- .setProvider("BC").build().get(CertificateID.HASH_SHA1);
- X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
- CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
-
- // request
- //create a nonce to avoid replay attack
- BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
- DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
- Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
- Extensions exts = new Extensions(ext);
-
- OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
- ocspReqBuilder.addRequest(certId);
- ocspReqBuilder.setRequestExtensions(exts);
- OCSPReq ocspReq = ocspReqBuilder.build();
-
-
- SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
- (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
-
- BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
- basicOCSPRespBuilder.setResponseExtensions(exts);
-
- // request processing
- Req[] requestList = ocspReq.getRequestList();
- for (Req ocspRequest : requestList) {
- CertificateID certificateID = ocspRequest.getCertID();
- CertificateStatus certificateStatus = CertificateStatus.GOOD;
- if (revoked) {
- certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn);
- }
- basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
- }
-
- // basic response generation
- X509CertificateHolder[] chain = null;
- if (!ocspResponderCertificate.equals(issuerCertificate)) {
- // TODO: HorribleProxy can't convert array input params yet
- chain = new X509CertificateHolder[] {
- new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
- issuerHolder
- };
- }
-
- ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
- .setProvider("BC").build(ocspResponderPrivateKey);
- BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
-
-
- OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
-
- return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
- }
-}
diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
index 4b9cff9b3c..0b3525ee1b 100644
--- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
+++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -36,19 +37,30 @@ import java.io.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.cert.X509CRL;
import java.security.cert.X509Certificate;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.RSAKeyGenParameterSpec;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -56,7 +68,9 @@ import java.util.Date;
import java.util.Iterator;
import java.util.List;
+import javax.xml.crypto.MarshalException;
import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.XMLSignatureException;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
@@ -90,8 +104,53 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.xmlbeans.SystemProperties;
import org.apache.xmlbeans.XmlObject;
+import org.bouncycastle.asn1.DERIA5String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AuthorityInformationAccess;
+import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.CRLNumber;
+import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.DistributionPoint;
+import org.bouncycastle.asn1.x509.DistributionPointName;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509ExtensionUtils;
+import org.bouncycastle.cert.X509v2CRLBuilder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.ocsp.BasicOCSPResp;
+import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.CertificateID;
+import org.bouncycastle.cert.ocsp.CertificateStatus;
+import org.bouncycastle.cert.ocsp.OCSPReq;
+import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
import org.bouncycastle.cert.ocsp.OCSPResp;
+import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
+import org.bouncycastle.cert.ocsp.Req;
+import org.bouncycastle.cert.ocsp.RevokedStatus;
+import org.bouncycastle.crypto.params.RSAKeyParameters;
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
import org.junit.AfterClass;
@@ -408,7 +467,7 @@ public class TestSignatureInfo {
try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) {
initKeyPair("Test", "CN=Test");
- final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate());
+ final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
// setup
SignatureConfig signatureConfig = new SignatureConfig();
@@ -481,7 +540,7 @@ public class TestSignatureInfo {
final RevocationData revocationData = new RevocationData();
revocationData.addCRL(crl);
- OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false,
+ OCSPResp ocspResp = createOcspResp(x509, false,
x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis());
revocationData.addOCSP(ocspResp.getEncoded());
@@ -721,18 +780,84 @@ public class TestSignatureInfo {
@Test
public void testMultiSign() throws Exception {
- initKeyPair("KeyA", "CN=KeyA");
- //KeyPair keyPairA = keyPair;
- //X509Certificate x509A = x509;
- initKeyPair("KeyB", "CN=KeyB");
- //KeyPair keyPairB = keyPair;
- //X509Certificate x509B = x509;
-
- File tpl = copy(testdata.getFile("bug58630.xlsx"));
+ cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
+ cal.clear();
+ cal.setTimeZone(LocaleUtil.TIMEZONE_UTC);
+ cal.set(2018, Calendar.DECEMBER, 14);
+
+ // test signing with separate opened packages
+ File tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
+ try (OPCPackage pkg = OPCPackage.open(tpl)) {
+ signPkg63011(pkg, "bug63011_key1.pem", true);
+ }
+
+ try (OPCPackage pkg = OPCPackage.open(tpl)) {
+ signPkg63011(pkg, "bug63011_key2.pem", true);
+ }
+
+ verifyPkg63011(tpl, true);
+
+ // test signing with single opened package
+ tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
+ try (OPCPackage pkg = OPCPackage.open(tpl)) {
+ signPkg63011(pkg, "bug63011_key1.pem", true);
+ signPkg63011(pkg, "bug63011_key2.pem", true);
+ }
+
+ verifyPkg63011(tpl, true);
+
try (OPCPackage pkg = OPCPackage.open(tpl)) {
- //SignatureConfig signatureConfig = new SignatureConfig();
- assertNotNull(pkg);
+ signPkg63011(pkg, "bug63011_key1.pem", true);
+ signPkg63011(pkg, "bug63011_key2.pem", false);
}
+
+ verifyPkg63011(tpl, false);
+ }
+
+ private void verifyPkg63011(File tpl, boolean multi) throws InvalidFormatException, IOException {
+ try (OPCPackage pkg = OPCPackage.open(tpl, PackageAccess.READ)) {
+ SignatureConfig sic = new SignatureConfig();
+ sic.setOpcPackage(pkg);
+ SignatureInfo si = new SignatureInfo();
+ si.setSignatureConfig(sic);
+ List<X509Certificate> result = new ArrayList<>();
+ for (SignaturePart sp : si.getSignatureParts()) {
+ if (sp.validate()) {
+ result.add(sp.getSigner());
+ }
+ }
+
+ assertNotNull(result);
+
+ if (multi) {
+ assertEquals(2, result.size());
+ assertEquals("CN=Muj Klic", result.get(0).getSubjectDN().toString());
+ assertEquals("CN=My Second key", result.get(1).getSubjectDN().toString());
+ } else {
+ assertEquals(1, result.size());
+ assertEquals("CN=My Second key", result.get(0).getSubjectDN().toString());
+ }
+
+ assertTrue(si.verifySignature());
+ pkg.revert();
+ }
+ }
+
+ private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi)
+ throws IOException, CertificateException, XMLSignatureException, MarshalException {
+ assertNotNull(pkg);
+ initKeyFromPEM(testdata.getFile(pemFile));
+
+ SignatureConfig config = new SignatureConfig();
+ config.setKey(keyPair.getPrivate());
+ config.setSigningCertificateChain(Collections.singletonList(x509));
+ config.setExecutionTime(cal.getTime());
+ config.setAllowMultipleSignatures(multi);
+ config.setOpcPackage(pkg);
+
+ SignatureInfo si = new SignatureInfo();
+ si.setSignatureConfig(config);
+ si.confirmSignature();
}
@Test
@@ -829,14 +954,14 @@ public class TestSignatureInfo {
x509 = (X509Certificate)keystore.getCertificate(alias);
keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
} else {
- keyPair = PkiTestUtils.generateKeyPair();
+ keyPair = generateKeyPair();
Date notBefore = cal.getTime();
Calendar cal2 = (Calendar)cal.clone();
cal2.add(Calendar.YEAR, 1);
Date notAfter = cal2.getTime();
KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
- x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN
+ x509 = generateCertificate(keyPair.getPublic(), subjectDN
, notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage);
keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
@@ -849,6 +974,27 @@ public class TestSignatureInfo {
}
}
+ private void initKeyFromPEM(File pemFile) throws IOException, CertificateException {
+ // see https://stackoverflow.com/questions/11787571/how-to-read-pem-file-to-get-private-and-public-key
+ PrivateKey key = null;
+ x509 = null;
+
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pemFile), StandardCharsets.ISO_8859_1))) {
+ PEMParser parser = new PEMParser(br);
+ for (Object obj; (obj = parser.readObject()) != null; ) {
+ if (obj instanceof PrivateKeyInfo) {
+ key = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)obj);
+ } else if (obj instanceof X509CertificateHolder) {
+ x509 = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)obj);
+ }
+ }
+ }
+
+ if (key != null && x509 != null) {
+ keyPair = new KeyPair(x509.getPublicKey(), key);
+ }
+ }
+
private static File copy(File input) throws IOException {
String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1");
if (extension == null || extension.isEmpty()) {
@@ -872,4 +1018,187 @@ public 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,
+ String subjectDn, Date notBefore, Date notAfter,
+ X509Certificate issuerCertificate, PrivateKey issuerPrivateKey,
+ boolean caFlag, int pathLength, String crlUri, String ocspUri,
+ KeyUsage keyUsage)
+ throws IOException, OperatorCreationException, CertificateException
+ {
+ String signatureAlgorithm = "SHA1withRSA";
+ X500Name issuerName;
+ if (issuerCertificate != null) {
+ issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer();
+ } else {
+ issuerName = new X500Name(subjectDn);
+ }
+
+ RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
+ RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
+
+ SubjectPublicKeyInfo subjectPublicKeyInfo =
+ SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
+
+ DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
+ .setProvider("BC").build().get(CertificateID.HASH_SHA1);
+
+ X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
+ issuerName
+ , new BigInteger(128, new SecureRandom())
+ , notBefore
+ , notAfter
+ , new X500Name(subjectDn)
+ , subjectPublicKeyInfo
+ );
+
+ X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
+ SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
+ AuthorityKeyIdentifier autKeyId = (issuerCertificate != null)
+ ? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded()))
+ : exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
+
+ certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
+ certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
+
+ if (caFlag) {
+ BasicConstraints bc;
+
+ if (-1 == pathLength) {
+ bc = new BasicConstraints(true);
+ } else {
+ bc = new BasicConstraints(pathLength);
+ }
+ certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
+ }
+
+ if (null != crlUri) {
+ int uri = GeneralName.uniformResourceIdentifier;
+ DERIA5String crlUriDer = new DERIA5String(crlUri);
+ GeneralName gn = new GeneralName(uri, crlUriDer);
+
+ DERSequence gnDer = new DERSequence(gn);
+ GeneralNames gns = GeneralNames.getInstance(gnDer);
+
+ DistributionPointName dpn = new DistributionPointName(0, gns);
+ DistributionPoint distp = new DistributionPoint(dpn, null, null);
+ DERSequence distpDer = new DERSequence(distp);
+ certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer);
+ }
+
+ if (null != ocspUri) {
+ int uri = GeneralName.uniformResourceIdentifier;
+ GeneralName ocspName = new GeneralName(uri, ocspUri);
+
+ AuthorityInformationAccess authorityInformationAccess =
+ new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName);
+
+ certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess);
+ }
+
+ if (null != keyUsage) {
+ certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
+ }
+
+ JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
+ signerBuilder.setProvider("BC");
+
+ X509CertificateHolder certHolder =
+ certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
+
+ /*
+ * Next certificate factory trick is needed to make sure that the
+ * certificate delivered to the caller is provided by the default
+ * security provider instead of BouncyCastle. If we don't do this trick
+ * we might run into trouble when trying to use the CertPath validator.
+ */
+// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+// certificate = (X509Certificate) certificateFactory
+// .generateCertificate(new ByteArrayInputStream(certificate
+// .getEncoded()));
+ return new JcaX509CertificateConverter().getCertificate(certHolder);
+ }
+
+ private static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey)
+ throws CertificateEncodingException, IOException, CRLException, OperatorCreationException {
+
+ X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
+ X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
+ crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
+ JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
+
+ CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
+
+ crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
+ X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
+ return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
+ }
+
+ private static OCSPResp createOcspResp(X509Certificate certificate,
+ boolean revoked, X509Certificate issuerCertificate,
+ X509Certificate ocspResponderCertificate,
+ PrivateKey ocspResponderPrivateKey, String signatureAlgorithm,
+ long nonceTimeinMillis)
+ throws Exception {
+ DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
+ .setProvider("BC").build().get(CertificateID.HASH_SHA1);
+ X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
+ CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
+
+ // request
+ //create a nonce to avoid replay attack
+ BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
+ DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
+ Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
+ Extensions exts = new Extensions(ext);
+
+ OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
+ ocspReqBuilder.addRequest(certId);
+ ocspReqBuilder.setRequestExtensions(exts);
+ OCSPReq ocspReq = ocspReqBuilder.build();
+
+
+ SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
+ (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
+
+ BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
+ basicOCSPRespBuilder.setResponseExtensions(exts);
+
+ // request processing
+ Req[] requestList = ocspReq.getRequestList();
+ for (Req ocspRequest : requestList) {
+ CertificateID certificateID = ocspRequest.getCertID();
+ CertificateStatus certificateStatus = CertificateStatus.GOOD;
+ if (revoked) {
+ certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn);
+ }
+ basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
+ }
+
+ // basic response generation
+ X509CertificateHolder[] chain = null;
+ if (!ocspResponderCertificate.equals(issuerCertificate)) {
+ // TODO: HorribleProxy can't convert array input params yet
+ chain = new X509CertificateHolder[] {
+ new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
+ issuerHolder
+ };
+ }
+
+ ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
+ .setProvider("BC").build(ocspResponderPrivateKey);
+ BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
+
+
+ OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
+
+ return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
+ }
}
diff --git a/test-data/xmldsign/bug63011_key1.pem b/test-data/xmldsign/bug63011_key1.pem
new file mode 100644
index 0000000000..dac21223fb
--- /dev/null
+++ b/test-data/xmldsign/bug63011_key1.pem
@@ -0,0 +1,45 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCr+sR6OR6R49ni
+/w/jjCSOewGvOjzJelKaGoT/cYmvx6JFrFwy3PV7FZEhRPQCKvE+cCOYAwBFTC7K
+uN0h/cen7tVF/idVWlzOTU/3Tdy/J7sb2KfToHvjxKkeMUl1SHvf1et+3HQHQSII
+Vf8A3cu0cL91sYSd32ZZYm2TeKxbTc1GO/R9OzvSOxCQaIVbAiMQvY04EQZb0F7/
+eBRj8UAdMbsTRVYuOSWrXX2DL5U3/nq/c8F0ozeyUOEbzWacZ83OcDEFC0hCrGyy
+ptKtEQBWhbgSZ9BRc5nZ9q97VoDOLwrb24zBI45AsRBvwRAhf5DLV5cOw6X8MdQd
+p0pKcN9HAgMBAAECggEAVcPyNe3EZAcYQw7mMplSJcgMOAG4DNY22Wk+SFGr04Cg
+WVSyih8NQPupa8kCUw5tTrEH3ygn+2cZsrlsdiYkaog9zfEIVpWA0NVXesJWwvGi
+aymp0G0pO5Z4rHjx5E5okGETVynDp1aBDV0tlZYGn47WvG/x7fVaClt+v9ufQMyD
+snUEf2lpWhUtoUhlOnDZr791QPLUkVi0roLSD0M2vPM1cbV8EO4SdhOOyTDCNFvo
+IAcysKV0UBN/TJOyB7FyDw1cJtJ9Ja/X9YoLcW+0fTZNhlYq6VrowCQyEchlb8Pz
+bRhUJc6oGffpaxtrMk8pmTSScpSTV5PtB3E4t6mYYQKBgQDVPX+GdnuNyyNdvF3g
+SplJZdyAjW21mZ4gdYLxyGHIsgTrz4SKJ49HZoaDdwwpQRZWnQC7gxCXbJ2IHwMF
++Qz7pp7eoRhQ3eBqQxfIypOeRSC6n3OIOYxIJlcV3O/PTGwu2eJhxck1ZG/j90DW
+NDo9hQYLFpD2xGiaWd611HncsQKBgQDOdzBZF/1PbUh/zz1nwAVPT85IM1vwU85k
+LIQUY/P+LFUvSa7gk3sabn6Zop5Cv0z+I1j5NW1lobLR4u02jqIb/dqeJ+XOt27n
+UMiYI/6WBA6Mkrf9j10BpFuSBWYotpkr8PZcog1v+FnODcOsG6Nl7fiJjkpPD6AF
+MCIHip4ZdwKBgC4fKhkMQXcOy+x3VJqxp+v/My0+6c7QlioRIKxpGfVNw9C5RsKX
+Ad+ApnGC60d1A37iYIkuNQV7gasygfXlw1Ae3tfqYhcDlomFT3ynjDw8WXLkEBoT
+0Gq+mDFrYxckQXX0vIlHPVjmC2l0TjrGex5ZSlBVpi8pljJkY85SUbVRAoGBAICq
+BCYxbfl2aAzGEEU8g9KWMD4MS4osH92LZE/0rhPCet96MpHfNoMVQq3pimicIJXr
+X0IGSoNgTjjACwXJwzpga0HOKUc2RtW+IRO2squ4IXz23dQU6GfijfIkjTJoAHJC
+urSlhGw3v3dKWptBqgUWVKEcXDCC0z0Ibtx2ROonAoGAWcRq3xpkbeR4K4yd+K9w
+4CBi8sdlz48tmZDSVigjq27Jk/6Ttk5K6x8M1rhUWYZ+ak1Y2NVV24oqnIdtIZrE
+BdgvzJCJN3Yx3FMYzUVqONFfGyppi0E7WvSYX0qlXKeZDcjgp1ORCOhiQL3ufE1+
+LdXLunLfjJzNnvX7XoTAyyE=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICojCCAYqgAwIBAgIEXA5r3jANBgkqhkiG9w0BAQsFADATMREwDwYDVQQDDAhN
+dWogS2xpYzAeFw0xODEyMTAxMzM2MzBaFw0xOTEyMTAxMzM2MzBaMBMxETAPBgNV
+BAMMCE11aiBLbGljMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/rE
+ejkekePZ4v8P44wkjnsBrzo8yXpSmhqE/3GJr8eiRaxcMtz1exWRIUT0AirxPnAj
+mAMARUwuyrjdIf3Hp+7VRf4nVVpczk1P903cvye7G9in06B748SpHjFJdUh739Xr
+ftx0B0EiCFX/AN3LtHC/dbGEnd9mWWJtk3isW03NRjv0fTs70jsQkGiFWwIjEL2N
+OBEGW9Be/3gUY/FAHTG7E0VWLjklq119gy+VN/56v3PBdKM3slDhG81mnGfNznAx
+BQtIQqxssqbSrREAVoW4EmfQUXOZ2fave1aAzi8K29uMwSOOQLEQb8EQIX+Qy1eX
+DsOl/DHUHadKSnDfRwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAJ5hi+ZinJ4QOA
+hC2sPuawdj6k+C3+CXNpRV5eMagBJZYPMsUfEPrP7ZjRiBpQVQyx6rktXqObrmTw
+e12yMor5cc8kGyQ6GmYoEoCKFS/S08fK5j5bwwy8KWfyH8tRGsEHeowPw3eIZCv7
+gowUhb3SOsh3osAtafSe9aS0rGNTGBdSTFnJEiew8zpWbdIFKySYxU8nmHNpIPXh
+O/EuAYMwbcF3BhM20Gm5hxqrgSWe7S+q3KqSJbs+k95j0jr8xoNzwUd8NzI40Uu1
+L1ejyqvGHjxhQooIej1Ea/MSp7v5ifpBWSp3yxlOjAnZPSEewCutMyHylIulS0sK
+2JQfgcdd
+-----END CERTIFICATE-----
diff --git a/test-data/xmldsign/bug63011_key2.pem b/test-data/xmldsign/bug63011_key2.pem
new file mode 100644
index 0000000000..18de50128d
--- /dev/null
+++ b/test-data/xmldsign/bug63011_key2.pem
@@ -0,0 +1,45 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDMcC/+xW5l/R8i
+M9IKNdebiCQxkn/gfSjXD7oddrg42POCuPElhaGlHXPI+DCvNQ1+lbCTDJ7rk8F2
+DbefV80uwzMkMKnn0i3GW2K8LIGrv74ACYIzueHK+Mrm3N90/Xb6IqSR430SYH9r
+RbIfCqCVa0LJbVS2Pw2vBmvJHE/DVoSjKXlO73EbN4G4lJnj80NaZp6I0DDsoVup
+htKJWLtYwB3CDpI0oDst8/Beq7esDyyHHWf3v0TOtH19lRWSoD9yEmg+bRMKYfnu
+T5rWj8tF+cRjh+SIyBjuIn7n4qwnAs62CQKFLjp/3PjOEbaD/aCX+6xMWvAtk0UT
+KIDwYsejAgMBAAECggEAX0h2hibconpMFXPTlGCt4dadD+G46hdLfpjc5Lidehlb
+vXSXKCbVRRCqE2PhxPdUF8iKqK68Lw4JoA0apRCWGBJwdpCbz+k83nNfXFbeBP9z
+/a5w2czr+N7mKp2NJIix+DlHPJurgnIpUQUF1MPuPlXkpd7oGZzfstCqUex5HJAU
+N03lL/Z5/hgq93sLh1ZgPWhwAOYnps0rsOnFYXlXW7WVXvL1xpyBidZstOw+BdVC
+7BQNvKVa4KGC/PdgW7K4327JeMjRsX1nDUzZbORM5wihLE5idIHmDbQUm4G8JZul
+tLeV2Ew28L91jcc55Pbe40tBeMSqgSuU2r6/dU4uCQKBgQDlUCI8vW1Q7J92m+zU
+Y8lHu5mS4nlwXJEpuUoCuD2falAZW40+sG3fqZ1tEiCEpEYSYKMP0vbFrNLQ68av
+HWhjXWCQFvuUfAK8BsMusRIBQ/ZJduSa5vSgsQA+ZVl/Pnj9PyMN70nhueHcXjnM
+SNo5X3BQMNERaec7Lr0mRjDGHwKBgQDkOvcbdt4gCwpAeARzCTh15uw+q8KWPz77
+hPbaO4GJIMZZmRULhsrgBNZAsREasO4ZKJLI+wtukCXqgHAGzdSl6aK1fDLgFYqP
+Hp8UvgvIdF5yV7oAma9gRbK47sQlyBSFvQUyAuvA7wcviQ43/TIa173iBS07WfpA
+KlZu6+Ol/QKBgQDMJfglDRtKJS6eIJjKSQADzZ8eZmNoxfAyLhQWscGir7oZqSjo
++5cFvPI7DR1IGRuM9t2Uk+M//uk8N/uNIOgzNglmnh9hhLnGfVq8scVuvPuBUciZ
+oy179bha1E3F+28pPlFN8Y9b1umeD4DzPpSQ6UeLDLrD/v1t8eFRNsHrTwKBgE7v
+4gW6wCrfBqWznP5YoxGMVAt9BqlGqLb/jw195ViTYGce3juFXGfM5HmthFfx9/f1
+o4cl5RdRffu0foqr6C+WNjOFCGeeq7TCh4z6CkNDlGMB2pBYl2K52I3D702N/SMg
+dEqO3hF12stjIOrWhNzp33/sAG/1t+s3eXuV1L/VAoGAe0d/g5LcACFbl0sDRcSV
+WYsKHyR2dieALKf47mhzQtKmY506zM22n52s9sUT92j4jf/4dnP5OciJniSppgfW
+V64/856DuMNPoIssmNh3tQwjm8I0/t+iDGJJSHnQthG0AwD2NRe/D8Uty4P7ED64
+0gEVrDJEgT60MrQt15f+1sM=
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIICrDCCAZSgAwIBAgIEXA/YwjANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDDA1N
+eSBTZWNvbmQga2V5MB4XDTE4MTIxMTE1MzMyMloXDTE5MTIxMTE1MzMyMlowGDEW
+MBQGA1UEAwwNTXkgU2Vjb25kIGtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAMxwL/7FbmX9HyIz0go115uIJDGSf+B9KNcPuh12uDjY84K48SWFoaUd
+c8j4MK81DX6VsJMMnuuTwXYNt59XzS7DMyQwqefSLcZbYrwsgau/vgAJgjO54cr4
+yubc33T9dvoipJHjfRJgf2tFsh8KoJVrQsltVLY/Da8Ga8kcT8NWhKMpeU7vcRs3
+gbiUmePzQ1pmnojQMOyhW6mG0olYu1jAHcIOkjSgOy3z8F6rt6wPLIcdZ/e/RM60
+fX2VFZKgP3ISaD5tEwph+e5PmtaPy0X5xGOH5IjIGO4ifufirCcCzrYJAoUuOn/c
++M4RtoP9oJf7rExa8C2TRRMogPBix6MCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEA
+n3rvm1DatfrrFdhNOfNwlYMF12ND1BIEFYYI13vIkZ5wmb+gNYZu32FqdTpX//qV
+VkNLXJj/DrR+rKWH2jZwabaYyv5XmJl9Y0/LqN3ExiqijYsPgpWEKCzhfyv+Qe5B
+Qzx8WCJjGh0UMQ3P8/FRYsUPf5AFMdz9jZK0uYCFFgkb4LDp23MbNYG4eRBB+/+R
+Arq/poWd+eb5G79+rnrGGCtqhVuxrMP8xkPy4Rkg8zr56CxQSHX7qXehYc9x/dTj
+CyHPrOUAw4W/pYShYtHsqORQivPvwnwQQ4upK6s2l5pbPwyEzV45clIPU+3tjvn7
+67DLhc66Sz4/XkWeEXeeMg==
+-----END CERTIFICATE-----