From eb75c1eff51e4e121f7ef690cb56b8b22d43f143 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Mon, 30 Apr 2018 17:40:20 +0530 Subject: [PATCH] ca: Fixes #2530 have all IPs from KVM host in issued X509 cert This ensures that certificate setup includes all the IP addresses (v4 and v6) when a (KVM) host is added to CloudStack. This fixes #2530. Signed-off-by: Rohit Yadav --- .../ca/provider/RootCAProvider.java | 49 +++++++++++++++++-- scripts/util/keystore-setup | 3 +- .../cloudstack/utils/security/CertUtils.java | 5 +- 3 files changed, 49 insertions(+), 8 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 6584b35861a..f36d06799b3 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 @@ -20,6 +20,7 @@ package org.apache.cloudstack.ca.provider; import java.io.IOException; import java.io.StringReader; import java.math.BigInteger; +import java.net.InetAddress; import java.security.InvalidKeyException; import java.security.KeyManagementException; import java.security.KeyPair; @@ -34,6 +35,7 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -45,6 +47,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.xml.bind.DatatypeConverter; import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.framework.ca.CAProvider; @@ -55,9 +58,15 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.utils.security.CertUtils; import org.apache.cloudstack.utils.security.KeyStoreUtils; import org.apache.log4j.Logger; -import org.bouncycastle.jce.PKCS10CertificationRequest; +import org.bouncycastle.asn1.pkcs.Attribute; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.Extensions; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; @@ -137,7 +146,17 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con return new Certificate(clientCertificate, keyPair.getPrivate(), Collections.singletonList(caCertificate)); } - private Certificate generateCertificateUsingCsr(final String csr, final List domainNames, final List ipAddresses, final int validityDays) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, CertificateException, SignatureException, IOException, OperatorCreationException { + private Certificate generateCertificateUsingCsr(final String csr, final List names, final List ips, final int validityDays) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, CertificateException, SignatureException, IOException, OperatorCreationException { + final List dnsNames = new ArrayList<>(); + final List ipAddresses = new ArrayList<>(); + + if (names != null) { + dnsNames.addAll(names); + } + if (ips != null) { + ipAddresses.addAll(ips); + } + PemObject pemObject = null; try { @@ -151,13 +170,33 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con throw new CloudRuntimeException("Unable to read/process CSR: " + csr); } - final PKCS10CertificationRequest request = new PKCS10CertificationRequest(pemObject.getContent()); + final JcaPKCS10CertificationRequest request = new JcaPKCS10CertificationRequest(pemObject.getContent()); + final String subject = request.getSubject().toString(); + for (final Attribute attribute : request.getAttributes()) { + if (attribute == null) { + continue; + } + if (attribute.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)) { + final Extensions extensions = Extensions.getInstance(attribute.getAttrValues().getObjectAt(0)); + final GeneralNames gns = GeneralNames.fromExtensions(extensions, Extension.subjectAlternativeName); + if (gns != null && gns.getNames() != null && gns.getNames().length > 0) { + for (final GeneralName name : gns.getNames()) { + if (name.getTagNo() == GeneralName.dNSName) { + dnsNames.add(name.getName().toString()); + } + if (name.getTagNo() == GeneralName.iPAddress) { + final InetAddress address = InetAddress.getByAddress(DatatypeConverter.parseHexBinary(name.getName().toString().substring(1))); + ipAddresses.add(address.toString().replace("/", "")); + } + } + } + } + } - final String subject = request.getCertificationRequestInfo().getSubject().toString(); final X509Certificate clientCertificate = CertUtils.generateV3Certificate( caCertificate, caKeyPair, request.getPublicKey(), subject, CAManager.CertSignatureAlgorithm.value(), - validityDays, domainNames, ipAddresses); + validityDays, dnsNames, ipAddresses); return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate)); } diff --git a/scripts/util/keystore-setup b/scripts/util/keystore-setup index 48ce06220ca..ce963363c1d 100755 --- a/scripts/util/keystore-setup +++ b/scripts/util/keystore-setup @@ -42,7 +42,8 @@ keytool -genkey -storepass "$KS_PASS" -keypass "$KS_PASS" -alias "$ALIAS" -keyal # Generate CSR rm -f "$CSR_FILE" -keytool -certreq -storepass "$KS_PASS" -alias "$ALIAS" -file $CSR_FILE -keystore "$KS_FILE" > /dev/null 2>&1 +addresses=$(ip address | grep inet | awk '{print $2}' | sed 's/\/.*//g' | grep -v '^169.254.' | grep -v '^127.0.0.1' | grep -v '^::1' | sed 's/^/ip:/g' | tr '\r\n' ',') +keytool -certreq -storepass "$KS_PASS" -alias "$ALIAS" -file $CSR_FILE -keystore "$KS_FILE" -ext san="$addresses" > /dev/null 2>&1 cat "$CSR_FILE" # Fix file permissions 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 7e783622c40..aea65a7df6f 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 @@ -40,6 +40,7 @@ import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import javax.security.auth.x500.X500Principal; @@ -219,7 +220,7 @@ public class CertUtils { final List subjectAlternativeNames = new ArrayList(); if (publicIPAddresses != null) { - for (final String publicIPAddress: publicIPAddresses) { + for (final String publicIPAddress: new HashSet<>(publicIPAddresses)) { if (Strings.isNullOrEmpty(publicIPAddress)) { continue; } @@ -227,7 +228,7 @@ public class CertUtils { } } if (dnsNames != null) { - for (final String dnsName : dnsNames) { + for (final String dnsName : new HashSet<>(dnsNames)) { if (Strings.isNullOrEmpty(dnsName)) { continue; }