/* * Copyright (c) 2013-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.keystore.impl; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.*; import java.security.KeyStore.LoadStoreParameter; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.util.*; import java.util.Map.Entry; import com.emc.storageos.security.helpers.SecurityService; import com.emc.storageos.security.helpers.SecurityUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.security.exceptions.SecurityException; import com.emc.storageos.security.keystore.DistributedKeyStore; import com.emc.storageos.security.keystore.KeystoreParam; /** * Implements JAVA's KeyStoreSpi and allows us to use java's Security framework with our * own implementation */ public class KeystoreEngine extends KeyStoreSpi { private static Logger log = LoggerFactory.getLogger(KeystoreEngine.class); public static final String ViPR_KEY_AND_CERTIFICATE_ALIAS = "ViPR_KEY_AND_CERTIFICATE"; public static final String SUFFIX_VIPR_SUPPLY_CERT = "@@VIPR"; // the keystore in zk. private DistributedKeyStore distributedKeyStore; private KeyCertificatePairGenerator keyGen = new KeyCertificatePairGenerator(); private SecurityService secService; /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineAliases() */ @Override public Enumeration<String> engineAliases() { log.debug("engineAliases called"); List<String> allAliases = new ArrayList<String>(); allAliases.add(ViPR_KEY_AND_CERTIFICATE_ALIAS); Set<String> userAddCertsAliases = distributedKeyStore.getTrustedCertificates().keySet(); Set<String> viprSuppliedCertsAliases = distributedKeyStore.getCACertificates().keySet(); allAliases.addAll(userAddCertsAliases); allAliases.addAll(viprSuppliedCertsAliases); log.debug("engineAliases called. vipr ca certs # = {}, user added certs # = {}", viprSuppliedCertsAliases.size(), userAddCertsAliases.size()); return Collections.enumeration(allAliases); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineContainsAlias(java.lang.String) */ @Override public boolean engineContainsAlias(String alias) { log.debug("engineContainsAlias called ( alias = {} )", alias); if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { return true; } if (distributedKeyStore.containsUserAddedCerts(alias)) { return true; } if (distributedKeyStore.containsViprSuppliedCerts(suffixedAlias(alias))) { return true; } return false; } private String suffixedAlias(String alias) { if (alias.endsWith(SUFFIX_VIPR_SUPPLY_CERT)) { return alias; } else { return alias + SUFFIX_VIPR_SUPPLY_CERT; } } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineDeleteEntry(java.lang.String) */ @Override public synchronized void engineDeleteEntry(String alias) throws KeyStoreException { log.debug("engineDeleteEntry called ( alias = {} )", alias); if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { throw SecurityException.fatals.viprKeyCertificateEntryCannotBeDeleted(); } if (distributedKeyStore.containsUserAddedCerts(alias)) { distributedKeyStore.removeTrustedCertificate(alias); return; } if (distributedKeyStore.containsViprSuppliedCerts(suffixedAlias(alias))) { distributedKeyStore.removeCACertificate(suffixedAlias(alias)); return; } throw new KeyStoreException("The specified alias " + alias + " does not exist"); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineGetCertificate(java.lang.String) */ @Override public Certificate engineGetCertificate(String alias) { log.debug("engineGetCertificate called ( alias = {} )", alias); if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { return getKeyCertificatePair(false).getCertificateChain()[0]; } TrustedCertificateEntry entry = null; entry = distributedKeyStore.getUserAddedCert(alias); if (entry != null) { return entry.getCertificate(); } entry = distributedKeyStore.getViprAddedCert(suffixedAlias(alias)); if (entry != null) { return entry.getCertificate(); } return null; } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineGetCertificateAlias(java.security.cert.Certificate) */ @Override public String engineGetCertificateAlias(Certificate cert) { if (cert.equals(getKeyCertificatePair(false).getCertificateChain()[0])) { return ViPR_KEY_AND_CERTIFICATE_ALIAS; } Map<String, TrustedCertificateEntry> userCerts = distributedKeyStore.getTrustedCertificates(); for (Entry<String, TrustedCertificateEntry> entry : userCerts.entrySet()) { if (cert.equals(entry.getValue().getCertificate())) { return entry.getKey(); } } Map<String, TrustedCertificateEntry> viprCerts = distributedKeyStore.getCACertificates(); for (Entry<String, TrustedCertificateEntry> entry : viprCerts.entrySet()) { if (cert.equals(entry.getValue().getCertificate())) { return entry.getKey(); } } return null; } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineGetCertificateChain(java.lang.String) */ @Override public Certificate[] engineGetCertificateChain(String alias) { log.debug("engineGetCertificateChain called ( alias = {} )", alias); if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { return getKeyCertificatePair(false).getCertificateChain(); } return null; } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineGetCreationDate(java.lang.String) */ @Override public Date engineGetCreationDate(String alias) { log.debug("engineGetCreationDate called ( alias = {} )", alias); if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { return getKeyCertificatePair(false).getCreationDate(); } TrustedCertificateEntry entry = distributedKeyStore.getUserAddedCert(alias); if (entry != null) { return entry.getCreationDate(); } entry = distributedKeyStore.getViprAddedCert(alias); if (entry != null) { return entry.getCreationDate(); } return null; } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineGetKey(java.lang.String, char[]) */ @Override public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException { log.debug("engineGetKey called ( alias = {} )", alias); if (!alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { return null; } KeyCertificateEntry entry = getKeyCertificatePair(false); return KeyCertificatePairGenerator.loadPrivateKeyFromBytes(entry.getKey()); } /** * @return KeyCertificatePair */ private KeyCertificateEntry getKeyCertificatePair(boolean makeKeyNull) { log.debug("getKeyCertificatePair called "); KeyCertificateEntry pair = distributedKeyStore.getKeyCertificatePair(); if (makeKeyNull) { SecurityUtil.clearSensitiveData(pair.getKey()); } return pair; } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineIsCertificateEntry(java.lang.String) */ @Override public boolean engineIsCertificateEntry(String alias) { log.debug("engineIsCertificateEntry called ( alias = {} )", alias); if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { return false; } return engineContainsAlias(alias); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineIsKeyEntry(java.lang.String) */ @Override public boolean engineIsKeyEntry(String alias) { return alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineLoad(java.io.InputStream, char[]) */ @Override public void engineLoad(InputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { throw SecurityException.fatals .failedToInitializedKeystoreNeedDistKeystoreParams(); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineLoad(java.security.KeyStore.LoadStoreParameter) */ @Override public void engineLoad(KeyStore.LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException { log.debug("engineLoad called "); if (!(param instanceof KeystoreParam)) { throw SecurityException.fatals .failedToInitializedKeystoreNeedDistKeystoreParams(); } KeystoreParam scheduledReloadParam = (KeystoreParam) param; distributedKeyStore = new DistributedKeyStoreImpl(); distributedKeyStore.init(scheduledReloadParam); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineSetCertificateEntry(java.lang.String, java.security.cert.Certificate) */ @Override public void engineSetCertificateEntry(String alias, Certificate cert) throws KeyStoreException { if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { throw SecurityException.fatals.cannotSetTrustedCertificateWithViPRAlias(); } TrustedCertificateEntry entry = new TrustedCertificateEntry(cert, new Date()); distributedKeyStore.addTrustedCertificate(alias, entry); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineSetKeyEntry(java.lang.String, byte[], java.security.cert.Certificate[]) */ @Override public synchronized void engineSetKeyEntry(String alias, byte[] key, Certificate[] chain) throws KeyStoreException { if (alias.equalsIgnoreCase(ViPR_KEY_AND_CERTIFICATE_ALIAS)) { KeyCertificateEntry entryToSet = new KeyCertificateEntry(key, chain, new Date()); keyGen.verifyKeyCertificateEntry(entryToSet); distributedKeyStore.setKeyCertificatePair(entryToSet); } else { throw SecurityException.fatals.canOnlyUpdateViPRKeyCertificate(); } } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineSetKeyEntry(java.lang.String, java.security.Key, char[], java.security.cert.Certificate[]) */ @Override public synchronized void engineSetKeyEntry(String alias, Key key, char[] password, Certificate[] chain) throws KeyStoreException { engineSetKeyEntry(alias, key.getEncoded(), chain); } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineSize() */ @Override public int engineSize() { return distributedKeyStore.getCACertificates().size() + distributedKeyStore.getTrustedCertificates().size() + 1; } /* * (non-Javadoc) * * @see * java.security.KeyStoreSpi#engineStore(java.security.KeyStore.LoadStoreParameter) */ @Override public void engineStore(LoadStoreParameter param) throws IOException, NoSuchAlgorithmException, CertificateException { } /* * (non-Javadoc) * * @see java.security.KeyStoreSpi#engineStore(java.io.OutputStream, char[]) */ @Override public void engineStore(OutputStream stream, char[] password) throws IOException, NoSuchAlgorithmException, CertificateException { KeyStore ks = loadAndPopulateJKS(); try { logKeystore(ks, password); ks.store(stream, password); } catch (Exception e) { // should not get here since keystore is loaded in loadAndPopulateJKS() log.error(e.getMessage(), e); } } private void logKeystore(KeyStore ks, char[] password) throws Exception { Enumeration<String> aliases = ks.aliases(); while (aliases.hasMoreElements()) { String als = aliases.nextElement(); log.info("Populated a Keystore to be exported has an alias {} ", als); } } /** * @return * @throws IOException * @throws NoSuchAlgorithmException * @throws CertificateException */ private KeyStore loadAndPopulateJKS() throws IOException, NoSuchAlgorithmException, CertificateException { KeyStore ks; KeyCertificateEntry entry = null; Key privateKey = null; try { ks = KeyStore.getInstance("JKS", "SUN"); ks.load(null, null); entry = getKeyCertificatePair(false); privateKey = KeyCertificatePairGenerator.loadPrivateKeyFromBytes(entry.getKey()); ks.setKeyEntry(ViPR_KEY_AND_CERTIFICATE_ALIAS, privateKey, "changeit".toCharArray(), entry.getCertificateChain()); for (Entry<String, TrustedCertificateEntry> certEntry : distributedKeyStore.getTrustedCertificates() .entrySet()) { ks.setCertificateEntry(certEntry.getKey(), certEntry.getValue() .getCertificate()); } return ks; } catch (KeyStoreException | NoSuchProviderException e) { throw new CertificateException(e); } finally { if (entry != null) { SecurityUtil.clearSensitiveData(entry.getKey()); } if (privateKey != null) { SecurityUtil.clearSensitiveData(privateKey); } } } public static boolean isUserSuppliedCerts(String alias) { return !alias.endsWith(KeystoreEngine.SUFFIX_VIPR_SUPPLY_CERT); } }