322 lines
15 KiB
Java
322 lines
15 KiB
Java
/* ====================================================================
|
|
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.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.ASN1InputStream;
|
|
import org.bouncycastle.asn1.ASN1Sequence;
|
|
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.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.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;
|
|
}
|
|
|
|
@SuppressWarnings("resource")
|
|
private static SubjectKeyIdentifier createSubjectKeyId(PublicKey publicKey)
|
|
throws IOException {
|
|
ASN1InputStream asnObj = new ASN1InputStream(publicKey.getEncoded());
|
|
SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(asnObj.readObject());
|
|
SubjectKeyIdentifier keyId = SubjectKeyIdentifier.getInstance(info.getEncoded());
|
|
return keyId;
|
|
}
|
|
|
|
@SuppressWarnings("resource")
|
|
private static AuthorityKeyIdentifier createAuthorityKeyId(PublicKey publicKey)
|
|
throws IOException {
|
|
ASN1InputStream asnObj = new ASN1InputStream(publicKey.getEncoded());
|
|
SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(asnObj.readObject());
|
|
AuthorityKeyIdentifier keyId = AuthorityKeyIdentifier.getInstance(info);
|
|
return keyId;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
SubjectPublicKeyInfo subjectPublicKeyInfo = new SubjectPublicKeyInfo(
|
|
ASN1Sequence.getInstance(subjectPublicKey.getEncoded()));
|
|
|
|
X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
|
|
issuerName
|
|
, new BigInteger(128, new SecureRandom())
|
|
, notBefore
|
|
, notAfter
|
|
, new X500Name(subjectDn)
|
|
, subjectPublicKeyInfo
|
|
);
|
|
|
|
certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, createSubjectKeyId(subjectPublicKey));
|
|
certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, createAuthorityKeyId(subjectPublicKey));
|
|
|
|
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;
|
|
}
|
|
}
|