FR12: Have basic constraint in CA certificate (#52)

* FR12: Have basic constraint in CA certificate

- Refactors certificate generation to use V3
- Removes use of V1 based certificate generator
- Puts basic constraint and keyusage extentions in certificate generator
  when caCert is not provided, i.e. for building CA certificate
- For normal certificate generation, skips putting basic constraint
  instead puts authority key identifier (the ca cert)
- Fixes tests to use the V3 certificate generator

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>

* FR12: backup and restore cpvm/ssvm keystore during reboot

This is backported from:
https://github.com/apache/cloudstack/pull/2278

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2017-10-10 10:38:13 +05:30 committed by GitHub
parent 5cd57a5011
commit 1f52cd4245
8 changed files with 79 additions and 54 deletions

View File

@ -35,7 +35,6 @@ import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
@ -139,11 +138,12 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
final String subject = "CN=" + domainNames.get(0);
final KeyPair keyPair = CertUtils.generateRandomKeyPair(CAManager.CertKeySize.value());
final X509Certificate clientCertificate = CertUtils.generateV3Certificate(
final X509Certificate clientCertificate = CertUtils.generateCertificate(
caCertificate,
caKeyPair.getPrivate(),
caKeyPair,
keyPair.getPublic(),
subject,
caCertificate.getIssuerDN().getName(),
CAManager.CertSignatureAlgorithm.value(),
validityDays,
domainNames,
@ -167,10 +167,11 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
final PKCS10CertificationRequest request = new PKCS10CertificationRequest(pemObject.getContent());
final X509Certificate clientCertificate = CertUtils.generateV3Certificate(
caCertificate, caKeyPair.getPrivate(),
final X509Certificate clientCertificate = CertUtils.generateCertificate(
caCertificate, caKeyPair,
request.getPublicKey(),
request.getCertificationRequestInfo().getSubject().toString(),
caCertificate.getIssuerDN().getName(),
CAManager.CertSignatureAlgorithm.value(),
validityDays,
domainNames,
@ -461,16 +462,18 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
}
try {
LOG.debug("Generating root CA certificate");
final X509Certificate rootCaCertificate = CertUtils.generateV1Certificate(
final X509Certificate rootCaCertificate = CertUtils.generateCertificate(
null,
caKeyPair,
caKeyPair.getPublic(),
rootCAIssuerDN.value(),
rootCAIssuerDN.value(),
caValidityYears,
CAManager.CertSignatureAlgorithm.value());
CAManager.CertSignatureAlgorithm.value(),
caValidityYears * 365, null, null);
if (!configDao.update(rootCACertificate.key(), rootCACertificate.category(), CertUtils.x509CertificateToPem(rootCaCertificate))) {
LOG.error("Failed to update RootCA public/x509 certificate");
}
} catch (final NoSuchAlgorithmException | NoSuchProviderException | CertificateEncodingException | SignatureException | InvalidKeyException | IOException e) {
} catch (final NoSuchAlgorithmException | NoSuchProviderException | CertificateException | SignatureException | InvalidKeyException | IOException e) {
LOG.error("Failed to generate RootCA certificate from private/public keys due to exception:", e);
return false;
}

View File

@ -56,10 +56,10 @@ 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(),
"CN=cloudstack.apache.org", "SHA256withRSA", 0, Collections.singletonList("cloudstack.apache.org"), Collections.singletonList(clientIp));
caCertificate = CertUtils.generateCertificate(null, caKeypair, caKeypair.getPublic(), "CN=ca", "CN=ca",
"SHA256withRSA", 1, null, null);
expiredClientCertificate = CertUtils.generateCertificate(caCertificate, caKeypair, clientKeypair.getPublic(),
"CN=cloudstack.apache.org", caCertificate.getIssuerDN().getName(), "SHA256withRSA", 0, Collections.singletonList("cloudstack.apache.org"), Collections.singletonList(clientIp));
}
@Test

View File

@ -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.generateCertificate(null, caKeyPair, caKeyPair.getPublic(), "CN=ca", "CN=ca", "SHA256withRSA", 1, null, null);
provider = new RootCAProvider();

View File

@ -68,8 +68,8 @@ 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.generateCertificate(null, keypair, keypair.getPublic(), "CN=ca", "CN=ca",
"SHA256withRSA", 0, null, null);
Mockito.when(hostDao.findByIp(Mockito.anyString())).thenReturn(host);
Mockito.when(caManager.getActiveCertificatesMap()).thenReturn(certMap);

View File

@ -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.generateCertificate(null, keyPair, keyPair.getPublic(), "CN=ca", "CN=ca", "SHA256withRSA", 1, 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);

View File

@ -21,10 +21,21 @@ logfile="/var/log/patchsystemvm.log"
# To use existing console proxy .zip-based package file
patch_console_proxy() {
local patchfile=$1
local backupfolder="/tmp/.conf.backup"
if [ -f /usr/local/cloud/systemvm/conf/cloud.jks ]; then
rm -fr $backupfolder
mkdir -p $backupfolder
cp -r /usr/local/cloud/systemvm/conf/* $backupfolder/
fi
rm /usr/local/cloud/systemvm -rf
mkdir -p /usr/local/cloud/systemvm
echo "All" | unzip $patchfile -d /usr/local/cloud/systemvm >$logfile 2>&1
find /usr/local/cloud/systemvm/ -name \*.sh | xargs chmod 555
if [ -f $backupfolder/cloud.jks ]; then
cp -r $backupfolder/* /usr/local/cloud/systemvm/conf/
echo "Restored keystore file and certs using backup" >> $logfile
fi
rm -fr $backupfolder
return 0
}

View File

@ -34,7 +34,6 @@ import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
@ -48,13 +47,14 @@ import javax.security.auth.x500.X500Principal;
import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
@ -145,47 +145,56 @@ public class CertUtils {
return new BigInteger(64, new SecureRandom());
}
public static X509Certificate generateV1Certificate(final KeyPair keyPair,
final String subjectDN,
final String issuerDN,
final int validityYears,
final String signatureAlgorithm) throws NoSuchAlgorithmException, NoSuchProviderException, CertificateEncodingException, SignatureException, InvalidKeyException {
final DateTime now = DateTime.now(DateTimeZone.UTC);
final X500Principal subjectDn = new X500Principal(subjectDN);
final X500Principal issuerDn = new X500Principal(issuerDN);
final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
certGen.setSerialNumber(generateRandomBigInt());
certGen.setSubjectDN(subjectDn);
certGen.setIssuerDN(issuerDn);
certGen.setNotBefore(now.minusDays(1).toDate());
certGen.setNotAfter(now.plusYears(validityYears).toDate());
certGen.setPublicKey(keyPair.getPublic());
certGen.setSignatureAlgorithm(signatureAlgorithm);
return certGen.generate(keyPair.getPrivate(), "BC");
}
public static X509Certificate generateV3Certificate(final X509Certificate caCert,
final PrivateKey caPrivateKey,
final PublicKey clientPublicKey,
final String subjectDN,
final String signatureAlgorithm,
final int validityDays,
final List<String> dnsNames,
final List<String> publicIPAddresses) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
/**
* Generates a X509 V3 Certificate based on provided arguments
* @param caCert when caCert is not provided self-signed root CA certificate is generated with basic constraints
* @param caKeyPair the ca KeyPair that contains public and private keys
* @param clientPublicKey the client public key, for CA self-signing this will be CA public key
* @param subjectDN
* @param issuerDN
* @param signatureAlgorithm
* @param validityDays
* @param dnsNames
* @param publicIPAddresses
* @return returns a X509Certificate
* @throws IOException
* @throws NoSuchAlgorithmException
* @throws CertificateException
* @throws NoSuchProviderException
* @throws InvalidKeyException
* @throws SignatureException
*/
public static X509Certificate generateCertificate(final X509Certificate caCert,
final KeyPair caKeyPair,
final PublicKey clientPublicKey,
final String subjectDN,
final String issuerDN,
final String signatureAlgorithm,
final int validityDays,
final List<String> dnsNames,
final List<String> publicIPAddresses) throws IOException, NoSuchAlgorithmException, CertificateException, NoSuchProviderException, InvalidKeyException, SignatureException {
final DateTime now = DateTime.now(DateTimeZone.UTC);
final BigInteger serial = generateRandomBigInt();
final X500Principal subject = new X500Principal(subjectDN);
final X500Principal issuer = new X500Principal(issuerDN);
final X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();;
certGen.setSerialNumber(serial);
certGen.setIssuerDN(caCert.getSubjectX500Principal());
certGen.setSubjectDN(subject);
certGen.setIssuerDN(issuer);
certGen.setNotBefore(now.minusHours(12).toDate());
certGen.setNotAfter(now.plusDays(validityDays).toDate());
certGen.setPublicKey(clientPublicKey);
certGen.setSignatureAlgorithm(signatureAlgorithm);
certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
new AuthorityKeyIdentifierStructure(caCert));
if (caCert == null) {
certGen.addExtension(X509Extensions.BasicConstraints, true,
new BasicConstraints(true, 0));
certGen.addExtension(X509Extensions.KeyUsage, true,
new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign));
} else {
certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,
new AuthorityKeyIdentifierStructure(caCert));
}
certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
new SubjectKeyIdentifierStructure(clientPublicKey));
@ -212,8 +221,8 @@ public class CertUtils {
certGen.addExtension(X509Extensions.SubjectAlternativeName, false,
subjectAlternativeNamesExtension);
}
final X509Certificate certificate = certGen.generate(caPrivateKey, "BC");
certificate.verify(caCert.getPublicKey());
final X509Certificate certificate = certGen.generate(caKeyPair.getPrivate(), "BC");
certificate.verify(caKeyPair.getPublic());
return certificate;
}
}

View File

@ -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.generateCertificate(null, caKeyPair, caKeyPair.getPublic(), "CN=test", "CN=test", "SHA256WithRSAEncryption", 365, null, null);
}
@Test
@ -93,8 +93,8 @@ public class CertUtilsTest {
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(),
"CN=domain.example", "SHA256WithRSAEncryption", 10, domainNames, addressList);
final X509Certificate clientCert = CertUtils.generateCertificate(caCertificate, caKeyPair, clientKeyPair.getPublic(),
"CN=domain.example", caCertificate.getIssuerDN().getName(), "SHA256WithRSAEncryption", 10, domainNames, addressList);
clientCert.verify(caKeyPair.getPublic());
Assert.assertEquals(clientCert.getIssuerDN(), caCertificate.getIssuerDN());