From fb89a2d8f6c75d92ac926e9728ef9fa974ffcdd1 Mon Sep 17 00:00:00 2001 From: Syed Ahmed Date: Thu, 12 Dec 2013 12:11:20 +0530 Subject: [PATCH] CLOUDSTACK-5296: Add certificate chain support for netscaler. adds support for trust chains in the netscaler --- .gitignore | 2 +- .../cloud/network/lb/LoadBalancingRule.java | 15 +- .../com/cloud/network/dao/SslCertDaoImpl.java | 2 +- .../network/resource/NetscalerResource.java | 235 +++++++++++++++--- .../lb/LoadBalancingRulesManagerImpl.java | 8 +- .../network/lb/CertServiceImpl.java | 92 ++++--- .../utils/security/CertificateHelper.java | 56 +++++ 7 files changed, 326 insertions(+), 84 deletions(-) diff --git a/.gitignore b/.gitignore index 74739b26a40..2af2d17ba74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -G# Licensed to the Apache Software Foundation (ASF) under one +# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file diff --git a/api/src/com/cloud/network/lb/LoadBalancingRule.java b/api/src/com/cloud/network/lb/LoadBalancingRule.java index 39c969c7001..36f3a44e59f 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRule.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRule.java @@ -439,16 +439,17 @@ public class LoadBalancingRule { public static class LbSslCert { private String cert; private String key; - private String password=null; - private String chain=null; + private String password = null; + private String chain = null; + private String fingerprint = null; private boolean revoked; - - public LbSslCert(String cert, String key, String password, String chain, boolean revoked) { + public LbSslCert(String cert, String key, String password, String chain, String fingerprint, boolean revoked) { this.cert = cert; this.key = key; this.password = password; this.chain = chain; + this.fingerprint = fingerprint; this.revoked = revoked; } @@ -469,7 +470,11 @@ public class LoadBalancingRule { return chain; } - public boolean isRevoked(){ + public String getFingerprint() { + return fingerprint; + } + + public boolean isRevoked() { return revoked; } } diff --git a/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java b/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java index af0d9705d3f..5fd6cfb9389 100644 --- a/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java +++ b/engine/schema/src/com/cloud/network/dao/SslCertDaoImpl.java @@ -34,7 +34,7 @@ public class SslCertDaoImpl extends GenericDaoBase implements S listByAccountId = createSearchBuilder(); listByAccountId.and("accountId", listByAccountId.entity().getAccountId(), SearchCriteria.Op.EQ); listByAccountId.done(); - } + } @Override public List listByAccountId(Long accountId) { diff --git a/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java b/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java index e48d31d3f0a..4cd70cb7884 100644 --- a/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java +++ b/plugins/network-elements/netscaler/src/com/cloud/network/resource/NetscalerResource.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.network.resource; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.security.cert.Certificate; import java.util.ArrayList; import java.util.Formatter; import java.util.HashMap; @@ -26,9 +29,14 @@ import java.util.Map; import javax.naming.ConfigurationException; import com.citrix.netscaler.nitro.resource.config.ssl.sslcertkey; +import com.citrix.netscaler.nitro.resource.config.ssl.sslcertkey_sslvserver_binding; +import com.citrix.netscaler.nitro.resource.config.ssl.sslcertlink; import com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding; import com.cloud.network.lb.LoadBalancingRule.LbSslCert; +import com.cloud.utils.security.CertificateHelper; import com.cloud.utils.ssh.SshHelper; +import com.google.common.collect.Lists; +import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.log4j.Logger; import com.citrix.netscaler.nitro.exception.nitro_exception; @@ -119,6 +127,7 @@ import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.exception.ExecutionException; import com.cloud.utils.net.NetUtils; +import org.bouncycastle.openssl.PEMWriter; class NitroError { static final int NS_RESOURCE_EXISTS = 273; @@ -664,25 +673,61 @@ public class NetscalerResource implements ServerResource { } - - if(sslCert != null && lbProtocol.equals(NetUtils.SSL_PROTO)) { - if ( sslCert.isRevoked() ){ + if (sslCert != null && lbProtocol.equalsIgnoreCase(NetUtils.SSL_PROTO)) { + if (sslCert.isRevoked()) { deleteCert = true; } else{ - String certName = generateSslCertName(srcIp, srcPort); - String keyName = generateSslKeyName(srcIp, srcPort); - String certKeyName = generateSslCertKeyName(srcIp, srcPort); + // If there is a chain, that should go first to the NS - if ( SSL.isSslCertKeyPresent(_netscalerService, certKeyName)){ - SSL.deleteSslCertKey(_netscalerService, certKeyName); + String previousCertKeyName = null; + + if ( sslCert.getChain() != null ) { + List chainList = CertificateHelper.parseChain(sslCert.getChain()); + // go from ROOT to intermediate CAs + for ( Certificate intermediateCert : Lists.reverse(chainList)){ + + String fingerPrint=CertificateHelper.generateFingerPrint(intermediateCert); + String intermediateCertKeyName = generateSslCertKeyName(fingerPrint); + String intermediateCertFileName = intermediateCertKeyName + ".pem"; + + if (! SSL.isSslCertKeyPresent(_netscalerService, intermediateCertKeyName)) { + byte[] certData= intermediateCert.getEncoded(); + StringWriter textWriter = new StringWriter(); + PEMWriter pemWriter = new PEMWriter(textWriter); + pemWriter.writeObject(intermediateCert); + pemWriter.flush(); + + SSL.uploadCert(_ip, _username, _password, intermediateCertFileName, textWriter.toString().getBytes()); + SSL.createSslCertKey(_netscalerService, intermediateCertFileName, null, intermediateCertKeyName, null); + } + + if ( previousCertKeyName != null && ! SSL.certLinkExists(_netscalerService, intermediateCertKeyName, previousCertKeyName)){ + SSL.linkCerts(_netscalerService, intermediateCertKeyName, previousCertKeyName); + } + + previousCertKeyName = intermediateCertKeyName; + } } + String certFilename = generateSslCertName(sslCert.getFingerprint()) + ".pem"; //netscaler uses ".pem" format for "bundle" files + String keyFilename = generateSslKeyName(sslCert.getFingerprint()) + ".pem"; //netscaler uses ".pem" format for "bundle" files + String certKeyName = generateSslCertKeyName(sslCert.getFingerprint()); - SSL.uploadCert(_ip, _username, _password, certName, sslCert.getCert().getBytes()); - SSL.uploadKey(_ip, _username, _password, keyName, sslCert.getKey().getBytes()); + ByteArrayOutputStream certDataStream = new ByteArrayOutputStream( ); + certDataStream.write(sslCert.getCert().getBytes()); + + if (! SSL.isSslCertKeyPresent(_netscalerService, certKeyName)) { + + SSL.uploadCert(_ip, _username, _password, certFilename, certDataStream.toByteArray()); + SSL.uploadKey(_ip, _username, _password, keyFilename, sslCert.getKey().getBytes()); + SSL.createSslCertKey(_netscalerService, certFilename, keyFilename, certKeyName, sslCert.getPassword()); + } + + if (previousCertKeyName != null && ! SSL.certLinkExists(_netscalerService, certKeyName, previousCertKeyName)){ + SSL.linkCerts(_netscalerService, certKeyName, previousCertKeyName); + } - SSL.createSslCertKey(_netscalerService, certName, keyName, certKeyName, sslCert.getPassword()); SSL.bindCertKeyToVserver(_netscalerService, certKeyName, nsVirtualServerName); } @@ -773,18 +818,50 @@ public class NetscalerResource implements ServerResource { } if ( sslCert != null && deleteCert){ - String certName = generateSslCertName(srcIp, srcPort); - String keyName = generateSslKeyName(srcIp, srcPort); - String certKeyName = generateSslCertKeyName(srcIp, srcPort); + String certFilename = generateSslCertName(sslCert.getFingerprint()) + ".pem"; //netscaler uses ".pem" format for "bundle" files + String keyFilename = generateSslKeyName(sslCert.getFingerprint()) + ".pem"; //netscaler uses ".pem" format for "bundle" files + String certKeyName = generateSslCertKeyName(sslCert.getFingerprint()); // unbind before deleting - if ( nsVirtualServerExists(nsVirtualServerName) ){ + if (nsVirtualServerExists(nsVirtualServerName) && + SSL.isSslCertKeyPresent(_netscalerService, certKeyName) && + SSL.isBoundToVserver(_netscalerService, certKeyName, nsVirtualServerName)) { SSL.unbindCertKeyFromVserver(_netscalerService, certKeyName, nsVirtualServerName); } - SSL.deleteSslCertKey(_netscalerService, certKeyName); - SSL.deleteCertFile(_ip, _username, _password, certName); - SSL.deleteKeyFile(_ip, _username, _password, keyName); + if (SSL.isSslCertKeyPresent(_netscalerService, certKeyName)) { + + SSL.deleteSslCertKey(_netscalerService, certKeyName); + SSL.deleteCertFile(_ip, _username, _password, certFilename); + SSL.deleteKeyFile(_ip, _username, _password, keyFilename); + } + + + /* + * Check and delete intermediate certs: + * we can delete an intermediate cert if no other + * cert references it as the athority + */ + + if ( sslCert.getChain() != null ) { + List chainList = CertificateHelper.parseChain(sslCert.getChain()); + //go from intermediate CAs to ROOT + for ( Certificate intermediateCert : chainList){ + + String fingerPrint=CertificateHelper.generateFingerPrint(intermediateCert); + String intermediateCertKeyName = generateSslCertKeyName(fingerPrint); + String intermediateCertFileName = intermediateCertKeyName + ".pem"; + + if (SSL.isSslCertKeyPresent(_netscalerService, intermediateCertKeyName) && + ! SSL.isCaforCerts(_netscalerService, intermediateCertKeyName)) { + SSL.deleteSslCertKey(_netscalerService, intermediateCertKeyName); + SSL.deleteCertFile(_ip, _username, _password, intermediateCertFileName); + }else { + break;// if this cert has another certificate as a child then stop at this point because we need the whole chain + } + + } + } } } @@ -1737,7 +1814,7 @@ public class NetscalerResource implements ServerResource { return false; } - private static void deleteSslCertKey(nitro_service ns, String certKeyName) throws ExecutionException { + private static void deleteSslCertKey(nitro_service ns, String certKeyName) throws ExecutionException { try { sslcertkey certkey = new sslcertkey(); @@ -1752,21 +1829,23 @@ public class NetscalerResource implements ServerResource { } - private static void deleteCertFile(String nsIp, String username, String password, String certName) throws Exception { - SshHelper.sshExecute(nsIp,SSH_PORT,username,null,password,"shell rm " + SSL_CERT_PATH + certName); + private static void deleteCertFile(String nsIp, String username, String password, String certFilename) throws Exception { + SshHelper.sshExecute(nsIp,SSH_PORT,username,null,password,"shell rm " + SSL_CERT_PATH + certFilename); } - private static void deleteKeyFile(String nsIp, String username, String password, String keyName) throws Exception { - SshHelper.sshExecute(nsIp,SSH_PORT,username,null,password,"shell rm " + SSL_CERT_PATH + keyName); + private static void deleteKeyFile(String nsIp, String username, String password, String keyFilename) throws Exception { + SshHelper.sshExecute(nsIp, SSH_PORT, username, null, password, "shell rm " + SSL_CERT_PATH + keyFilename); } - private static void createSslCertKey(nitro_service ns, String certName, String keyName, String certKeyName, String password) throws ExecutionException { + private static void createSslCertKey(nitro_service ns, String certFilename, String keyFilename, String certKeyName, String password) throws ExecutionException { s_logger.debug("Adding cert to netscaler"); try { sslcertkey certkey = new sslcertkey(); certkey.set_certkey(certKeyName); - certkey.set_cert(SSL_CERT_PATH + certName); - certkey.set_key(SSL_CERT_PATH + keyName); + certkey.set_cert(SSL_CERT_PATH + certFilename); + + if ( keyFilename != null ) + certkey.set_key(SSL_CERT_PATH + keyFilename); if( password != null ) { certkey.set_passplain(password); @@ -1832,19 +1911,18 @@ public class NetscalerResource implements ServerResource { } - - private static void uploadCert(String nsIp, String user, String password, String certName, byte[] certData) throws ExecutionException { + private static void uploadCert(String nsIp, String user, String password, String certFilename, byte[] certData) throws ExecutionException { try { - SshHelper.scpTo(nsIp,SSH_PORT,user,null,password, SSL_CERT_PATH, certData, certName, null); + SshHelper.scpTo(nsIp,SSH_PORT,user,null,password, SSL_CERT_PATH, certData, certFilename, null); } catch (Exception e){ throw new ExecutionException("Failed to copy private key to device " + e.getMessage()); } } - private static void uploadKey(String nsIp, String user, String password, String keyName, byte[] keyData) throws ExecutionException { + private static void uploadKey(String nsIp, String user, String password, String keyFilename, byte[] keyData) throws ExecutionException { try { - SshHelper.scpTo(nsIp, SSH_PORT, user, null, password, SSL_CERT_PATH, keyData, keyName, null); - } catch (Exception e){ + SshHelper.scpTo(nsIp, SSH_PORT, user, null, password, SSL_CERT_PATH, keyData, keyFilename, null); + } catch (Exception e) { throw new ExecutionException("Failed to copy private key to device " + e.getMessage()); } } @@ -1853,7 +1931,7 @@ public class NetscalerResource implements ServerResource { private static void enableSslFeature(nitro_service ns) throws ExecutionException { try { base_response result = ns.enable_features(new String[]{"SSL"}); - if( result.errorcode != 0 ) + if (result.errorcode != 0) throw new ExecutionException("Unable to enable SSL on LB"); } catch (nitro_exception e){ throw new ExecutionException("Failed to enable ssl feature on load balancer due to " + e.getMessage()); @@ -1880,7 +1958,80 @@ public class NetscalerResource implements ServerResource { } } + public static boolean certLinkExists(nitro_service ns, String userCertName, String caCertName) throws ExecutionException { + try { + // check if there is a link from userCertName to caCertName + sslcertkey userCert = sslcertkey.get(ns,userCertName); + String nsCaCert = userCert.get_linkcertkeyname(); + + if (nsCaCert != null && nsCaCert.equals(caCertName)) + return true; + + } catch (nitro_exception e) { + throw new ExecutionException("Failed to check cert link on load balancer to " + e.getMessage()); + } catch (Exception e) { + throw new ExecutionException("Failed to check cert link on load balancer due to " + e.getMessage()); + } + return false; + } + + public static void linkCerts(nitro_service ns, String userCertName, String caCertName) throws ExecutionException { + try { + + // the assumption is that that both userCertName and caCertName are present on NS + + sslcertkey caCert = sslcertkey.get(ns, caCertName); + sslcertkey userCert = sslcertkey.get(ns, userCertName); + + sslcertkey linkResource = new sslcertkey(); + + // link user cert to CA cert + linkResource.set_certkey(userCert.get_certkey()); + linkResource.set_linkcertkeyname(caCert.get_certkey()); + sslcertkey.link(ns, linkResource); + + } catch (nitro_exception e) { + throw new ExecutionException("Failed to check cert link on load balancer to " + e.getMessage()); + } catch (Exception e) { + throw new ExecutionException("Failed to check cert link on load balancer due to " + e.getMessage()); + } + + } + + public static boolean isCaforCerts(nitro_service ns, String caCertName) throws ExecutionException { + // check if this certificate serves as a CA for other certificates + try { + sslcertlink[] childLinks = sslcertlink.get_filtered(ns,"linkcertkeyname:" + caCertName); + if(childLinks != null && childLinks.length > 0){ + return true; + } + + } catch (nitro_exception e) { + throw new ExecutionException("Failed to check cert link on load balancer to " + e.getMessage()); + } catch (Exception e) { + throw new ExecutionException("Failed to check cert link on load balancer due to " + e.getMessage()); + } + return false; + + } + + public static boolean isBoundToVserver(nitro_service ns, String certKeyName, String nsVirtualServerName) throws ExecutionException { + try { + + sslcertkey_sslvserver_binding[] cert_vs_binding = sslcertkey_sslvserver_binding.get_filtered(ns, certKeyName, "vservername:" + nsVirtualServerName); + if(cert_vs_binding != null && cert_vs_binding.length > 0){ + return true; + } + + } catch (nitro_exception e) { + throw new ExecutionException("Failed to check cert link on load balancer to " + e.getMessage()); + } catch (Exception e) { + throw new ExecutionException("Failed to check cert link on load balancer due to " + e.getMessage()); + } + return false; + + } } @@ -3624,16 +3775,22 @@ public class NetscalerResource implements ServerResource { return counterName.replace(' ', '_'); } - private String generateSslCertName(String srcIp, long srcPort) { + private String generateSslCertName(String fingerPrint) { // maximum length supported by NS is 31 - return genObjectName("Cloud-Cert", srcIp, srcPort); + // the first 20 characters of the SHA-1 checksum are the unique id + String uniqueId = fingerPrint.replace(":","").substring(0,20); + + return genObjectName("Cloud-Cert", uniqueId); } - private String generateSslKeyName(String srcIp, long srcPort) { - return genObjectName("Cloud-Key", srcIp, srcPort); + private String generateSslKeyName(String fingerPrint) { + String uniqueId = fingerPrint.replace(":","").substring(0,20); + return genObjectName("Cloud-Key", uniqueId); } - private String generateSslCertKeyName(String srcIp, long srcPort) { - return genObjectName("Cloud-CertKey", srcIp, srcPort); + + private String generateSslCertKeyName(String fingerPrint){ + String uniqueId = fingerPrint.replace(":","").substring(0,20); + return genObjectName("Cloud-Cert", uniqueId); } private String genObjectName(Object... args) { diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index a1650e07109..bd58e398473 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -1121,8 +1121,7 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements return null; } - return new LbSslCert(certVO.getCertificate(), certVO.getKey(), - certVO.getChain(), certVO.getPassword(), lbCertMap.isRevoke()); + return new LbSslCert(certVO.getCertificate(), certVO.getKey(), certVO.getPassword(), certVO.getChain(), certVO.getFingerPrint(), lbCertMap.isRevoke()); } @Override @@ -1174,11 +1173,6 @@ public class LoadBalancingRulesManagerImpl extends ManagerBase implements LoadBalancerCertMapVO certMap = new LoadBalancerCertMapVO(lbRuleId,certId,false); _lbCertMapDao.persist(certMap); applyLoadBalancerConfig(loadBalancer.getId()); - /*s_logger.warn("Failed to apply Ssl Cert to LB " + loadBalancer.getId()); - CloudRuntimeException ex = new CloudRuntimeException( - "Failed to apply Ssl Cert to LB " + loadBalancer.getId()); - ex.addProxyObject(loadBalancer.getUuid(), "loadBalancerId"); - throw ex;*/ success = true; } catch (ResourceUnavailableException e) { if (isRollBackAllowedForProvider(loadBalancer)) { diff --git a/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java b/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java index 74adb373e57..b699fc562fc 100644 --- a/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java +++ b/server/src/org/apache/cloudstack/network/lb/CertServiceImpl.java @@ -16,14 +16,60 @@ // under the License. package org.apache.cloudstack.network.lb; +import java.io.IOException; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.CertPathBuilder; +import java.security.cert.CertPathBuilderException; +import java.security.cert.CertStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXBuilderParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CertSelector; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.ejb.Local; +import javax.inject.Inject; + import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; -import com.cloud.network.dao.*; +import com.cloud.network.dao.LoadBalancerCertMapDao; +import com.cloud.network.dao.LoadBalancerCertMapVO; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.SslCertDao; +import com.cloud.network.dao.SslCertVO; import com.cloud.network.lb.CertService; import com.cloud.network.rules.LoadBalancer; import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.commons.io.IOUtils; +import org.apache.log4j.Logger; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMReader; +import org.bouncycastle.openssl.PasswordFinder; import org.apache.cloudstack.api.command.user.loadbalancer.DeleteSslCertCmd; import org.apache.cloudstack.api.command.user.loadbalancer.ListSslCertsCmd; @@ -37,30 +83,6 @@ import com.cloud.utils.db.DB; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.context.CallContext; -import org.apache.commons.io.IOUtils; -import org.apache.log4j.Logger; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.openssl.PEMReader; -import org.bouncycastle.openssl.PasswordFinder; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.ejb.Local; -import javax.inject.Inject; - -import java.io.IOException; -import java.io.StringReader; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.security.*; -import java.security.cert.*; -import java.security.cert.Certificate; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; @Local(value = {CertService.class}) public class CertServiceImpl implements CertService { @@ -68,10 +90,14 @@ public class CertServiceImpl implements CertService { private static final Logger s_logger = Logger.getLogger(CertServiceImpl.class); @Inject AccountManager _accountMgr; - @Inject AccountDao _accountDao; - @Inject SslCertDao _sslCertDao; - @Inject LoadBalancerCertMapDao _lbCertDao; - @Inject EntityManager _entityMgr; + @Inject + AccountDao _accountDao; + @Inject + SslCertDao _sslCertDao; + @Inject + LoadBalancerCertMapDao _lbCertDao; + @Inject + EntityManager _entityMgr; @@ -92,6 +118,7 @@ public class CertServiceImpl implements CertService { validate(cert, key, password, chain); s_logger.debug("Certificate Validation succeeded"); + String fingerPrint = generateFingerPrint(parseCertificate(cert)); Long accountId = CallContext.current().getCallingAccount().getId(); @@ -365,14 +392,17 @@ public class CertServiceImpl implements CertService { params = new PKIXBuilderParameters(anchors, target); params.setRevocationEnabled(false); params.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certs))); - CertPathBuilder builder = CertPathBuilder.getInstance("PKIX"); + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); builder.build(params); + } catch (InvalidAlgorithmParameterException e) { throw new IllegalArgumentException("Invalid certificate chain", e); } catch (CertPathBuilderException e) { throw new IllegalArgumentException("Invalid certificate chain", e); } catch (NoSuchAlgorithmException e) { throw new IllegalArgumentException("Invalid certificate chain", e); + } catch (NoSuchProviderException e) { + throw new CloudRuntimeException("No provider for certificate validation", e); } } @@ -485,4 +515,4 @@ public class CertServiceImpl implements CertService { return password_requested; } } -} \ No newline at end of file +} diff --git a/utils/src/com/cloud/utils/security/CertificateHelper.java b/utils/src/com/cloud/utils/security/CertificateHelper.java index 8344d725bc6..e1edbbdd472 100644 --- a/utils/src/com/cloud/utils/security/CertificateHelper.java +++ b/utils/src/com/cloud/utils/security/CertificateHelper.java @@ -20,21 +20,28 @@ import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.StringReader; import java.security.Key; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; import java.util.List; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.commons.codec.binary.Base64; import com.cloud.utils.Ternary; +import org.bouncycastle.openssl.PEMReader; public class CertificateHelper { public static byte[] buildAndSaveKeystore(String alias, String cert, String privateKey, String storePassword) throws KeyStoreException, CertificateException, @@ -105,4 +112,53 @@ public class CertificateHelper { PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec (Base64.decodeBase64(base64EncodedKeyContent)); return kf.generatePrivate (keysp); } + + public static List parseChain(String chain) throws IOException { + + List certs = new ArrayList(); + PEMReader reader = new PEMReader(new StringReader(chain)); + + Certificate crt = null; + + while ((crt = (Certificate)reader.readObject()) != null) { + if (crt instanceof X509Certificate) { + certs.add(crt); + } + } + if (certs.size() == 0) + throw new IllegalArgumentException("Unable to decode certificate chain"); + + return certs; + } + + public static String generateFingerPrint(Certificate cert) { + + final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + StringBuilder buffer = new StringBuilder(60); + try { + + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] data = md.digest(cert.getEncoded()); + + for (int i = 0; i < data.length; i++) { + if (buffer.length() > 0) { + buffer.append(":"); + } + + buffer.append(HEX[(0xF0 & data[i]) >>> 4]); + buffer.append(HEX[0x0F & data[i]]); + } + + } catch (CertificateEncodingException e) { + throw new CloudRuntimeException("Bad certificate encoding"); + } catch (NoSuchAlgorithmException e) { + throw new CloudRuntimeException("Bad certificate algorithm"); + } + + return buffer.toString(); + } + + + }