/* ==================================================================== | |
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); | |
KeyPair keyPair = keyPairGenerator.generateKeyPair(); | |
return keyPair; | |
} | |
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(); | |
Document document = documentBuilder.parse(inputSource); | |
return document; | |
} | |
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(); | |
OCSPResp ocspResp = ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp); | |
return ocspResp; | |
} | |
} |