From 200d579e45c29e2e28ec0c9c94fe09d29aa98f49 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sat, 15 Dec 2018 00:51:10 +0000 Subject: [PATCH] #63011 - Multiple digital signature in excel file broke first signature git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1848974 13f79535-47bb-0310-9956-ffa450edef68 --- .../poi/poifs/crypt/dsig/DSigRelation.java | 62 +++ .../poi/poifs/crypt/dsig/SignatureConfig.java | 28 ++ .../poi/poifs/crypt/dsig/SignatureInfo.java | 85 +++-- .../poi/poifs/crypt/dsig/SignaturePart.java | 4 +- .../dsig/facets/KeyInfoSignatureFacet.java | 6 +- .../apache/poi/poifs/crypt/PkiTestUtils.java | 314 --------------- .../poi/poifs/crypt/TestSignatureInfo.java | 357 +++++++++++++++++- test-data/xmldsign/bug63011_key1.pem | 45 +++ test-data/xmldsign/bug63011_key2.pem | 45 +++ 9 files changed, 574 insertions(+), 372 deletions(-) create mode 100644 src/ooxml/java/org/apache/poi/poifs/crypt/dsig/DSigRelation.java delete mode 100644 src/ooxml/testcases/org/apache/poi/poifs/crypt/PkiTestUtils.java create mode 100644 test-data/xmldsign/bug63011_key1.pem create mode 100644 test-data/xmldsign/bug63011_key2.pem 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 _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 cls) { + super(type, rel, defaultName, cls); + _table.put(rel, this); + } + + /** + * Get POIXMLRelation by relation type + * + * @param rel relation type, for example, + * http://schemas.openxmlformats.org/officeDocument/2006/relationships/image + * @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 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 namespaceMap = new HashMap<>(); - for(Map.Entry 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 { - // - sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml"); // - 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); - } + // + 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 nsMap = new HashMap<>(); { - for (Map.Entry 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 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 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----- -- 2.39.5