git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1848974 13f79535-47bb-0310-9956-ffa450edef68pull/133/head^2
/* ==================================================================== | |||||
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); | |||||
} | |||||
} |
*/ | */ | ||||
private boolean updateConfigOnValidate = false; | 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. | * Inits and checks the config object. | ||||
public void setUpdateConfigOnValidate(boolean updateConfigOnValidate) { | public void setUpdateConfigOnValidate(boolean updateConfigOnValidate) { | ||||
this.updateConfigOnValidate = 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; | |||||
} | |||||
} | } |
import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData; | import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData; | ||||
import org.apache.poi.EncryptedDocumentException; | import org.apache.poi.EncryptedDocumentException; | ||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | 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.OPCPackage; | ||||
import org.apache.poi.openxml4j.opc.PackagePart; | import org.apache.poi.openxml4j.opc.PackagePart; | ||||
import org.apache.poi.openxml4j.opc.PackagePartName; | import org.apache.poi.openxml4j.opc.PackagePartName; | ||||
xmlSignContext.setURIDereferencer(uriDereferencer); | 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(""); | xmlSignContext.setDefaultNamespacePrefix(""); | ||||
// signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS)); | // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS)); | ||||
protected void writeDocument(Document document) throws MarshalException { | protected void writeDocument(Document document) throws MarshalException { | ||||
XmlOptions xo = new XmlOptions(); | XmlOptions xo = new XmlOptions(); | ||||
Map<String,String> namespaceMap = new HashMap<>(); | 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.setSaveSuggestedPrefixes(namespaceMap); | ||||
xo.setUseDefaultNamespace(); | xo.setUseDefaultNamespace(); | ||||
*/ | */ | ||||
OPCPackage pkg = signatureConfig.getOpcPackage(); | OPCPackage pkg = signatureConfig.getOpcPackage(); | ||||
PackagePartName sigPartName, sigsPartName; | |||||
try { | 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"/> | // <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) { | private Element getDsigElement(final Document document, final String localName) { |
final Map<String,String> nsMap = new HashMap<>(); | 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("dsss", MS_DIGSIG_NS); | ||||
nsMap.put("ds", XML_DIGSIG_NS); | nsMap.put("ds", XML_DIGSIG_NS); | ||||
} | } |
DOMSignContext domSignContext = (nextSibling == null) | DOMSignContext domSignContext = (nextSibling == null) | ||||
? new DOMSignContext(key, n) | ? new DOMSignContext(key, n) | ||||
: new DOMSignContext(key, n, nextSibling); | : 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); | DOMStructure domStructure = new DOMStructure(n); | ||||
domKeyInfo.marshal(domStructure, domSignContext); | domKeyInfo.marshal(domStructure, domSignContext); | ||||
/* ==================================================================== | |||||
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); | |||||
} | |||||
} |
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assume.assumeTrue; | import static org.junit.Assume.assumeTrue; | ||||
import java.io.BufferedReader; | |||||
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
import java.io.File; | import java.io.File; | ||||
import java.io.FileOutputStream; | import java.io.FileOutputStream; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.io.InputStream; | import java.io.InputStream; | ||||
import java.io.InputStreamReader; | |||||
import java.io.OutputStream; | import java.io.OutputStream; | ||||
import java.math.BigInteger; | |||||
import java.net.ConnectException; | import java.net.ConnectException; | ||||
import java.net.HttpURLConnection; | import java.net.HttpURLConnection; | ||||
import java.net.MalformedURLException; | import java.net.MalformedURLException; | ||||
import java.net.SocketTimeoutException; | import java.net.SocketTimeoutException; | ||||
import java.net.URL; | import java.net.URL; | ||||
import java.nio.charset.StandardCharsets; | |||||
import java.security.Key; | import java.security.Key; | ||||
import java.security.KeyPair; | import java.security.KeyPair; | ||||
import java.security.KeyPairGenerator; | |||||
import java.security.KeyStore; | import java.security.KeyStore; | ||||
import java.security.PrivateKey; | 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.Certificate; | ||||
import java.security.cert.CertificateEncodingException; | |||||
import java.security.cert.CertificateException; | |||||
import java.security.cert.X509CRL; | import java.security.cert.X509CRL; | ||||
import java.security.cert.X509Certificate; | import java.security.cert.X509Certificate; | ||||
import java.security.interfaces.RSAPublicKey; | |||||
import java.security.spec.RSAKeyGenParameterSpec; | |||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Calendar; | import java.util.Calendar; | ||||
import java.util.Collections; | import java.util.Collections; | ||||
import java.util.Iterator; | import java.util.Iterator; | ||||
import java.util.List; | import java.util.List; | ||||
import javax.xml.crypto.MarshalException; | |||||
import javax.xml.crypto.dsig.CanonicalizationMethod; | import javax.xml.crypto.dsig.CanonicalizationMethod; | ||||
import javax.xml.crypto.dsig.XMLSignatureException; | |||||
import javax.xml.crypto.dsig.dom.DOMSignContext; | import javax.xml.crypto.dsig.dom.DOMSignContext; | ||||
import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo; | import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo; | ||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; | import org.apache.poi.xssf.usermodel.XSSFWorkbook; | ||||
import org.apache.xmlbeans.SystemProperties; | import org.apache.xmlbeans.SystemProperties; | ||||
import org.apache.xmlbeans.XmlObject; | 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.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.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.DigestAlgAndValueType; | ||||
import org.etsi.uri.x01903.v13.QualifyingPropertiesType; | import org.etsi.uri.x01903.v13.QualifyingPropertiesType; | ||||
import org.junit.AfterClass; | import org.junit.AfterClass; | ||||
try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) { | try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) { | ||||
initKeyPair("Test", "CN=Test"); | initKeyPair("Test", "CN=Test"); | ||||
final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate()); | |||||
final X509CRL crl = generateCrl(x509, keyPair.getPrivate()); | |||||
// setup | // setup | ||||
SignatureConfig signatureConfig = new SignatureConfig(); | SignatureConfig signatureConfig = new SignatureConfig(); | ||||
final RevocationData revocationData = new RevocationData(); | final RevocationData revocationData = new RevocationData(); | ||||
revocationData.addCRL(crl); | revocationData.addCRL(crl); | ||||
OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false, | |||||
OCSPResp ocspResp = createOcspResp(x509, false, | |||||
x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis()); | x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis()); | ||||
revocationData.addOCSP(ocspResp.getEncoded()); | revocationData.addOCSP(ocspResp.getEncoded()); | ||||
@Test | @Test | ||||
public void testMultiSign() throws Exception { | 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)) { | 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 | @Test | ||||
x509 = (X509Certificate)keystore.getCertificate(alias); | x509 = (X509Certificate)keystore.getCertificate(alias); | ||||
keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key); | keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key); | ||||
} else { | } else { | ||||
keyPair = PkiTestUtils.generateKeyPair(); | |||||
keyPair = generateKeyPair(); | |||||
Date notBefore = cal.getTime(); | Date notBefore = cal.getTime(); | ||||
Calendar cal2 = (Calendar)cal.clone(); | Calendar cal2 = (Calendar)cal.clone(); | ||||
cal2.add(Calendar.YEAR, 1); | cal2.add(Calendar.YEAR, 1); | ||||
Date notAfter = cal2.getTime(); | Date notAfter = cal2.getTime(); | ||||
KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature); | 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); | , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage); | ||||
keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509}); | keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509}); | ||||
} | } | ||||
} | } | ||||
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 { | private static File copy(File input) throws IOException { | ||||
String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1"); | String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1"); | ||||
if (extension == null || extension.isEmpty()) { | if (extension == null || extension.isEmpty()) { | ||||
return tmpFile; | 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); | |||||
} | |||||
} | } |
-----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----- |
-----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----- |