From c3ed1b38e52b2b47578e7c3eecd4694360e97bfe Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 11 Oct 2017 17:41:37 +0530 Subject: [PATCH] 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 --- .../ca/provider/RootCAProvider.java | 38 +++++------- .../RootCACustomTrustManagerTest.java | 5 +- .../ca/provider/RootCAProviderTest.java | 2 +- .../cloudstack/ca/CABackgroundTaskTest.java | 3 +- .../cloudstack/ca/CAManagerImplTest.java | 4 +- .../cloudstack/utils/security/CertUtils.java | 58 +++++++++++-------- .../utils/security/CertUtilsTest.java | 4 +- 7 files changed, 60 insertions(+), 54 deletions(-) diff --git a/plugins/ca/root-ca/src/org/apache/cloudstack/ca/provider/RootCAProvider.java b/plugins/ca/root-ca/src/org/apache/cloudstack/ca/provider/RootCAProvider.java index f21cae38d79..93c8d9d2f22 100644 --- a/plugins/ca/root-ca/src/org/apache/cloudstack/ca/provider/RootCAProvider.java +++ b/plugins/ca/root-ca/src/org/apache/cloudstack/ca/provider/RootCAProvider.java @@ -137,14 +137,9 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con final KeyPair keyPair = CertUtils.generateRandomKeyPair(CAManager.CertKeySize.value()); final X509Certificate clientCertificate = CertUtils.generateV3Certificate( - caCertificate, - caKeyPair.getPrivate(), - keyPair.getPublic(), - subject, - CAManager.CertSignatureAlgorithm.value(), - validityDays, - domainNames, - ipAddresses); + caCertificate, caKeyPair, keyPair.getPublic(), + subject, CAManager.CertSignatureAlgorithm.value(), + validityDays, domainNames, ipAddresses); 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 String subject = request.getCertificationRequestInfo().getSubject().toString(); final X509Certificate clientCertificate = CertUtils.generateV3Certificate( - caCertificate, caKeyPair.getPrivate(), - request.getPublicKey(), - request.getCertificationRequestInfo().getSubject().toString(), - CAManager.CertSignatureAlgorithm.value(), - validityDays, - domainNames, - ipAddresses); + caCertificate, caKeyPair, request.getPublicKey(), + subject, CAManager.CertSignatureAlgorithm.value(), + validityDays, domainNames, ipAddresses); return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate)); } @@ -257,6 +249,10 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con /////////////// Root CA Config /////////////////// ////////////////////////////////////////////////// + private int getCaValidityDays() { + return 365 * caValidityYears; + } + private char[] findKeyStorePassphrase() { char[] passphrase = KeyStoreUtils.defaultKeystorePassphrase; 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) { 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) { 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 { LOG.debug("Generating root CA certificate"); - final X509Certificate rootCaCertificate = CertUtils.generateV1Certificate( - caKeyPair, - rootCAIssuerDN.value(), - rootCAIssuerDN.value(), - caValidityYears, - CAManager.CertSignatureAlgorithm.value()); + final X509Certificate rootCaCertificate = CertUtils.generateV3Certificate( + null, caKeyPair, caKeyPair.getPublic(), + rootCAIssuerDN.value(), CAManager.CertSignatureAlgorithm.value(), + getCaValidityDays(), null, null); if (!configDao.update(rootCACertificate.key(), rootCACertificate.category(), CertUtils.x509CertificateToPem(rootCaCertificate))) { LOG.error("Failed to update RootCA public/x509 certificate"); } diff --git a/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCACustomTrustManagerTest.java b/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCACustomTrustManagerTest.java index cab1941fa96..8161d75e0ef 100644 --- a/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCACustomTrustManagerTest.java +++ b/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCACustomTrustManagerTest.java @@ -56,9 +56,8 @@ public class RootCACustomTrustManagerTest { certMap.clear(); caKeypair = CertUtils.generateRandomKeyPair(1024); clientKeypair = CertUtils.generateRandomKeyPair(1024); - caCertificate = CertUtils.generateV1Certificate(caKeypair, "CN=ca", "CN=ca", 1, - "SHA256withRSA"); - expiredClientCertificate = CertUtils.generateV3Certificate(caCertificate, caKeypair.getPrivate(), clientKeypair.getPublic(), + caCertificate = CertUtils.generateV3Certificate(null, caKeypair, caKeypair.getPublic(), "CN=ca", "SHA256withRSA", 365, null, null); + expiredClientCertificate = CertUtils.generateV3Certificate(caCertificate, caKeypair, clientKeypair.getPublic(), "CN=cloudstack.apache.org", "SHA256withRSA", 0, Collections.singletonList("cloudstack.apache.org"), Collections.singletonList(clientIp)); } diff --git a/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCAProviderTest.java b/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCAProviderTest.java index 44e2bcfcc49..d5d64289da2 100644 --- a/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCAProviderTest.java +++ b/plugins/ca/root-ca/test/org/apache/cloudstack/ca/provider/RootCAProviderTest.java @@ -66,7 +66,7 @@ public class RootCAProviderTest { @Before public void setUp() throws Exception { 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(); diff --git a/server/test/org/apache/cloudstack/ca/CABackgroundTaskTest.java b/server/test/org/apache/cloudstack/ca/CABackgroundTaskTest.java index d2c800d8272..564bbe36f6d 100644 --- a/server/test/org/apache/cloudstack/ca/CABackgroundTaskTest.java +++ b/server/test/org/apache/cloudstack/ca/CABackgroundTaskTest.java @@ -68,8 +68,7 @@ public class CABackgroundTaskTest { host.setManagementServerId(ManagementServerNode.getManagementServerId()); task = new CAManagerImpl.CABackgroundTask(caManager, hostDao); final KeyPair keypair = CertUtils.generateRandomKeyPair(1024); - expiredCertificate = CertUtils.generateV1Certificate(keypair, "CN=ca", "CN=ca", 0, - "SHA256withRSA"); + expiredCertificate = CertUtils.generateV3Certificate(null, keypair, keypair.getPublic(), "CN=ca", "SHA256withRSA", 0, null, null); Mockito.when(hostDao.findByIp(Mockito.anyString())).thenReturn(host); Mockito.when(caManager.getActiveCertificatesMap()).thenReturn(certMap); diff --git a/server/test/org/apache/cloudstack/ca/CAManagerImplTest.java b/server/test/org/apache/cloudstack/ca/CAManagerImplTest.java index 87e128c772e..14ecc971d5f 100644 --- a/server/test/org/apache/cloudstack/ca/CAManagerImplTest.java +++ b/server/test/org/apache/cloudstack/ca/CAManagerImplTest.java @@ -21,6 +21,7 @@ package org.apache.cloudstack.ca; import java.lang.reflect.Field; import java.math.BigInteger; +import java.security.KeyPair; import java.security.cert.X509Certificate; import java.util.Collections; @@ -108,7 +109,8 @@ public class CAManagerImplTest { public void testProvisionCertificate() throws Exception { final Host host = Mockito.mock(Host.class); 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(agentManager.send(Mockito.anyLong(), Mockito.any(SetupKeyStoreCommand.class))).thenReturn(new SetupKeystoreAnswer("someCsr")); Mockito.when(agentManager.reconnect(Mockito.anyLong())).thenReturn(true); diff --git a/utils/src/main/java/org/apache/cloudstack/utils/security/CertUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/security/CertUtils.java index c2ef9edb8c4..7e783622c40 100644 --- a/utils/src/main/java/org/apache/cloudstack/utils/security/CertUtils.java +++ b/utils/src/main/java/org/apache/cloudstack/utils/security/CertUtils.java @@ -48,9 +48,11 @@ import org.apache.log4j.Logger; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509v1CertificateBuilder; import org.bouncycastle.cert.X509v3CertificateBuilder; @@ -176,7 +178,7 @@ public class CertUtils { } public static X509Certificate generateV3Certificate(final X509Certificate caCert, - final PrivateKey caPrivateKey, + final KeyPair caKeyPair, final PublicKey clientPublicKey, final String subject, final String signatureAlgorithm, @@ -186,25 +188,34 @@ public class CertUtils { final DateTime now = DateTime.now(DateTimeZone.UTC); 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 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( - Extension.subjectKeyIdentifier, - false, - extUtils.createSubjectKeyIdentifier(clientPublicKey)); + certBuilder.addExtension(Extension.basicConstraints, true, new BasicConstraints(true)); + certBuilder.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign)); + } else { + // Generate client certificate + certBuilder = new JcaX509v3CertificateBuilder( + caCert, + serial, + now.minusHours(12).toDate(), + now.plusDays(validityDays).toDate(), + new X500Principal(subject), + clientPublicKey); - certBuilder.addExtension( - Extension.authorityKeyIdentifier, - false, - extUtils.createAuthorityKeyIdentifier(caCert)); + certBuilder.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(caCert)); + } + + certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(clientPublicKey)); final List subjectAlternativeNames = new ArrayList(); if (publicIPAddresses != null) { @@ -225,16 +236,17 @@ public class CertUtils { } if (subjectAlternativeNames.size() > 0) { final GeneralNames subjectAltNames = GeneralNames.getInstance(new DERSequence(subjectAlternativeNames.toArray(new ASN1Encodable[] {}))); - certBuilder.addExtension( - Extension.subjectAlternativeName, - false, - subjectAltNames); + certBuilder.addExtension(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 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; } } diff --git a/utils/src/test/java/org/apache/cloudstack/utils/security/CertUtilsTest.java b/utils/src/test/java/org/apache/cloudstack/utils/security/CertUtilsTest.java index 406f60449f7..1fed1765601 100644 --- a/utils/src/test/java/org/apache/cloudstack/utils/security/CertUtilsTest.java +++ b/utils/src/test/java/org/apache/cloudstack/utils/security/CertUtilsTest.java @@ -39,7 +39,7 @@ public class CertUtilsTest { @Before public void setUp() throws Exception { 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 @@ -93,7 +93,7 @@ public class CertUtilsTest { final List domainNames = Arrays.asList("domain1.com", "www.2.domain2.com", "3.domain3.com"); final List 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); clientCert.verify(caKeyPair.getPublic());