You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by am...@apache.org on 2018/11/20 09:22:32 UTC

[ambari] branch trunk updated: AMBARI-24894. Sensitive service configuration values should be decrypted when processing the Ambari agent command script, if enabled (amagyar) (#2613)

This is an automated email from the ASF dual-hosted git repository.

amagyar pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new f8a0199  AMBARI-24894. Sensitive service configuration values should be decrypted when processing the Ambari agent command script, if enabled (amagyar) (#2613)
f8a0199 is described below

commit f8a019926fdca0073f08de54c5e9720260beef52
Author: Attila Magyar <m....@gmail.com>
AuthorDate: Tue Nov 20 10:22:26 2018 +0100

    AMBARI-24894. Sensitive service configuration values should be decrypted when processing the Ambari agent command script, if enabled (amagyar) (#2613)
---
 ambari-agent/pom.xml                               |   4 +-
 .../src/main/python/ambari_agent/Constants.py      |   3 +-
 .../ambari_agent/CustomServiceOrchestrator.py      |   6 +
 .../main/python/ambari_agent/HeartbeatThread.py    |   6 +-
 .../listeners/EncryptionKeyListener.py             |  43 ++
 .../main/python/ambari_agent/listeners/__init__.py |   3 +-
 ambari-agent/src/packages/tarball/all.xml          |  12 +
 .../python/resource_management/TestEncryption.py   |  27 +
 .../src/main/python/ambari_pbkdf2/README.txt       |  86 +++
 .../src/main/python/ambari_pbkdf2/__init__.py      |   0
 .../src/main/python/ambari_pbkdf2/pbkdf2.py        | 297 +++++++++++
 .../src/main/python/ambari_pyaes/LICENSE.txt       |  22 +
 .../src/main/python/ambari_pyaes/README.md         | 363 +++++++++++++
 .../src/main/python/ambari_pyaes/__init__.py       |  53 ++
 ambari-common/src/main/python/ambari_pyaes/aes.py  | 589 +++++++++++++++++++++
 .../src/main/python/ambari_pyaes/blockfeeder.py    | 227 ++++++++
 ambari-common/src/main/python/ambari_pyaes/util.py |  60 +++
 .../python/resource_management/core/encryption.py  |  47 ++
 .../main/python/resource_management/core/utils.py  |   2 +-
 .../libraries/script/config_dictionary.py          |   8 +-
 ambari-server/pom.xml                              |   2 +
 ambari-server/src/main/assemblies/server.xml       |   8 +
 .../ambari/server/agent/AgentEncryptionKey.java    |  88 +++
 .../ambari/server/agent/HeartBeatHandler.java      |  33 +-
 .../server/agent/stomp/AgentConfigsHolder.java     |  12 +-
 .../ambari/server/agent/stomp/AgentDataHolder.java |   5 +-
 .../ambari/server/controller/ControllerModule.java |  10 +-
 .../server/events/DefaultMessageEmitter.java       |   4 +-
 .../server/events/EncryptionKeyUpdateEvent.java    |  52 ++
 .../apache/ambari/server/events/STOMPEvent.java    |   3 +-
 .../security/encryption/AESEncryptionService.java  |  52 +-
 .../server/security/encryption/AESEncryptor.java   |  63 +--
 .../encryption/AgentConfigUpdateEncryptor.java     |  84 +++
 .../encryption/ConfigPropertiesEncryptor.java      |  91 +---
 .../security/encryption/EncryptionService.java     |  34 +-
 .../server/security/encryption/Encryptor.java      |  15 +-
 .../security/encryption/PropertiesEncryptor.java   | 115 ++++
 .../org/apache/ambari/server/state/ConfigImpl.java |  33 +-
 .../ambari/server/agent/AgentResourceTest.java     |   5 +-
 .../server/agent/HeartbeatProcessorTest.java       |   3 +-
 .../ambari/server/agent/HeartbeatTestHelper.java   |   3 +-
 .../ambari/server/agent/TestHeartbeatHandler.java  |  53 +-
 .../ambari/server/agent/TestHeartbeatMonitor.java  |  14 +-
 .../server/agent/stomp/AgentDataHolderTest.java    |   3 +-
 .../server/controller/KerberosHelperTest.java      |   1 -
 .../PreUpgradeCheckResourceProviderTest.java       |   5 +
 .../UserAuthorizationResourceProviderTest.java     |   5 +
 .../internal/UserResourceProviderTest.java         |   5 +
 .../server/orm/InMemoryDefaultTestModule.java      |   5 +
 .../AbstractPrepareKerberosServerActionTest.java   |   5 +
 .../upgrades/PreconfigureKerberosActionTest.java   |   4 +
 .../apache/ambari/server/state/host/HostTest.java  |   3 +-
 .../server/testutils/PartialNiceMockBinder.java    |   4 +-
 pom.xml                                            |   3 +
 start-build-env.sh                                 |   2 +-
 55 files changed, 2423 insertions(+), 262 deletions(-)

diff --git a/ambari-agent/pom.xml b/ambari-agent/pom.xml
index da0f3b1..9389ac3 100644
--- a/ambari-agent/pom.xml
+++ b/ambari-agent/pom.xml
@@ -42,6 +42,8 @@
     <simplejson.install.dir>/usr/lib/ambari-agent/lib/ambari_simplejson</simplejson.install.dir>
     <stomp.install.dir>/usr/lib/ambari-agent/lib/ambari_stomp</stomp.install.dir>
     <ws4py.install.dir>/usr/lib/ambari-agent/lib/ambari_ws4py</ws4py.install.dir>
+    <pbkdf2.install.dir>/usr/lib/ambari-agent/lib/ambari_pbkdf2</pbkdf2.install.dir>
+    <pyaes.install.dir>/usr/lib/ambari-agent/lib/ambari_pyaes</pyaes.install.dir>
     <lib.dir>/usr/lib/ambari-agent/lib</lib.dir>
     <deb.architecture>amd64</deb.architecture>
     <ambari.server.module>../ambari-server</ambari.server.module>
@@ -698,7 +700,7 @@
       <executable.shell>sh</executable.shell>
       <fileextension.shell>sh</fileextension.shell>
       <fileextension.dot.shell-default></fileextension.dot.shell-default>
-      <path.python.1>${project.basedir}/../ambari-common/src/main/python:${project.basedir}/../ambari-agent/src/main/python:${project.basedir}/../ambari-common/src/main/python/ambari_jinja2:${project.basedir}/../ambari-common/src/main/python/ambari_commons:${project.basedir}/../ambari-common/src/test/python:${project.basedir}/src/main/python:${project.basedir}/src/main/python/ambari_agent:${project.basedir}/src/main/python/resource_management:${project.basedir}/src/test/python:${project. [...]
+      <path.python.1>${project.basedir}/../ambari-common/src/main/python:${project.basedir}/../ambari-agent/src/main/python:${project.basedir}/../ambari-common/src/main/python/ambari_jinja2:${project.basedir}/../ambari-agent/src/main/python:${project.basedir}/../ambari-common/src/main/python/ambari_commons:${project.basedir}/../ambari-common/src/test/python:${project.basedir}/src/main/python:${project.basedir}/src/main/python/ambari_agent:${project.basedir}/src/main/python/resource_manag [...]
      </properties>
     </profile>
     <profile>
diff --git a/ambari-agent/src/main/python/ambari_agent/Constants.py b/ambari-agent/src/main/python/ambari_agent/Constants.py
index 91141a6..ef522e7 100644
--- a/ambari-agent/src/main/python/ambari_agent/Constants.py
+++ b/ambari-agent/src/main/python/ambari_agent/Constants.py
@@ -27,8 +27,9 @@ METADATA_TOPIC = '/events/metadata'
 TOPOLOGIES_TOPIC = '/events/topologies'
 SERVER_RESPONSES_TOPIC = '/user/'
 AGENT_ACTIONS_TOPIC = '/user/agent_actions'
+ENCRYPTION_KEY_TOPIC = '/events/encryption_key'
 
-PRE_REGISTRATION_TOPICS_TO_SUBSCRIBE = [SERVER_RESPONSES_TOPIC, AGENT_ACTIONS_TOPIC]
+PRE_REGISTRATION_TOPICS_TO_SUBSCRIBE = [SERVER_RESPONSES_TOPIC, AGENT_ACTIONS_TOPIC, ENCRYPTION_KEY_TOPIC]
 POST_REGISTRATION_TOPICS_TO_SUBSCRIBE = [COMMANDS_TOPIC]
 
 AGENT_RESPONSES_TOPIC = '/reports/responses'
diff --git a/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py b/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py
index 8abb479..0ea3656 100644
--- a/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py
+++ b/ambari-agent/src/main/python/ambari_agent/CustomServiceOrchestrator.py
@@ -34,6 +34,7 @@ from ambari_commons import shell, subprocess32
 from ambari_commons.constants import AGENT_TMP_DIR
 from resource_management.libraries.functions.log_process_information import log_process_information
 from resource_management.core.utils import PasswordString
+from resource_management.core.encryption import ensure_decrypted
 
 from ambari_agent.models.commands import AgentCommand
 from ambari_agent.Utils import Utils
@@ -111,6 +112,7 @@ class CustomServiceOrchestrator(object):
 
     # save count (not boolean) for parallel execution cases
     self.commands_for_component_in_progress = defaultdict(lambda:defaultdict(lambda:0))
+    self.encryption_key = None
 
   def map_task_to_process(self, task_id, processId):
     with self.commands_in_progress_lock:
@@ -297,6 +299,7 @@ class CustomServiceOrchestrator(object):
       logger.info('provider_path={0}'.format(provider_path))
       for alias, pwd in credentials.items():
         logger.debug("config={0}".format(config))
+        pwd = ensure_decrypted(pwd, self.encryption_key)
         protected_pwd = PasswordString(pwd)
         # Generate the JCEKS file
         cmd = (java_bin, '-cp', cs_lib_path, self.credential_shell_cmd, 'create',
@@ -406,6 +409,9 @@ class CustomServiceOrchestrator(object):
 
         raise AgentException("Background commands are supported without hooks only")
 
+      if self.encryption_key:
+        os.environ['AGENT_ENCRYPTION_KEY'] = self.encryption_key
+
       python_executor = self.get_py_executor(forced_command_name)
       backup_log_files = command_name not in self.DONT_BACKUP_LOGS_FOR_COMMANDS
       try:
diff --git a/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py b/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
index 36b88d6..4fbefdb 100644
--- a/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
+++ b/ambari-agent/src/main/python/ambari_agent/HeartbeatThread.py
@@ -19,7 +19,6 @@ limitations under the License.
 '''
 
 import logging
-import ambari_stomp
 import threading
 from socket import error as socket_error
 
@@ -28,7 +27,6 @@ from ambari_agent.Register import Register
 from ambari_agent.Utils import BlockingDictionary
 from ambari_agent.Utils import Utils
 from ambari_agent.ComponentVersionReporter import ComponentVersionReporter
-from ambari_agent.listeners.ServerResponsesListener import ServerResponsesListener
 from ambari_agent.listeners.TopologyEventListener import TopologyEventListener
 from ambari_agent.listeners.ConfigurationEventListener import ConfigurationEventListener
 from ambari_agent.listeners.AgentActionsListener import AgentActionsListener
@@ -36,6 +34,7 @@ from ambari_agent.listeners.MetadataEventListener import MetadataEventListener
 from ambari_agent.listeners.CommandsEventListener import CommandsEventListener
 from ambari_agent.listeners.HostLevelParamsEventListener import HostLevelParamsEventListener
 from ambari_agent.listeners.AlertDefinitionsEventListener import AlertDefinitionsEventListener
+from ambari_agent.listeners.EncryptionKeyListener import EncryptionKeyListener
 from ambari_agent import security
 from ambari_stomp.adapter.websocket import ConnectionIsAlreadyClosed
 
@@ -64,11 +63,12 @@ class HeartbeatThread(threading.Thread):
     self.metadata_events_listener = MetadataEventListener(initializer_module)
     self.topology_events_listener = TopologyEventListener(initializer_module)
     self.configuration_events_listener = ConfigurationEventListener(initializer_module)
+    self.encryption_key_events_listener = EncryptionKeyListener(initializer_module)
     self.host_level_params_events_listener = HostLevelParamsEventListener(initializer_module)
     self.alert_definitions_events_listener = AlertDefinitionsEventListener(initializer_module)
     self.agent_actions_events_listener = AgentActionsListener(initializer_module)
     self.component_status_executor = initializer_module.component_status_executor
-    self.listeners = [self.server_responses_listener, self.commands_events_listener, self.metadata_events_listener, self.topology_events_listener, self.configuration_events_listener, self.host_level_params_events_listener, self.alert_definitions_events_listener, self.agent_actions_events_listener]
+    self.listeners = [self.server_responses_listener, self.commands_events_listener, self.metadata_events_listener, self.topology_events_listener, self.configuration_events_listener, self.host_level_params_events_listener, self.alert_definitions_events_listener, self.agent_actions_events_listener, self.encryption_key_events_listener]
 
     self.post_registration_requests = [
     (Constants.TOPOLOGY_REQUEST_ENDPOINT, initializer_module.topology_cache, self.topology_events_listener, Constants.TOPOLOGIES_TOPIC),
diff --git a/ambari-agent/src/main/python/ambari_agent/listeners/EncryptionKeyListener.py b/ambari-agent/src/main/python/ambari_agent/listeners/EncryptionKeyListener.py
new file mode 100644
index 0000000..109506e
--- /dev/null
+++ b/ambari-agent/src/main/python/ambari_agent/listeners/EncryptionKeyListener.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+'''
+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.
+'''
+
+import logging
+
+from ambari_agent.listeners import EventListener
+from ambari_agent import Constants
+
+logger = logging.getLogger(__name__)
+
+class EncryptionKeyListener(EventListener):
+  """
+  Listener of Constants.ENCRYPTION_KEY_TOPIC events from server.
+  """
+
+  def __init__(self, initializer_module):
+    super(EncryptionKeyListener, self).__init__(initializer_module)
+
+  def on_event(self, headers, message):
+    logger.info("EncryptionKey received")
+    self.initializer_module.customServiceOrchestrator.encryption_key = message['encryptionKey']
+
+  def get_handled_path(self):
+    return Constants.ENCRYPTION_KEY_TOPIC
+
+
diff --git a/ambari-agent/src/main/python/ambari_agent/listeners/__init__.py b/ambari-agent/src/main/python/ambari_agent/listeners/__init__.py
index b50bdaa..69b59da 100644
--- a/ambari-agent/src/main/python/ambari_agent/listeners/__init__.py
+++ b/ambari-agent/src/main/python/ambari_agent/listeners/__init__.py
@@ -79,7 +79,8 @@ class EventListener(ambari_stomp.ConnectionListener):
         self.report_status_to_sender(headers, message, ex)
         return
 
-      logger.info("Event from server at {0}{1}".format(destination, self.get_log_message(headers, copy.deepcopy(message_json))))
+      if destination != Constants.ENCRYPTION_KEY_TOPIC:
+        logger.info("Event from server at {0}{1}".format(destination, self.get_log_message(headers, copy.deepcopy(message_json))))
 
       if not self.enabled:
         with self.event_queue_lock:
diff --git a/ambari-agent/src/packages/tarball/all.xml b/ambari-agent/src/packages/tarball/all.xml
index 2ff57cd..62ab5be 100644
--- a/ambari-agent/src/packages/tarball/all.xml
+++ b/ambari-agent/src/packages/tarball/all.xml
@@ -90,6 +90,18 @@
     <fileSet>
       <directoryMode>755</directoryMode>
       <fileMode>755</fileMode>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pbkdf2</directory>
+      <outputDirectory>${pbkdf2.install.dir}</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directoryMode>755</directoryMode>
+      <fileMode>755</fileMode>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pyaes</directory>
+      <outputDirectory>${pyaes.install.dir}</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directoryMode>755</directoryMode>
+      <fileMode>755</fileMode>
       <directory>src/examples</directory>
       <outputDirectory>${lib.dir}/examples</outputDirectory>
     </fileSet>
diff --git a/ambari-agent/src/test/python/resource_management/TestEncryption.py b/ambari-agent/src/test/python/resource_management/TestEncryption.py
new file mode 100644
index 0000000..0f42274
--- /dev/null
+++ b/ambari-agent/src/test/python/resource_management/TestEncryption.py
@@ -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.
+'''
+
+from unittest import TestCase
+from resource_management.core.encryption import ensure_decrypted
+
+class TestUtils(TestCase):
+
+  def test_attr_to_bitmask(self):
+    encypted_value = '${enc=aes256_hex, value=616639333036363938646230613262383a3a32313537386561376136326362656436656135626165313664613265316336663a3a6361633666333432653532393863313364393064626133653562353663663235}'
+    encyption_key = 'i%r041K%1VC!C5 K=('
+    self.assertEquals('mysecret', ensure_decrypted(encypted_value, encyption_key))
diff --git a/ambari-common/src/main/python/ambari_pbkdf2/README.txt b/ambari-common/src/main/python/ambari_pbkdf2/README.txt
new file mode 100644
index 0000000..0bb6489
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pbkdf2/README.txt
@@ -0,0 +1,86 @@
+Python PKCS#5 v2.0 PBKDF2 Module
+--------------------------------
+
+This module implements the password-based key derivation function, PBKDF2,
+specified in `RSA PKCS#5 v2.0 <http://www.rsa.com/rsalabs/node.asp?id=2127>`_.
+
+Example PBKDF2 usage
+====================
+
+::
+
+ from pbkdf2 import PBKDF2
+ from Crypto.Cipher import AES
+ import os
+
+ salt = os.urandom(8)    # 64-bit salt
+ key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
+ iv = os.urandom(16)     # 128-bit IV
+ cipher = AES.new(key, AES.MODE_CBC, iv)
+ # ...
+
+Example crypt() usage
+=====================
+
+::
+
+ from pbkdf2 import crypt
+ pwhash = crypt("secret")
+ alleged_pw = raw_input("Enter password: ")
+ if pwhash == crypt(alleged_pw, pwhash):
+     print "Password good"
+ else:
+     print "Invalid password"
+
+Example crypt() output
+======================
+
+::
+
+ >>> from pbkdf2 import crypt
+ >>> crypt("secret")
+ '$p5k2$$hi46RA73$aGBpfPOgOrgZLaHGweSQzJ5FLz4BsQVs'
+ >>> crypt("secret", "XXXXXXXX")
+ '$p5k2$$XXXXXXXX$L9mVVdq7upotdvtGvXTDTez3FIu3z0uG'
+ >>> crypt("secret", "XXXXXXXX", 400)  # 400 iterations (the default for crypt)
+ '$p5k2$$XXXXXXXX$L9mVVdq7upotdvtGvXTDTez3FIu3z0uG'
+ >>> crypt("spam", iterations=400)
+ '$p5k2$$FRsH3HJB$SgRWDNmB2LukCy0OTal6LYLHZVgtOi7s'
+ >>> crypt("spam", iterations=1000)    # 1000 iterations
+ '$p5k2$3e8$H0NX9mT/$wk/sE8vv6OMKuMaqazCJYDSUhWY9YB2J'
+
+
+Resources
+=========
+
+Homepage
+    https://www.dlitz.net/software/python-pbkdf2/
+
+Source Code
+    https://github.com/dlitz/python-pbkdf2/
+
+PyPI package name
+    `pbkdf2 <http://pypi.python.org/pypi/pbkdf2>`_
+
+License
+=======
+Copyright (C) 2007-2011 Dwayne C. Litzenberger <dl...@dlitz.net>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ambari-common/src/main/python/ambari_pbkdf2/__init__.py b/ambari-common/src/main/python/ambari_pbkdf2/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ambari-common/src/main/python/ambari_pbkdf2/pbkdf2.py b/ambari-common/src/main/python/ambari_pbkdf2/pbkdf2.py
new file mode 100644
index 0000000..937a99a
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pbkdf2/pbkdf2.py
@@ -0,0 +1,297 @@
+#!/usr/bin/python
+# -*- coding: ascii -*-
+###########################################################################
+# pbkdf2 - PKCS#5 v2.0 Password-Based Key Derivation
+#
+# Copyright (C) 2007-2011 Dwayne C. Litzenberger <dl...@dlitz.net>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Country of origin: Canada
+#
+###########################################################################
+# Sample PBKDF2 usage:
+#   from Crypto.Cipher import AES
+#   from pbkdf2 import PBKDF2
+#   import os
+#
+#   salt = os.urandom(8)    # 64-bit salt
+#   key = PBKDF2("This passphrase is a secret.", salt).read(32) # 256-bit key
+#   iv = os.urandom(16)     # 128-bit IV
+#   cipher = AES.new(key, AES.MODE_CBC, iv)
+#     ...
+#
+# Sample crypt() usage:
+#   from pbkdf2 import crypt
+#   pwhash = crypt("secret")
+#   alleged_pw = raw_input("Enter password: ")
+#   if pwhash == crypt(alleged_pw, pwhash):
+#       print "Password good"
+#   else:
+#       print "Invalid password"
+#
+###########################################################################
+
+__version__ = "1.3"
+__all__ = ['PBKDF2', 'crypt']
+
+from struct import pack
+from random import randint
+import string
+import sys
+
+try:
+    # Use PyCrypto (if available).
+    from Crypto.Hash import HMAC, SHA as SHA1
+except ImportError:
+    # PyCrypto not available.  Use the Python standard library.
+    import hmac as HMAC
+    try:
+        from hashlib import sha1 as SHA1
+    except ImportError:
+        # hashlib not available.  Use the old sha module.
+        import sha as SHA1
+
+#
+# Python 2.1 thru 3.2 compatibility
+#
+
+if sys.version_info[0] == 2:
+    _0xffffffffL = long(1) << 32
+    def isunicode(s):
+        return isinstance(s, unicode)
+    def isbytes(s):
+        return isinstance(s, str)
+    def isinteger(n):
+        return isinstance(n, (int, long))
+    def b(s):
+        return s
+    def binxor(a, b):
+        return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)])
+    def b64encode(data, chars="+/"):
+        tt = string.maketrans("+/", chars)
+        return data.encode('base64').replace("\n", "").translate(tt)
+    from binascii import b2a_hex
+else:
+    _0xffffffffL = 0xffffffff
+    def isunicode(s):
+        return isinstance(s, str)
+    def isbytes(s):
+        return isinstance(s, bytes)
+    def isinteger(n):
+        return isinstance(n, int)
+    def callable(obj):
+        return hasattr(obj, '__call__')
+    def b(s):
+       return s.encode("latin-1")
+    def binxor(a, b):
+        return bytes([x ^ y for (x, y) in zip(a, b)])
+    from base64 import b64encode as _b64encode
+    def b64encode(data, chars="+/"):
+        if isunicode(chars):
+            return _b64encode(data, chars.encode('utf-8')).decode('utf-8')
+        else:
+            return _b64encode(data, chars)
+    from binascii import b2a_hex as _b2a_hex
+    def b2a_hex(s):
+        return _b2a_hex(s).decode('us-ascii')
+    xrange = range
+
+class PBKDF2(object):
+    """PBKDF2.py : PKCS#5 v2.0 Password-Based Key Derivation
+
+    This implementation takes a passphrase and a salt (and optionally an
+    iteration count, a digest module, and a MAC module) and provides a
+    file-like object from which an arbitrarily-sized key can be read.
+
+    If the passphrase and/or salt are unicode objects, they are encoded as
+    UTF-8 before they are processed.
+
+    The idea behind PBKDF2 is to derive a cryptographic key from a
+    passphrase and a salt.
+
+    PBKDF2 may also be used as a strong salted password hash.  The
+    'crypt' function is provided for that purpose.
+
+    Remember: Keys generated using PBKDF2 are only as strong as the
+    passphrases they are derived from.
+    """
+
+    def __init__(self, passphrase, salt, iterations=1000,
+                 digestmodule=SHA1, macmodule=HMAC):
+        self.__macmodule = macmodule
+        self.__digestmodule = digestmodule
+        self._setup(passphrase, salt, iterations, self._pseudorandom)
+
+    def _pseudorandom(self, key, msg):
+        """Pseudorandom function.  e.g. HMAC-SHA1"""
+        return self.__macmodule.new(key=key, msg=msg,
+            digestmod=self.__digestmodule).digest()
+
+    def read(self, bytes):
+        """Read the specified number of key bytes."""
+        if self.closed:
+            raise ValueError("file-like object is closed")
+
+        size = len(self.__buf)
+        blocks = [self.__buf]
+        i = self.__blockNum
+        while size < bytes:
+            i += 1
+            if i > _0xffffffffL or i < 1:
+                # We could return "" here, but
+                raise OverflowError("derived key too long")
+            block = self.__f(i)
+            blocks.append(block)
+            size += len(block)
+        buf = b("").join(blocks)
+        retval = buf[:bytes]
+        self.__buf = buf[bytes:]
+        self.__blockNum = i
+        return retval
+
+    def __f(self, i):
+        # i must fit within 32 bits
+        assert 1 <= i <= _0xffffffffL
+        U = self.__prf(self.__passphrase, self.__salt + pack("!L", i))
+        result = U
+        for j in xrange(2, 1+self.__iterations):
+            U = self.__prf(self.__passphrase, U)
+            result = binxor(result, U)
+        return result
+
+    def hexread(self, octets):
+        """Read the specified number of octets. Return them as hexadecimal.
+
+        Note that len(obj.hexread(n)) == 2*n.
+        """
+        return b2a_hex(self.read(octets))
+
+    def _setup(self, passphrase, salt, iterations, prf):
+        # Sanity checks:
+
+        # passphrase and salt must be str or unicode (in the latter
+        # case, we convert to UTF-8)
+        if isunicode(passphrase):
+            passphrase = passphrase.encode("UTF-8")
+        elif not isbytes(passphrase):
+            raise TypeError("passphrase must be str or unicode")
+        if isunicode(salt):
+            salt = salt.encode("UTF-8")
+        elif not isbytes(salt):
+            raise TypeError("salt must be str or unicode")
+
+        # iterations must be an integer >= 1
+        if not isinteger(iterations):
+            raise TypeError("iterations must be an integer")
+        if iterations < 1:
+            raise ValueError("iterations must be at least 1")
+
+        # prf must be callable
+        if not callable(prf):
+            raise TypeError("prf must be callable")
+
+        self.__passphrase = passphrase
+        self.__salt = salt
+        self.__iterations = iterations
+        self.__prf = prf
+        self.__blockNum = 0
+        self.__buf = b("")
+        self.closed = False
+
+    def close(self):
+        """Close the stream."""
+        if not self.closed:
+            del self.__passphrase
+            del self.__salt
+            del self.__iterations
+            del self.__prf
+            del self.__blockNum
+            del self.__buf
+            self.closed = True
+
+def crypt(word, salt=None, iterations=None):
+    """PBKDF2-based unix crypt(3) replacement.
+
+    The number of iterations specified in the salt overrides the 'iterations'
+    parameter.
+
+    The effective hash length is 192 bits.
+    """
+
+    # Generate a (pseudo-)random salt if the user hasn't provided one.
+    if salt is None:
+        salt = _makesalt()
+
+    # salt must be a string or the us-ascii subset of unicode
+    if isunicode(salt):
+        salt = salt.encode('us-ascii').decode('us-ascii')
+    elif isbytes(salt):
+        salt = salt.decode('us-ascii')
+    else:
+        raise TypeError("salt must be a string")
+
+    # word must be a string or unicode (in the latter case, we convert to UTF-8)
+    if isunicode(word):
+        word = word.encode("UTF-8")
+    elif not isbytes(word):
+        raise TypeError("word must be a string or unicode")
+
+    # Try to extract the real salt and iteration count from the salt
+    if salt.startswith("$p5k2$"):
+        (iterations, salt, dummy) = salt.split("$")[2:5]
+        if iterations == "":
+            iterations = 400
+        else:
+            converted = int(iterations, 16)
+            if iterations != "%x" % converted:  # lowercase hex, minimum digits
+                raise ValueError("Invalid salt")
+            iterations = converted
+            if not (iterations >= 1):
+                raise ValueError("Invalid salt")
+
+    # Make sure the salt matches the allowed character set
+    allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./"
+    for ch in salt:
+        if ch not in allowed:
+            raise ValueError("Illegal character %r in salt" % (ch,))
+
+    if iterations is None or iterations == 400:
+        iterations = 400
+        salt = "$p5k2$$" + salt
+    else:
+        salt = "$p5k2$%x$%s" % (iterations, salt)
+    rawhash = PBKDF2(word, salt, iterations).read(24)
+    return salt + "$" + b64encode(rawhash, "./")
+
+# Add crypt as a static method of the PBKDF2 class
+# This makes it easier to do "from PBKDF2 import PBKDF2" and still use
+# crypt.
+PBKDF2.crypt = staticmethod(crypt)
+
+def _makesalt():
+    """Return a 48-bit pseudorandom salt for crypt().
+
+    This function is not suitable for generating cryptographic secrets.
+    """
+    binarysalt = b("").join([pack("@H", randint(0, 0xffff)) for i in range(3)])
+    return b64encode(binarysalt, "./")
+
+# vim:set ts=4 sw=4 sts=4 expandtab:
diff --git a/ambari-common/src/main/python/ambari_pyaes/LICENSE.txt b/ambari-common/src/main/python/ambari_pyaes/LICENSE.txt
new file mode 100644
index 0000000..0417a6c
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pyaes/LICENSE.txt
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Richard Moore
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/ambari-common/src/main/python/ambari_pyaes/README.md b/ambari-common/src/main/python/ambari_pyaes/README.md
new file mode 100644
index 0000000..05b39f3
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pyaes/README.md
@@ -0,0 +1,363 @@
+pyaes
+=====
+
+A pure-Python implmentation of the AES block cipher algorithm and the common modes of operation (CBC, CFB, CTR, ECB and OFB).
+
+
+Features
+--------
+
+* Supports all AES key sizes
+* Supports all AES common modes
+* Pure-Python (no external dependancies)
+* BlockFeeder API allows streams to easily be encrypted and decrypted
+* Python 2.x and 3.x support (make sure you pass in bytes(), not strings for Python 3)
+
+
+API
+---
+
+All keys may be 128 bits (16 bytes), 192 bits (24 bytes) or 256 bits (32 bytes) long.
+
+To generate a random key use:
+```python
+import os
+
+# 128 bit, 192 bit and 256 bit keys
+key_128 = os.urandom(16)
+key_192 = os.urandom(24)
+key_256 = os.urandom(32)
+```
+
+To generate keys from simple-to-remember passwords, consider using a _password-based key-derivation function_ such as [scrypt](https://github.com/ricmoo/pyscrypt).
+
+
+### Common Modes of Operation
+
+There are many modes of operations, each with various pros and cons. In general though, the **CBC** and **CTR** modes are recommended. The **ECB is NOT recommended.**, and is included primarilty for completeness.
+
+Each of the following examples assumes the following key:
+```python
+import pyaes
+
+# A 256 bit (32 byte) key
+key = "This_key_for_demo_purposes_only!"
+
+# For some modes of operation we need a random initialization vector
+# of 16 bytes
+iv = "InitializationVe"
+```
+
+
+#### Counter Mode of Operation (recommended)
+
+```python
+aes = pyaes.AESModeOfOperationCTR(key)
+plaintext = "Text may be any length you wish, no padding is required"
+ciphertext = aes.encrypt(plaintext)
+
+# '''\xb6\x99\x10=\xa4\x96\x88\xd1\x89\x1co\xe6\x1d\xef;\x11\x03\xe3\xee
+#    \xa9V?wY\xbfe\xcdO\xe3\xdf\x9dV\x19\xe5\x8dk\x9fh\xb87>\xdb\xa3\xd6
+#    \x86\xf4\xbd\xb0\x97\xf1\t\x02\xe9 \xed'''
+print repr(ciphertext)
+
+# The counter mode of operation maintains state, so decryption requires
+# a new instance be created
+aes = pyaes.AESModeOfOperationCTR(key)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+
+# To use a custom initial value
+counter = pyaes.Counter(initial_value = 100)
+aes = pyaes.AESModeOfOperationCTR(key, counter = counter)
+ciphertext = aes.encrypt(plaintext)
+
+# '''WZ\x844\x02\xbfoY\x1f\x12\xa6\xce\x03\x82Ei)\xf6\x97mX\x86\xe3\x9d
+#    _1\xdd\xbd\x87\xb5\xccEM_4\x01$\xa6\x81\x0b\xd5\x04\xd7Al\x07\xe5
+#    \xb2\x0e\\\x0f\x00\x13,\x07'''
+print repr(ciphertext)
+```
+
+
+#### Cipher-Block Chaining (recommended)
+
+```python
+aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
+plaintext = "TextMustBe16Byte"
+ciphertext = aes.encrypt(plaintext)
+
+# '\xd6:\x18\xe6\xb1\xb3\xc3\xdc\x87\xdf\xa7|\x08{k\xb6'
+print repr(ciphertext)
+
+
+# The cipher-block chaining mode of operation maintains state, so 
+# decryption requires a new instance be created
+aes = pyaes.AESModeOfOperationCBC(key, iv = iv)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Cipher Feedback
+
+```python
+# Each block into the mode of operation must be a multiple of the segment
+# size. For this example we choose 8 bytes.
+aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
+plaintext =  "TextMustBeAMultipleOfSegmentSize"
+ciphertext = aes.encrypt(plaintext)
+
+# '''v\xa9\xc1w"\x8aL\x93\xcb\xdf\xa0/\xf8Y\x0b\x8d\x88i\xcb\x85rmp
+#    \x85\xfe\xafM\x0c)\xd5\xeb\xaf'''
+print repr(ciphertext)
+
+
+# The cipher-block chaining mode of operation maintains state, so 
+# decryption requires a new instance be created
+aes = pyaes.AESModeOfOperationCFB(key, iv = iv, segment_size = 8)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Output Feedback Mode of Operation
+
+```python
+aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
+plaintext = "Text may be any length you wish, no padding is required"
+ciphertext = aes.encrypt(plaintext)
+
+# '''v\xa9\xc1wO\x92^\x9e\rR\x1e\xf7\xb1\xa2\x9d"l1\xc7\xe7\x9d\x87(\xc26s
+#    \xdd8\xc8@\xb6\xd9!\xf5\x0cM\xaa\x9b\xc4\xedLD\xe4\xb9\xd8\xdf\x9e\xac
+#    \xa1\xb8\xea\x0f\x8ev\xb5'''
+print repr(ciphertext)
+
+# The counter mode of operation maintains state, so decryption requires
+# a new instance be created
+aes = pyaes.AESModeOfOperationOFB(key, iv = iv)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+#### Electronic Codebook (NOT recommended)
+
+```python
+aes = pyaes.AESModeOfOperationECB(key)
+plaintext = "TextMustBe16Byte"
+ciphertext = aes.encrypt(plaintext)
+
+# 'L6\x95\x85\xe4\xd9\xf1\x8a\xfb\xe5\x94X\x80|\x19\xc3'
+print repr(ciphertext)
+
+# Since there is no state stored in this mode of operation, it
+# is not necessary to create a new aes object for decryption.
+#aes = pyaes.AESModeOfOperationECB(key)
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext
+```
+
+
+### BlockFeeder
+
+Since most of the modes of operations require data in specific block-sized or segment-sized blocks, it can be difficult when working with large arbitrary streams or strings of data.
+
+The BlockFeeder class is meant to make life easier for you, by buffering bytes across multiple calls and returning bytes as they are available, as well as padding or stripping the output when finished, if necessary.
+
+```python
+import pyaes
+
+# Any mode of operation can be used; for this example CBC
+key = "This_key_for_demo_purposes_only!"
+iv = "InitializationVe"
+
+ciphertext = ''
+
+# We can encrypt one line at a time, regardles of length
+encrypter = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, iv))
+for line in file('/etc/passwd'):
+    ciphertext += encrypter.feed(line)
+
+# Make a final call to flush any remaining bytes and add paddin
+ciphertext += encrypter.feed()
+
+# We can decrypt the cipher text in chunks (here we split it in half)
+decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv))
+decrypted = decrypter.feed(ciphertext[:len(ciphertext) / 2])
+decrypted += decrypter.feed(ciphertext[len(ciphertext) / 2:])
+
+# Again, make a final call to flush any remaining bytes and strip padding
+decrypted += decrypter.feed()
+
+print file('/etc/passwd').read() == decrypted
+```
+
+### Stream Feeder
+
+This is meant to make it even easier to encrypt and decrypt streams and large files.
+
+```python
+import pyaes
+
+# Any mode of operation can be used; for this example CTR
+key = "This_key_for_demo_purposes_only!"
+
+# Create the mode of operation to encrypt with
+mode = pyaes.AESModeOfOperationCTR(key)
+
+# The input and output files
+file_in = file('/etc/passwd')
+file_out = file('/tmp/encrypted.bin', 'wb')
+
+# Encrypt the data as a stream, the file is read in 8kb chunks, be default
+pyaes.encrypt_stream(mode, file_in, file_out)
+
+# Close the files
+file_in.close()
+file_out.close()
+```
+
+Decrypting is identical, except you would use `pyaes.decrypt_stream`, and the encrypted file would be the `file_in` and target for decryption the `file_out`.
+
+### AES block cipher
+
+Generally you should use one of the modes of operation above. This may however be useful for experimenting with a custom mode of operation or dealing with encrypted blocks.
+
+The block cipher requires exactly one block of data to encrypt or decrypt, and each block should be an array with each element an integer representation of a byte.
+
+```python
+import pyaes
+
+# 16 byte block of plain text
+plaintext = "Hello World!!!!!"
+plaintext_bytes = [ ord(c) for c in plaintext ]
+
+# 32 byte key (256 bit)
+key = "This_key_for_demo_purposes_only!"
+
+# Our AES instance
+aes = pyaes.AES(key)
+
+# Encrypt!
+ciphertext = aes.encrypt(plaintext_bytes)
+
+# [55, 250, 182, 25, 185, 208, 186, 95, 206, 115, 50, 115, 108, 58, 174, 115]
+print repr(ciphertext)
+
+# Decrypt!
+decrypted = aes.decrypt(ciphertext)
+
+# True
+print decrypted == plaintext_bytes
+```
+
+What is a key?
+--------------
+
+This seems to be a point of confusion for many people new to using encryption. You can think of the key as the *"password"*. However, these algorithms require the *"password"* to be a specific length.
+
+With AES, there are three possible key lengths, 16-bytes, 24-bytes or 32-bytes. When you create an AES object, the key size is automatically detected, so it is important to pass in a key of the correct length.
+
+Often, you wish to provide a password of arbitrary length, for example, something easy to remember or write down. In these cases, you must come up with a way to transform the password into a key, of a specific length. A **Password-Based Key Derivation Function** (PBKDF) is an algorithm designed for this exact purpose.
+
+Here is an example, using the popular (possibly obsolete?) *crypt* PBKDF:
+
+```
+# See: https://www.dlitz.net/software/python-pbkdf2/
+import pbkdf2
+
+password = "HelloWorld"
+
+# The crypt PBKDF returns a 48-byte string
+key = pbkdf2.crypt(password)
+
+# A 16-byte, 24-byte and 32-byte key, respectively
+key_16 = key[:16]
+key_24 = key[:24]
+key_32 = key[:32]
+```
+
+The [scrypt](https://github.com/ricmoo/pyscrypt) PBKDF is intentionally slow, to make it more difficult to brute-force guess a password:
+
+```
+# See: https://github.com/ricmoo/pyscrypt
+import pyscrypt
+
+password = "HelloWorld"
+
+# Salt is required, and prevents Rainbow Table attacks
+salt = "SeaSalt"
+
+# N, r, and p are parameters to specify how difficult it should be to
+# generate a key; bigger numbers take longer and more memory
+N = 1024
+r = 1
+p = 1
+
+# A 16-byte, 24-byte and 32-byte key, respectively; the scrypt algorithm takes
+# a 6-th parameter, indicating key length
+key_16 = pyscrypt.hash(password, salt, N, r, p, 16)
+key_24 = pyscrypt.hash(password, salt, N, r, p, 24)
+key_32 = pyscrypt.hash(password, salt, N, r, p, 32)
+```
+
+Another possibility, is to use a hashing function, such as SHA256 to hash the password, but this method may be vulnerable to [Rainbow Attacks](http://en.wikipedia.org/wiki/Rainbow_table), unless you use a [salt](http://en.wikipedia.org/wiki/Salt_(cryptography)).
+
+```python
+import hashlib
+
+password = "HelloWorld"
+
+# The SHA256 hash algorithm returns a 32-byte string
+hashed = hashlib.sha256(password).digest()
+
+# A 16-byte, 24-byte and 32-byte key, respectively
+key_16 = hashed[:16]
+key_24 = hashed[:24]
+key_32 = hashed
+```
+
+
+
+
+Performance
+-----------
+
+There is a test case provided in _/tests/test-aes.py_ which does some basic performance testing (its primary purpose is moreso as a regression test).
+
+Based on that test, in **CPython**, this library is about 30x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 80x slower for CFB; and 300x slower for CTR.
+
+Based on that same test, in **Pypy**, this library is about 4x slower than [PyCrypto](https://www.dlitz.net/software/pycrypto/) for CBC, ECB and OFB; about 12x slower for CFB; and 19x slower for CTR.
+
+The PyCrypto documentation makes reference to the counter call being responsible for the speed problems of the counter (CTR) mode of operation, which is why they use a specially optimized counter. I will investigate this problem further in the future.
+
+
+FAQ
+---
+
+#### Why do this?
+
+The short answer, *why not?*
+
+The longer answer, is for my [pyscrypt](https://github.com/ricmoo/pyscrypt) library. I required a pure-Python AES implementation that supported 256-bit keys with the counter (CTR) mode of operation. After searching, I found several implementations, but all were missing CTR or only supported 128 bit keys. After all the work of learning AES inside and out to implement the library, it was only a marginal amount of extra work to library-ify a more general solution. So, *why not?*
+
+#### How do I get a question I have added?
+
+E-mail me at pyaes@ricmoo.com with any questions, suggestions, comments, et cetera.
+
+
+#### Can I give you my money?
+
+Umm... Ok? :-)
+
+_Bitcoin_  - `18UDs4qV1shu2CgTS2tKojhCtM69kpnWg9`
diff --git a/ambari-common/src/main/python/ambari_pyaes/__init__.py b/ambari-common/src/main/python/ambari_pyaes/__init__.py
new file mode 100644
index 0000000..5712f79
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pyaes/__init__.py
@@ -0,0 +1,53 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+
+# Supported key sizes:
+#   128-bit
+#   192-bit
+#   256-bit
+
+
+# Supported modes of operation:
+#   ECB - Electronic Codebook
+#   CBC - Cipher-Block Chaining
+#   CFB - Cipher Feedback
+#   OFB - Output Feedback
+#   CTR - Counter
+
+# See the README.md for API details and general information.
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+VERSION = [1, 3, 0]
+
+from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
+from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter
+from .blockfeeder import PADDING_NONE, PADDING_DEFAULT
diff --git a/ambari-common/src/main/python/ambari_pyaes/aes.py b/ambari-common/src/main/python/ambari_pyaes/aes.py
new file mode 100644
index 0000000..c6e8bc0
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pyaes/aes.py
@@ -0,0 +1,589 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+
+# Honestly, the best description of the modes of operations are the wonderful
+# diagrams on Wikipedia. They explain in moments what my words could never
+# achieve. Hence the inline documentation here is sparer than I'd prefer.
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+# Supported key sizes:
+#   128-bit
+#   192-bit
+#   256-bit
+
+
+# Supported modes of operation:
+#   ECB - Electronic Codebook
+#   CBC - Cipher-Block Chaining
+#   CFB - Cipher Feedback
+#   OFB - Output Feedback
+#   CTR - Counter
+
+
+# See the README.md for API details and general information.
+
+
+import copy
+import struct
+
+__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
+           "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]
+
+
+def _compact_word(word):
+    return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
+
+def _string_to_bytes(text):
+    return list(ord(c) for c in text)
+
+def _bytes_to_string(binary):
+    return "".join(chr(b) for b in binary)
+
+def _concat_list(a, b):
+    return a + b
+
+
+# Python 3 compatibility
+try:
+    xrange
+except Exception:
+    xrange = range
+
+    # Python 3 supports bytes, which is already an array of integers
+    def _string_to_bytes(text):
+        if isinstance(text, bytes):
+            return text
+        return [ord(c) for c in text]
+
+    # In Python 3, we return bytes
+    def _bytes_to_string(binary):
+        return bytes(binary)
+
+    # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
+    def _concat_list(a, b):
+        return a + bytes(b)
+
+
+# Based *largely* on the Rijndael implementation
+# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
+class AES(object):
+    '''Encapsulates the AES block cipher.
+
+    You generally should not need this. Use the AESModeOfOperation classes
+    below instead.'''
+
+    # Number of rounds by keysize
+    number_of_rounds = {16: 10, 24: 12, 32: 14}
+
+    # Round constant words
+    rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]
+
+    # S-box and Inverse S-box (S is for Substitution)
+    S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x [...]
+    Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x [...]
+
+    # Transformations for encryption
+    T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0 [...]
+    T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0 [...]
+    T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0 [...]
+    T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0 [...]
+
+    # Transformations for decryption
+    T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0 [...]
+    T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0 [...]
+    T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0 [...]
+    T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0 [...]
+
+    # Transformations for decryption key expansion
+    U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0 [...]
+    U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0 [...]
+    U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0 [...]
+    U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0 [...]
+
+    def __init__(self, key):
+
+        if len(key) not in (16, 24, 32):
+            raise ValueError('Invalid key size')
+
+        rounds = self.number_of_rounds[len(key)]
+
+        # Encryption round keys
+        self._Ke = [[0] * 4 for i in xrange(rounds + 1)]
+
+        # Decryption round keys
+        self._Kd = [[0] * 4 for i in xrange(rounds + 1)]
+
+        round_key_count = (rounds + 1) * 4
+        KC = len(key) // 4
+
+        # Convert the key into ints
+        tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ]
+
+        # Copy values into round key arrays
+        for i in xrange(0, KC):
+            self._Ke[i // 4][i % 4] = tk[i]
+            self._Kd[rounds - (i // 4)][i % 4] = tk[i]
+
+        # Key expansion (fips-197 section 5.2)
+        rconpointer = 0
+        t = KC
+        while t < round_key_count:
+
+            tt = tk[KC - 1]
+            tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
+                      (self.S[(tt >>  8) & 0xFF] << 16) ^
+                      (self.S[ tt        & 0xFF] <<  8) ^
+                       self.S[(tt >> 24) & 0xFF]        ^
+                      (self.rcon[rconpointer] << 24))
+            rconpointer += 1
+
+            if KC != 8:
+                for i in xrange(1, KC):
+                    tk[i] ^= tk[i - 1]
+
+            # Key expansion for 256-bit keys is "slightly different" (fips-197)
+            else:
+                for i in xrange(1, KC // 2):
+                    tk[i] ^= tk[i - 1]
+                tt = tk[KC // 2 - 1]
+
+                tk[KC // 2] ^= (self.S[ tt        & 0xFF]        ^
+                               (self.S[(tt >>  8) & 0xFF] <<  8) ^
+                               (self.S[(tt >> 16) & 0xFF] << 16) ^
+                               (self.S[(tt >> 24) & 0xFF] << 24))
+
+                for i in xrange(KC // 2 + 1, KC):
+                    tk[i] ^= tk[i - 1]
+
+            # Copy values into round key arrays
+            j = 0
+            while j < KC and t < round_key_count:
+                self._Ke[t // 4][t % 4] = tk[j]
+                self._Kd[rounds - (t // 4)][t % 4] = tk[j]
+                j += 1
+                t += 1
+
+        # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
+        for r in xrange(1, rounds):
+            for j in xrange(0, 4):
+                tt = self._Kd[r][j]
+                self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
+                                  self.U2[(tt >> 16) & 0xFF] ^
+                                  self.U3[(tt >>  8) & 0xFF] ^
+                                  self.U4[ tt        & 0xFF])
+
+    def encrypt(self, plaintext):
+        'Encrypt a block of plain text using the AES block cipher.'
+
+        if len(plaintext) != 16:
+            raise ValueError('wrong block length')
+
+        rounds = len(self._Ke) - 1
+        (s1, s2, s3) = [1, 2, 3]
+        a = [0, 0, 0, 0]
+
+        # Convert plaintext to (ints ^ key)
+        t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]
+
+        # Apply round transforms
+        for r in xrange(1, rounds):
+            for i in xrange(0, 4):
+                a[i] = (self.T1[(t[ i          ] >> 24) & 0xFF] ^
+                        self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+                        self.T3[(t[(i + s2) % 4] >>  8) & 0xFF] ^
+                        self.T4[ t[(i + s3) % 4]        & 0xFF] ^
+                        self._Ke[r][i])
+            t = copy.copy(a)
+
+        # The last round is special
+        result = [ ]
+        for i in xrange(0, 4):
+            tt = self._Ke[rounds][i]
+            result.append((self.S[(t[ i           ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+            result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+            result.append((self.S[(t[(i + s2) % 4] >>  8) & 0xFF] ^ (tt >>  8)) & 0xFF)
+            result.append((self.S[ t[(i + s3) % 4]        & 0xFF] ^  tt       ) & 0xFF)
+
+        return result
+
+    def decrypt(self, ciphertext):
+        'Decrypt a block of cipher text using the AES block cipher.'
+
+        if len(ciphertext) != 16:
+            raise ValueError('wrong block length')
+
+        rounds = len(self._Kd) - 1
+        (s1, s2, s3) = [3, 2, 1]
+        a = [0, 0, 0, 0]
+
+        # Convert ciphertext to (ints ^ key)
+        t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
+
+        # Apply round transforms
+        for r in xrange(1, rounds):
+            for i in xrange(0, 4):
+                a[i] = (self.T5[(t[ i          ] >> 24) & 0xFF] ^
+                        self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+                        self.T7[(t[(i + s2) % 4] >>  8) & 0xFF] ^
+                        self.T8[ t[(i + s3) % 4]        & 0xFF] ^
+                        self._Kd[r][i])
+            t = copy.copy(a)
+
+        # The last round is special
+        result = [ ]
+        for i in xrange(0, 4):
+            tt = self._Kd[rounds][i]
+            result.append((self.Si[(t[ i           ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+            result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+            result.append((self.Si[(t[(i + s2) % 4] >>  8) & 0xFF] ^ (tt >>  8)) & 0xFF)
+            result.append((self.Si[ t[(i + s3) % 4]        & 0xFF] ^  tt       ) & 0xFF)
+
+        return result
+
+
+class Counter(object):
+    '''A counter object for the Counter (CTR) mode of operation.
+
+       To create a custom counter, you can usually just override the
+       increment method.'''
+
+    def __init__(self, initial_value = 1):
+
+        # Convert the value into an array of bytes long
+        self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ]
+
+    value = property(lambda s: s._counter)
+
+    def increment(self):
+        '''Increment the counter (overflow rolls back to 0).'''
+
+        for i in xrange(len(self._counter) - 1, -1, -1):
+            self._counter[i] += 1
+
+            if self._counter[i] < 256: break
+
+            # Carry the one
+            self._counter[i] = 0
+
+        # Overflow
+        else:
+            self._counter = [ 0 ] * len(self._counter)
+
+
+class AESBlockModeOfOperation(object):
+    '''Super-class for AES modes of operation that require blocks.'''
+    def __init__(self, key):
+        self._aes = AES(key)
+
+    def decrypt(self, ciphertext):
+        raise Exception('not implemented')
+
+    def encrypt(self, plaintext):
+        raise Exception('not implemented')
+
+
+class AESStreamModeOfOperation(AESBlockModeOfOperation):
+    '''Super-class for AES modes of operation that are stream-ciphers.'''
+
+class AESSegmentModeOfOperation(AESStreamModeOfOperation):
+    '''Super-class for AES modes of operation that segment data.'''
+
+    segment_bytes = 16
+
+
+
+class AESModeOfOperationECB(AESBlockModeOfOperation):
+    '''AES Electronic Codebook Mode of Operation.
+
+       o Block-cipher, so data must be padded to 16 byte boundaries
+
+   Security Notes:
+       o This mode is not recommended
+       o Any two identical blocks produce identical encrypted values,
+         exposing data patterns. (See the image of Tux on wikipedia)
+
+   Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''
+
+
+    name = "Electronic Codebook (ECB)"
+
+    def encrypt(self, plaintext):
+        if len(plaintext) != 16:
+            raise ValueError('plaintext block must be 16 bytes')
+
+        plaintext = _string_to_bytes(plaintext)
+        return _bytes_to_string(self._aes.encrypt(plaintext))
+
+    def decrypt(self, ciphertext):
+        if len(ciphertext) != 16:
+            raise ValueError('ciphertext block must be 16 bytes')
+
+        ciphertext = _string_to_bytes(ciphertext)
+        return _bytes_to_string(self._aes.decrypt(ciphertext))
+
+
+
+class AESModeOfOperationCBC(AESBlockModeOfOperation):
+    '''AES Cipher-Block Chaining Mode of Operation.
+
+       o The Initialization Vector (IV)
+       o Block-cipher, so data must be padded to 16 byte boundaries
+       o An incorrect initialization vector will only cause the first
+         block to be corrupt; all other blocks will be intact
+       o A corrupt bit in the cipher text will cause a block to be
+         corrupted, and the next block to be inverted, but all other
+         blocks will be intact.
+
+   Security Notes:
+       o This method (and CTR) ARE recommended.
+
+   Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''
+
+
+    name = "Cipher-Block Chaining (CBC)"
+
+    def __init__(self, key, iv = None):
+        if iv is None:
+            self._last_cipherblock = [ 0 ] * 16
+        elif len(iv) != 16:
+            raise ValueError('initialization vector must be 16 bytes')
+        else:
+            self._last_cipherblock = _string_to_bytes(iv)
+
+        AESBlockModeOfOperation.__init__(self, key)
+
+    def encrypt(self, plaintext):
+        if len(plaintext) != 16:
+            raise ValueError('plaintext block must be 16 bytes')
+
+        plaintext = _string_to_bytes(plaintext)
+        precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
+        self._last_cipherblock = self._aes.encrypt(precipherblock)
+
+        return _bytes_to_string(self._last_cipherblock)
+
+    def decrypt(self, ciphertext):
+        if len(ciphertext) != 16:
+            raise ValueError('ciphertext block must be 16 bytes')
+
+        cipherblock = _string_to_bytes(ciphertext)
+        plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ]
+        self._last_cipherblock = cipherblock
+
+        return _bytes_to_string(plaintext)
+
+
+
+class AESModeOfOperationCFB(AESSegmentModeOfOperation):
+    '''AES Cipher Feedback Mode of Operation.
+
+       o A stream-cipher, so input does not need to be padded to blocks,
+         but does need to be padded to segment_size
+
+    Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''
+
+
+    name = "Cipher Feedback (CFB)"
+
+    def __init__(self, key, iv, segment_size = 1):
+        if segment_size == 0: segment_size = 1
+
+        if iv is None:
+            self._shift_register = [ 0 ] * 16
+        elif len(iv) != 16:
+            raise ValueError('initialization vector must be 16 bytes')
+        else:
+          self._shift_register = _string_to_bytes(iv)
+
+        self._segment_bytes = segment_size
+
+        AESBlockModeOfOperation.__init__(self, key)
+
+    segment_bytes = property(lambda s: s._segment_bytes)
+
+    def encrypt(self, plaintext):
+        if len(plaintext) % self._segment_bytes != 0:
+            raise ValueError('plaintext block must be a multiple of segment_size')
+
+        plaintext = _string_to_bytes(plaintext)
+
+        # Break block into segments
+        encrypted = [ ]
+        for i in xrange(0, len(plaintext), self._segment_bytes):
+            plaintext_segment = plaintext[i: i + self._segment_bytes]
+            xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
+            cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]
+
+            # Shift the top bits out and the ciphertext in
+            self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
+
+            encrypted.extend(cipher_segment)
+
+        return _bytes_to_string(encrypted)
+
+    def decrypt(self, ciphertext):
+        if len(ciphertext) % self._segment_bytes != 0:
+            raise ValueError('ciphertext block must be a multiple of segment_size')
+
+        ciphertext = _string_to_bytes(ciphertext)
+
+        # Break block into segments
+        decrypted = [ ]
+        for i in xrange(0, len(ciphertext), self._segment_bytes):
+            cipher_segment = ciphertext[i: i + self._segment_bytes]
+            xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
+            plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]
+
+            # Shift the top bits out and the ciphertext in
+            self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
+
+            decrypted.extend(plaintext_segment)
+
+        return _bytes_to_string(decrypted)
+
+
+
+class AESModeOfOperationOFB(AESStreamModeOfOperation):
+    '''AES Output Feedback Mode of Operation.
+
+       o A stream-cipher, so input does not need to be padded to blocks,
+         allowing arbitrary length data.
+       o A bit twiddled in the cipher text, twiddles the same bit in the
+         same bit in the plain text, which can be useful for error
+         correction techniques.
+
+    Also see:
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''
+
+
+    name = "Output Feedback (OFB)"
+
+    def __init__(self, key, iv = None):
+        if iv is None:
+            self._last_precipherblock = [ 0 ] * 16
+        elif len(iv) != 16:
+            raise ValueError('initialization vector must be 16 bytes')
+        else:
+          self._last_precipherblock = _string_to_bytes(iv)
+
+        self._remaining_block = [ ]
+
+        AESBlockModeOfOperation.__init__(self, key)
+
+    def encrypt(self, plaintext):
+        encrypted = [ ]
+        for p in _string_to_bytes(plaintext):
+            if len(self._remaining_block) == 0:
+                self._remaining_block = self._aes.encrypt(self._last_precipherblock)
+                self._last_precipherblock = [ ]
+            precipherbyte = self._remaining_block.pop(0)
+            self._last_precipherblock.append(precipherbyte)
+            cipherbyte = p ^ precipherbyte
+            encrypted.append(cipherbyte)
+
+        return _bytes_to_string(encrypted)
+
+    def decrypt(self, ciphertext):
+        # AES-OFB is symetric
+        return self.encrypt(ciphertext)
+
+
+
+class AESModeOfOperationCTR(AESStreamModeOfOperation):
+    '''AES Counter Mode of Operation.
+
+       o A stream-cipher, so input does not need to be padded to blocks,
+         allowing arbitrary length data.
+       o The counter must be the same size as the key size (ie. len(key))
+       o Each block independant of the other, so a corrupt byte will not
+         damage future blocks.
+       o Each block has a uniue counter value associated with it, which
+         contributes to the encrypted value, so no data patterns are
+         leaked.
+       o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
+         Segmented Integer Counter (SIC
+
+   Security Notes:
+       o This method (and CBC) ARE recommended.
+       o Each message block is associated with a counter value which must be
+         unique for ALL messages with the same key. Otherwise security may be
+         compromised.
+
+    Also see:
+
+       o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
+       o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
+         and Appendix B for managing the initial counter'''
+
+
+    name = "Counter (CTR)"
+
+    def __init__(self, key, counter = None):
+        AESBlockModeOfOperation.__init__(self, key)
+
+        if counter is None:
+            counter = Counter()
+
+        self._counter = counter
+        self._remaining_counter = [ ]
+
+    def encrypt(self, plaintext):
+        while len(self._remaining_counter) < len(plaintext):
+            self._remaining_counter += self._aes.encrypt(self._counter.value)
+            self._counter.increment()
+
+        plaintext = _string_to_bytes(plaintext)
+
+        encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ]
+        self._remaining_counter = self._remaining_counter[len(encrypted):]
+
+        return _bytes_to_string(encrypted)
+
+    def decrypt(self, crypttext):
+        # AES-CTR is symetric
+        return self.encrypt(crypttext)
+
+
+# Simple lookup table for each mode
+AESModesOfOperation = dict(
+    ctr = AESModeOfOperationCTR,
+    cbc = AESModeOfOperationCBC,
+    cfb = AESModeOfOperationCFB,
+    ecb = AESModeOfOperationECB,
+    ofb = AESModeOfOperationOFB,
+)
diff --git a/ambari-common/src/main/python/ambari_pyaes/blockfeeder.py b/ambari-common/src/main/python/ambari_pyaes/blockfeeder.py
new file mode 100644
index 0000000..f4113c3
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pyaes/blockfeeder.py
@@ -0,0 +1,227 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
+from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable
+
+
+# First we inject three functions to each of the modes of operations
+#
+#    _can_consume(size)
+#       - Given a size, determine how many bytes could be consumed in
+#         a single call to either the decrypt or encrypt method
+#
+#    _final_encrypt(data, padding = PADDING_DEFAULT)
+#       - call and return encrypt on this (last) chunk of data,
+#         padding as necessary; this will always be at least 16
+#         bytes unless the total incoming input was less than 16
+#         bytes
+#
+#    _final_decrypt(data, padding = PADDING_DEFAULT)
+#       - same as _final_encrypt except for decrypt, for
+#         stripping off padding
+#
+
+PADDING_NONE       = 'none'
+PADDING_DEFAULT    = 'default'
+
+# @TODO: Ciphertext stealing and explicit PKCS#7
+# PADDING_CIPHERTEXT_STEALING
+# PADDING_PKCS7
+
+# ECB and CBC are block-only ciphers
+
+def _block_can_consume(self, size):
+    if size >= 16: return 16
+    return 0
+
+# After padding, we may have more than one block
+def _block_final_encrypt(self, data, padding = PADDING_DEFAULT):
+    if padding == PADDING_DEFAULT:
+        data = append_PKCS7_padding(data)
+
+    elif padding == PADDING_NONE:
+        if len(data) != 16:
+            raise Exception('invalid data length for final block')
+    else:
+        raise Exception('invalid padding option')
+
+    if len(data) == 32:
+        return self.encrypt(data[:16]) + self.encrypt(data[16:])
+
+    return self.encrypt(data)
+
+
+def _block_final_decrypt(self, data, padding = PADDING_DEFAULT):
+    if padding == PADDING_DEFAULT:
+        return strip_PKCS7_padding(self.decrypt(data))
+
+    if padding == PADDING_NONE:
+        if len(data) != 16:
+            raise Exception('invalid data length for final block')
+        return self.decrypt(data)
+
+    raise Exception('invalid padding option')
+
+AESBlockModeOfOperation._can_consume = _block_can_consume
+AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
+AESBlockModeOfOperation._final_decrypt = _block_final_decrypt
+
+
+
+# CFB is a segment cipher
+
+def _segment_can_consume(self, size):
+    return self.segment_bytes * int(size // self.segment_bytes)
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT):
+    if padding != PADDING_DEFAULT:
+        raise Exception('invalid padding option')
+
+    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+    padded = data + to_bufferable(faux_padding)
+    return self.encrypt(padded)[:len(data)]
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT):
+    if padding != PADDING_DEFAULT:
+        raise Exception('invalid padding option')
+
+    faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+    padded = data + to_bufferable(faux_padding)
+    return self.decrypt(padded)[:len(data)]
+
+AESSegmentModeOfOperation._can_consume = _segment_can_consume
+AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
+AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt
+
+
+
+# OFB and CTR are stream ciphers
+
+def _stream_can_consume(self, size):
+    return size
+
+def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT):
+    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
+        raise Exception('invalid padding option')
+
+    return self.encrypt(data)
+
+def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT):
+    if padding not in [PADDING_NONE, PADDING_DEFAULT]:
+        raise Exception('invalid padding option')
+
+    return self.decrypt(data)
+
+AESStreamModeOfOperation._can_consume = _stream_can_consume
+AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
+AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt
+
+
+
+class BlockFeeder(object):
+    '''The super-class for objects to handle chunking a stream of bytes
+       into the appropriate block size for the underlying mode of operation
+       and applying (or stripping) padding, as necessary.'''
+
+    def __init__(self, mode, feed, final, padding = PADDING_DEFAULT):
+        self._mode = mode
+        self._feed = feed
+        self._final = final
+        self._buffer = to_bufferable("")
+        self._padding = padding
+
+    def feed(self, data = None):
+        '''Provide bytes to encrypt (or decrypt), returning any bytes
+           possible from this or any previous calls to feed.
+
+           Call with None or an empty string to flush the mode of
+           operation and return any final bytes; no further calls to
+           feed may be made.'''
+
+        if self._buffer is None:
+            raise ValueError('already finished feeder')
+
+        # Finalize; process the spare bytes we were keeping
+        if not data:
+            result = self._final(self._buffer, self._padding)
+            self._buffer = None
+            return result
+
+        self._buffer += to_bufferable(data)
+
+        # We keep 16 bytes around so we can determine padding
+        result = to_bufferable('')
+        while len(self._buffer) > 16:
+            can_consume = self._mode._can_consume(len(self._buffer) - 16)
+            if can_consume == 0: break
+            result += self._feed(self._buffer[:can_consume])
+            self._buffer = self._buffer[can_consume:]
+
+        return result
+
+
+class Encrypter(BlockFeeder):
+    'Accepts bytes of plaintext and returns encrypted ciphertext.'
+
+    def __init__(self, mode, padding = PADDING_DEFAULT):
+        BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding)
+
+
+class Decrypter(BlockFeeder):
+    'Accepts bytes of ciphertext and returns decrypted plaintext.'
+
+    def __init__(self, mode, padding = PADDING_DEFAULT):
+        BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding)
+
+
+# 8kb blocks
+BLOCK_SIZE = (1 << 13)
+
+def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE):
+    'Uses feeder to read and convert from in_stream and write to out_stream.'
+
+    while True:
+        chunk = in_stream.read(block_size)
+        if not chunk:
+            break
+        converted = feeder.feed(chunk)
+        out_stream.write(converted)
+    converted = feeder.feed()
+    out_stream.write(converted)
+
+
+def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
+    'Encrypts a stream of bytes from in_stream to out_stream using mode.'
+
+    encrypter = Encrypter(mode, padding = padding)
+    _feed_stream(encrypter, in_stream, out_stream, block_size)
+
+
+def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
+    'Decrypts a stream of bytes from in_stream to out_stream using mode.'
+
+    decrypter = Decrypter(mode, padding = padding)
+    _feed_stream(decrypter, in_stream, out_stream, block_size)
diff --git a/ambari-common/src/main/python/ambari_pyaes/util.py b/ambari-common/src/main/python/ambari_pyaes/util.py
new file mode 100644
index 0000000..081a375
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_pyaes/util.py
@@ -0,0 +1,60 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# Why to_bufferable?
+# Python 3 is very different from Python 2.x when it comes to strings of text
+# and strings of bytes; in Python 3, strings of bytes do not exist, instead to
+# represent arbitrary binary data, we must use the "bytes" object. This method
+# ensures the object behaves as we need it to.
+
+def to_bufferable(binary):
+    return binary
+
+def _get_byte(c):
+    return ord(c)
+
+try:
+    xrange
+except:
+
+    def to_bufferable(binary):
+        if isinstance(binary, bytes):
+            return binary
+        return bytes(ord(b) for b in binary)
+
+    def _get_byte(c):
+        return c
+
+def append_PKCS7_padding(data):
+    pad = 16 - (len(data) % 16)
+    return data + to_bufferable(chr(pad) * pad)
+
+def strip_PKCS7_padding(data):
+    if len(data) % 16 != 0:
+        raise ValueError("invalid length")
+
+    pad = _get_byte(data[-1])
+
+    if pad > 16:
+        raise ValueError("invalid padding byte")
+
+    return data[:-pad]
diff --git a/ambari-common/src/main/python/resource_management/core/encryption.py b/ambari-common/src/main/python/resource_management/core/encryption.py
new file mode 100644
index 0000000..956f0b8
--- /dev/null
+++ b/ambari-common/src/main/python/resource_management/core/encryption.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+"""
+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.
+
+Ambari Agent
+
+"""
+import os
+import ambari_pyaes
+from ambari_pbkdf2.pbkdf2 import PBKDF2
+
+def ensure_decrypted(value, encryption_key=None):
+  if is_encrypted(value):
+    return decrypt(encrypted_value(value), agent_encryption_key() if encryption_key is None else encryption_key)
+  else:
+    return value
+
+def decrypt(encrypted_value, encryption_key):
+  salt, iv, data = [each.decode('hex') for each in encrypted_value.decode('hex').split('::')]
+  key = PBKDF2(encryption_key, salt, iterations=65536).read(16)
+  aes = ambari_pyaes.AESModeOfOperationCBC(key, iv=iv)
+  return ambari_pyaes.util.strip_PKCS7_padding(aes.decrypt(data))
+
+def is_encrypted(value):
+  return isinstance(value, basestring) and value.startswith('${enc=aes256_hex, value=') # XXX: ideally it shouldn't be hardcoded but currently only one enc type is supported
+
+def encrypted_value(value):
+  return value.split('value=')[1][:-1]
+
+def agent_encryption_key():
+  if 'AGENT_ENCRYPTION_KEY' not in os.environ:
+    raise RuntimeError('Missing encryption key: AGENT_ENCRYPTION_KEY is not defined at environment.')
+  return os.environ['AGENT_ENCRYPTION_KEY']
diff --git a/ambari-common/src/main/python/resource_management/core/utils.py b/ambari-common/src/main/python/resource_management/core/utils.py
index 53f6b8b..68c6d90 100644
--- a/ambari-common/src/main/python/resource_management/core/utils.py
+++ b/ambari-common/src/main/python/resource_management/core/utils.py
@@ -139,7 +139,7 @@ class PasswordString(unicode):
     self.value = value
     
   def __str__(self):
-    return value
+    return self.value
   
   def __repr__(self):
     return PASSWORDS_HIDE_STRING
diff --git a/ambari-common/src/main/python/resource_management/libraries/script/config_dictionary.py b/ambari-common/src/main/python/resource_management/libraries/script/config_dictionary.py
index a2f7cda..5f66d51 100644
--- a/ambari-common/src/main/python/resource_management/libraries/script/config_dictionary.py
+++ b/ambari-common/src/main/python/resource_management/libraries/script/config_dictionary.py
@@ -18,6 +18,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 '''
 from resource_management.core.exceptions import Fail
+from resource_management.core.encryption import ensure_decrypted
 
 IMMUTABLE_MESSAGE = """Configuration dictionary is immutable!
 
@@ -52,8 +53,9 @@ class ConfigDictionary(dict):
       value = super(ConfigDictionary, self).__getitem__(name)
     except KeyError:
       return UnknownConfiguration(name)
-      
-    
+
+    value = ensure_decrypted(value)
+
     if value == "true":
       value = True
     elif value == "false":
@@ -76,4 +78,4 @@ class UnknownConfiguration():
     """
     Allow [] 
     """
-    return self
\ No newline at end of file
+    return self
diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml
index 53ca716..c410c42 100644
--- a/ambari-server/pom.xml
+++ b/ambari-server/pom.xml
@@ -34,6 +34,8 @@
     <resource_management.install.dir>/usr/lib/ambari-server/lib/resource_management</resource_management.install.dir>
     <jinja.install.dir>/usr/lib/ambari-server/lib/ambari_jinja2</jinja.install.dir>
     <simplejson.install.dir>/usr/lib/ambari-server/lib/ambari_simplejson</simplejson.install.dir>
+    <pbkdf2.install.dir>/usr/lib/ambari-server/lib/ambari_pbkdf2</pbkdf2.install.dir>
+    <pyaes.install.dir>/usr/lib/ambari-server/lib/ambari_pyaes</pyaes.install.dir>
     <swagger.spec.dir>${basedir}/docs/api/generated/</swagger.spec.dir>
     <swagger.generated.resources.dir>${project.build.directory}/generated-sources/swagger/</swagger.generated.resources.dir>
     <ambari-web-dir>${basedir}/../ambari-web/public</ambari-web-dir>
diff --git a/ambari-server/src/main/assemblies/server.xml b/ambari-server/src/main/assemblies/server.xml
index 21524ee..9f5c1aa 100644
--- a/ambari-server/src/main/assemblies/server.xml
+++ b/ambari-server/src/main/assemblies/server.xml
@@ -63,6 +63,14 @@
       <outputDirectory>${simplejson.install.dir}</outputDirectory>
     </fileSet>
     <fileSet>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pbkdf2</directory>
+      <outputDirectory>${pbkdf2.install.dir}</outputDirectory>
+    </fileSet>
+    <fileSet>
+      <directory>${project.basedir}/../ambari-common/src/main/python/ambari_pyaes</directory>
+      <outputDirectory>${pyaes.install.dir}</outputDirectory>
+    </fileSet>
+    <fileSet>
       <fileMode>700</fileMode>
       <directory>src/main/resources/db</directory>
       <outputDirectory>/var/lib/ambari-server/keys/db</outputDirectory>
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/AgentEncryptionKey.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/AgentEncryptionKey.java
new file mode 100644
index 0000000..b392c04
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/AgentEncryptionKey.java
@@ -0,0 +1,88 @@
+/*
+ *
+ *  * 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.agent;
+
+import java.util.Arrays;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.AmbariRuntimeException;
+import org.apache.ambari.server.security.SecurePasswordHelper;
+import org.apache.ambari.server.security.credential.Credential;
+import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.CredentialStoreType;
+
+/**
+ * The encryption key which is used to encrypt sensitive data in agent config updates.
+ * This key is shared with the agent so that it can decrypt the encrypted values.
+ */
+public class AgentEncryptionKey implements Credential {
+  private static final String ALIAS = "agent.encryption.key";
+  private static final SecurePasswordHelper securePasswordHelper = new SecurePasswordHelper();
+  private final char[] key;
+
+  public static AgentEncryptionKey random() {
+    return new AgentEncryptionKey(securePasswordHelper.createSecurePassword().toCharArray());
+  }
+
+  public static AgentEncryptionKey loadFrom(CredentialStoreService credentialStoreService, boolean createIfNotFound) {
+    try {
+      if (!credentialStoreService.containsCredential(null, AgentEncryptionKey.ALIAS)) {
+        if (createIfNotFound) {
+          AgentEncryptionKey encryptionKey = AgentEncryptionKey.random();
+          encryptionKey.saveToCredentialStore(credentialStoreService);
+          return loadKey(credentialStoreService); // load it again because after saving the key is cleared
+        } else {
+          throw new AmbariRuntimeException("AgentEncryptionKey with alias: " + ALIAS + " doesn't exist in credential store.");
+        }
+      } else {
+        return loadKey(credentialStoreService);
+      }
+    } catch (AmbariException e) {
+      throw new AmbariRuntimeException("Cannot load agent encryption key: " + e.getMessage(), e);
+    }
+  }
+
+  private static AgentEncryptionKey loadKey(CredentialStoreService credentialStoreService) throws AmbariException {
+    return new AgentEncryptionKey(credentialStoreService.getCredential(null, ALIAS).toValue());
+  }
+
+  public AgentEncryptionKey(char[] key) {
+    this.key = Arrays.copyOf(key, key.length);
+  }
+
+  @Override
+  public String toString() {
+    return new String(key);
+  }
+
+  @Override
+  public char[] toValue() {
+    return key;
+  }
+
+  public void saveToCredentialStore(CredentialStoreService credentialStoreService) {
+    try {
+      credentialStoreService.setCredential(null, ALIAS, this, CredentialStoreType.PERSISTED);
+    } catch (AmbariException e) {
+      throw new AmbariRuntimeException("Cannot save agent encryption key: " + e.getMessage(), e);
+    }
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
index 1c225a9..063abc7 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/HeartBeatHandler.java
@@ -23,6 +23,8 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Pattern;
 
+import javax.inject.Named;
+
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.HostNotFoundException;
 import org.apache.ambari.server.actionmanager.ActionManager;
@@ -31,20 +33,21 @@ import org.apache.ambari.server.agent.stomp.dto.HostStatusReport;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.events.AgentActionEvent;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
+import org.apache.ambari.server.events.EncryptionKeyUpdateEvent;
 import org.apache.ambari.server.events.HostRegisteredEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.events.publishers.STOMPUpdatePublisher;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ComponentInfo;
-import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
 import org.apache.ambari.server.state.HostState;
 import org.apache.ambari.server.state.ServiceComponent;
 import org.apache.ambari.server.state.ServiceComponentHost;
 import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.state.alert.AlertDefinitionHash;
 import org.apache.ambari.server.state.alert.AlertHelper;
 import org.apache.ambari.server.state.fsm.InvalidStateTransitionException;
 import org.apache.ambari.server.state.host.HostHealthyHeartbeatEvent;
@@ -73,7 +76,7 @@ public class HeartBeatHandler {
 
   private static final Pattern DOT_PATTERN = Pattern.compile("\\.");
   private final Clusters clusterFsm;
-  private final ActionManager actionManager;
+  private final Encryptor<AgentConfigsUpdateEvent> encryptor;
   private HeartbeatMonitor heartbeatMonitor;
   private HeartbeatProcessor heartbeatProcessor;
 
@@ -84,15 +87,6 @@ public class HeartBeatHandler {
   private AmbariMetaInfo ambariMetaInfo;
 
   @Inject
-  private ConfigHelper configHelper;
-
-  @Inject
-  private AlertDefinitionHash alertDefinitionHash;
-
-  @Inject
-  private RecoveryConfigHelper recoveryConfigHelper;
-
-  @Inject
   private STOMPUpdatePublisher STOMPUpdatePublisher;
 
   @Inject
@@ -109,12 +103,12 @@ public class HeartBeatHandler {
   private Map<String, HeartBeatResponse> hostResponses = new ConcurrentHashMap<>();
 
   @Inject
-  public HeartBeatHandler(Clusters fsm, ActionManager am,
+  public HeartBeatHandler(Clusters fsm, ActionManager am, @Named("AgentConfigEncryptor") Encryptor<AgentConfigsUpdateEvent> encryptor,
                           Injector injector) {
-    clusterFsm = fsm;
-    actionManager = am;
-    heartbeatMonitor = new HeartbeatMonitor(fsm, am, 60000, injector);
-    heartbeatProcessor = new HeartbeatProcessor(fsm, am, heartbeatMonitor, injector); //TODO modify to match pattern
+    this.clusterFsm = fsm;
+    this.encryptor = encryptor;
+    this.heartbeatMonitor = new HeartbeatMonitor(fsm, am, 60000, injector);
+    this.heartbeatProcessor = new HeartbeatProcessor(fsm, am, heartbeatMonitor, injector); //TODO modify to match pattern
     injector.injectMembers(this);
   }
 
@@ -361,6 +355,11 @@ public class HeartBeatHandler {
     HostRegisteredEvent event = new HostRegisteredEvent(hostname, hostObject.getHostId());
     ambariEventPublisher.publish(event);
 
+    if (config.shouldEncryptSensitiveData()) {
+      EncryptionKeyUpdateEvent encryptionKeyUpdateEvent = new EncryptionKeyUpdateEvent(encryptor.getEncryptionKey());
+      STOMPUpdatePublisher.publish(encryptionKeyUpdateEvent);
+    }
+
     RegistrationResponse response = new RegistrationResponse();
 
     Long requestId = 0L;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
index 958f36d..66a0809 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentConfigsHolder.java
@@ -16,12 +16,14 @@
  * limitations under the License.
  */
 package org.apache.ambari.server.agent.stomp;
+
 import java.util.List;
 import java.util.stream.Collectors;
 
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.ConfigHelper;
 import org.apache.ambari.server.state.Host;
@@ -33,10 +35,12 @@ import org.slf4j.LoggerFactory;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
+import com.google.inject.name.Named;
 
 @Singleton
 public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEvent> {
   public static final Logger LOG = LoggerFactory.getLogger(AgentConfigsHolder.class);
+  private final Encryptor<AgentConfigsUpdateEvent> encryptor;
 
   @Inject
   private ConfigHelper configHelper;
@@ -45,7 +49,8 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
   private Provider<Clusters> clusters;
 
   @Inject
-  public AgentConfigsHolder(AmbariEventPublisher ambariEventPublisher) {
+  public AgentConfigsHolder(AmbariEventPublisher ambariEventPublisher, @Named("AgentConfigEncryptor") Encryptor<AgentConfigsUpdateEvent> encryptor) {
+    this.encryptor = encryptor;
     ambariEventPublisher.register(this);
   }
 
@@ -59,7 +64,7 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
   }
 
   @Override
-  protected AgentConfigsUpdateEvent handleUpdate(AgentConfigsUpdateEvent current, AgentConfigsUpdateEvent update) throws AmbariException {
+  protected AgentConfigsUpdateEvent handleUpdate(AgentConfigsUpdateEvent current, AgentConfigsUpdateEvent update) {
     return update;
   }
 
@@ -90,7 +95,8 @@ public class AgentConfigsHolder extends AgentHostDataHolder<AgentConfigsUpdateEv
 
   @Override
   protected void regenerateDataIdentifiers(AgentConfigsUpdateEvent data) {
-    data.setHash(getHash(data));
+    data.setHash(getHash(data, encryptor.getEncryptionKey()));
+    encryptor.encryptSensitiveData(data);
     data.setTimestamp(System.currentTimeMillis());
   }
 
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentDataHolder.java b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentDataHolder.java
index abd37d4..a8de1fa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentDataHolder.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/AgentDataHolder.java
@@ -37,7 +37,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
  * @param <T> event with hash to control version
  */
 public abstract class AgentDataHolder<T extends Hashable> {
-  private final String salt = "";
   protected final ReentrantLock updateLock = new ReentrantLock();
   private final static ObjectMapper MAPPER = new ObjectMapper();
   static {
@@ -56,6 +55,10 @@ public abstract class AgentDataHolder<T extends Hashable> {
   }
 
   protected String getHash(T data) {
+    return getHash(data, "");
+  }
+
+  protected String getHash(T data, String salt) {
     String json = null;
     try {
       json = MAPPER.writeValueAsString(data);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
index 2e60deb..403565a 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java
@@ -85,6 +85,7 @@ import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetric
 import org.apache.ambari.server.controller.metrics.timeline.cache.TimelineMetricCacheProvider;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.KerberosChecker;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
 import org.apache.ambari.server.hooks.HookContext;
@@ -115,6 +116,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationHelper;
 import org.apache.ambari.server.security.authorization.internal.InternalAuthenticationInterceptor;
 import org.apache.ambari.server.security.authorization.internal.RunWithInternalSecurityContext;
 import org.apache.ambari.server.security.encryption.AESEncryptionService;
+import org.apache.ambari.server.security.encryption.AgentConfigUpdateEncryptor;
 import org.apache.ambari.server.security.encryption.ConfigPropertiesEncryptor;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
@@ -336,7 +338,13 @@ public class ControllerModule extends AbstractModule {
     bind(CredentialStoreService.class).to(CredentialStoreServiceImpl.class);
     bind(EncryptionService.class).to(AESEncryptionService.class);
     //to support different Encryptor implementation we have to annotate them by their name and use them as @Named injects
-    bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
+    if (configuration.shouldEncryptSensitiveData()) {
+      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).to(AgentConfigUpdateEncryptor.class);
+    } else {
+      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(Encryptor.NONE);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
+    }
 
     bind(Configuration.class).toInstance(configuration);
     bind(OsFamily.class).toInstance(os_family);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/DefaultMessageEmitter.java b/ambari-server/src/main/java/org/apache/ambari/server/events/DefaultMessageEmitter.java
index e9f5c93..e5a7eef 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/DefaultMessageEmitter.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/DefaultMessageEmitter.java
@@ -51,6 +51,7 @@ public class DefaultMessageEmitter extends MessageEmitter {
         put(STOMPEvent.Type.UI_ALERT_DEFINITIONS, "/events/alert_definitions");
         put(STOMPEvent.Type.UPGRADE, "/events/upgrade");
         put(STOMPEvent.Type.AGENT_ACTIONS, "/agent_actions");
+        put(STOMPEvent.Type.ENCRYPTION_KEY_UPDATE, "/events/encryption_key");
   }});
   public static final Set<STOMPEvent.Type> DEFAULT_AGENT_EVENT_TYPES =
       Collections.unmodifiableSet(new HashSet<STOMPEvent.Type>(Arrays.asList(
@@ -60,7 +61,8 @@ public class DefaultMessageEmitter extends MessageEmitter {
         STOMPEvent.Type.AGENT_CONFIGS,
         STOMPEvent.Type.COMMAND,
         STOMPEvent.Type.ALERT_DEFINITIONS,
-        STOMPEvent.Type.AGENT_ACTIONS
+        STOMPEvent.Type.AGENT_ACTIONS,
+        STOMPEvent.Type.ENCRYPTION_KEY_UPDATE
   )));
   public static final Set<STOMPEvent.Type> DEFAULT_API_EVENT_TYPES =
       Collections.unmodifiableSet(new HashSet<STOMPEvent.Type>(Arrays.asList(
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/EncryptionKeyUpdateEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/EncryptionKeyUpdateEvent.java
new file mode 100644
index 0000000..59f3715
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/EncryptionKeyUpdateEvent.java
@@ -0,0 +1,52 @@
+/*
+ *
+ *  * 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.events;
+
+import java.util.Objects;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class EncryptionKeyUpdateEvent extends STOMPEvent {
+  private final String encryptionKey;
+
+  public EncryptionKeyUpdateEvent(String encryptionKey) {
+    super(Type.ENCRYPTION_KEY_UPDATE);
+    this.encryptionKey = encryptionKey;
+  }
+
+  public String getEncryptionKey() {
+    return encryptionKey;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    EncryptionKeyUpdateEvent that = (EncryptionKeyUpdateEvent) o;
+    return Objects.equals(encryptionKey, that.encryptionKey);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(encryptionKey);
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/STOMPEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/STOMPEvent.java
index 15c3b1e..2ff31f5 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/events/STOMPEvent.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/events/STOMPEvent.java
@@ -61,7 +61,8 @@ public abstract class STOMPEvent {
     ALERT_DEFINITIONS("alert_definitions"),
     UPGRADE("events.upgrade"),
     COMMAND("events.commands"),
-    AGENT_ACTIONS("events.agentactions");
+    AGENT_ACTIONS("events.agentactions"),
+    ENCRYPTION_KEY_UPDATE("events.encryption_key_update");
 
     /**
      * Is used to collect info about event appearing frequency.
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
index c92a752..effaddb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptionService.java
@@ -17,6 +17,8 @@
  */
 package org.apache.ambari.server.security.encryption;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.io.UnsupportedEncodingException;
 import java.nio.charset.StandardCharsets;
 
@@ -24,6 +26,7 @@ import javax.inject.Inject;
 
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.utils.TextEncoding;
+import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.Hex;
 
@@ -45,24 +48,28 @@ public class AESEncryptionService implements EncryptionService {
   private Configuration configuration;
 
   @Override
-  public String encrypt(String toBeEncrypted) throws Exception {
+  public String encrypt(String toBeEncrypted) {
     return encrypt(toBeEncrypted, TextEncoding.BASE_64);
   }
 
   @Override
-  public String encrypt(String toBeEncrypted, TextEncoding textEncoding) throws Exception {
+  public String encrypt(String toBeEncrypted, TextEncoding textEncoding) {
     return encrypt(toBeEncrypted, getAmbariMasterKey(), textEncoding);
   }
 
   @Override
-  public String encrypt(String toBeEncrypted, String key) throws Exception {
+  public String encrypt(String toBeEncrypted, String key) {
     return encrypt(toBeEncrypted, key, TextEncoding.BASE_64);
   }
 
   @Override
-  public String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding) throws Exception {
-    final EncryptionResult encryptionResult = getAesEncryptor(key).encrypt(toBeEncrypted);
-    return TextEncoding.BASE_64 == textEncoding ? encodeEncryptionResultBase64(encryptionResult) : encodeEncryptionResultBinHex(encryptionResult);
+  public String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding) {
+    try {
+      final EncryptionResult encryptionResult = getAesEncryptor(key).encrypt(toBeEncrypted);
+      return TextEncoding.BASE_64 == textEncoding ? encodeEncryptionResultBase64(encryptionResult) : encodeEncryptionResultBinHex(encryptionResult);
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
   }
 
   private AESEncryptor getAesEncryptor(String key) {
@@ -75,7 +82,8 @@ public class AESEncryptionService implements EncryptionService {
     return aesEncryptor;
   }
 
-  private final String getAmbariMasterKey() {
+  @Override
+  public final String getAmbariMasterKey() {
     initEnvironmentMasterKeyService();
     return String.valueOf(environmentMasterKeyService.getMasterSecret());
   }
@@ -100,32 +108,38 @@ public class AESEncryptionService implements EncryptionService {
   }
 
   @Override
-  public String decrypt(String toBeDecrypted) throws Exception {
+  public String decrypt(String toBeDecrypted) {
     return decrypt(toBeDecrypted, TextEncoding.BASE_64);
   }
 
   @Override
-  public String decrypt(String toBeDecrypted, TextEncoding textEncoding) throws Exception {
+  public String decrypt(String toBeDecrypted, TextEncoding textEncoding) {
     return decrypt(toBeDecrypted, getAmbariMasterKey(), textEncoding);
   }
 
   @Override
-  public String decrypt(String toBeDecrypted, String key) throws Exception {
+  public String decrypt(String toBeDecrypted, String key) {
     return decrypt(toBeDecrypted, key, TextEncoding.BASE_64);
   }
 
   @Override
-  public String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding) throws Exception {
-    final byte[] decodedValue = TextEncoding.BASE_64 == textEncoding ? Base64.decodeBase64(toBeDecrypted) : Hex.decodeHex(toBeDecrypted.toCharArray());
-    final String decodedText = new String(decodedValue, UTF_8_CHARSET);
-    final String[] decodedParts = decodedText.split(ENCODED_TEXT_FIELD_DELIMITER);
-    final AESEncryptor aes = getAesEncryptor(key);
-    if (TextEncoding.BASE_64 == textEncoding) {
-      return new String(aes.decrypt(Base64.decodeBase64(decodedParts[0]), Base64.decodeBase64(decodedParts[1]), Base64.decodeBase64(decodedParts[2])), UTF_8_CHARSET);
-    } else {
-      return new String(
+  public String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding) {
+    try {
+      final byte[] decodedValue = TextEncoding.BASE_64 == textEncoding ? Base64.decodeBase64(toBeDecrypted) : Hex.decodeHex(toBeDecrypted.toCharArray());
+      final String decodedText = new String(decodedValue, UTF_8_CHARSET);
+      final String[] decodedParts = decodedText.split(ENCODED_TEXT_FIELD_DELIMITER);
+      final AESEncryptor aes = getAesEncryptor(key);
+      if (TextEncoding.BASE_64 == textEncoding) {
+        return new String(aes.decrypt(Base64.decodeBase64(decodedParts[0]), Base64.decodeBase64(decodedParts[1]), Base64.decodeBase64(decodedParts[2])), UTF_8_CHARSET);
+      } else {
+        return new String(
           aes.decrypt(Hex.decodeHex(decodedParts[0].toCharArray()), Hex.decodeHex(decodedParts[1].toCharArray()), Hex.decodeHex(decodedParts[2].toCharArray())),
           UTF_8_CHARSET);
+      }
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    } catch (DecoderException e) {
+      throw new RuntimeException(e);
     }
   }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptor.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptor.java
index ab3953d..f8dc16f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AESEncryptor.java
@@ -17,6 +17,9 @@
  */
 package org.apache.ambari.server.security.encryption;
 
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.GeneralSecurityException;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
@@ -63,21 +66,6 @@ public class AESEncryptor {
     }
   }
 
-  AESEncryptor(SecretKey secret) {
-    try {
-      this.secret = new SecretKeySpec (secret.getEncoded(), "AES");
-
-      ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-      ecipher.init(Cipher.ENCRYPT_MODE, secret);
-
-      dcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
-      byte[] iv = ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
-      dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
-    } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | InvalidParameterSpecException | InvalidKeyException | NoSuchPaddingException e) {
-      e.printStackTrace();
-    }
-  }
-
   public SecretKey getKeyFromPassword(String passPhrase) {
     return getKeyFromPassword(passPhrase, salt);
   }
@@ -96,32 +84,33 @@ public class AESEncryptor {
     return key;
   }
 
-  public EncryptionResult encrypt(String encrypt) throws Exception {
-    byte[] bytes = encrypt.getBytes("UTF8");
-    EncryptionResult atom = encrypt(bytes);
-    return atom;
-  }
-
-  public EncryptionResult encrypt(byte[] plain) throws Exception {
-    EncryptionResult atom = new EncryptionResult(salt, ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(), ecipher.doFinal(plain));
-    return atom;
+  public EncryptionResult encrypt(String encrypt) {
+    try {
+      byte[] bytes = encrypt.getBytes("UTF8");
+      EncryptionResult atom = encrypt(bytes);
+      return atom;
+    } catch (IOException e) {
+      throw new UncheckedIOException(e);
+    }
   }
 
-  public String decrypt(String salt, String iv, String cipher) throws Exception {
-    byte[] decrypted = decrypt(salt.getBytes("UTF8"), iv.getBytes("UTF8"), cipher.getBytes("UTF8"));
-    return new String(decrypted, "UTF8");
+  public EncryptionResult encrypt(byte[] plain) {
+    try {
+      return new EncryptionResult(salt, ecipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(), ecipher.doFinal(plain));
+    } catch (GeneralSecurityException e) {
+      throw new RuntimeException(e);
+    }
   }
 
-  public byte[] decrypt(byte[] salt, byte[] iv, byte[] encrypt) throws Exception {
-    SecretKey tmp = getKeyFromPassword(new String(passPhrase), salt);
-    secret = new SecretKeySpec(tmp.getEncoded(), "AES");
-
-    dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
-    return dcipher.doFinal(encrypt);
-  }
+  public byte[] decrypt(byte[] salt, byte[] iv, byte[] encrypt) {
+    try {
+      SecretKey tmp = getKeyFromPassword(new String(passPhrase), salt);
+      secret = new SecretKeySpec(tmp.getEncoded(), "AES");
 
-  public byte[] decrypt(byte[] encrypt) throws Exception {
-    dcipher.init(Cipher.DECRYPT_MODE, secret);
-    return dcipher.doFinal(encrypt);
+      dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
+      return dcipher.doFinal(encrypt);
+    } catch (GeneralSecurityException e) {
+      throw new RuntimeException(e);
+    }
   }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AgentConfigUpdateEncryptor.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AgentConfigUpdateEncryptor.java
new file mode 100644
index 0000000..a55a7bf
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/AgentConfigUpdateEncryptor.java
@@ -0,0 +1,84 @@
+/*
+ * 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 java.util.Map;
+import java.util.SortedMap;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.AmbariRuntimeException;
+import org.apache.ambari.server.agent.AgentEncryptionKey;
+import org.apache.ambari.server.agent.stomp.dto.ClusterConfigs;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.Singleton;
+
+/**
+ * This encryptor encrypts the sensitive data in config updates sent to ambari-agent.
+ * Decryption happens on the agent side.
+ */
+@Singleton
+public class AgentConfigUpdateEncryptor extends PropertiesEncryptor implements Encryptor<AgentConfigsUpdateEvent> {
+  private final AgentEncryptionKey encryptionKey;
+  private final Provider<Clusters> clusters;
+
+  @Inject
+  public AgentConfigUpdateEncryptor(EncryptionService encryptionService, CredentialStoreService credentialStore, Provider<Clusters> clusters) {
+    super(encryptionService);
+    this.encryptionKey = AgentEncryptionKey.loadFrom(credentialStore, true);
+    this.clusters = clusters;
+  }
+
+  @Override
+  public void encryptSensitiveData(AgentConfigsUpdateEvent event) {
+    for (Map.Entry<String, ClusterConfigs> each : event.getClustersConfigs().entrySet()) {
+      Cluster cluster = getCluster(Long.parseLong(each.getKey()));
+      ClusterConfigs clusterConfigs = each.getValue();
+      for (Map.Entry<String, SortedMap<String, String>> clusterConfig : clusterConfigs.getConfigurations().entrySet()) {
+        encrypt(
+          clusterConfig.getValue(),
+          cluster,
+          clusterConfig.getKey(),
+          encryptionKey.toString());
+      }
+    }
+  }
+
+  @Override
+  public void decryptSensitiveData(AgentConfigsUpdateEvent event) {
+    throw new UnsupportedOperationException("Not supported"); // Decryption happens on the agent side, this is not needed right now
+  }
+
+  private Cluster getCluster(long clusterId) throws AmbariRuntimeException {
+    try {
+      return clusters.get().getCluster(clusterId);
+    } catch (AmbariException e) {
+      throw new AmbariRuntimeException("Cannot load cluster: " + clusterId, e);
+    }
+  }
+
+  @Override
+  public String getEncryptionKey() {
+    return encryptionKey.toString();
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
index 68710f4..e485ce1 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/ConfigPropertiesEncryptor.java
@@ -17,20 +17,9 @@
  */
 package org.apache.ambari.server.security.encryption;
 
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.ambari.server.AmbariException;
-import org.apache.ambari.server.AmbariRuntimeException;
-import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Config;
-import org.apache.ambari.server.state.PropertyInfo.PropertyType;
-import org.apache.ambari.server.state.StackId;
-import org.apache.ambari.server.utils.TextEncoding;
-import org.apache.commons.collections.CollectionUtils;
 
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -41,87 +30,29 @@ import com.google.inject.Singleton;
  */
 
 @Singleton
-public class ConfigPropertiesEncryptor implements Encryptor<Config> {
-
-  private static final String ENCRYPTED_PROPERTY_PREFIX = "${enc=aes256_hex, value=";
-  private static final String ENCRYPTED_PROPERTY_SCHEME = ENCRYPTED_PROPERTY_PREFIX + "%s}";
-
-  private final EncryptionService encryptionService;
-  private final Map<Long, Map<StackId, Map<String, Set<String>>>> clusterPasswordProperties = new ConcurrentHashMap<>(); //Map<clusterId, <Map<stackId, Map<configType, Set<passwordPropertyKeys>>>>;
+public class ConfigPropertiesEncryptor extends PropertiesEncryptor implements Encryptor<Config> {
 
   @Inject
   public ConfigPropertiesEncryptor(EncryptionService encryptionService) {
-    this.encryptionService = encryptionService;
+    super(encryptionService);
   }
 
   @Override
   public void encryptSensitiveData(Config config) {
-    try {
-      final Map<String, String> configProperties = config.getProperties();
-      if (configProperties != null) {
-        final Set<String> passwordProperties = getPasswordProperties(config.getCluster(), config.getType());
-        if (CollectionUtils.isNotEmpty(passwordProperties)) {
-          final Map<String, String> encryptedProperties = new HashMap<>(configProperties);
-          for (Map.Entry<String, String> property : configProperties.entrySet()) {
-            if (passwordProperties.contains(property.getKey()) && !isEncryptedPassword(property.getValue())) {
-              encryptedProperties.put(property.getKey(), encryptAndDecoratePropertyValue(property.getValue()));
-            }
-          }
-          config.setProperties(encryptedProperties);
-        }
-      }
-    } catch (Exception e) {
-      throw new AmbariRuntimeException("Error while encrypting sensitive data", e);
-    }
-  }
-
-  private boolean isEncryptedPassword(String password) {
-    return password != null && password.startsWith(ENCRYPTED_PROPERTY_PREFIX); // assuming previous encryption by this class
-  }
-
-  private Set<String> getPasswordProperties(Cluster cluster, String configType) throws AmbariException {
-    //in case of normal configuration change on the UI - or via the API - the current and desired stacks are equal
-    //in case of an upgrade they are different; in this case we want to get password properties from the desired stack
-    if (cluster.getCurrentStackVersion().equals(cluster.getDesiredStackVersion())) {
-      return getPasswordProperties(cluster, cluster.getCurrentStackVersion(), configType);
-    } else {
-      return getPasswordProperties(cluster, cluster.getDesiredStackVersion(), configType);
-    }
-  }
-
-  private Set<String> getPasswordProperties(Cluster cluster, StackId stackId, String configType) {
-    final long clusterId = cluster.getClusterId();
-    clusterPasswordProperties.computeIfAbsent(clusterId, v -> new ConcurrentHashMap<>()).computeIfAbsent(stackId, v -> new ConcurrentHashMap<>())
-        .computeIfAbsent(configType, v -> cluster.getConfigPropertiesTypes(configType, stackId).getOrDefault(PropertyType.PASSWORD, new HashSet<>()));
-    return clusterPasswordProperties.get(clusterId).get(stackId).getOrDefault(configType, new HashSet<>());
-  }
-
-  private String encryptAndDecoratePropertyValue(String propertyValue) throws Exception {
-    final String encrypted = encryptionService.encrypt(propertyValue, TextEncoding.BIN_HEX);
-    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+    Map<String, String> properties = config.getProperties();
+    encrypt(properties, config.getCluster(), config.getType());
+    config.setProperties(properties);
   }
 
   @Override
   public void decryptSensitiveData(Config config) {
-    final Map<String, String> configProperties = config.getProperties();
-    if (configProperties != null) {
-      final Map<String, String> decryptedProperties = new HashMap<>(configProperties);
-      for (Map.Entry<String, String> property : configProperties.entrySet()) {
-        if (isEncryptedPassword(property.getValue())) {
-          decryptedProperties.put(property.getKey(), decryptProperty(property.getValue()));
-        }
-      }
-      config.setProperties(decryptedProperties);
-    }
+    Map<String, String> properties = config.getProperties();
+    decrypt(properties);
+    config.setProperties(properties);
   }
 
-  private String decryptProperty(String property) {
-    try {
-      // sample value: ${enc=aes256_hex, value=5248...303d}
-      final String encrypted = property.substring(ENCRYPTED_PROPERTY_PREFIX.length(), property.indexOf('}'));
-      return encryptionService.decrypt(encrypted, TextEncoding.BIN_HEX);
-    } catch (Exception e) {
-      throw new AmbariRuntimeException("Error while decrypting property", e);
-    }
+  @Override
+  public String getEncryptionKey() {
+    return encryptionService.getAmbariMasterKey();
   }
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/EncryptionService.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/EncryptionService.java
index 2f4c2f9..d538cbe 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/EncryptionService.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/EncryptionService.java
@@ -48,7 +48,7 @@ public interface EncryptionService {
    * @throws Exception
    *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted) throws Exception;
+  String encrypt(String toBeEncrypted);
 
   /**
    * Encrypts the given text using Ambari's master key found in the environment.
@@ -59,10 +59,8 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which the encrypted text is encoded with
    * @return the String representation of the encrypted text
-   * @throws Exception
-   *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted, TextEncoding textEncoding) throws Exception;
+  String encrypt(String toBeEncrypted, TextEncoding textEncoding);
 
   /**
    * Encrypts the given text using the given key. The returned value will be
@@ -73,10 +71,9 @@ public interface EncryptionService {
    * @param key
    *          the key to be used for encryption
    * @return the String representation of the encrypted text
-   * @throws Exception
    *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted, String key) throws Exception;
+  String encrypt(String toBeEncrypted, String key);
 
   /**
    * Encrypts the given text using the given key.The returned value will be
@@ -89,10 +86,13 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which the encrypted text is encoded with
    * @return the String representation of the encrypted text
-   * @throws Exception
-   *           in case any error happened during the encryption process
    */
-  String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding) throws Exception;
+  String encrypt(String toBeEncrypted, String key, TextEncoding textEncoding);
+
+  /**
+   * @return the default encryption key used by this encryption service
+   */
+  String getAmbariMasterKey();
 
   /**
    * Decrypts the given text (must be encoded with BASE_64 encoding) using
@@ -101,10 +101,8 @@ public interface EncryptionService {
    * @param toBeDecrypted
    *          the text to be decrypted
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted) throws Exception;
+  String decrypt(String toBeDecrypted);
 
   /**
    * Decrypts the given text (must be encoded with the given text encoding) using
@@ -115,10 +113,8 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which <code>toBeDecrypted</code> is encoded with
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted, TextEncoding textEncoding) throws Exception;
+  String decrypt(String toBeDecrypted, TextEncoding textEncoding);
 
   /**
    * Decrypts the given text (must be encoded with BASE_64 encoding) using the
@@ -129,10 +125,8 @@ public interface EncryptionService {
    * @param key
    *          the key to be used for decryption
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted, String key) throws Exception;
+  String decrypt(String toBeDecrypted, String key);
 
   /**
    * Decrypts the given text (must be encoded with the given text encoding) using
@@ -145,9 +139,7 @@ public interface EncryptionService {
    * @param textEncoding
    *          the text encoding which <code>toBeDecrypted</code> is encoded with
    * @return the String representation of the decrypted text
-   * @throws Exception
-   *           in case any error happened during the decryption process
    */
-  public String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding) throws Exception;
+  String decrypt(String toBeDecrypted, String key, TextEncoding textEncoding);
 
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
index 4fad723..3c9998f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/Encryptor.java
@@ -27,7 +27,6 @@ public interface Encryptor<T> {
    * 
    * @param encryptible
    *          to be encrypted
-   * @return the encrypted value
    */
   void encryptSensitiveData(T encryptible);
 
@@ -36,8 +35,20 @@ public interface Encryptor<T> {
    * 
    * @param decryptible
    *          to be decrypted
-   * @return the decrypted value
    */
   void decryptSensitiveData(T decryptible);
 
+  /**
+   * @return the default encryption key used by this encryptor
+   */
+  String getEncryptionKey();
+
+  Encryptor NONE = new Encryptor() {
+    @Override
+    public void encryptSensitiveData(Object data) { }
+    @Override
+    public void decryptSensitiveData(Object decryptible) { }
+    @Override
+    public String getEncryptionKey() { return ""; }
+  };
 }
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/PropertiesEncryptor.java b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/PropertiesEncryptor.java
new file mode 100644
index 0000000..b0c03c9
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/security/encryption/PropertiesEncryptor.java
@@ -0,0 +1,115 @@
+/*
+ *
+ *  * 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 java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.StackId;
+import org.apache.ambari.server.utils.TextEncoding;
+import org.apache.commons.collections.CollectionUtils;
+
+/**
+ * A common base class for various encryptor implementations
+ */
+public class PropertiesEncryptor {
+  private static final String ENCRYPTED_PROPERTY_PREFIX = "${enc=aes256_hex, value=";
+  private static final String ENCRYPTED_PROPERTY_SCHEME = ENCRYPTED_PROPERTY_PREFIX + "%s}";
+  private final Map<Long, Map<StackId, Map<String, Set<String>>>> clusterPasswordProperties = new ConcurrentHashMap<>(); //Map<clusterId, <Map<stackId, Map<configType, Set<passwordPropertyKeys>>>>;
+  protected final EncryptionService encryptionService;
+
+  public PropertiesEncryptor(EncryptionService encryptionService) {
+    this.encryptionService = encryptionService;
+  }
+
+  protected void encrypt(Map<String, String> configProperties, Cluster cluster, String configType, String encryptionKey) {
+    encrypt(configProperties, cluster, configType, value -> encryptAndDecoratePropertyValue(value, encryptionKey));
+  }
+
+  protected void encrypt(Map<String, String> configProperties, Cluster cluster, String configType) {
+    encrypt(configProperties, cluster, configType, value -> encryptAndDecoratePropertyValue(value));
+  }
+
+  protected void encrypt(Map<String, String> configProperties, Cluster cluster, String configType, Function<String,String> encryption) {
+    final Set<String> passwordProperties = getPasswordProperties(cluster, configType);
+    if (CollectionUtils.isNotEmpty(passwordProperties)) {
+      for (Map.Entry<String, String> property : configProperties.entrySet()) {
+        if (shouldEncrypt(property, passwordProperties)) {
+          configProperties.put(property.getKey(), encryption.apply(property.getValue()));
+        }
+      }
+    }
+  }
+
+  private boolean shouldEncrypt(Map.Entry<String, String> property, Set<String> passwordProperties) {
+    return passwordProperties.contains(property.getKey()) && !isEncryptedPassword(property.getValue());
+  }
+
+  private boolean isEncryptedPassword(String password) {
+    return password != null && password.startsWith(ENCRYPTED_PROPERTY_PREFIX); // assuming previous encryption by this class
+  }
+
+  private Set<String> getPasswordProperties(Cluster cluster, String configType) {
+    //in case of normal configuration change on the UI - or via the API - the current and desired stacks are equal
+    //in case of an upgrade they are different; in this case we want to get password properties from the desired stack
+    if (cluster.getCurrentStackVersion().equals(cluster.getDesiredStackVersion())) {
+      return getPasswordProperties(cluster, cluster.getCurrentStackVersion(), configType);
+    } else {
+      return getPasswordProperties(cluster, cluster.getDesiredStackVersion(), configType);
+    }
+  }
+
+  private Set<String> getPasswordProperties(Cluster cluster, StackId stackId, String configType) {
+    final long clusterId = cluster.getClusterId();
+    clusterPasswordProperties.computeIfAbsent(clusterId, v -> new ConcurrentHashMap<>()).computeIfAbsent(stackId, v -> new ConcurrentHashMap<>())
+        .computeIfAbsent(configType, v -> cluster.getConfigPropertiesTypes(configType, stackId).getOrDefault(PropertyInfo.PropertyType.PASSWORD, new HashSet<>()));
+    return clusterPasswordProperties.get(clusterId).get(stackId).getOrDefault(configType, new HashSet<>());
+  }
+
+  private String encryptAndDecoratePropertyValue(String propertyValue) {
+    final String encrypted = encryptionService.encrypt(propertyValue, TextEncoding.BIN_HEX);
+    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+  }
+
+  private String encryptAndDecoratePropertyValue(String propertyValue, String encryptionKey) {
+    final String encrypted = encryptionService.encrypt(propertyValue, encryptionKey, TextEncoding.BIN_HEX);
+    return String.format(ENCRYPTED_PROPERTY_SCHEME, encrypted);
+  }
+
+  protected void decrypt(Map<String, String> configProperties) {
+    for (Map.Entry<String, String> property : configProperties.entrySet()) {
+      if (isEncryptedPassword(property.getValue())) {
+        configProperties.put(property.getKey(), decryptProperty(property.getValue()));
+      }
+    }
+  }
+
+  private String decryptProperty(String property) {
+    // sample value: ${enc=aes256_hex, value=5248...303d}
+    final String encrypted = property.substring(ENCRYPTED_PROPERTY_PREFIX.length(), property.indexOf('}'));
+    return encryptionService.decrypt(encrypted, TextEncoding.BIN_HEX);
+  }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
index e04a6bb..3a0cd6d 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ConfigImpl.java
@@ -27,9 +27,7 @@ import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.locks.ReadWriteLock;
 
 import javax.annotation.Nullable;
-import javax.inject.Named;
 
-import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.events.ClusterConfigChangedEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.logging.LockFactory;
@@ -50,6 +48,7 @@ import com.google.gson.JsonSyntaxException;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
+import com.google.inject.name.Named;
 import com.google.inject.persist.Transactional;
 
 public class ConfigImpl implements Config {
@@ -104,34 +103,32 @@ public class ConfigImpl implements Config {
 
   @AssistedInject
   ConfigImpl(@Assisted Cluster cluster, @Assisted("type") String type,
-      @Assisted("tag") @Nullable String tag,
-      @Assisted Map<String, String> properties,
-      @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
-      ClusterDAO clusterDAO, StackDAO stackDAO,
-      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
-      Configuration serverConfiguration, @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
+             @Assisted("tag") @Nullable String tag,
+             @Assisted Map<String, String> properties,
+             @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
+             ClusterDAO clusterDAO, StackDAO stackDAO,
+             Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
+             @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
     this(cluster.getDesiredStackVersion(), cluster, type, tag, properties, propertiesAttributes,
-        clusterDAO, stackDAO, gson, eventPublisher, lockFactory, serverConfiguration, configPropertiesEncryptor);
+        clusterDAO, stackDAO, gson, eventPublisher, lockFactory, configPropertiesEncryptor);
   }
 
 
   @AssistedInject
   ConfigImpl(@Assisted @Nullable StackId stackId, @Assisted Cluster cluster, @Assisted("type") String type,
-      @Assisted("tag") @Nullable String tag,
-      @Assisted Map<String, String> properties,
-      @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
-      ClusterDAO clusterDAO, StackDAO stackDAO,
-      Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
-      Configuration serverConfiguration, @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
+             @Assisted("tag") @Nullable String tag,
+             @Assisted Map<String, String> properties,
+             @Assisted @Nullable Map<String, Map<String, String>> propertiesAttributes,
+             ClusterDAO clusterDAO, StackDAO stackDAO,
+             Gson gson, AmbariEventPublisher eventPublisher, LockFactory lockFactory,
+             @Named("ConfigPropertiesEncryptor") Encryptor<Config> configPropertiesEncryptor) {
 
     propertyLock = lockFactory.newReadWriteLock(PROPERTY_LOCK_LABEL);
 
     this.cluster = cluster;
     this.type = type;
     this.properties = properties;
-    if (serverConfiguration.shouldEncryptSensitiveData()) {
-      configPropertiesEncryptor.encryptSensitiveData(this);
-    }
+    configPropertiesEncryptor.encryptSensitiveData(this);
 
     // only set this if it's non-null
     this.propertiesAttributes = null == propertiesAttributes ? null
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
index d992d4a..90e5f79 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/AgentResourceTest.java
@@ -40,6 +40,7 @@ import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
@@ -60,7 +61,6 @@ import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
 import org.apache.ambari.server.security.SecurityHelper;
 import org.apache.ambari.server.security.SecurityHelperImpl;
 import org.apache.ambari.server.security.encryption.AESEncryptionService;
-import org.apache.ambari.server.security.encryption.ConfigPropertiesEncryptor;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
 import org.apache.ambari.server.security.encryption.EncryptionService;
@@ -358,7 +358,8 @@ public class AgentResourceTest extends RandomPortJerseyTest {
       bind(KerberosHelper.class).toInstance(createNiceMock(KerberosHelper.class));
       bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
       bind(EncryptionService.class).to(AESEncryptionService.class);
-      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).to(ConfigPropertiesEncryptor.class);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
+      bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(Encryptor.NONE);
     }
 
     private void installDependencies() {
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java
index 83e7303..b42aec6 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatProcessorTest.java
@@ -68,6 +68,7 @@ import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.dao.RepositoryVersionDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Alert;
 import org.apache.ambari.server.state.AlertState;
 import org.apache.ambari.server.state.Cluster;
@@ -1046,7 +1047,7 @@ public class HeartbeatProcessorTest {
     hostObject.setIPv6("ipv6");
     hostObject.setOsType(DummyOsType);
 
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, Encryptor.NONE, injector);
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(DummyHostname1);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
index fd6fc02..a4bc199 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/HeartbeatTestHelper.java
@@ -57,6 +57,7 @@ import org.apache.ambari.server.orm.entities.ResourceEntity;
 import org.apache.ambari.server.orm.entities.ResourceTypeEntity;
 import org.apache.ambari.server.orm.entities.StackEntity;
 import org.apache.ambari.server.security.authorization.ResourceType;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
@@ -126,7 +127,7 @@ public class HeartbeatTestHelper {
 
   public HeartBeatHandler getHeartBeatHandler(ActionManager am)
       throws InvalidStateTransitionException, AmbariException {
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(DummyHostname1);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
index c5bac8b..75bd581 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatHandler.java
@@ -86,6 +86,7 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWriter;
 import org.apache.ambari.server.serveraction.kerberos.KerberosServerAction;
 import org.apache.ambari.server.serveraction.kerberos.stageutils.KerberosKeytabController;
@@ -203,7 +204,6 @@ public class TestHeartbeatHandler {
     Collection<Host> hosts = cluster.getHosts();
     assertEquals(hosts.size(), 1);
 
-    Clusters fsm = clusters;
     Host hostObject = hosts.iterator().next();
     hostObject.setIPv4("ipv4");
     hostObject.setIPv6("ipv6");
@@ -211,7 +211,7 @@ public class TestHeartbeatHandler {
 
     String hostname = hostObject.getHostName();
 
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(hostname);
@@ -361,9 +361,7 @@ public class TestHeartbeatHandler {
       InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -387,6 +385,10 @@ public class TestHeartbeatHandler {
         hostObject.getLastRegistrationTime());
   }
 
+  private HeartBeatHandler createHeartBeatHandler() {
+    return new HeartBeatHandler(clusters, actionManagerTestHelper.getMockActionManager(), Encryptor.NONE, injector);
+  }
+
   @Test
   @Ignore
   // TODO should be rewritten after STOMP protocol implementation.
@@ -409,9 +411,7 @@ public class TestHeartbeatHandler {
     // timestamp invalidation (RecoveryConfigHelper.handleServiceComponentInstalledEvent())
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     handler.start();
 
     Host hostObject = clusters.getHost(DummyHostname1);
@@ -457,9 +457,7 @@ public class TestHeartbeatHandler {
       throws Exception, InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-            injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     Cluster cluster = heartbeatTestHelper.getDummyCluster();
     Service hdfs = addService(cluster, HDFS);
 
@@ -508,9 +506,7 @@ public class TestHeartbeatHandler {
       InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-                                                    injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -541,9 +537,7 @@ public class TestHeartbeatHandler {
 
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -582,9 +576,7 @@ public class TestHeartbeatHandler {
   public void testRegistrationPublicHostname() throws Exception, InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -615,9 +607,7 @@ public class TestHeartbeatHandler {
       InvalidStateTransitionException {
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -644,9 +634,7 @@ public class TestHeartbeatHandler {
 
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-            injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
     hostObject.setIPv4("ipv4");
@@ -678,8 +666,7 @@ public class TestHeartbeatHandler {
     hostObject.setIPv4("ipv4");
     hostObject.setIPv6("ipv6");
 
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     Register reg = new Register();
     HostInfo hi = new HostInfo();
     hi.setHostName(DummyHostname1);
@@ -765,9 +752,7 @@ public class TestHeartbeatHandler {
 
     ActionManager am = actionManagerTestHelper.getMockActionManager();
     replay(am);
-    Clusters fsm = clusters;
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am,
-        injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     handler.setHeartbeatMonitor(hm);
     clusters.addHost(DummyHostname1);
     Host hostObject = clusters.getHost(DummyHostname1);
@@ -969,8 +954,6 @@ public class TestHeartbeatHandler {
 
   @Test
   public void testRecoveryStatusReports() throws Exception {
-    Clusters fsm = clusters;
-
     Cluster cluster = heartbeatTestHelper.getDummyCluster();
     Host hostObject = clusters.getHost(DummyHostname1);
     Service hdfs = addService(cluster, HDFS);
@@ -989,7 +972,7 @@ public class TestHeartbeatHandler {
           add(command);
         }}).anyTimes();
     replay(am);
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
 
     Register reg = new Register();
     HostInfo hi = new HostInfo();
@@ -1070,7 +1053,7 @@ public class TestHeartbeatHandler {
               add(command);
             }}).anyTimes();
     replay(am);
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = createHeartBeatHandler();
     HeartbeatProcessor heartbeatProcessor = handler.getHeartbeatProcessor();
 
     Register reg = new Register();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
index 33ea633..a0ff2a5 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/TestHeartbeatMonitor.java
@@ -41,6 +41,7 @@ import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
 import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
 import org.apache.ambari.server.state.Config;
@@ -118,7 +119,7 @@ public class TestHeartbeatMonitor {
     fsm.addHost(hostname);
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(fsm, am, 10, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(fsm, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname);
     reg.setResponseId(12);
@@ -186,7 +187,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
@@ -305,7 +306,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
@@ -400,8 +401,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am,
-        injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
@@ -476,7 +476,7 @@ public class TestHeartbeatMonitor {
 
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am, 10, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
 
     Register reg = new Register();
     reg.setHostname(hostname1);
@@ -598,7 +598,7 @@ public class TestHeartbeatMonitor {
     ActionManager am = mock(ActionManager.class);
     HeartbeatMonitor hm = new HeartbeatMonitor(clusters, am,
       heartbeatMonitorWakeupIntervalMS, injector);
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, am, Encryptor.NONE, injector);
     Register reg = new Register();
     reg.setHostname(hostname1);
     reg.setResponseId(12);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
index d5ba86f..284c8d0 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/AgentDataHolderTest.java
@@ -25,6 +25,7 @@ import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.MetadataUpdateEvent;
 import org.apache.ambari.server.events.UpdateEventType;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.commons.collections.MapUtils;
 import org.junit.Test;
 
@@ -33,7 +34,7 @@ public class AgentDataHolderTest {
   @Test
   public void testGetHashWithTimestamp() {
     AmbariEventPublisher ambariEventPublisher = createNiceMock(AmbariEventPublisher.class);
-    AgentConfigsHolder agentConfigsHolder = new AgentConfigsHolder(ambariEventPublisher);
+    AgentConfigsHolder agentConfigsHolder = new AgentConfigsHolder(ambariEventPublisher, Encryptor.NONE);
 
     AgentConfigsUpdateEvent event1 = new AgentConfigsUpdateEvent(null, null);
     event1.setHash("01");
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index c229eb2..9721e8b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -277,7 +277,6 @@ public class KerberosHelperTest extends EasyMockSupport {
         bind(RoleCommandOrderProvider.class).to(CachedRoleCommandOrderProvider.class);
         bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
-
         requestStaticInjection(KerberosChecker.class);
       }
     });
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
index 1613ddd..d5e2af3 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/PreUpgradeCheckResourceProviderTest.java
@@ -53,6 +53,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
 import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
@@ -65,6 +66,7 @@ import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
 import org.apache.ambari.server.sample.checks.SampleServiceCheck;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.serveraction.kerberos.KerberosConfigDataFileWriterFactory;
 import org.apache.ambari.server.serveraction.kerberos.KerberosIdentityDataFileWriterFactory;
 import org.apache.ambari.server.stack.StackManagerFactory;
@@ -109,6 +111,8 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
 
 /**
  * PreUpgradeCheckResourceProvider tests.
@@ -322,6 +326,7 @@ public class PreUpgradeCheckResourceProviderTest extends EasyMockSupport {
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
         Provider<EntityManager> entityManagerProvider = createNiceMock(Provider.class);
         bind(EntityManager.class).toProvider(entityManagerProvider);
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
 
         requestStaticInjection(PreUpgradeCheckResourceProvider.class);
       }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
index 729a8d6..f9f92d4 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserAuthorizationResourceProviderTest.java
@@ -48,6 +48,7 @@ import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.spi.SystemException;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
 import org.apache.ambari.server.metadata.CachedRoleCommandOrderProvider;
@@ -67,6 +68,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.security.authorization.Users;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
@@ -93,7 +95,9 @@ import org.springframework.security.crypto.password.PasswordEncoder;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
 
 
 /**
@@ -430,6 +434,7 @@ public class UserAuthorizationResourceProviderTest extends EasyMockSupport {
         bind(HostRoleCommandFactory.class).to(HostRoleCommandFactoryImpl.class);
         bind(PersistedState.class).to(PersistedStateImpl.class);
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       }
     });
   }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
index 288dd8f..1fa27cf 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserResourceProviderTest.java
@@ -58,6 +58,7 @@ import org.apache.ambari.server.controller.spi.Resource;
 import org.apache.ambari.server.controller.spi.ResourceProvider;
 import org.apache.ambari.server.controller.utilities.PredicateBuilder;
 import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
 import org.apache.ambari.server.hooks.HookContext;
 import org.apache.ambari.server.hooks.HookContextFactory;
@@ -90,6 +91,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationException;
 import org.apache.ambari.server.security.authorization.UserAuthenticationType;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
 import org.apache.ambari.server.security.encryption.CredentialStoreServiceImpl;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContextFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
@@ -122,7 +124,9 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
 
 /**
  * UserResourceProvider tests.
@@ -432,6 +436,7 @@ public class UserResourceProviderTest extends EasyMockSupport {
         bind(ResourceDAO.class).toInstance(createMock(ResourceDAO.class));
         bind(PrincipalTypeDAO.class).toInstance(createMock(PrincipalTypeDAO.class));
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       }
     });
   }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/InMemoryDefaultTestModule.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/InMemoryDefaultTestModule.java
index d36a7bc..caf013b 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/InMemoryDefaultTestModule.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/InMemoryDefaultTestModule.java
@@ -26,10 +26,12 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.apache.ambari.server.audit.AuditLogger;
 import org.apache.ambari.server.configuration.Configuration;
 import org.apache.ambari.server.controller.ControllerModule;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.ldap.LdapModule;
 import org.apache.ambari.server.mpack.MpackManager;
 import org.apache.ambari.server.mpack.MpackManagerFactory;
 import org.apache.ambari.server.mpack.MpackManagerMock;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManager;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.StackManagerMock;
@@ -37,7 +39,9 @@ import org.easymock.EasyMock;
 import org.springframework.beans.factory.config.BeanDefinition;
 
 import com.google.inject.AbstractModule;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
+import com.google.inject.name.Names;
 import com.google.inject.util.Modules;
 
 public class InMemoryDefaultTestModule extends AbstractModule {
@@ -132,6 +136,7 @@ public class InMemoryDefaultTestModule extends AbstractModule {
       AuditLogger al = EasyMock.createNiceMock(AuditLogger.class);
       EasyMock.expect(al.isEnabled()).andReturn(false).anyTimes();
       bind(AuditLogger.class).toInstance(al);
+      bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
     } catch (Exception e) {
       throw new RuntimeException(e);
     }
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
index 9281df1..9b6fe4f 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/AbstractPrepareKerberosServerActionTest.java
@@ -52,6 +52,7 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.KerberosHelperImpl;
 import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.hooks.HookContextFactory;
 import org.apache.ambari.server.hooks.HookService;
 import org.apache.ambari.server.metadata.RoleCommandOrderProvider;
@@ -59,6 +60,7 @@ import org.apache.ambari.server.mpack.MpackManagerFactory;
 import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stageplanner.RoleGraphFactory;
 import org.apache.ambari.server.state.Cluster;
@@ -91,6 +93,8 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
 import com.google.inject.Provider;
+import com.google.inject.TypeLiteral;
+import com.google.inject.name.Names;
 
 public class AbstractPrepareKerberosServerActionTest extends EasyMockSupport {
   private static final String KERBEROS_DESCRIPTOR_JSON = "" +
@@ -227,6 +231,7 @@ public class AbstractPrepareKerberosServerActionTest extends EasyMockSupport {
         Provider<EntityManager> entityManagerProvider = createNiceMock(Provider.class);
         bind(EntityManager.class).toProvider(entityManagerProvider);
         bind(MpackManagerFactory.class).toInstance(createNiceMock(MpackManagerFactory.class));
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       }
     });
 
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosActionTest.java
index ec82453..929a124 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosActionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/upgrades/PreconfigureKerberosActionTest.java
@@ -71,6 +71,7 @@ import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.KerberosHelperImpl;
 import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
 import org.apache.ambari.server.hooks.HookContext;
@@ -95,6 +96,7 @@ import org.apache.ambari.server.orm.entities.UpgradeEntity;
 import org.apache.ambari.server.scheduler.ExecutionScheduler;
 import org.apache.ambari.server.scheduler.ExecutionSchedulerImpl;
 import org.apache.ambari.server.security.encryption.CredentialStoreService;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.stack.StackManagerFactory;
 import org.apache.ambari.server.stack.upgrade.Direction;
 import org.apache.ambari.server.stack.upgrade.orchestrate.UpgradeContext;
@@ -152,6 +154,7 @@ import com.google.gson.reflect.TypeToken;
 import com.google.inject.AbstractModule;
 import com.google.inject.Guice;
 import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
 import com.google.inject.assistedinject.FactoryModuleBuilder;
 import com.google.inject.name.Names;
 
@@ -654,6 +657,7 @@ public class PreconfigureKerberosActionTest extends EasyMockSupport {
         bind(HostDAO.class).toInstance(createMock(HostDAO.class));
         bind(ExecutionScheduler.class).to(ExecutionSchedulerImpl.class);
         bind(ActionDBAccessor.class).to(ActionDBAccessorImpl.class);
+        bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
 
         install(new FactoryModuleBuilder().implement(HookContext.class, PostUserCreationHookContext.class)
             .build(HookContextFactory.class));
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java b/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
index 7d4737b..53fe8af 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/state/host/HostTest.java
@@ -45,6 +45,7 @@ import org.apache.ambari.server.orm.OrmTestHelper;
 import org.apache.ambari.server.orm.dao.HostDAO;
 import org.apache.ambari.server.orm.entities.HostEntity;
 import org.apache.ambari.server.orm.entities.HostStateEntity;
+import org.apache.ambari.server.security.encryption.Encryptor;
 import org.apache.ambari.server.state.AgentVersion;
 import org.apache.ambari.server.state.Cluster;
 import org.apache.ambari.server.state.Clusters;
@@ -132,7 +133,7 @@ public class HostTest {
     Injector injector = mock(Injector.class);
     doNothing().when(injector).injectMembers(any());
     when(injector.getInstance(AmbariEventPublisher.class)).thenReturn(mock(AmbariEventPublisher.class));
-    HeartBeatHandler handler = new HeartBeatHandler(clusters, manager, injector);
+    HeartBeatHandler handler = new HeartBeatHandler(clusters, manager, Encryptor.NONE, injector);
     String os = handler.getOsType("RedHat", "6.1");
     Assert.assertEquals("redhat6", os);
     os = handler.getOsType("RedHat", "6");
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java b/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
index 94d64ac..5cd2f70 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/testutils/PartialNiceMockBinder.java
@@ -37,6 +37,7 @@ import org.apache.ambari.server.controller.AbstractRootServiceResponseFactory;
 import org.apache.ambari.server.controller.AmbariManagementController;
 import org.apache.ambari.server.controller.KerberosHelper;
 import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.events.AgentConfigsUpdateEvent;
 import org.apache.ambari.server.events.AmbariEvent;
 import org.apache.ambari.server.hooks.AmbariEventFactory;
 import org.apache.ambari.server.hooks.HookContext;
@@ -213,7 +214,8 @@ public class PartialNiceMockBinder implements Module {
     public Builder addPasswordEncryptorBindings() {
       configurers.add((Binder binder) -> {
         binder.bind(EncryptionService.class).toInstance(easyMockSupport.createNiceMock(EncryptionService.class));
-        binder.bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(easyMockSupport.createNiceMock(Encryptor.class));
+        binder.bind(new TypeLiteral<Encryptor<Config>>() {}).annotatedWith(Names.named("ConfigPropertiesEncryptor")).toInstance(Encryptor.NONE);
+        binder.bind(new TypeLiteral<Encryptor<AgentConfigsUpdateEvent>>() {}).annotatedWith(Names.named("AgentConfigEncryptor")).toInstance(Encryptor.NONE);
       });
       return this;
     }
diff --git a/pom.xml b/pom.xml
index a43f7c5..3e5f4c8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -319,6 +319,9 @@
             <!--Jinja2 library (BSD license)-->
             <exclude>ambari-common/src/main/python/ambari_jinja2/**</exclude>
             <exclude>ambari-common/src/main/python/jinja2/**</exclude>
+            <exclude>ambari-common/src/main/python/ambari_pyaes/**</exclude>
+            <exclude>ambari-common/src/main/python/ambari_pbkdf2/**</exclude>
+            <exclude>**/.metadata_never_index</exclude>
             <!--Simplejson library (MIT license)-->
             <exclude>ambari-common/src/main/python/ambari_simplejson/**</exclude>
             <!--Subprocess32 library (PSF license)-->
diff --git a/start-build-env.sh b/start-build-env.sh
index 2041d97..a7b78a3 100755
--- a/start-build-env.sh
+++ b/start-build-env.sh
@@ -28,7 +28,7 @@ cd "$(dirname "$0")"
 : ${AMBARI_DIR:=$(pwd -P)}
 
 # Maven version
-: ${MAVEN_VERSION:=3.3.9}
+: ${MAVEN_VERSION:=3.6.0}
 
 docker build -t ambari-build-base:${BUILD_OS} dev-support/docker/${BUILD_OS}
 docker build -t ambari-build:${BUILD_OS} --build-arg BUILD_OS="${BUILD_OS}" --build-arg MAVEN_VERSION="${MAVEN_VERSION}" dev-support/docker/common