#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
This commit is contained in:
Andreas Beeker 2018-12-15 00:51:10 +00:00
parent 9b72bdca50
commit 17ce79b69c
9 changed files with 574 additions and 372 deletions

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());
}
// <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
final DSigRelation sigDesc = DSigRelation.SIG;
int nextSigIdx = pkg.getUnusedPartIndex(sigDesc.getDefaultFileName());
if (!signatureConfig.isAllowMultipleSignatures()) {
PackageRelationshipCollection prc = originPart.getRelationshipsByType(sigDesc.getRelation());
for (int i=2; i<nextSigIdx; i++) {
PackagePartName pn = PackagingURIHelper.createPartName(sigDesc.getFileName(i));
for (PackageRelationship rel : prc) {
PackagePart pp = originPart.getRelatedPart(rel);
if (pp.getPartName().equals(pn)) {
originPart.removeRelationship(rel.getId());
prc.removeRelationship(rel.getId());
break;
}
}
pkg.removePart(pkg.getPart(pn));
}
nextSigIdx = 1;
}
PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx));
PackagePart sigPart = pkg.getPart(sigPartName);
if (sigPart == null) {
sigPart = pkg.createPart(sigPartName, ContentTypes.DIGITAL_SIGNATURE_XML_SIGNATURE_PART);
sigPart = pkg.createPart(sigPartName, sigDesc.getContentType());
originPart.addRelationship(sigPartName, TargetMode.INTERNAL, sigDesc.getRelation());
} else {
sigPart.clear();
}
try {
OutputStream os = sigPart.getOutputStream();
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);
}
PackagePart sigsPart = pkg.getPart(sigsPartName);
if (sigsPart == null) {
// touch empty marker file
sigsPart = pkg.createPart(sigsPartName, ContentTypes.DIGITAL_SIGNATURE_ORIGIN_PART);
}
PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
for (PackageRelationship pr : relCol) {
pkg.removeRelationship(pr.getId());
}
pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);
}
private Element getDsigElement(final Document document, final String localName) {

View File

@ -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);
}

View File

@ -129,9 +129,7 @@ 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);

View File

@ -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);
}
}

View File

@ -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;
cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
cal.clear();
cal.setTimeZone(LocaleUtil.TIMEZONE_UTC);
cal.set(2018, Calendar.DECEMBER, 14);
File tpl = copy(testdata.getFile("bug58630.xlsx"));
// test signing with separate opened packages
File tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
try (OPCPackage pkg = OPCPackage.open(tpl)) {
//SignatureConfig signatureConfig = new SignatureConfig();
assertNotNull(pkg);
signPkg63011(pkg, "bug63011_key1.pem", true);
}
try (OPCPackage pkg = OPCPackage.open(tpl)) {
signPkg63011(pkg, "bug63011_key2.pem", true);
}
verifyPkg63011(tpl, true);
// test signing with single opened package
tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
try (OPCPackage pkg = OPCPackage.open(tpl)) {
signPkg63011(pkg, "bug63011_key1.pem", true);
signPkg63011(pkg, "bug63011_key2.pem", true);
}
verifyPkg63011(tpl, true);
try (OPCPackage pkg = OPCPackage.open(tpl)) {
signPkg63011(pkg, "bug63011_key1.pem", true);
signPkg63011(pkg, "bug63011_key2.pem", false);
}
verifyPkg63011(tpl, false);
}
private void verifyPkg63011(File tpl, boolean multi) throws InvalidFormatException, IOException {
try (OPCPackage pkg = OPCPackage.open(tpl, PackageAccess.READ)) {
SignatureConfig sic = new SignatureConfig();
sic.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(sic);
List<X509Certificate> result = new ArrayList<>();
for (SignaturePart sp : si.getSignatureParts()) {
if (sp.validate()) {
result.add(sp.getSigner());
}
}
assertNotNull(result);
if (multi) {
assertEquals(2, result.size());
assertEquals("CN=Muj Klic", result.get(0).getSubjectDN().toString());
assertEquals("CN=My Second key", result.get(1).getSubjectDN().toString());
} else {
assertEquals(1, result.size());
assertEquals("CN=My Second key", result.get(0).getSubjectDN().toString());
}
assertTrue(si.verifySignature());
pkg.revert();
}
}
private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi)
throws IOException, CertificateException, XMLSignatureException, MarshalException {
assertNotNull(pkg);
initKeyFromPEM(testdata.getFile(pemFile));
SignatureConfig config = new SignatureConfig();
config.setKey(keyPair.getPrivate());
config.setSigningCertificateChain(Collections.singletonList(x509));
config.setExecutionTime(cal.getTime());
config.setAllowMultipleSignatures(multi);
config.setOpcPackage(pkg);
SignatureInfo si = new SignatureInfo();
si.setSignatureConfig(config);
si.confirmSignature();
}
@Test
@ -829,14 +954,14 @@ 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);
}
}

View File

@ -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-----

View File

@ -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-----