/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.keystore.impl; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.security.keystore.DistributedKeyStore; import org.apache.commons.codec.digest.DigestUtils; import org.apache.curator.framework.recipes.locks.InterProcessLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.*; import java.security.KeyStore; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * Load the trust store from local file system to zookeeper. */ public class TrustStoreLoader { private static final Logger log = LoggerFactory.getLogger(TrustStoreLoader.class); private static final String CA_CERTS_LOCK = "caCertsLock"; private static final String CA_CERTS_CONFIG_LOCK = "caCertsConfigLock"; private String tsVersionFilePath; private String caCertFile; private CoordinatorClient coordinatorClient; private CoordinatorConfigStoringHelper coordHelper; private CertificateFactory certFactory = null; public void setTsVersionFilePath(String tsVersionFilePath) { this.tsVersionFilePath = tsVersionFilePath; } public void setCaCertFile(String caCertFile) { this.caCertFile = caCertFile; } public void setCoordinatorClient(CoordinatorClient coordinatorClient) { this.coordinatorClient = coordinatorClient; } public void setCoordHelper(CoordinatorConfigStoringHelper coordHelper) { this.coordHelper = coordHelper; } public void load() { InterProcessLock tsLock = null; try { /* * the lock and version check to make sure, within one vdc only one service which uses truststore (like authsvc) * fill up zk truststore at same time. */ log.info("Loading the builtin trust store ..."); tsLock = coordHelper.acquireLock(CA_CERTS_LOCK); if (compareTrustStoreVersion()) { // same version in zk and local log.info("CA certs version match, no need to do anything."); return; } log.info("CA certs version doesn't match, need to load root certs from file to zk."); loadCertsFromLocalKeyStore(); addVersionInZK(); log.info("Loaded the builtin trust store successfully"); } catch (Exception e) { throw new RuntimeException(e); } finally { coordHelper.releaseLock(tsLock); } } private void addVersionInZK() { try { String version = getVersionFromFile(); coordHelper.createOrUpdateConfig(version, CA_CERTS_CONFIG_LOCK, DistributedKeyStoreImpl.CA_CERTIFICATES_CONFIG_KIND, DistributedKeyStoreImpl.CA_CERTIFICATES_CONFIG_ID, DistributedKeyStoreImpl.CA_CERTIFICATES_CONFIG_KEY_VERSION ); log.info("saved the new version of ca certs to ZK: {}", version); } catch (Exception e) { throw new RuntimeException(e); } } private String getVersionFromFile() { File tsVersionFile = new File(tsVersionFilePath); if (!tsVersionFile.exists()) { throw new RuntimeException("The version file of trust store not found: " + tsVersionFilePath); } BufferedReader in = null; try { in = new BufferedReader(new FileReader(tsVersionFile)); return in.readLine().trim(); } catch (Exception e) { throw new RuntimeException(e); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { log.error("Unexpected error", e); } } } private boolean compareTrustStoreVersion() { String localVersion = getVersionFromFile(); String zkVersion = getVersionFromZK(); log.info("The local version of cacerts is [{}], the zk version is [{}].", localVersion, zkVersion); if (zkVersion == null) { return false; } // return true if zk version is newer or equal return (Integer.parseInt(localVersion) <= Integer.parseInt(zkVersion)) ? true : false; } private String getVersionFromZK() { try { String v = coordHelper.readConfig( DistributedKeyStoreImpl.CA_CERTIFICATES_CONFIG_KIND, DistributedKeyStoreImpl.CA_CERTIFICATES_CONFIG_ID, DistributedKeyStoreImpl.CA_CERTIFICATES_CONFIG_KEY_VERSION); return (v == null) ? null : v.trim(); } catch (Exception e) { throw new RuntimeException(e); } } private void loadCertsFromLocalKeyStore() { log.info("Adding root certificates from " + caCertFile + " into ViPR."); Map<String, TrustedCertificateEntry> cacerts = loadAllRootCerts(); writeCertsToViPRKeyStore(cacerts); log.info("Added " + cacerts.keySet().size() + " root certificates into ViPR."); } private Map<String, TrustedCertificateEntry> loadAllRootCerts() { Map<String, TrustedCertificateEntry> cacerts = new HashMap<>(); try { // load key store from file FileInputStream is = new FileInputStream(caCertFile); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(is, null); Enumeration<String> aliases = keyStore.aliases(); // put all certs in the map. the key is the hash value of each cert string. while (aliases.hasMoreElements()) { String als = aliases.nextElement(); X509Certificate cert = (X509Certificate) keyStore.getCertificate(als); String alias = DigestUtils.sha512Hex(cert.getEncoded()) + KeystoreEngine.SUFFIX_VIPR_SUPPLY_CERT; cacerts.put(alias, new TrustedCertificateEntry(cert, new Date())); } return cacerts; } catch (Exception e) { throw new RuntimeException(e); } } private void writeCertsToViPRKeyStore(Map<String, TrustedCertificateEntry> cacerts) { DistributedKeyStore zkKeystore = new DistributedKeyStoreImpl(); DistributedLoadKeyStoreParam loadStoreParam = new DistributedLoadKeyStoreParam(); loadStoreParam.setCoordinator(coordinatorClient); zkKeystore.init(loadStoreParam); zkKeystore.setCACertificates(cacerts); } }