CLOUDSTACK-9993: Have basic constraint in CA certificate (#2286)

- Refactors V3 x509 cert generator to put basic constraint and key usage
  extensions when CA cert is created
- Refactors root CA provider to use V3 generator to generate CA cert

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2017-10-11 17:41:37 +05:30 committed by GitHub
parent 0fedbdd7a9
commit c3ed1b38e5
7 changed files with 60 additions and 54 deletions

View File

@ -137,14 +137,9 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
final KeyPair keyPair = CertUtils.generateRandomKeyPair(CAManager.CertKeySize.value()); final KeyPair keyPair = CertUtils.generateRandomKeyPair(CAManager.CertKeySize.value());
final X509Certificate clientCertificate = CertUtils.generateV3Certificate( final X509Certificate clientCertificate = CertUtils.generateV3Certificate(
caCertificate, caCertificate, caKeyPair, keyPair.getPublic(),
caKeyPair.getPrivate(), subject, CAManager.CertSignatureAlgorithm.value(),
keyPair.getPublic(), validityDays, domainNames, ipAddresses);
subject,
CAManager.CertSignatureAlgorithm.value(),
validityDays,
domainNames,
ipAddresses);
return new Certificate(clientCertificate, keyPair.getPrivate(), Collections.singletonList(caCertificate)); return new Certificate(clientCertificate, keyPair.getPrivate(), Collections.singletonList(caCertificate));
} }
@ -164,14 +159,11 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
final PKCS10CertificationRequest request = new PKCS10CertificationRequest(pemObject.getContent()); final PKCS10CertificationRequest request = new PKCS10CertificationRequest(pemObject.getContent());
final String subject = request.getCertificationRequestInfo().getSubject().toString();
final X509Certificate clientCertificate = CertUtils.generateV3Certificate( final X509Certificate clientCertificate = CertUtils.generateV3Certificate(
caCertificate, caKeyPair.getPrivate(), caCertificate, caKeyPair, request.getPublicKey(),
request.getPublicKey(), subject, CAManager.CertSignatureAlgorithm.value(),
request.getCertificationRequestInfo().getSubject().toString(), validityDays, domainNames, ipAddresses);
CAManager.CertSignatureAlgorithm.value(),
validityDays,
domainNames,
ipAddresses);
return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate)); return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate));
} }
@ -257,6 +249,10 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
/////////////// Root CA Config /////////////////// /////////////// Root CA Config ///////////////////
////////////////////////////////////////////////// //////////////////////////////////////////////////
private int getCaValidityDays() {
return 365 * caValidityYears;
}
private char[] findKeyStorePassphrase() { private char[] findKeyStorePassphrase() {
char[] passphrase = KeyStoreUtils.defaultKeystorePassphrase; char[] passphrase = KeyStoreUtils.defaultKeystorePassphrase;
final String configuredPassphrase = DbProperties.getDbProperties().getProperty("db.cloud.keyStorePassphrase"); final String configuredPassphrase = DbProperties.getDbProperties().getProperty("db.cloud.keyStorePassphrase");
@ -268,7 +264,7 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
private boolean createManagementServerKeystore(final String keyStoreFilePath, final char[] passphrase) { private boolean createManagementServerKeystore(final String keyStoreFilePath, final char[] passphrase) {
final Certificate managementServerCertificate = issueCertificate(Collections.singletonList(NetUtils.getHostName()), final Certificate managementServerCertificate = issueCertificate(Collections.singletonList(NetUtils.getHostName()),
Collections.singletonList(NetUtils.getDefaultHostIp()), caValidityYears * 365); Collections.singletonList(NetUtils.getDefaultHostIp()), getCaValidityDays());
if (managementServerCertificate == null || managementServerCertificate.getPrivateKey() == null) { if (managementServerCertificate == null || managementServerCertificate.getPrivateKey() == null) {
throw new CloudRuntimeException("Failed to generate certificate and setup management server keystore"); throw new CloudRuntimeException("Failed to generate certificate and setup management server keystore");
} }
@ -347,12 +343,10 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
} }
try { try {
LOG.debug("Generating root CA certificate"); LOG.debug("Generating root CA certificate");
final X509Certificate rootCaCertificate = CertUtils.generateV1Certificate( final X509Certificate rootCaCertificate = CertUtils.generateV3Certificate(
caKeyPair, null, caKeyPair, caKeyPair.getPublic(),
rootCAIssuerDN.value(), rootCAIssuerDN.value(), CAManager.CertSignatureAlgorithm.value(),
rootCAIssuerDN.value(), getCaValidityDays(), null, null);
caValidityYears,
CAManager.CertSignatureAlgorithm.value());
if (!configDao.update(rootCACertificate.key(), rootCACertificate.category(), CertUtils.x509CertificateToPem(rootCaCertificate))) { if (!configDao.update(rootCACertificate.key(), rootCACertificate.category(), CertUtils.x509CertificateToPem(rootCaCertificate))) {
LOG.error("Failed to update RootCA public/x509 certificate"); LOG.error("Failed to update RootCA public/x509 certificate");
} }

View File

@ -56,9 +56,8 @@ public class RootCACustomTrustManagerTest {
certMap.clear(); certMap.clear();
caKeypair = CertUtils.generateRandomKeyPair(1024); caKeypair = CertUtils.generateRandomKeyPair(1024);
clientKeypair = CertUtils.generateRandomKeyPair(1024); clientKeypair = CertUtils.generateRandomKeyPair(1024);
caCertificate = CertUtils.generateV1Certificate(caKeypair, "CN=ca", "CN=ca", 1, caCertificate = CertUtils.generateV3Certificate(null, caKeypair, caKeypair.getPublic(), "CN=ca", "SHA256withRSA", 365, null, null);
"SHA256withRSA"); expiredClientCertificate = CertUtils.generateV3Certificate(caCertificate, caKeypair, clientKeypair.getPublic(),
expiredClientCertificate = CertUtils.generateV3Certificate(caCertificate, caKeypair.getPrivate(), clientKeypair.getPublic(),
"CN=cloudstack.apache.org", "SHA256withRSA", 0, Collections.singletonList("cloudstack.apache.org"), Collections.singletonList(clientIp)); "CN=cloudstack.apache.org", "SHA256withRSA", 0, Collections.singletonList("cloudstack.apache.org"), Collections.singletonList(clientIp));
} }

View File

@ -66,7 +66,7 @@ public class RootCAProviderTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
caKeyPair = CertUtils.generateRandomKeyPair(1024); caKeyPair = CertUtils.generateRandomKeyPair(1024);
caCertificate = CertUtils.generateV1Certificate(caKeyPair, "CN=ca", "CN=ca", 1, "SHA256withRSA"); caCertificate = CertUtils.generateV3Certificate(null, caKeyPair, caKeyPair.getPublic(), "CN=ca", "SHA256withRSA", 365, null, null);
provider = new RootCAProvider(); provider = new RootCAProvider();

View File

@ -68,8 +68,7 @@ public class CABackgroundTaskTest {
host.setManagementServerId(ManagementServerNode.getManagementServerId()); host.setManagementServerId(ManagementServerNode.getManagementServerId());
task = new CAManagerImpl.CABackgroundTask(caManager, hostDao); task = new CAManagerImpl.CABackgroundTask(caManager, hostDao);
final KeyPair keypair = CertUtils.generateRandomKeyPair(1024); final KeyPair keypair = CertUtils.generateRandomKeyPair(1024);
expiredCertificate = CertUtils.generateV1Certificate(keypair, "CN=ca", "CN=ca", 0, expiredCertificate = CertUtils.generateV3Certificate(null, keypair, keypair.getPublic(), "CN=ca", "SHA256withRSA", 0, null, null);
"SHA256withRSA");
Mockito.when(hostDao.findByIp(Mockito.anyString())).thenReturn(host); Mockito.when(hostDao.findByIp(Mockito.anyString())).thenReturn(host);
Mockito.when(caManager.getActiveCertificatesMap()).thenReturn(certMap); Mockito.when(caManager.getActiveCertificatesMap()).thenReturn(certMap);

View File

@ -21,6 +21,7 @@ package org.apache.cloudstack.ca;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.KeyPair;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collections; import java.util.Collections;
@ -108,7 +109,8 @@ public class CAManagerImplTest {
public void testProvisionCertificate() throws Exception { public void testProvisionCertificate() throws Exception {
final Host host = Mockito.mock(Host.class); final Host host = Mockito.mock(Host.class);
Mockito.when(host.getPrivateIpAddress()).thenReturn("1.2.3.4"); Mockito.when(host.getPrivateIpAddress()).thenReturn("1.2.3.4");
final X509Certificate certificate = CertUtils.generateV1Certificate(CertUtils.generateRandomKeyPair(1024), "CN=ca", "CN=ca", 1, "SHA256withRSA"); final KeyPair keyPair = CertUtils.generateRandomKeyPair(1024);
final X509Certificate certificate = CertUtils.generateV3Certificate(null, keyPair, keyPair.getPublic(), "CN=ca", "SHA256withRSA", 365, null, null);
Mockito.when(caProvider.issueCertificate(Mockito.anyString(), Mockito.anyList(), Mockito.anyList(), Mockito.anyInt())).thenReturn(new Certificate(certificate, null, Collections.singletonList(certificate))); Mockito.when(caProvider.issueCertificate(Mockito.anyString(), Mockito.anyList(), Mockito.anyList(), Mockito.anyInt())).thenReturn(new Certificate(certificate, null, Collections.singletonList(certificate)));
Mockito.when(agentManager.send(Mockito.anyLong(), Mockito.any(SetupKeyStoreCommand.class))).thenReturn(new SetupKeystoreAnswer("someCsr")); Mockito.when(agentManager.send(Mockito.anyLong(), Mockito.any(SetupKeyStoreCommand.class))).thenReturn(new SetupKeystoreAnswer("someCsr"));
Mockito.when(agentManager.reconnect(Mockito.anyLong())).thenReturn(true); Mockito.when(agentManager.reconnect(Mockito.anyLong())).thenReturn(true);

View File

@ -48,9 +48,11 @@ import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder; import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder; import org.bouncycastle.cert.X509v3CertificateBuilder;
@ -176,7 +178,7 @@ public class CertUtils {
} }
public static X509Certificate generateV3Certificate(final X509Certificate caCert, public static X509Certificate generateV3Certificate(final X509Certificate caCert,
final PrivateKey caPrivateKey, final KeyPair caKeyPair,
final PublicKey clientPublicKey, final PublicKey clientPublicKey,
final String subject, final String subject,
final String signatureAlgorithm, final String signatureAlgorithm,
@ -186,25 +188,34 @@ public class CertUtils {
final DateTime now = DateTime.now(DateTimeZone.UTC); final DateTime now = DateTime.now(DateTimeZone.UTC);
final BigInteger serial = generateRandomBigInt(); final BigInteger serial = generateRandomBigInt();
final X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
caCert,
serial,
now.minusHours(12).toDate(),
now.plusDays(validityDays).toDate(),
new X500Principal(subject),
clientPublicKey);
final JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); final JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
final X509v3CertificateBuilder certBuilder;
if (caCert == null) {
// Generate CA certificate
certBuilder = new JcaX509v3CertificateBuilder(
new X500Name(subject),
serial,
now.minusHours(12).toDate(),
now.plusDays(validityDays).toDate(),
new X500Name(subject),
clientPublicKey);
certBuilder.addExtension( certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true));
Extension.subjectKeyIdentifier, certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign));
false, } else {
extUtils.createSubjectKeyIdentifier(clientPublicKey)); // Generate client certificate
certBuilder = new JcaX509v3CertificateBuilder(
caCert,
serial,
now.minusHours(12).toDate(),
now.plusDays(validityDays).toDate(),
new X500Principal(subject),
clientPublicKey);
certBuilder.addExtension( certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert));
Extension.authorityKeyIdentifier, }
false,
extUtils.createAuthorityKeyIdentifier(caCert)); certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(clientPublicKey));
final List<ASN1Encodable> subjectAlternativeNames = new ArrayList<ASN1Encodable>(); final List<ASN1Encodable> subjectAlternativeNames = new ArrayList<ASN1Encodable>();
if (publicIPAddresses != null) { if (publicIPAddresses != null) {
@ -225,16 +236,17 @@ public class CertUtils {
} }
if (subjectAlternativeNames.size() > 0) { if (subjectAlternativeNames.size() > 0) {
final GeneralNames subjectAltNames = GeneralNames.getInstance(new DERSequence(subjectAlternativeNames.toArray(new ASN1Encodable[] {}))); final GeneralNames subjectAltNames = GeneralNames.getInstance(new DERSequence(subjectAlternativeNames.toArray(new ASN1Encodable[] {})));
certBuilder.addExtension( certBuilder.addExtension(Extension.subjectAlternativeName, false, subjectAltNames);
Extension.subjectAlternativeName,
false,
subjectAltNames);
} }
final ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).setProvider("BC").build(caPrivateKey); final ContentSigner signer = new JcaContentSignerBuilder(signatureAlgorithm).setProvider("BC").build(caKeyPair.getPrivate());
final X509CertificateHolder certHolder = certBuilder.build(signer); final X509CertificateHolder certHolder = certBuilder.build(signer);
final X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder); final X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
cert.verify(caCert.getPublicKey()); if (caCert != null) {
cert.verify(caCert.getPublicKey());
} else {
cert.verify(caKeyPair.getPublic());
}
return cert; return cert;
} }
} }

View File

@ -39,7 +39,7 @@ public class CertUtilsTest {
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
caKeyPair = CertUtils.generateRandomKeyPair(1024); caKeyPair = CertUtils.generateRandomKeyPair(1024);
caCertificate = CertUtils.generateV1Certificate(caKeyPair, "CN=test", "CN=test", 1, "SHA256WithRSAEncryption"); caCertificate = CertUtils.generateV3Certificate(null, caKeyPair, caKeyPair.getPublic(), "CN=test", "SHA256WithRSAEncryption", 365, null, null);
} }
@Test @Test
@ -93,7 +93,7 @@ public class CertUtilsTest {
final List<String> domainNames = Arrays.asList("domain1.com", "www.2.domain2.com", "3.domain3.com"); final List<String> domainNames = Arrays.asList("domain1.com", "www.2.domain2.com", "3.domain3.com");
final List<String> addressList = Arrays.asList("1.2.3.4", "192.168.1.1", "2a02:120b:2c16:f6d0:d9df:8ebc:e44a:f181"); final List<String> addressList = Arrays.asList("1.2.3.4", "192.168.1.1", "2a02:120b:2c16:f6d0:d9df:8ebc:e44a:f181");
final X509Certificate clientCert = CertUtils.generateV3Certificate(caCertificate, caKeyPair.getPrivate(), clientKeyPair.getPublic(), final X509Certificate clientCert = CertUtils.generateV3Certificate(caCertificate, caKeyPair, clientKeyPair.getPublic(),
"CN=domain.example", "SHA256WithRSAEncryption", 10, domainNames, addressList); "CN=domain.example", "SHA256WithRSAEncryption", 10, domainNames, addressList);
clientCert.verify(caKeyPair.getPublic()); clientCert.verify(caKeyPair.getPublic());