Skip to content

Commit

Permalink
CLOUDSTACK-9993: Have basic constraint in CA certificate (apache#2286)
Browse files Browse the repository at this point in the history
- 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>
  • Loading branch information
rohityadavcloud authored Oct 11, 2017
1 parent 0fedbdd commit c3ed1b3
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,9 @@ private Certificate generateCertificate(final List<String> domainNames, final Li

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));
}

Expand All @@ -164,14 +159,11 @@ private Certificate generateCertificateUsingCsr(final String csr, final List<Str

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));

}
Expand Down Expand Up @@ -257,6 +249,10 @@ public SSLEngine createSSLEngine(final SSLContext sslContext, final String remot
/////////////// 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");
Expand All @@ -268,7 +264,7 @@ private char[] findKeyStorePassphrase() {

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");
}
Expand Down Expand Up @@ -347,12 +343,10 @@ private boolean saveNewRootCACertificate() {
}
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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,8 @@ public void setUp() throws Exception {
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));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private void overrideDefaultConfigValue(final ConfigKey configKey, final String
@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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ public void setUp() throws Exception {
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);
Expand Down
4 changes: 3 additions & 1 deletion server/test/org/apache/cloudstack/ca/CAManagerImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.lang.reflect.Field;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
import java.util.Collections;

Expand Down Expand Up @@ -108,7 +109,8 @@ public void testRevokeCertificate() throws Exception {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,11 @@
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;
Expand Down Expand Up @@ -176,7 +178,7 @@ public static X509Certificate generateV1Certificate(final KeyPair keyPair,
}

public static X509Certificate generateV3Certificate(final X509Certificate caCert,
final PrivateKey caPrivateKey,
final KeyPair caKeyPair,
final PublicKey clientPublicKey,
final String subject,
final String signatureAlgorithm,
Expand All @@ -186,25 +188,34 @@ public static X509Certificate generateV3Certificate(final X509Certificate caCert

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.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.subjectKeyIdentifier,
false,
extUtils.createSubjectKeyIdentifier(clientPublicKey));

certBuilder.addExtension(
Extension.authorityKeyIdentifier,
false,
extUtils.createAuthorityKeyIdentifier(caCert));
certBuilder.addExtension(Extension.subjectKeyIdentifier, false, extUtils.createSubjectKeyIdentifier(clientPublicKey));

final List<ASN1Encodable> subjectAlternativeNames = new ArrayList<ASN1Encodable>();
if (publicIPAddresses != null) {
Expand All @@ -225,16 +236,17 @@ public static X509Certificate generateV3Certificate(final X509Certificate caCert
}
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -93,7 +93,7 @@ public void testGenerateCertificate() throws Exception {
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 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());
Expand Down

0 comments on commit c3ed1b3

Please sign in to comment.