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 <rohit.yadav@shapeblue.com>
This commit is contained in:
Rohit Yadav 2018-04-30 17:40:20 +05:30 committed by Rohit Yadav
parent 2be45c2186
commit eb75c1eff5
3 changed files with 49 additions and 8 deletions

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.ca.provider;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader; import java.io.StringReader;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.InetAddress;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyPair; import java.security.KeyPair;
@ -34,6 +35,7 @@ import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -45,6 +47,7 @@ import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.xml.bind.DatatypeConverter;
import org.apache.cloudstack.ca.CAManager; import org.apache.cloudstack.ca.CAManager;
import org.apache.cloudstack.framework.ca.CAProvider; 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.CertUtils;
import org.apache.cloudstack.utils.security.KeyStoreUtils; import org.apache.cloudstack.utils.security.KeyStoreUtils;
import org.apache.log4j.Logger; 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.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest;
import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader; 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)); return new Certificate(clientCertificate, keyPair.getPrivate(), Collections.singletonList(caCertificate));
} }
private Certificate generateCertificateUsingCsr(final String csr, final List<String> domainNames, final List<String> ipAddresses, final int validityDays) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, CertificateException, SignatureException, IOException, OperatorCreationException { private Certificate generateCertificateUsingCsr(final String csr, final List<String> names, final List<String> ips, final int validityDays) throws NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, CertificateException, SignatureException, IOException, OperatorCreationException {
final List<String> dnsNames = new ArrayList<>();
final List<String> ipAddresses = new ArrayList<>();
if (names != null) {
dnsNames.addAll(names);
}
if (ips != null) {
ipAddresses.addAll(ips);
}
PemObject pemObject = null; PemObject pemObject = null;
try { try {
@ -151,13 +170,33 @@ public final class RootCAProvider extends AdapterBase implements CAProvider, Con
throw new CloudRuntimeException("Unable to read/process CSR: " + csr); 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( final X509Certificate clientCertificate = CertUtils.generateV3Certificate(
caCertificate, caKeyPair, request.getPublicKey(), caCertificate, caKeyPair, request.getPublicKey(),
subject, CAManager.CertSignatureAlgorithm.value(), subject, CAManager.CertSignatureAlgorithm.value(),
validityDays, domainNames, ipAddresses); validityDays, dnsNames, ipAddresses);
return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate)); return new Certificate(clientCertificate, null, Collections.singletonList(caCertificate));
} }

View File

@ -42,7 +42,8 @@ keytool -genkey -storepass "$KS_PASS" -keypass "$KS_PASS" -alias "$ALIAS" -keyal
# Generate CSR # Generate CSR
rm -f "$CSR_FILE" 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" cat "$CSR_FILE"
# Fix file permissions # Fix file permissions

View File

@ -40,6 +40,7 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
@ -219,7 +220,7 @@ public class CertUtils {
final List<ASN1Encodable> subjectAlternativeNames = new ArrayList<ASN1Encodable>(); final List<ASN1Encodable> subjectAlternativeNames = new ArrayList<ASN1Encodable>();
if (publicIPAddresses != null) { if (publicIPAddresses != null) {
for (final String publicIPAddress: publicIPAddresses) { for (final String publicIPAddress: new HashSet<>(publicIPAddresses)) {
if (Strings.isNullOrEmpty(publicIPAddress)) { if (Strings.isNullOrEmpty(publicIPAddress)) {
continue; continue;
} }
@ -227,7 +228,7 @@ public class CertUtils {
} }
} }
if (dnsNames != null) { if (dnsNames != null) {
for (final String dnsName : dnsNames) { for (final String dnsName : new HashSet<>(dnsNames)) {
if (Strings.isNullOrEmpty(dnsName)) { if (Strings.isNullOrEmpty(dnsName)) {
continue; continue;
} }