You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by nc...@apache.org on 2015/10/02 23:12:19 UTC

[15/32] ambari git commit: AMBARI-13214. Create a credentials resource used to securely set, update, and remove credentials used by Ambari (rlevas)

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/credential/PrincipalKeyCredential.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/credential/PrincipalKeyCredential.java b/ambari-server/src/main/java/org/apache/ambari/server/security/credential/PrincipalKeyCredential.java
new file mode 100644
index 0000000..55bf076
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/credential/PrincipalKeyCredential.java
@@ -0,0 +1,154 @@
+/*
+ * 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
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.security.credential;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+import java.util.Arrays;
+
+/**
+ * PrincipalKeyCredential encapsulates a credential consisting of a principal (or username) and
+ * a (secret) key.
+ */
+public class PrincipalKeyCredential implements Credential {
+
+  private static final String VALUE_PREFIX = "PrincipalKeyCredential";
+  /**
+   * This principal value
+   */
+  private String principal;
+
+  /**
+   * The plaintext key value
+   */
+  private char[] key;
+
+  /**
+   * Creates an empty PrincipalKeyCredential
+   */
+  public PrincipalKeyCredential() {
+    this(null, (char[]) null);
+  }
+
+  /**
+   * Creates a new PrincipalKeyCredential
+   *
+   * @param principal a String containing the principal name for this credential
+   * @param key       a String containing the secret key for this credential
+   */
+  public PrincipalKeyCredential(String principal, String key) {
+    this(principal, (key == null) ? null : key.toCharArray());
+  }
+
+  /**
+   * Creates a new PrincipalKeyCredential
+   *
+   * @param principal a String containing the principal name for this credential
+   * @param key       a char array containing the secret key for this credential
+   */
+  public PrincipalKeyCredential(String principal, char[] key) {
+    this.principal = principal;
+    this.key = key;
+  }
+
+  /**
+   * @return a String containing the principal name for this credential
+   */
+  public String getPrincipal() {
+    return principal;
+  }
+
+  /**
+   * @param principal a String containing the principal name for this credential
+   */
+  public void setPrincipal(String principal) {
+    this.principal = principal;
+  }
+
+  /**
+   * @return a char array containing the secret key for this credential
+   */
+  public char[] getKey() {
+    return key;
+  }
+
+  /**
+   * @param key a char array containing the secret key for this credential
+   */
+  public void setKey(char[] key) {
+    this.key = key;
+  }
+
+
+  /**
+   * Returns a value representation of this PrincipalKeyCredential
+   *
+   * @return a String containing the value representation of this PrincipalKeyCredential
+   */
+  public char[] toValue() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("PrincipalKeyCredential");
+    builder.append(new Gson().toJson(this));
+    return builder.toString().toCharArray();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    } else if (this == obj) {
+      return true;
+    } else if (obj.getClass() == PrincipalKeyCredential.class) {
+      PrincipalKeyCredential other = (PrincipalKeyCredential) obj;
+      return ((this.principal == null) ? (other.principal == null) : this.principal.equals(other.principal)) &&
+          ((this.key == null) ? (other.key == null) : Arrays.equals(this.key, other.key));
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    return ((principal == null) ? 0 : principal.hashCode()) +
+        ((key == null) ? 0 : Arrays.hashCode(key));
+  }
+
+  /**
+   * Renders a new PrincipalKeyCredential from its value representation
+   *
+   * @param value a String containing the value representation of this PrincipalKeyCredential
+   * @return a new PrincipalKeyCredential or null if a new PrincipalKeyCredential cannot be created
+   */
+  public static PrincipalKeyCredential fromValue(String value) throws InvalidCredentialValueException {
+    if (isValidValue(value)) {
+      value = value.substring(VALUE_PREFIX.length());
+      try {
+        return (value.isEmpty()) ? null : new Gson().fromJson(value, PrincipalKeyCredential.class);
+      } catch (JsonSyntaxException e) {
+        throw new InvalidCredentialValueException("The value does not represent a PrincipalKeyCredential", e);
+      }
+    } else {
+      throw new InvalidCredentialValueException("The value does not represent a PrincipalKeyCredential");
+    }
+  }
+
+  public static boolean isValidValue(String value) {
+    return ((value != null) && value.startsWith(VALUE_PREFIX));
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AbstractCredentialStore.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AbstractCredentialStore.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AbstractCredentialStore.java
new file mode 100644
index 0000000..96faa4a
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AbstractCredentialStore.java
@@ -0,0 +1,415 @@
+/**
+ * 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
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * <p/>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p/>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ambari.server.security.encryption;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.security.credential.Credential;
+import org.apache.ambari.server.security.credential.CredentialFactory;
+
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * AbstractCredentialStore is an abstract implementation of CredentialStore that loads and
+ * stores @{link KeyStore} data. Implementations of this class, provide the input and output streams
+ * used to read and write the data.
+ */
+public abstract class AbstractCredentialStore implements CredentialStore {
+  protected static final String DEFAULT_STORE_TYPE = "JCEKS";
+
+  /**
+   * A lock object to lock access to the credential store, allowing only one thread to access
+   * the credential store at a time.
+   */
+  private final Lock lock = new ReentrantLock();
+
+  /**
+   * The MasterKeyService containing the key used to encrypt the KeyStore data
+   */
+  private MasterKeyService masterKeyService;
+
+  /**
+   * Adds a new credential to this CredentialStore
+   * <p/>
+   * The supplied key will be converted into UTF-8 bytes before being stored.
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias      a string declaring the alias (or name) of the credential
+   * @param credential the credential to store
+   * @throws AmbariException if an error occurs while storing the new credential
+   */
+  @Override
+  public void addCredential(String alias, Credential credential) throws AmbariException {
+    if ((alias == null) || alias.isEmpty()) {
+      throw new IllegalArgumentException("Alias cannot be null or empty.");
+    }
+
+    lock.lock();
+    try {
+      KeyStore ks = loadCredentialStore();
+      addCredential(ks, alias, credential);
+      persistCredentialStore(ks);
+    } finally {
+      lock.unlock();
+    }
+  }
+
+  /**
+   * Retrieves the specified credential from this CredentialStore
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return a Credential or null of not found
+   * @throws AmbariException if an error occurs while retrieving the new credential
+   */
+  @Override
+  public Credential getCredential(String alias) throws AmbariException {
+    if (alias == null) {
+      return null;
+    } else {
+      lock.lock();
+      try {
+        return getCredential(loadCredentialStore(), alias);
+      } finally {
+        lock.unlock();
+      }
+    }
+  }
+
+  /**
+   * Removes the specified credential from this CredentialStore
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @throws AmbariException if an error occurs while removing the new credential
+   */
+  @Override
+  public void removeCredential(String alias) throws AmbariException {
+    if ((alias != null) && !alias.isEmpty()) {
+      lock.lock();
+      try {
+        KeyStore ks = loadCredentialStore();
+        if (ks != null) {
+          try {
+            ks.deleteEntry(alias);
+            persistCredentialStore(ks);
+          } catch (KeyStoreException e) {
+            throw new AmbariException("Failed to delete the KeyStore entry - the key store may not have been initialized", e);
+          }
+        }
+      } finally {
+        lock.unlock();
+      }
+    }
+  }
+
+  /**
+   * Returns a list of the alias names for the credentials stored in the CredentialStore
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @return a Set of Strings representing alias names for the credentials stored in the CredentialStore
+   * @throws AmbariException if an error occurs while searching forthe credential
+   */
+  @Override
+  public Set<String> listCredentials() throws AmbariException {
+    Set<String> credentials = null;
+
+    lock.lock();
+    try {
+      KeyStore ks = loadCredentialStore();
+      if (ks != null) {
+        try {
+          Enumeration<String> aliases = ks.aliases();
+
+          if (aliases != null) {
+            credentials = new HashSet<String>();
+            while (aliases.hasMoreElements()) {
+              credentials.add(aliases.nextElement());
+            }
+          }
+        } catch (KeyStoreException e) {
+          throw new AmbariException("Failed to read KeyStore - the key store may not have been initialized", e);
+        }
+      }
+    } finally {
+      lock.unlock();
+    }
+
+    return credentials;
+  }
+
+  /**
+   * Tests this CredentialStore for the existence of a credential with the specified alias
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return true if the alias exists; otherwise false
+   * @throws AmbariException if an error occurs while searching forthe credential
+   */
+  @Override
+  public boolean containsCredential(String alias) throws AmbariException {
+    boolean exists = false;
+
+    if ((alias != null) && !alias.isEmpty()) {
+      lock.lock();
+      try {
+        KeyStore ks = loadCredentialStore();
+        if (ks != null) {
+          try {
+            exists = ks.containsAlias(alias);
+          } catch (KeyStoreException e) {
+            throw new AmbariException("Failed to search the KeyStore for the requested entry - the key store may not have been initialized", e);
+          }
+        }
+      } finally {
+        lock.unlock();
+      }
+    }
+
+    return exists;
+  }
+
+  @Override
+  public void setMasterKeyService(MasterKeyService masterKeyService) {
+    this.masterKeyService = masterKeyService;
+  }
+
+  /**
+   * Adds a new credential to the supplied KeyStore
+   * <p/>
+   * The supplied key will be converted into UTF-8 bytes before being stored.
+   *
+   * @param keyStore   the KeyStore
+   * @param alias      a string declaring the alias (or name) of the credential
+   * @param credential the credential to store
+   * @throws AmbariException if an error occurs while storing the new credential
+   */
+  protected void addCredential(KeyStore keyStore, String alias, Credential credential) throws AmbariException {
+    if (keyStore != null) {
+      try {
+        Key key;
+        char[] value = (credential == null) ? null : credential.toValue();
+
+        if ((value == null) || (value.length == 0)) {
+          key = null;
+        } else {
+          key = new SecretKeySpec(toBytes(value), "AES");
+        }
+
+        keyStore.setKeyEntry(alias, key, masterKeyService.getMasterSecret(), null);
+      } catch (KeyStoreException e) {
+        throw new AmbariException("The key store has not been initialized", e);
+      }
+    }
+  }
+
+  /**
+   * Retrieves the specified credential from a KeyStore
+   *
+   * @param keyStore the KeyStore
+   * @param alias    a string declaring the alias (or name) of the credential
+   * @return an array of chars containing the credential
+   * @throws AmbariException if an error occurs while retrieving the new credential
+   */
+  protected Credential getCredential(KeyStore keyStore, String alias) throws AmbariException {
+    char[] value = null;
+
+    if (keyStore != null) {
+      try {
+        Key key = keyStore.getKey(alias, masterKeyService.getMasterSecret());
+        if (key != null) {
+          value = toChars(key.getEncoded());
+        }
+      } catch (UnrecoverableKeyException e) {
+        throw new AmbariException("The key cannot be recovered (e.g., the given password is wrong)", e);
+      } catch (KeyStoreException e) {
+        throw new AmbariException("The key store has not been initialized", e);
+      } catch (NoSuchAlgorithmException e) {
+        throw new AmbariException(" if the algorithm for recovering the key cannot be found", e);
+      }
+    }
+
+    return CredentialFactory.createCredential(value);
+  }
+
+  /**
+   * Calls the implementation-specific facility to persist the KeyStore
+   *
+   * @param keyStore the KeyStore to persist
+   * @throws AmbariException if an error occurs while persisting the key store data
+   */
+  protected abstract void persistCredentialStore(KeyStore keyStore) throws AmbariException;
+
+  /**
+   * Calls the implementation-specific facility to load the KeyStore
+   *
+   * @throws AmbariException if an error occurs while loading the key store data
+   */
+  protected abstract KeyStore loadCredentialStore() throws AmbariException;
+
+
+  /**
+   * Gets the lock object used to protect access to the credential store
+   *
+   * @return a Lock object
+   */
+  protected Lock getLock() {
+    return lock;
+  }
+
+  /**
+   * Loads a KeyStore from an InputStream
+   * <p/>
+   * Implementations are expected to call this to load the relevant KeyStore data from the
+   * InputStream of some storage facility.
+   *
+   * @param inputStream  the InputStream to read the data from
+   * @param keyStoreType the type of key store data expected
+   * @return a new KeyStore instance with the loaded data
+   * @throws AmbariException if an error occurs while loading the key store data from the InputStream
+   */
+  protected KeyStore loadKeyStore(InputStream inputStream, String keyStoreType) throws AmbariException {
+    if (masterKeyService == null) {
+      throw new AmbariException("Master Key Service is not set for this Credential store.");
+    }
+
+    KeyStore keyStore;
+    try {
+      keyStore = KeyStore.getInstance(keyStoreType);
+    } catch (KeyStoreException e) {
+      throw new AmbariException(String.format("No provider supports a key store implementation for the specified type: %s", keyStoreType), e);
+    }
+
+    try {
+      keyStore.load(inputStream, masterKeyService.getMasterSecret());
+    } catch (CertificateException e) {
+      throw new AmbariException(String.format("One or more credentials from the key store could not be loaded: %s", e.getLocalizedMessage()), e);
+    } catch (NoSuchAlgorithmException e) {
+      throw new AmbariException(String.format("The algorithm used to check the integrity of the key store cannot be found: %s", e.getLocalizedMessage()), e);
+    } catch (IOException e) {
+      if (e.getCause() instanceof UnrecoverableKeyException) {
+        throw new AmbariException(String.format("The password used to decrypt the key store is incorrect: %s", e.getLocalizedMessage()), e);
+      } else {
+        throw new AmbariException(String.format("Failed to read the key store: %s", e.getLocalizedMessage()), e);
+      }
+    }
+
+    return keyStore;
+  }
+
+  /**
+   * Writes a KeyStore to an OutputStream
+   * <p/>
+   * Implementations are expected to call this to write the relevant KeyStore data to the
+   * OutputStream of some storage facility.
+   *
+   * @param keyStore     the KeyStore to write
+   * @param outputStream the OutputStream to write the data into
+   * @throws AmbariException if an error occurs while writing the key store data
+   */
+  protected void writeKeyStore(KeyStore keyStore, OutputStream outputStream) throws AmbariException {
+    if (masterKeyService == null) {
+      throw new AmbariException("Master Key Service is not set for this Credential store.");
+    }
+
+    try {
+      keyStore.store(outputStream, masterKeyService.getMasterSecret());
+    } catch (CertificateException e) {
+      throw new AmbariException(String.format("A credential within in the key store data could not be stored: %s", e.getLocalizedMessage()), e);
+    } catch (NoSuchAlgorithmException e) {
+      throw new AmbariException(String.format("The appropriate data integrity algorithm could not be found: %s", e.getLocalizedMessage()), e);
+    } catch (KeyStoreException e) {
+      throw new AmbariException(String.format("The key store has not been initialized: %s", e.getLocalizedMessage()), e);
+    } catch (IOException e) {
+      throw new AmbariException(String.format("Failed to write the key store: %s", e.getLocalizedMessage()), e);
+    }
+  }
+
+  /**
+   * Converts an array of characters to an array of bytes by encoding each character into UTF-8 bytes.
+   * <p/>
+   * An attempt is made to clear out sensitive data by filling any buffers with 0's
+   *
+   * @param chars the array of chars to convert
+   * @return an array of bytes, or null if the original array was null
+   */
+  protected byte[] toBytes(char[] chars) {
+    if (chars == null) {
+      return null;
+    } else {
+      CharBuffer charBuffer = CharBuffer.wrap(chars);
+      ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
+
+      byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
+
+      // Clear sensitive data
+      Arrays.fill(charBuffer.array(), '\u0000');
+      Arrays.fill(byteBuffer.array(), (byte) 0);
+
+      return bytes;
+    }
+  }
+
+  /**
+   * Converts an array of bytes to an array of character by decoding the bytes using the UTF-8
+   * character set.
+   * <p/>
+   * An attempt is made to clear out sensitive data by filling any buffers with 0's
+   *
+   * @param bytes the array of bytes to convert
+   * @return an array of chars, or null if the original array was null
+   */
+  protected char[] toChars(byte[] bytes) {
+    if (bytes == null) {
+      return null;
+    } else {
+      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+      CharBuffer charBuffer = Charset.forName("UTF-8").decode(byteBuffer);
+
+      char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit());
+
+      // Clear sensitive data
+      Arrays.fill(charBuffer.array(), '\u0000');
+      Arrays.fill(byteBuffer.array(), (byte) 0);
+
+      return chars;
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java
index b812337..0c420e0 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialProvider.java
@@ -19,6 +19,8 @@ package org.apache.ambari.server.security.encryption;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.credential.Credential;
+import org.apache.ambari.server.security.credential.GenericKeyCredential;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,20 +41,20 @@ public class CredentialProvider {
       'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
       '2', '3', '4', '5', '6', '7', '8', '9'};
 
-  private CredentialStoreService keystoreService;
+  private CredentialStore keystoreService;
   static final Logger LOG = LoggerFactory.getLogger(CredentialProvider.class);
 
-  public CredentialProvider(String masterKey, String masterKeyLocation,
-                            boolean isMasterKeyPersisted) throws AmbariException {
+  public CredentialProvider(String masterKey, File masterKeyLocation,
+                            boolean isMasterKeyPersisted, File masterKeyStoreLocation) throws AmbariException {
     MasterKeyService masterKeyService;
     if (masterKey != null) {
       masterKeyService = new MasterKeyServiceImpl(masterKey);
     } else {
       if (isMasterKeyPersisted) {
-        if ((masterKeyLocation == null) || masterKeyLocation.isEmpty()) {
-          throw new IllegalArgumentException("The master key file location may not be null or empty if the master key is persisted");
+        if (masterKeyLocation == null) {
+          throw new IllegalArgumentException("The master key file location must be specified if the master key is persisted");
         }
-        masterKeyService = new MasterKeyServiceImpl(new File(masterKeyLocation));
+        masterKeyService = new MasterKeyServiceImpl(masterKeyLocation);
       } else {
         masterKeyService = new MasterKeyServiceImpl();
       }
@@ -60,17 +62,18 @@ public class CredentialProvider {
     if (!masterKeyService.isMasterKeyInitialized()) {
       throw new AmbariException("Master key initialization failed.");
     }
-    String storeDir = masterKeyLocation.substring(0,
-        masterKeyLocation.indexOf(Configuration.MASTER_KEY_FILENAME_DEFAULT));
-    this.keystoreService = new FileBasedCredentialStoreService(storeDir);
+    this.keystoreService = new FileBasedCredentialStore(masterKeyStoreLocation);
     this.keystoreService.setMasterKeyService(masterKeyService);
   }
 
   public char[] getPasswordForAlias(String alias) throws AmbariException {
-    if (isAliasString(alias)) {
-      return keystoreService.getCredential(getAliasFromString(alias));
-    }
-    return keystoreService.getCredential(alias);
+    Credential credential = (isAliasString(alias))
+        ? keystoreService.getCredential(getAliasFromString(alias))
+        : keystoreService.getCredential(alias);
+
+    return (credential instanceof GenericKeyCredential)
+        ? ((GenericKeyCredential) credential).getKey()
+        : null;
   }
 
   public void generateAliasWithPassword(String alias) throws AmbariException {
@@ -86,7 +89,7 @@ public class CredentialProvider {
     if (passwordString == null || passwordString.isEmpty()) {
       throw new IllegalArgumentException("Empty or null password not allowed.");
     }
-    keystoreService.addCredential(alias, passwordString.toCharArray());
+    keystoreService.addCredential(alias, new GenericKeyCredential(passwordString.toCharArray()));
   }
 
   private String generatePassword(int length) {
@@ -110,7 +113,7 @@ public class CredentialProvider {
     return strPasswd.substring(strPasswd.indexOf("=") + 1, strPasswd.length() - 1);
   }
 
-  protected CredentialStoreService getKeystoreService() {
+  protected CredentialStore getKeystoreService() {
     return keystoreService;
   }
 
@@ -144,7 +147,8 @@ public class CredentialProvider {
       try {
         credentialProvider = new CredentialProvider(masterKey,
             configuration.getMasterKeyLocation(),
-            configuration.isMasterKeyPersisted());
+            configuration.isMasterKeyPersisted(),
+            configuration.getMasterKeyStoreLocation());
       } catch (Exception ex) {
         ex.printStackTrace();
         System.exit(1);

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStore.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStore.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStore.java
new file mode 100644
index 0000000..efe5e23
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStore.java
@@ -0,0 +1,78 @@
+/*
+ * 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
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.security.encryption;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.security.credential.Credential;
+
+import java.util.Set;
+
+public interface CredentialStore {
+  /**
+   * Adds a new credential to this CredentialStore
+   * <p/>
+   * The supplied key will be converted into UTF-8 bytes before being stored.
+   *
+   * @param alias      a string declaring the alias (or name) of the credential
+   * @param credential the credential to store
+   * @throws AmbariException if an error occurs while storing the new credential
+   */
+  void addCredential(String alias, Credential credential) throws AmbariException;
+
+  /**
+   * Retrieves the specified credential from this CredentialStore
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return a Credential or null of not found
+   * @throws AmbariException if an error occurs while retrieving the new credential
+   */
+  Credential getCredential(String alias) throws AmbariException;
+
+  /**
+   * Removes the specified credential from this CredentialStore
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @throws AmbariException if an error occurs while removing the new credential
+   */
+  void removeCredential(String alias) throws AmbariException;
+
+  /**
+   * Returns a list of the alias names for the credentials stored in the CredentialStore
+   *
+   * @return a Set of Strings representing alias names for the credentials stored in the CredentialStore
+   * @throws AmbariException if an error occurs while searching forthe credential
+   */
+  Set<String> listCredentials() throws AmbariException;
+
+  /**
+   * Tests this CredentialStore for the existence of a credential with the specified alias
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return true if the alias exists; otherwise false
+   * @throws AmbariException if an error occurs while searching forthe credential
+   */
+  boolean containsCredential(String alias) throws AmbariException;
+
+  /**
+   * Sets the MasterKeyService for this CredentialStore
+   *
+   * @param masterKeyService the MasterKeyService
+   */
+  void setMasterKeyService(MasterKeyService masterKeyService);
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java
index 4aa3b0a..c21154e 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreService.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -19,40 +19,117 @@
 package org.apache.ambari.server.security.encryption;
 
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.security.credential.Credential;
+
+import java.util.Map;
 
 public interface CredentialStoreService {
   /**
-   * Adds a new credential to this CredentialStoreService
+   * Adds a new credential to ether the persistent or the temporary CredentialStore
    * <p/>
    * The supplied key will be converted into UTF-8 bytes before being stored.
    *
-   * @param alias a string declaring the alias (or name) of the credential
-   * @param key   an array of chars containing the credential
+   * @param clusterName         the name of the cluster the credential is related to
+   * @param alias               a string declaring the alias (or name) of the credential
+   * @param credential          the credential value to store
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
    * @throws AmbariException if an error occurs while storing the new credential
    */
-  void addCredential(String alias, char[] key) throws AmbariException;
+  void setCredential(String clusterName, String alias, Credential credential, CredentialStoreType credentialStoreType) throws AmbariException;
+
+  /**
+   * Retrieves the specified credential looking in the temporary and then the persistent CredentialStore
+   *
+   * @param clusterName the name of the cluster the credential is related to
+   * @param alias       a string declaring the alias (or name) of the credential
+   * @return the requested Credential
+   * @throws AmbariException if an error occurs while retrieving the credential
+   */
+  Credential getCredential(String clusterName, String alias) throws AmbariException;
+
+  /**
+   * Retrieves the specified credential looking in ether the persistent or the temporary CredentialStore
+   *
+   * @param clusterName         the name of the cluster this credential is related to
+   * @param alias               a string declaring the alias (or name) of the credential
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @return the requested Credential
+   * @throws AmbariException if an error occurs while retrieving the credential
+   */
+  Credential getCredential(String clusterName, String alias, CredentialStoreType credentialStoreType) throws AmbariException;
+
+  /**
+   * Removes the specified credential from all CredentialStores
+   *
+   * @param clusterName the name of the cluster this credential is related to
+   * @param alias       a string declaring the alias (or name) of the credential
+   * @throws AmbariException if an error occurs while removing the credential
+   */
+  void removeCredential(String clusterName, String alias) throws AmbariException;
+
+  /**
+   * Removes the specified credential from ether the persistent or the temporary CredentialStore
+   *
+   * @param clusterName         the name of the cluster this credential is related to
+   * @param alias               a string declaring the alias (or name) of the credential
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @throws AmbariException if an error occurs while removing the credential
+   */
+  void removeCredential(String clusterName, String alias, CredentialStoreType credentialStoreType) throws AmbariException;
+
+  /**
+   * Tests to see if the requested alias exists in any CredentialStore
+   *
+   * @param clusterName the name of the cluster this credential is related to
+   * @param alias       a string declaring the alias (or name) of the credential
+   * @return true if it exists; otherwise false
+   * @throws AmbariException if an error occurs while searching for the credential
+   */
+  boolean containsCredential(String clusterName, String alias) throws AmbariException;
+
+  /**
+   * Tests to see if the requested alias exists in ether the persistent or the temporary CredentialStore
+   *
+   * @param clusterName         the name of the cluster this credential is related to
+   * @param alias               a string declaring the alias (or name) of the credential
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @return true if it exists; otherwise false
+   * @throws AmbariException if an error occurs while searching for the credential
+   */
+  boolean containsCredential(String clusterName, String alias, CredentialStoreType credentialStoreType) throws AmbariException;
+
+  /**
+   * Gets the type of the credential store used to store the requested credential
+   *
+   * @param clusterName the name of the cluster this credential is related to
+   * @param alias       a string declaring the alias (or name) of the credential
+   * @return a CredentialStoreType
+   * @throws AmbariException if an error occurs while searching for the credential
+   */
+  CredentialStoreType getCredentialStoreType(String clusterName, String alias) throws AmbariException;
 
   /**
-   * Retrieves the specified credential from this CredentialStoreService
+   * Maps the existing alias names to their relevant credential store types.
    *
-   * @param alias a string declaring the alias (or name) of the credential
-   * @return an array of chars containing the credential
-   * @throws AmbariException if an error occurs while retrieving the new credential
+   * @param clusterName the name of the cluster this credential is related to
+   * @return a map of alias names to CredentialStoreTypes
+   * @throws AmbariException if an error occurs while searching for the credentials
    */
-  char[] getCredential(String alias) throws AmbariException;
+  Map<String, CredentialStoreType> listCredentials(String clusterName) throws AmbariException;
 
   /**
-   * Removes the specified credential from this CredentialStoreService
+   * Tests this CredentialStoreService to check if it has been properly initialized
    *
-   * @param alias a string declaring the alias (or name) of the credential
-   * @throws AmbariException if an error occurs while removing the new credential
+   * @return true if initialized; otherwise false
    */
-  void removeCredential(String alias) throws AmbariException;
+  boolean isInitialized();
 
   /**
-   * Sets the MasterKeyService for this CredentialStoreService
+   * Tests this CredentialStoreService to check if ether the persistent or the temporary CredentialStore
+   * has been properly initialized
    *
-   * @param masterKeyService the MasterKeyService
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @return true if initialized; otherwise false
    */
-  void setMasterKeyService(MasterKeyService masterKeyService);
+  boolean isInitialized(CredentialStoreType credentialStoreType);
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java
index 968e96a..fe14004 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreServiceImpl.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -15,266 +15,328 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.ambari.server.security.encryption;
 
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
 import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.ambari.server.security.SecurePasswordHelper;
+import org.apache.ambari.server.security.credential.Credential;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import javax.crypto.spec.SecretKeySpec;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.security.Key;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.UnrecoverableKeyException;
-import java.security.cert.CertificateException;
-import java.util.Arrays;
-
-/**
- * CredentialStoreServiceImpl is an abstract implementation of CredentialStoreService that loads and
- * stores @{link KeyStore} data. Implementations of this class, provide the input and output streams
- * used to read and write the data.
- */
-public abstract class CredentialStoreServiceImpl implements CredentialStoreService {
-  protected static final String DEFAULT_STORE_TYPE = "JCEKS";
+import java.io.File;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
-  /**
-   * The MasterKeyService containing the key used to encrypt the KeyStore data
-   */
-  private MasterKeyService masterKeyService;
+@Singleton
+public class CredentialStoreServiceImpl implements CredentialStoreService {
 
-  @Override
-  public void addCredential(String alias, char[] value) throws AmbariException {
-    if ((alias == null) || alias.isEmpty()) {
-      throw new IllegalArgumentException("Alias cannot be null or empty.");
-    }
+  private static final Logger LOG = LoggerFactory.getLogger(CredentialStoreServiceImpl.class);
 
-    KeyStore ks = loadCredentialStore();
-    addCredential(ks, alias, value);
-    persistCredentialStore(ks);
-  }
+  private SecurePasswordHelper securePasswordHelper;
 
-  @Override
-  public char[] getCredential(String alias) throws AmbariException {
-    if (alias == null) {
-      return null;
-    } else {
-      return getCredential(loadCredentialStore(), alias);
-    }
-  }
+  private FileBasedCredentialStore persistedCredentialStore = null;
+  private InMemoryCredentialStore temporaryCredentialStore = null;
 
-  @Override
-  public void removeCredential(String alias) throws AmbariException {
-    if ((alias != null) && !alias.isEmpty()) {
-      KeyStore ks = loadCredentialStore();
-      if (ks != null) {
+
+  @Inject
+  public CredentialStoreServiceImpl(Configuration configuration, SecurePasswordHelper securePasswordHelper) {
+
+    this.securePasswordHelper = securePasswordHelper;
+
+    if (configuration != null) {
+      File masterKeyLocation = configuration.getMasterKeyLocation();
+
+      try {
+        initializeTemporaryCredentialStore(configuration.getTemporaryKeyStoreRetentionMinutes(),
+            TimeUnit.MINUTES,
+            configuration.isActivelyPurgeTemporaryKeyStore());
+        LOG.info("Initialized the temporary credential store. KeyStore entries will be retained for {} minutes and {} be actively purged",
+            configuration.getTemporaryKeyStoreRetentionMinutes(), (configuration.isActivelyPurgeTemporaryKeyStore()) ? "will" : "will not");
+      } catch (AmbariException e) {
+        LOG.error("Failed to initialize the temporary credential store.  Storage of temporary credentials will fail.", e);
+      }
+
+
+      // If the MasterKeyService is initialized, assume that we should be initializing the persistent
+      // CredentialStore; else do not initialize it.
+      MasterKeyService masterKeyService = new MasterKeyServiceImpl(masterKeyLocation);
+      if (masterKeyService.isMasterKeyInitialized()) {
         try {
-          ks.deleteEntry(alias);
-          persistCredentialStore(ks);
-        } catch (KeyStoreException e) {
-          throw new AmbariException("Failed to delete the KeyStore entry - the key store may not have been initialized", e);
+          initializePersistedCredentialStore(configuration.getMasterKeyStoreLocation(), masterKeyService);
+          LOG.info("Initialized the persistent credential store. Using KeyStore file at {}", persistedCredentialStore.getKeyStorePath().getAbsolutePath());
+        } catch (AmbariException e) {
+          LOG.error("Failed to initialize the persistent credential store.  Storage of persisted credentials will fail.", e);
         }
       }
     }
   }
 
-  @Override
-  public void setMasterKeyService(MasterKeyService masterKeyService) {
-    this.masterKeyService = masterKeyService;
+  public synchronized void initializeTemporaryCredentialStore(long retentionDuration, TimeUnit units, boolean activelyPurge) throws AmbariException {
+    if (isInitialized(CredentialStoreType.TEMPORARY)) {
+      throw new AmbariException("This temporary CredentialStore has already been initialized");
+    }
+
+    temporaryCredentialStore = new InMemoryCredentialStore(retentionDuration, units, activelyPurge);
+    temporaryCredentialStore.setMasterKeyService(new MasterKeyServiceImpl(securePasswordHelper.createSecurePassword()));
+  }
+
+  public synchronized void initializePersistedCredentialStore(File credentialStoreLocation, MasterKeyService masterKeyService) throws AmbariException {
+    if (isInitialized(CredentialStoreType.PERSISTED)) {
+      throw new AmbariException("This persisted CredentialStore has already been initialized");
+    }
+
+    persistedCredentialStore = new FileBasedCredentialStore(credentialStoreLocation);
+    persistedCredentialStore.setMasterKeyService(masterKeyService);
   }
 
   /**
-   * Adds a new credential to the supplied KeyStore
+   * Adds a new credential to either the persistent or the temporary CredentialStore
    * <p/>
    * The supplied key will be converted into UTF-8 bytes before being stored.
+   * <p/>
+   * The alias name will be canonicalized as follows:
+   * <ul>
+   * <li>if a cluster name is supplied, then "cluster.name." will be prepended to it</li>
+   * <li>the characters will converted to all lowercase</li>
+   * </ul>
+   * Only a single instance of the named credential will be stored, therefore if a credential with
+   * alias ALIAS1 is stored in the persisted CredentialStore and a call is made to store a credentials
+   * with alias ALIAS1 into the temporary CredentialStore, the instance in the persisted CredentialStore
+   * will be removed.
    *
-   * @param keyStore the KeyStore
-   * @param alias    a string declaring the alias (or name) of the credential
-   * @param value    an array of chars containing the credential
-   * @throws AmbariException if an error occurs while storing the new credential
+   * @param clusterName         the name of the cluster this credential is related to
+   * @param alias               a string declaring the alias (or name) of the credential
+   * @param credential          the credential value to store
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @throws AmbariException
    */
-  protected void addCredential(KeyStore keyStore, String alias, char[] value) throws AmbariException {
-    if (keyStore != null) {
-      try {
-        Key key;
+  @Override
+  public void setCredential(String clusterName, String alias, Credential credential, CredentialStoreType credentialStoreType) throws AmbariException {
+    validateInitialized(credentialStoreType);
 
-        if ((value == null) || (value.length == 0)) {
-          key = null;
-        } else {
-          key = new SecretKeySpec(toBytes(value), "AES");
-        }
+    // Ensure only one copy of this alias exists.. either in the persisted or the temporary CertificateStore
+    removeCredential(clusterName, alias);
+    getCredentialStore(credentialStoreType).addCredential(canonicalizeAlias(clusterName, alias), credential);
+  }
 
-        keyStore.setKeyEntry(alias, key, masterKeyService.getMasterSecret(), null);
-      } catch (KeyStoreException e) {
-        throw new AmbariException("The key store has not been initialized", e);
-      }
+  @Override
+  public Credential getCredential(String clusterName, String alias) throws AmbariException {
+    // First check the temporary CredentialStore
+    Credential credential = getCredential(clusterName, alias, CredentialStoreType.TEMPORARY);
+
+    if (credential == null) {
+      // If needed, check the persisted CredentialStore
+      credential = getCredential(clusterName, alias, CredentialStoreType.PERSISTED);
     }
+
+    return credential;
   }
 
-  /**
-   * Retrieves the specified credential from a KeyStore
-   *
-   * @param keyStore the KeyStore
-   * @param alias    a string declaring the alias (or name) of the credential
-   * @return an array of chars containing the credential
-   * @throws AmbariException if an error occurs while retrieving the new credential
-   */
-  protected char[] getCredential(KeyStore keyStore, String alias) throws AmbariException {
-    char[] credential = null;
+  @Override
+  public Credential getCredential(String clusterName, String alias, CredentialStoreType credentialStoreType) throws AmbariException {
+    return (isInitialized(credentialStoreType))
+        ? getCredentialStore(credentialStoreType).getCredential(canonicalizeAlias(clusterName, alias))
+        : null;
+  }
 
-    if (keyStore != null) {
-      try {
-        Key key = keyStore.getKey(alias, masterKeyService.getMasterSecret());
-        if (key != null) {
-          credential = toChars(key.getEncoded());
+  @Override
+  public void removeCredential(String clusterName, String alias) throws AmbariException {
+    removeCredential(clusterName, alias, CredentialStoreType.PERSISTED);
+    removeCredential(clusterName, alias, CredentialStoreType.TEMPORARY);
+  }
+
+  @Override
+  public void removeCredential(String clusterName, String alias, CredentialStoreType credentialStoreType) throws AmbariException {
+    if (isInitialized(credentialStoreType)) {
+      getCredentialStore(credentialStoreType).removeCredential(canonicalizeAlias(clusterName, alias));
+    }
+  }
+
+  @Override
+  public boolean containsCredential(String clusterName, String alias) throws AmbariException {
+    return containsCredential(clusterName, alias, CredentialStoreType.TEMPORARY) ||
+        containsCredential(clusterName, alias, CredentialStoreType.PERSISTED);
+  }
+
+  @Override
+  public boolean containsCredential(String clusterName, String alias, CredentialStoreType credentialStoreType) throws AmbariException {
+    return isInitialized(credentialStoreType) &&
+        getCredentialStore(credentialStoreType).containsCredential(canonicalizeAlias(clusterName, alias));
+  }
+
+  @Override
+  public CredentialStoreType getCredentialStoreType(String clusterName, String alias) throws AmbariException {
+    if (containsCredential(clusterName, alias, CredentialStoreType.TEMPORARY)) {
+      return CredentialStoreType.TEMPORARY;
+    } else if (containsCredential(clusterName, alias, CredentialStoreType.PERSISTED)) {
+      return CredentialStoreType.PERSISTED;
+    } else {
+      throw new AmbariException("The alias was not found in either the persisted or temporary credential stores");
+    }
+  }
+
+  @Override
+  public Map<String, CredentialStoreType> listCredentials(String clusterName) throws AmbariException {
+    if (!isInitialized()) {
+      throw new AmbariException("This CredentialStoreService has not yet been initialized");
+    }
+
+    Collection<String> persistedAliases = isInitialized(CredentialStoreType.PERSISTED)
+        ? persistedCredentialStore.listCredentials()
+        : null;
+
+    Collection<String> temporaryAliases = isInitialized(CredentialStoreType.TEMPORARY)
+        ? temporaryCredentialStore.listCredentials()
+        : null;
+
+    Map<String, CredentialStoreType> map = new HashMap<String, CredentialStoreType>();
+
+    if (persistedAliases != null) {
+      for (String alias : persistedAliases) {
+        if (isAliasRequested(clusterName, alias)) {
+          map.put(decanonicalizeAlias(clusterName, alias), CredentialStoreType.PERSISTED);
         }
-      } catch (UnrecoverableKeyException e) {
-        throw new AmbariException("The key cannot be recovered (e.g., the given password is wrong)", e);
-      } catch (KeyStoreException e) {
-        throw new AmbariException("The key store has not been initialized", e);
-      } catch (NoSuchAlgorithmException e) {
-        throw new AmbariException(" if the algorithm for recovering the key cannot be found", e);
       }
     }
 
-    return credential;
+    if (temporaryAliases != null) {
+      for (String alias : temporaryAliases) {
+        if (isAliasRequested(clusterName, alias)) {
+          map.put(decanonicalizeAlias(clusterName, alias), CredentialStoreType.TEMPORARY);
+        }
+      }
+    }
+
+    return map;
   }
 
-  /**
-   * Calls the implementation-specific facility to persist the KeyStore
-   *
-   * @param keyStore the KeyStore to persist
-   * @throws AmbariException if an error occurs while persisting the key store data
-   */
-  protected abstract void persistCredentialStore(KeyStore keyStore) throws AmbariException;
+  @Override
+  public synchronized boolean isInitialized() {
+    return isInitialized(CredentialStoreType.PERSISTED) || isInitialized(CredentialStoreType.TEMPORARY);
+  }
 
-  /**
-   * Calls the implementation-specific facility to load the KeyStore
-   *
-   * @throws AmbariException if an error occurs while loading the key store data
-   */
-  protected abstract KeyStore loadCredentialStore() throws AmbariException;
+  @Override
+  public synchronized boolean isInitialized(CredentialStoreType credentialStoreType) {
+    if (CredentialStoreType.PERSISTED == credentialStoreType) {
+      return persistedCredentialStore != null;
+    } else if (CredentialStoreType.TEMPORARY == credentialStoreType) {
+      return temporaryCredentialStore != null;
+    } else {
+      throw new IllegalArgumentException("Invalid or unexpected credential store type specified");
+    }
+  }
 
   /**
-   * Loads a KeyStore from an InputStream
+   * Canonicalizes an alias name by making sure that is contains the prefix indicating what cluster it belongs to.
+   * This helps to reduce collisions of alias between clusters pointing to the same keystore files.
    * <p/>
-   * Implementations are expected to call this to load the relevant KeyStore data from the
-   * InputStream of some storage facility.
+   * Each alias is expected to have a prefix of <code>cluster.:clusterName.</code>, and the
+   * combination is to be converted to have all lowercase characters.  For example if the alias was
+   * "external.DB" and the cluster name is "c1", then the canonicalized alias name would be
+   * "cluster.c1.external.db".
    *
-   * @param inputStream  the InputStream to read the data from
-   * @param keyStoreType the type of key store data expected
-   * @return a new KeyStore instance with the loaded data
-   * @throws AmbariException if an error occurs while loading the key store data from the InputStream
+   * @param clusterName the name of the cluster
+   * @param alias       a string declaring the alias (or name) of the credential
+   * @return a ccanonicalized alias name
    */
-  protected KeyStore loadKeyStore(InputStream inputStream, String keyStoreType) throws AmbariException {
-    if (masterKeyService == null) {
-      throw new AmbariException("Master Key Service is not set for this Credential store.");
-    }
+  public static String canonicalizeAlias(String clusterName, String alias) {
+    String canonicaizedAlias;
 
-    KeyStore keyStore;
-    try {
-      keyStore = KeyStore.getInstance(keyStoreType);
-    } catch (KeyStoreException e) {
-      throw new AmbariException(String.format("No provider supports a key store implementation for the specified type: %s", keyStoreType), e);
-    }
+    if ((clusterName == null) || clusterName.isEmpty() || (alias == null) || alias.isEmpty()) {
+      canonicaizedAlias = alias;
+    } else {
+      String prefix = createAliasPrefix(clusterName);
 
-    try {
-      keyStore.load(inputStream, masterKeyService.getMasterSecret());
-    } catch (CertificateException e) {
-      throw new AmbariException(String.format("One or more credentials from the key store could not be loaded: %s", e.getLocalizedMessage()), e);
-    } catch (NoSuchAlgorithmException e) {
-      throw new AmbariException(String.format("The algorithm used to check the integrity of the key store cannot be found: %s", e.getLocalizedMessage()), e);
-    } catch (IOException e) {
-      if (e.getCause() instanceof UnrecoverableKeyException) {
-        throw new AmbariException(String.format("The password used to decrypt the key store is incorrect: %s", e.getLocalizedMessage()), e);
+      if (alias.toLowerCase().startsWith(prefix)) {
+        canonicaizedAlias = alias;
       } else {
-        throw new AmbariException(String.format("Failed to read the key store: %s", e.getLocalizedMessage()), e);
+        canonicaizedAlias = prefix + alias;
       }
     }
 
-    return keyStore;
+    return (canonicaizedAlias == null)
+        ? null
+        : canonicaizedAlias.toLowerCase();
   }
 
   /**
-   * Writes a KeyStore to an OutputStream
-   * <p/>
-   * Implementations are expected to call this to write the relevant KeyStore data to the
-   * OutputStream of some storage facility.
+   * Removes the prefix (if exists) from the front of a canonicalized alias
    *
-   * @param keyStore     the KeyStore to write
-   * @param outputStream the OutputStream to write the data into
-   * @throws AmbariException if an error occurs while writing the key store data
+   * @param clusterName       the name the name of the cluster
+   * @param canonicaizedAlias the canonicalized alias to process
+   * @return an alias name
    */
-  protected void writeKeyStore(KeyStore keyStore, OutputStream outputStream) throws AmbariException {
-    if (masterKeyService == null) {
-      throw new AmbariException("Master Key Service is not set for this Credential store.");
-    }
+  public static String decanonicalizeAlias(String clusterName, String canonicaizedAlias) {
+    if ((clusterName == null) || clusterName.isEmpty() || (canonicaizedAlias == null) || canonicaizedAlias.isEmpty()) {
+      return canonicaizedAlias;
+    } else {
+      String prefix = createAliasPrefix(clusterName);
 
-    try {
-      keyStore.store(outputStream, masterKeyService.getMasterSecret());
-    } catch (CertificateException e) {
-      throw new AmbariException(String.format("A credential within in the key store data could not be stored: %s", e.getLocalizedMessage()), e);
-    } catch (NoSuchAlgorithmException e) {
-      throw new AmbariException(String.format("The appropriate data integrity algorithm could not be found: %s", e.getLocalizedMessage()), e);
-    } catch (KeyStoreException e) {
-      throw new AmbariException(String.format("The key store has not been initialized: %s", e.getLocalizedMessage()), e);
-    } catch (IOException e) {
-      throw new AmbariException(String.format("Failed to write the key store: %s", e.getLocalizedMessage()), e);
+      if (canonicaizedAlias.startsWith(prefix)) {
+        return canonicaizedAlias.substring(prefix.length());
+      } else {
+        return canonicaizedAlias;
+      }
     }
   }
 
   /**
-   * Converts an array of characters to an array of bytes by encoding each character into UTF-8 bytes.
-   * <p/>
-   * An attempt is made to clear out sensitive data by filling any buffers with 0's
+   * Creates the prefix that is to be set in a canonicalized alias name.
    *
-   * @param chars the array of chars to convert
-   * @return an array of bytes, or null if the original array was null
+   * @param clusterName the name of the cluster
+   * @return the prefix value
    */
-  protected byte[] toBytes(char[] chars) {
-    if (chars == null) {
-      return null;
-    } else {
-      CharBuffer charBuffer = CharBuffer.wrap(chars);
-      ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
-
-      byte[] bytes = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
-
-      // Clear sensitive data
-      Arrays.fill(charBuffer.array(), '\u0000');
-      Arrays.fill(byteBuffer.array(), (byte) 0);
-
-      return bytes;
-    }
+  private static String createAliasPrefix(String clusterName) {
+    return ("cluster." + clusterName + ".").toLowerCase();
   }
 
   /**
-   * Converts an array of bytes to an array of character by decoding the bytes using the UTF-8
-   * character set.
+   * Tests the canonicalized alias name to see if it should be returned within the set of credentials.
    * <p/>
-   * An attempt is made to clear out sensitive data by filling any buffers with 0's
+   * This filters out all credentials not tagged for a specific cluster.
    *
-   * @param bytes the array of bytes to convert
-   * @return an array of chars, or null if the original array was null
+   * @param clusterName        the name of the cluster
+   * @param canonicalizedAlias the canonicalized alias
+   * @return true if the alias is tagged for the requested cluster; otherwise false
    */
-  protected char[] toChars(byte[] bytes) {
-    if (bytes == null) {
-      return null;
-    } else {
-      ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
-      CharBuffer charBuffer = Charset.forName("UTF-8").decode(byteBuffer);
+  private boolean isAliasRequested(String clusterName, String canonicalizedAlias) {
+    return (clusterName == null) || canonicalizedAlias.toLowerCase().startsWith(createAliasPrefix(clusterName));
+  }
 
-      char[] chars = Arrays.copyOfRange(charBuffer.array(), charBuffer.position(), charBuffer.limit());
 
-      // Clear sensitive data
-      Arrays.fill(charBuffer.array(), '\u0000');
-      Arrays.fill(byteBuffer.array(), (byte) 0);
+  /**
+   * Gets either the persisted or temporary CredentialStore as requested
+   *
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @return a CredentialStore implementation
+   */
+  private CredentialStore getCredentialStore(CredentialStoreType credentialStoreType) {
+    if (CredentialStoreType.PERSISTED == credentialStoreType) {
+      return persistedCredentialStore;
+    } else if (CredentialStoreType.TEMPORARY == credentialStoreType) {
+      return temporaryCredentialStore;
+    } else {
+      throw new IllegalArgumentException("Invalid or unexpected credential store type specified");
+    }
+  }
 
-      return chars;
+  /**
+   * Validate the relevant storage facility is initialized.
+   *
+   * @param credentialStoreType a CredentialStoreType indicating which credential store facility to use
+   * @throws AmbariException if the requested store has not been initialized
+   */
+  private void validateInitialized(CredentialStoreType credentialStoreType) throws AmbariException {
+    if (!isInitialized(credentialStoreType)) {
+      throw new AmbariException(String.format("The %s CredentialStore for this CredentialStoreService has not yet been initialized",
+          credentialStoreType.name().toLowerCase())
+      );
     }
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreType.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreType.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreType.java
new file mode 100644
index 0000000..a15fc84
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/CredentialStoreType.java
@@ -0,0 +1,27 @@
+/*
+ * 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
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.security.encryption;
+
+/**
+ * Defines available credential store types
+ */
+public enum CredentialStoreType {
+  PERSISTED,
+  TEMPORARY
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStore.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStore.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStore.java
new file mode 100644
index 0000000..0a6746e
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStore.java
@@ -0,0 +1,162 @@
+/*
+ * 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
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.security.encryption;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.configuration.Configuration;
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.security.KeyStore;
+
+/**
+ * FileBasedCredentialStore is a CredentialStore implementation that creates and manages
+ * a JCEKS (Java Cryptography Extension KeyStore) file on disk.  The key store and its contents are
+ * encrypted using the key from the supplied {@link MasterKeyService}.
+ * <p/>
+ * Most of the work for this implementation is handled by the {@link AbstractCredentialStore}.
+ * This class handles the details of the storage location and associated input and output streams.
+ */
+public class FileBasedCredentialStore extends AbstractCredentialStore {
+  private static final Logger LOG = LoggerFactory.getLogger(FileBasedCredentialStore.class);
+
+  /**
+   * The directory to use for storing the key store file
+   */
+  private File keyStoreFile;
+
+  /**
+   * Constructs a new FileBasedCredentialStore using the specified key store directory
+   *
+   * @param keyStoreLocation a File pointing to the directory in which to store the key store file; or the file itself
+   */
+  public FileBasedCredentialStore(File keyStoreLocation) {
+    if (keyStoreLocation == null) {
+      // If the keyStoreLocation is not set, create the file (using the default filename)  in the
+      // current working directory.
+      LOG.warn("Writing key store to the current working directory of the running process");
+      keyStoreLocation = new File(Configuration.MASTER_KEYSTORE_FILENAME_DEFAULT);
+    } else if (keyStoreLocation.isDirectory()) {
+      // If the keyStoreLocation is a directory, create the file (using the default filename) in
+      // that directory.
+      keyStoreLocation = new File(keyStoreLocation, Configuration.MASTER_KEYSTORE_FILENAME_DEFAULT);
+    }
+
+    if (keyStoreLocation.exists()) {
+      if (!keyStoreLocation.canWrite()) {
+        LOG.warn("The destination file is not writable. Failures may occur when writing the key store to disk: {}", keyStoreLocation.getAbsolutePath());
+      }
+    } else {
+      File directory = keyStoreLocation.getParentFile();
+      if ((directory != null) && !directory.canWrite()) {
+        LOG.warn("The destination directory is not writable. Failures may occur when writing the key store to disk: {}", keyStoreLocation.getAbsolutePath());
+      }
+    }
+
+    this.keyStoreFile = keyStoreLocation;
+  }
+
+  /**
+   * Gets the path to this FileBasedCredentialStore's KeyStore file
+   *
+   * @return a file indicating the path to this FileBasedCredentialStore's KeyStore file
+   */
+  public File getKeyStorePath() {
+    return keyStoreFile;
+  }
+
+
+  @Override
+  protected void persistCredentialStore(KeyStore keyStore) throws AmbariException {
+    putKeyStore(keyStore, this.keyStoreFile);
+  }
+
+
+  @Override
+  protected KeyStore loadCredentialStore() throws AmbariException {
+    return getKeyStore(this.keyStoreFile, DEFAULT_STORE_TYPE);
+  }
+
+  /**
+   * Reads the key store data from the specified file. If the file does not exist, a new KeyStore
+   * will be created.
+   *
+   * @param keyStoreFile a File pointing to the key store file
+   * @param keyStoreType the type of key store data to read (or create)
+   * @return the loaded KeyStore
+   * @throws AmbariException if the Master Key Service is not set
+   * @see AbstractCredentialStore#loadCredentialStore()
+   */
+  private KeyStore getKeyStore(final File keyStoreFile, String keyStoreType) throws AmbariException {
+    KeyStore keyStore;
+    FileInputStream inputStream;
+
+    if (keyStoreFile.exists()) {
+      if (keyStoreFile.length() > 0) {
+        LOG.debug("Reading key store from {}", keyStoreFile.getAbsolutePath());
+        try {
+          inputStream = new FileInputStream(keyStoreFile);
+        } catch (FileNotFoundException e) {
+          throw new AmbariException(String.format("Failed to open the key store file: %s", e.getLocalizedMessage()), e);
+        }
+      } else {
+        LOG.debug("The key store file found in {} is empty. Returning new (non-persisted) KeyStore", keyStoreFile.getAbsolutePath());
+        inputStream = null;
+      }
+    } else {
+      LOG.debug("Key store file not found in {}. Returning new (non-persisted) KeyStore", keyStoreFile.getAbsolutePath());
+      inputStream = null;
+    }
+
+    try {
+      keyStore = loadKeyStore(inputStream, keyStoreType);
+    } finally {
+      IOUtils.closeQuietly(inputStream);
+    }
+
+    return keyStore;
+  }
+
+  /**
+   * Writes the specified KeyStore to a file.
+   *
+   * @param keyStore     the KeyStore to write to a file
+   * @param keyStoreFile the File in which to store the KeyStore data
+   * @throws AmbariException if an error occurs while writing the KeyStore data
+   */
+  private void putKeyStore(KeyStore keyStore, File keyStoreFile) throws AmbariException {
+    LOG.debug("Writing key store to {}", keyStoreFile.getAbsolutePath());
+
+    FileOutputStream outputStream = null;
+
+    try {
+      outputStream = new FileOutputStream(this.keyStoreFile);
+      writeKeyStore(keyStore, outputStream);
+    } catch (FileNotFoundException e) {
+      throw new AmbariException(String.format("Failed to open the key store file: %s", e.getLocalizedMessage()), e);
+    } finally {
+      IOUtils.closeQuietly(outputStream);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStoreService.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStoreService.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStoreService.java
deleted file mode 100644
index 41ff71b..0000000
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/FileBasedCredentialStoreService.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.ambari.server.security.encryption;
-
-import org.apache.ambari.server.AmbariException;
-import org.apache.commons.io.IOUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.security.KeyStore;
-
-/**
- * FileBasedCredentialStoreService is a CredentialStoreService implementation that creates and manages
- * a JCEKS (Java Cryptography Extension KeyStore) file on disk.  The key store and its contents are
- * encrypted using the key from the supplied {@link MasterKeyService}.
- * <p/>
- * Most of the work for this implementation is handled by the {@link CredentialStoreServiceImpl}.
- * This class handles the details of the storage location and associated input and output streams.
- */
-public class FileBasedCredentialStoreService extends CredentialStoreServiceImpl {
-  private static final String KEYSTORE_FILENAME = "credentials.jceks";
-  private static final Logger LOG = LoggerFactory.getLogger(FileBasedCredentialStoreService.class);
-
-  /**
-   * The directory to use for storing the key store file
-   */
-  private File keyStoreDir;
-
-  /**
-   * Constructs a new FileBasedCredentialStoreService using the specified key store directory
-   *
-   * @param keyStoreDir a String containing the absolute path to the directory in which to store the key store file
-   */
-  public FileBasedCredentialStoreService(String keyStoreDir) {
-    this(new File(keyStoreDir));
-  }
-
-  /**
-   * Constructs a new FileBasedCredentialStoreService using the specified key store directory
-   *
-   * @param keyStoreDir a File pointing to the directory in which to store the key store file
-   */
-  public FileBasedCredentialStoreService(File keyStoreDir) {
-    if (keyStoreDir == null) {
-      LOG.warn("Writing key store to the current working directory of the running process");
-    } else if (!keyStoreDir.exists()) {
-      LOG.warn("The destination directory does not exist. Failures may occur when writing the key store to disk: {}", keyStoreDir.getAbsolutePath());
-    } else if (!keyStoreDir.isDirectory()) {
-      LOG.warn("The destination does not point to directory. Failures may occur when writing the key store to disk: {}", keyStoreDir.getAbsolutePath());
-    }
-
-    this.keyStoreDir = keyStoreDir;
-  }
-
-  @Override
-  protected void persistCredentialStore(KeyStore keyStore) throws AmbariException {
-    putKeyStore(keyStore, getKeyStoreFile());
-  }
-
-
-  @Override
-  protected KeyStore loadCredentialStore() throws AmbariException {
-    return getKeyStore(getKeyStoreFile(), DEFAULT_STORE_TYPE);
-  }
-
-  /**
-   * Reads the key store data from the specified file. If the file does not exist, a new KeyStore
-   * will be created.
-   *
-   * @param keyStoreFile a File pointing to the key store file
-   * @param keyStoreType the type of key store data to read (or create)
-   * @return the loaded KeyStore
-   * @throws AmbariException           if the Master Key Service is not set
-   * @see CredentialStoreServiceImpl#loadCredentialStore()
-   */
-  private KeyStore getKeyStore(final File keyStoreFile, String keyStoreType) throws AmbariException{
-    KeyStore keyStore;
-    FileInputStream inputStream;
-
-    if (keyStoreFile.exists()) {
-      LOG.debug("Reading key store from {}", keyStoreFile.getAbsolutePath());
-      try {
-        inputStream = new FileInputStream(keyStoreFile);
-      } catch (FileNotFoundException e) {
-        throw new AmbariException(String.format("Failed to open the key store file: %s", e.getLocalizedMessage()), e);
-      }
-    } else {
-      LOG.debug("Key store file not found in {}. Returning new (non-persisted) KeyStore", keyStoreFile.getAbsolutePath());
-      inputStream = null;
-    }
-
-    try {
-      keyStore = loadKeyStore(inputStream, keyStoreType);
-    } finally {
-      IOUtils.closeQuietly(inputStream);
-    }
-
-    return keyStore;
-  }
-
-  /**
-   * Writes the specified KeyStore to a file.
-   *
-   * @param keyStore     the KeyStore to write to a file
-   * @param keyStoreFile the File in which to store the KeyStore data
-   * @throws AmbariException if an error occurs while writing the KeyStore data
-   */
-  private void putKeyStore(KeyStore keyStore, File keyStoreFile) throws AmbariException {
-    LOG.debug("Writing key store to {}", keyStoreFile.getAbsolutePath());
-
-    FileOutputStream outputStream = null;
-
-    try {
-      outputStream = new FileOutputStream(new File(keyStoreDir, KEYSTORE_FILENAME));
-      writeKeyStore(keyStore, outputStream);
-    } catch (FileNotFoundException e) {
-      throw new AmbariException(String.format("Failed to open the key store file: %s", e.getLocalizedMessage()), e);
-    } finally {
-      IOUtils.closeQuietly(outputStream);
-    }
-  }
-
-  /**
-   * Calculates the absolute path to the key store file
-   *
-   * @return a File pointing to the absolute path of the key store file
-   */
-  private File getKeyStoreFile() {
-    return new File(keyStoreDir, KEYSTORE_FILENAME);
-  }
-}

http://git-wip-us.apache.org/repos/asf/ambari/blob/3b411744/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/InMemoryCredentialStore.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/InMemoryCredentialStore.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/InMemoryCredentialStore.java
new file mode 100644
index 0000000..a7b9ba6
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/InMemoryCredentialStore.java
@@ -0,0 +1,233 @@
+/*
+ * 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
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.security.encryption;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.security.credential.Credential;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.KeyStore;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+
+/**
+ * InMemoryCredentialStore is a CredentialStore implementation that creates and manages
+ * a JCEKS (Java Cryptography Extension KeyStore) in memory.  The key store and its contents are
+ * encrypted using the key from the supplied {@link MasterKeyService}.
+ * <p/>
+ * This class handles the details of the in-memory storage buffer and associated input and output
+ * streams. Each credential is stored in its own KeyStore that may be be purged upon some
+ * retention timeout - if specified.
+ */
+public class InMemoryCredentialStore extends AbstractCredentialStore {
+  private static final Logger LOG = LoggerFactory.getLogger(InMemoryCredentialStore.class);
+
+  /**
+   * A cache containing the KeyStore data
+   */
+  private final Cache<String, KeyStore> cache;
+
+  /**
+   * Constructs a new InMemoryCredentialStore where credentials have no retention timeout
+   */
+  public InMemoryCredentialStore() {
+    this(0, TimeUnit.MINUTES, false);
+  }
+
+  /**
+   * Constructs a new InMemoryCredentialStore with a specified credential timeout
+   *
+   * @param retentionDuration the time in some units to keep stored credentials, from the time they are added
+   * @param units             the units for the retention duration (minutes, seconds, etc...)
+   * @param activelyPurge     true to actively purge credentials after the retention time has expired;
+   *                          otherwise false, to passively purge credentials after the retention time has expired
+   */
+  public InMemoryCredentialStore(final long retentionDuration, final TimeUnit units, boolean activelyPurge) {
+    CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
+
+    // If the retentionDuration is less the 1, then no retention policy is to be enforced
+    if (retentionDuration > 0) {
+      // If actively purging expired credentials, set up a timer to periodically clean the cache
+      if (activelyPurge) {
+        ThreadFactory threadFactory = new ThreadFactory() {
+          @Override
+          public Thread newThread(Runnable runnable) {
+            Thread t = Executors.defaultThreadFactory().newThread(runnable);
+            if (t != null) {
+              t.setName(String.format("%s active cleanup timer", InMemoryCredentialStore.class.getSimpleName()));
+              t.setDaemon(true);
+            }
+            return t;
+          }
+        };
+        Runnable runnable = new Runnable() {
+          @Override
+          public void run() {
+            if (LOG.isDebugEnabled()) {
+              LOG.debug("Cleaning up cache due to retention timeout of {} milliseconds",
+                  units.toMillis(retentionDuration));
+            }
+            cache.cleanUp();
+          }
+        };
+
+        Executors.newSingleThreadScheduledExecutor(threadFactory).schedule(runnable, 1, TimeUnit.MINUTES);
+      }
+
+      builder.expireAfterWrite(retentionDuration, units);
+    }
+
+    cache = builder.build();
+  }
+
+  /**
+   * Adds a new credential to this CredentialStore
+   * <p/>
+   * The supplied key will be converted into UTF-8 bytes before being stored.
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias      a string declaring the alias (or name) of the credential
+   * @param credential the credential to store
+   * @throws AmbariException if an error occurs while storing the new credential
+   */
+  @Override
+  public void addCredential(String alias, Credential credential) throws AmbariException {
+    if ((alias == null) || alias.isEmpty()) {
+      throw new IllegalArgumentException("Alias cannot be null or empty.");
+    }
+
+    Lock lock = getLock();
+    lock.lock();
+    try {
+      KeyStore keyStore = loadKeyStore(null, DEFAULT_STORE_TYPE);
+      addCredential(keyStore, alias, credential);
+      cache.put(alias, keyStore);
+    } finally {
+      lock.unlock();
+    }
+  }
+
+  /**
+   * Retrieves the specified credential from this CredentialStore
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return a Credential or null of not found
+   * @throws AmbariException if an error occurs while retrieving the new credential
+   */
+  @Override
+  public Credential getCredential(String alias) throws AmbariException {
+    Credential credential = null;
+
+    if ((alias != null) && !alias.isEmpty()) {
+      Lock lock = getLock();
+      lock.lock();
+      try {
+        KeyStore keyStore = cache.getIfPresent(alias);
+        if (keyStore != null) {
+          credential = getCredential(keyStore, alias);
+        }
+      } finally {
+        lock.unlock();
+      }
+    }
+
+    return credential;
+  }
+
+  /**
+   * Removes the specified credential from this CredentialStore
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @throws AmbariException if an error occurs while removing the new credential
+   */
+  @Override
+  public void removeCredential(String alias) throws AmbariException {
+    if (alias != null) {
+      Lock lock = getLock();
+      lock.lock();
+      try {
+        cache.invalidate(alias);
+      } finally {
+        lock.unlock();
+      }
+    }
+  }
+
+  /**
+   * Returns a list of the alias names for the credentials stored in the CredentialStore
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @return a Set of Strings representing alias names for the credentials stored in the CredentialStore
+   * @throws AmbariException if an error occurs while searching forthe credential
+   */
+  @Override
+  public Set<String> listCredentials() throws AmbariException {
+    Lock lock = getLock();
+    lock.lock();
+    try {
+      return new HashSet<String>(cache.asMap().keySet());
+    } finally {
+      lock.unlock();
+    }
+  }
+
+  /**
+   * Tests this CredentialStore for the existence of a credential with the specified alias
+   * <p/>
+   * This implementation is thread-safe, allowing one thread at a time to access the credential store.
+   *
+   * @param alias a string declaring the alias (or name) of the credential
+   * @return true if the alias exists; otherwise false
+   * @throws AmbariException if an error occurs while searching forthe credential
+   */
+  @Override
+  public boolean containsCredential(String alias) throws AmbariException {
+    Lock lock = getLock();
+    lock.lock();
+    try {
+      return (alias != null) && !alias.isEmpty() && (cache.getIfPresent(alias) != null);
+    } finally {
+      lock.unlock();
+    }
+  }
+
+
+  @Override
+  protected void persistCredentialStore(KeyStore keyStore) throws AmbariException {
+    throw new UnsupportedOperationException();
+  }
+
+  @Override
+  protected KeyStore loadCredentialStore() throws AmbariException {
+    throw new UnsupportedOperationException();
+  }
+}