blob: ad75e15fb1d84e28fe939114c4d1a4a8f390872f [file] [log] [blame]
/*
*
* 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.qpid.test.utils.tls;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.CRLDistPoint;
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.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509CRLHolder;
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.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.OperatorException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
public class TlsResourceBuilder
{
private static final int RSA_KEY_SIZE = 2048;
private static final int VALIDITY_DURATION = 365;
private static final String SIGNATURE_ALGORITHM_SHA_512_WITH_RSA = "SHA512WithRSA";
static
{
Security.addProvider(new BouncyCastleProvider());
}
private TlsResourceBuilder()
{
super();
}
public static KeyPair createRSAKeyPair()
{
KeyPairGenerator keyPairGenerator;
try
{
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
}
catch (NoSuchAlgorithmException e)
{
throw new IllegalStateException("RSA generator is not found");
}
keyPairGenerator.initialize(RSA_KEY_SIZE);
return keyPairGenerator.genKeyPair();
}
public static KeyCertificatePair createKeyPairAndRootCA(final String dn) throws CertificateException
{
return createKeyPairAndRootCA(dn, createValidityPeriod());
}
public static KeyCertificatePair createKeyPairAndIntermediateCA(final String dn,
final KeyCertificatePair rootCA,
final String crlUri)
throws CertificateException
{
return createKeyPairAndIntermediateCA(dn, createValidityPeriod(), rootCA, crlUri);
}
public static KeyCertificatePair createSelfSigned(final String dn,
final Instant validFrom,
final Instant validTo,
final AlternativeName... alternativeName)
throws CertificateException
{
return createSelfSigned(dn, new ValidityPeriod(validFrom, validTo), alternativeName);
}
public static KeyCertificatePair createSelfSigned(final String dn, final AlternativeName... alternativeName)
throws CertificateException
{
return createSelfSigned(dn, createValidityPeriod(), alternativeName);
}
public static KeyCertificatePair createKeyPairAndCertificate(final String dn,
final KeyCertificatePair ca,
final AlternativeName... alternativeName)
throws CertificateException
{
return createKeyPairAndCertificate(dn, createValidityPeriod(), ca, alternativeName);
}
public static X509Certificate createCertificate(final KeyPair keyPair,
final KeyCertificatePair ca,
final String dn,
final Instant from,
final Instant to,
final AlternativeName... alternativeName)
throws CertificateException
{
return createCertificate(keyPair,
ca,
dn,
new ValidityPeriod(from, to),
createKeyUsageExtension(),
createAlternateNamesExtension(alternativeName));
}
public static X509Certificate createCertificateForClientAuthorization(final KeyPair keyPair,
final KeyCertificatePair ca,
final String dn,
final AlternativeName... alternativeName)
throws CertificateException
{
return createCertificate(keyPair,
ca,
dn,
createValidityPeriod(),
createExtendedUsageExtension(new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_clientAuth})),
createAuthorityKeyExtension(ca.getCertificate().getPublicKey()),
createSubjectKeyExtension(keyPair.getPublic()),
createAlternateNamesExtension(alternativeName));
}
public static X509Certificate createCertificateForServerAuthorization(final KeyPair keyPair,
final KeyCertificatePair ca,
final String dn,
final AlternativeName... alternativeName)
throws CertificateException
{
return createCertificate(keyPair,
ca,
dn,
createValidityPeriod(),
createExtendedUsageExtension(new ExtendedKeyUsage(new KeyPurposeId[]{KeyPurposeId.id_kp_serverAuth})),
createAuthorityKeyExtension(ca.getCertificate().getPublicKey()),
createSubjectKeyExtension(keyPair.getPublic()),
createAlternateNamesExtension(alternativeName));
}
public static X509Certificate createCertificateWithCrlDistributionPoint(final KeyPair keyPair,
final KeyCertificatePair caPair,
final String dn,
final String crlUri)
throws CertificateException
{
return createCertificate(keyPair,
caPair,
dn,
createValidityPeriod(),
createKeyUsageExtension(),
createDistributionPointExtension(crlUri));
}
private static X509Certificate createCertificate(final KeyPair keyPair,
final KeyCertificatePair ca,
final String dn,
final ValidityPeriod validityPeriod,
final Extension... extensions)
throws CertificateException
{
try
{
final X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
ca.getCertificate(),
generateSerialNumber(),
new Date(validityPeriod.getFrom().toEpochMilli()),
new Date(validityPeriod.getTo().toEpochMilli()),
new X500Name(RFC4519Style.INSTANCE, dn),
keyPair.getPublic());
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
for (Extension e : extensions)
{
builder.addExtension(e);
}
return buildX509Certificate(builder, ca.getPrivateKey());
}
catch (OperatorException | IOException e)
{
throw new CertificateException(e);
}
}
private static X509Certificate createSelfSignedCertificate(final KeyPair keyPair,
final String dn,
final ValidityPeriod period,
final AlternativeName... alternativeName)
throws CertificateException
{
try
{
final X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
new X500Name(RFC4519Style.INSTANCE, dn),
generateSerialNumber(),
new Date(period.getFrom().toEpochMilli()),
new Date(period.getTo().toEpochMilli()),
new X500Name(RFC4519Style.INSTANCE, dn),
keyPair.getPublic());
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(false));
builder.addExtension(createKeyUsageExtension());
builder.addExtension(createSubjectKeyExtension(keyPair.getPublic()));
builder.addExtension(createAlternateNamesExtension(alternativeName));
return buildX509Certificate(builder, keyPair.getPrivate());
}
catch (OperatorException | IOException e)
{
throw new CertificateException(e);
}
}
static X509CRL createCertificateRevocationList(final KeyCertificatePair ca, X509Certificate... certificate)
throws CRLException
{
try
{
final X500Name issuerName = X500Name.getInstance(RFC4519Style.INSTANCE,
ca.getCertificate()
.getSubjectX500Principal()
.getEncoded());
final Instant nextUpdate = Instant.now().plus(10, ChronoUnit.DAYS);
final Date now = new Date();
final X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(issuerName, now);
crlBuilder.setNextUpdate(new Date(nextUpdate.toEpochMilli()));
for (X509Certificate c : certificate)
{
crlBuilder.addCRLEntry(c.getSerialNumber(), now, CRLReason.privilegeWithdrawn);
}
crlBuilder.addExtension(createAuthorityKeyExtension(ca.getCertificate().getPublicKey()));
crlBuilder.addExtension(Extension.cRLNumber, false, new CRLNumber(generateSerialNumber()));
final ContentSigner contentSigner = createContentSigner(ca.getPrivateKey());
final X509CRLHolder crl = crlBuilder.build(contentSigner);
return new JcaX509CRLConverter().getCRL(crl);
}
catch (OperatorException | IOException | CertificateException e)
{
throw new CRLException(e);
}
}
private static X509Certificate createRootCACertificate(final KeyPair keyPair,
final String dn,
final ValidityPeriod validityPeriod)
throws CertificateException
{
try
{
final X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
new X500Name(RFC4519Style.INSTANCE, dn),
generateSerialNumber(),
new Date(validityPeriod.getFrom().toEpochMilli()),
new Date(validityPeriod.getTo().toEpochMilli()),
new X500Name(RFC4519Style.INSTANCE, dn),
keyPair.getPublic());
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
builder.addExtension(createSubjectKeyExtension(keyPair.getPublic()));
builder.addExtension(createAuthorityKeyExtension(keyPair.getPublic()));
return buildX509Certificate(builder, keyPair.getPrivate());
}
catch (OperatorException | IOException e)
{
throw new CertificateException(e);
}
}
private static X509Certificate generateIntermediateCertificate(final KeyPair keyPair,
final KeyCertificatePair rootCA,
final String dn,
final ValidityPeriod validityPeriod,
final String crlUri)
throws CertificateException
{
try
{
final X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
rootCA.getCertificate(),
generateSerialNumber(),
new Date(validityPeriod.getFrom().toEpochMilli()),
new Date(validityPeriod.getTo().toEpochMilli()),
new X500Name(RFC4519Style.INSTANCE, dn),
keyPair.getPublic());
//builder.addExtension(Extension.keyUsage, false, new KeyUsage(KeyUsage.keyCertSign));
builder.addExtension(Extension.basicConstraints, false, new BasicConstraints(true));
builder.addExtension(createSubjectKeyExtension(keyPair.getPublic()));
builder.addExtension(createAuthorityKeyExtension(rootCA.getCertificate().getPublicKey()));
if (crlUri != null)
{
builder.addExtension(createDistributionPointExtension(crlUri));
}
return buildX509Certificate(builder, rootCA.getPrivateKey());
}
catch (OperatorException | IOException e)
{
throw new CertificateException(e);
}
}
private static KeyCertificatePair createKeyPairAndRootCA(final String dn,
final ValidityPeriod validityPeriod)
throws CertificateException
{
final KeyPair keyPair = createRSAKeyPair();
final X509Certificate rootCA = createRootCACertificate(keyPair, dn, validityPeriod);
return new KeyCertificatePair(keyPair.getPrivate(), rootCA);
}
private static KeyCertificatePair createKeyPairAndIntermediateCA(final String dn,
final ValidityPeriod validityPeriod,
final KeyCertificatePair rootCA,
final String crlUri)
throws CertificateException
{
final KeyPair keyPair = createRSAKeyPair();
final X509Certificate intermediateCA = generateIntermediateCertificate(keyPair, rootCA, dn, validityPeriod, crlUri);
return new KeyCertificatePair(keyPair.getPrivate(), intermediateCA);
}
private static KeyCertificatePair createKeyPairAndCertificate(final String dn,
final ValidityPeriod validityPeriod,
final KeyCertificatePair ca,
final AlternativeName... alternativeName)
throws CertificateException
{
final KeyPair keyPair = createRSAKeyPair();
final X509Certificate certificate = createCertificate(keyPair, ca, dn, validityPeriod, alternativeName);
return new KeyCertificatePair(keyPair.getPrivate(), certificate);
}
private static X509Certificate createCertificate(final KeyPair keyPair,
final KeyCertificatePair ca,
final String dn,
final ValidityPeriod validityPeriod,
final AlternativeName... alternativeName)
throws CertificateException
{
return createCertificate(keyPair,
ca,
dn,
validityPeriod,
createKeyUsageExtension(),
createAlternateNamesExtension(alternativeName));
}
private static KeyCertificatePair createSelfSigned(final String dn,
final ValidityPeriod validityPeriod,
final AlternativeName... alternativeName)
throws CertificateException
{
final KeyPair keyPair = createRSAKeyPair();
final X509Certificate certificate = createSelfSignedCertificate(keyPair, dn, validityPeriod, alternativeName);
return new KeyCertificatePair(keyPair.getPrivate(), certificate);
}
private static ValidityPeriod createValidityPeriod()
{
final Instant from = Instant.now().minus(1, ChronoUnit.DAYS);
final Instant to = from.plus(VALIDITY_DURATION, ChronoUnit.DAYS);
return new ValidityPeriod(from, to);
}
private static Extension createAuthorityKeyExtension(final PublicKey publicKey)
throws CertificateException
{
try
{
return new Extension(Extension.authorityKeyIdentifier,
false,
new JcaX509ExtensionUtils().createAuthorityKeyIdentifier(publicKey).getEncoded());
}
catch (IOException | NoSuchAlgorithmException e)
{
throw new CertificateException(e);
}
}
private static Extension createSubjectKeyExtension(final PublicKey publicKey)
throws CertificateException
{
try
{
return new Extension(Extension.subjectKeyIdentifier,
false,
new JcaX509ExtensionUtils().createSubjectKeyIdentifier(publicKey).getEncoded());
}
catch (IOException | NoSuchAlgorithmException e)
{
throw new CertificateException(e);
}
}
private static Extension createExtendedUsageExtension(final ExtendedKeyUsage extendedKeyUsage)
throws CertificateException
{
try
{
return new Extension(Extension.extendedKeyUsage, false, extendedKeyUsage.getEncoded());
}
catch (IOException e)
{
throw new CertificateException(e);
}
}
private static Extension createKeyUsageExtension()
{
return new Extension(Extension.keyUsage,
false,
new KeyUsage(KeyUsage.digitalSignature
| KeyUsage.nonRepudiation
| KeyUsage.keyEncipherment).getBytes());
}
private static Extension createDistributionPointExtension(final String crlUri) throws CertificateException
{
try
{
final GeneralName generalName = new GeneralName(GeneralName.uniformResourceIdentifier, crlUri);
final DistributionPointName pointName = new DistributionPointName(new GeneralNames(generalName));
final DistributionPoint[] points = new DistributionPoint[]{new DistributionPoint(pointName, null, null)};
return new Extension(Extension.cRLDistributionPoints, false, new CRLDistPoint(points).getEncoded());
}
catch (IOException e)
{
throw new CertificateException(e);
}
}
private static Extension createAlternateNamesExtension(final AlternativeName[] alternativeName)
throws CertificateException
{
try
{
final GeneralName[] generalNames = Arrays.stream(alternativeName)
.map(an -> new GeneralName(an.getType().ordinal(),
an.getName()))
.toArray(GeneralName[]::new);
return new Extension(Extension.subjectAlternativeName,
false,
new GeneralNames(generalNames).getEncoded());
}
catch (IOException e)
{
throw new CertificateException(e);
}
}
private static BigInteger generateSerialNumber()
{
return new BigInteger(64, new SecureRandom());
}
private static X509Certificate buildX509Certificate(final X509v3CertificateBuilder builder, final PrivateKey pk)
throws OperatorCreationException, CertificateException
{
ContentSigner contentSigner = createContentSigner(pk);
return new JcaX509CertificateConverter().getCertificate(builder.build(contentSigner));
}
private static ContentSigner createContentSigner(final PrivateKey privateKey)
throws OperatorCreationException
{
return new JcaContentSignerBuilder(SIGNATURE_ALGORITHM_SHA_512_WITH_RSA).setProvider("BC").build(privateKey);
}
}