You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by we...@apache.org on 2022/12/20 12:17:28 UTC

[cloudstack] branch 4.18-new-cipher-aead created (now 1f05d1b116)

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

weizhou pushed a change to branch 4.18-new-cipher-aead
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


      at 1f05d1b116 utils,framework/db: Introduce new database encryption cipher based on AesGcmJce

This branch includes the following new commits:

     new 1f05d1b116 utils,framework/db: Introduce new database encryption cipher based on AesGcmJce

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[cloudstack] 01/01: utils,framework/db: Introduce new database encryption cipher based on AesGcmJce

Posted by we...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

weizhou pushed a commit to branch 4.18-new-cipher-aead
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 1f05d1b1165d66978ea870f04464b6d3a0b1951e
Author: Wei Zhou <we...@apache.org>
AuthorDate: Tue Dec 20 13:15:41 2022 +0100

    utils,framework/db: Introduce new database encryption cipher based on AesGcmJce
    
    including all changes until 2022-12-19
---
 client/conf/db.properties.in                       |   1 +
 debian/rules                                       |   1 +
 .../com/cloud/upgrade/DatabaseUpgradeChecker.java  |  31 +
 .../com/cloud/upgrade/dao/Upgrade450to451.java     |   3 +-
 .../cloud/utils/crypt/DBEncryptionFinderCLI.java   |  32 +
 .../utils/crypt/EncryptionSecretKeyChanger.java    | 717 ++++++++++++++++-----
 .../java/com/cloud/utils/crypt/OVFPropertyTO.java  | 130 ++++
 .../crypt/EncryptionSecretKeyChangerTest.java      |  87 +++
 packaging/centos7/cloud.spec                       |   2 +
 packaging/centos8/cloud.spec                       |   2 +
 packaging/suse15/cloud.spec                        |   2 +
 pom.xml                                            |   2 +
 scripts/storage/secondary/cloud-install-sys-tmplt  |   4 +-
 .../com/cloud/server/ConfigurationServerImpl.java  |   3 +-
 setup/bindir/cloud-migrate-databases.in            | 299 +--------
 setup/bindir/cloud-setup-databases.in              |  29 +-
 setup/bindir/cloud-setup-encryption.in             |   6 +-
 .../files/default/cloud-install-sys-tmplt          |   5 +-
 .../java/com/cloud/usage/UsageManagerImpl.java     |  14 +-
 utils/pom.xml                                      |  38 ++
 .../main/java/com/cloud/utils/EncryptionUtil.java  |  10 +-
 .../java/com/cloud/utils/SerialVersionUID.java     |   1 +
 .../com/cloud/utils/crypt/AeadBase64Encryptor.java |  63 ++
 .../com/cloud/utils/crypt/Base64Encryptor.java     |  27 +
 .../com/cloud/utils/crypt/CloudStackEncryptor.java | 143 ++++
 .../com/cloud/utils/crypt/DBEncryptionUtil.java    |  34 +-
 .../java/com/cloud/utils/crypt/EncryptionCLI.java  |  79 +++
 .../com/cloud/utils/crypt/EncryptionException.java |  34 +
 .../utils/crypt/EncryptionSecretKeyChecker.java    |  53 +-
 .../cloud/utils/crypt/LegacyBase64Encryptor.java   |  58 ++
 .../main/java/com/cloud/utils/db/DbProperties.java |  18 +-
 .../com/cloud/utils/server/ServerProperties.java   |   7 +-
 .../crypt/EncryptionSecretKeyCheckerTest.java      |  58 ++
 33 files changed, 1484 insertions(+), 509 deletions(-)

diff --git a/client/conf/db.properties.in b/client/conf/db.properties.in
index 5ea63e43de..572cfbc1ff 100644
--- a/client/conf/db.properties.in
+++ b/client/conf/db.properties.in
@@ -51,6 +51,7 @@ db.cloud.trustStorePassword=
 # Encryption Settings
 db.cloud.encryption.type=none
 db.cloud.encrypt.secret=
+db.cloud.encryptor.version=
 
 # usage database settings
 db.usage.username=@DBUSER@
diff --git a/debian/rules b/debian/rules
index 287ec4256c..16f10ad804 100755
--- a/debian/rules
+++ b/debian/rules
@@ -135,6 +135,7 @@ override_dh_auto_install:
 	install -D systemvm/dist/* $(DESTDIR)/usr/share/$(PACKAGE)-common/vms/
 	# We need jasypt for cloud-install-sys-tmplt, so this is a nasty hack to get it into the right place
 	install -D agent/target/dependencies/jasypt-1.9.3.jar $(DESTDIR)/usr/share/$(PACKAGE)-common/lib
+	install -D utils/target/cloud-utils-$(VERSION).jar $(DESTDIR)/usr/share/$(PACKAGE)-common/lib/$(PACKAGE)-utils.jar
 
 	# cloudstack-python
 	mkdir -p $(DESTDIR)/usr/share/pyshared
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
index 728b30fc50..efc54c8608 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseUpgradeChecker.java
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Arrays;
 import java.util.Date;
@@ -109,6 +111,7 @@ import com.cloud.upgrade.dao.VersionDaoImpl;
 import com.cloud.upgrade.dao.VersionVO;
 import com.cloud.upgrade.dao.VersionVO.Step;
 import com.cloud.utils.component.SystemIntegrityChecker;
+import com.cloud.utils.crypt.DBEncryptionUtil;
 import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.db.ScriptRunner;
 import com.cloud.utils.db.TransactionLegacy;
@@ -369,6 +372,7 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
             }
 
             try {
+                initializeDatabaseEncryptors();
 
                 final CloudStackVersion dbVersion = CloudStackVersion.parse(_dao.getCurrentVersion());
                 final String currentVersionValue = this.getClass().getPackage().getImplementationVersion();
@@ -403,6 +407,33 @@ public class DatabaseUpgradeChecker implements SystemIntegrityChecker {
         }
     }
 
+    private void initializeDatabaseEncryptors() {
+        TransactionLegacy txn = TransactionLegacy.open("initializeDatabaseEncryptors");
+        txn.start();
+        String errorMessage = "";
+        try {
+            errorMessage = "Unable to get the database connections";
+            Connection conn = txn.getConnection();
+
+            errorMessage = "Unable to get the 'init' value from 'configuration' table in the 'cloud' database";
+            String sql = "SELECT value from configuration WHERE name = 'init'";
+            PreparedStatement pstmt = conn.prepareStatement(sql);
+            ResultSet result = pstmt.executeQuery();
+            if (result.next()) {
+                String init = result.getString(1);
+                s_logger.info("init = " + DBEncryptionUtil.decrypt(init));
+            }
+
+            txn.commit();
+        } catch (CloudRuntimeException | SQLException e) {
+            errorMessage = "Unable to initialize the database encryptors due to " + errorMessage;
+            s_logger.error(errorMessage, e);
+            throw new CloudRuntimeException(errorMessage, e);
+        } finally {
+            txn.close();
+        }
+    }
+
     @VisibleForTesting
     protected static final class NoopDbUpgrade implements DbUpgrade {
 
diff --git a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade450to451.java b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade450to451.java
index 71476e7dd6..015d463347 100644
--- a/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade450to451.java
+++ b/engine/schema/src/main/java/com/cloud/upgrade/dao/Upgrade450to451.java
@@ -27,7 +27,6 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.log4j.Logger;
-import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
 
 import com.cloud.utils.crypt.DBEncryptionUtil;
 import com.cloud.utils.exception.CloudRuntimeException;
@@ -111,7 +110,7 @@ public class Upgrade450to451 implements DbUpgrade {
                 String preSharedKey = resultSet.getString(2);
                 try {
                     preSharedKey = DBEncryptionUtil.decrypt(preSharedKey);
-                } catch (EncryptionOperationNotPossibleException ignored) {
+                } catch (CloudRuntimeException ignored) {
                     s_logger.debug("The ipsec_psk preshared key id=" + rowId + "in remote_access_vpn is not encrypted, encrypting it.");
                 }
                 try (PreparedStatement updateStatement = conn.prepareStatement("UPDATE `cloud`.`remote_access_vpn` SET ipsec_psk=? WHERE id=?");) {
diff --git a/framework/db/src/main/java/com/cloud/utils/crypt/DBEncryptionFinderCLI.java b/framework/db/src/main/java/com/cloud/utils/crypt/DBEncryptionFinderCLI.java
new file mode 100644
index 0000000000..3d05e438f4
--- /dev/null
+++ b/framework/db/src/main/java/com/cloud/utils/crypt/DBEncryptionFinderCLI.java
@@ -0,0 +1,32 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import java.util.Map;
+import java.util.Set;
+
+public class DBEncryptionFinderCLI {
+    public static void main(String[] args) {
+        Map<String, Set<String>> encryptedTableCols = EncryptionSecretKeyChanger.findEncryptedTableColumns();
+        encryptedTableCols.forEach((table, cols) -> {
+            System.out.printf("Table %s has encrypted columns %s\n", table, cols);
+        });
+    }
+}
diff --git a/framework/db/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java b/framework/db/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java
index a958d4ada7..52eb5d9422 100644
--- a/framework/db/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java
+++ b/framework/db/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java
@@ -22,148 +22,313 @@ import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
 import java.sql.Connection;
+import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
 import java.util.Properties;
-
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
-import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
-import org.jasypt.properties.EncryptableProperties;
+import org.apache.commons.lang3.StringUtils;
 
 import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.ReflectUtil;
+import com.cloud.utils.db.Encrypt;
 import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.exception.CloudRuntimeException;
 
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+
+import javax.persistence.Column;
+import javax.persistence.Table;
+
 /*
  * EncryptionSecretKeyChanger updates Management Secret Key / DB Secret Key or both.
  * DB secret key is validated against the key in db.properties
  * db.properties is updated with values encrypted using new MS secret key
+ * server.properties is updated with values encrypted using new MS secret key
  * DB data migrated using new DB secret key
  */
 public class EncryptionSecretKeyChanger {
 
-    private StandardPBEStringEncryptor oldEncryptor = new StandardPBEStringEncryptor();
-    private StandardPBEStringEncryptor newEncryptor = new StandardPBEStringEncryptor();
+    private CloudStackEncryptor oldEncryptor;
+    private CloudStackEncryptor newEncryptor;
     private static final String keyFile = "/etc/cloudstack/management/key";
+    private static final String envNewManagementKey = "CLOUD_SECRET_KEY_NEW";
+    private final Gson gson = new Gson();
+
+    private static final Options options = initializeOptions();
+    private static final HelpFormatter helper = initializeHelper();
+    private static final String cmdLineSyntax = "cloudstack-migrate-databases";
+    private static final int width = 100;
+    private static final String header = "Options:";
+    private static final String footer = "\nExamples:\n" +
+            "  " + cmdLineSyntax + " -m password -d password -n newmgmtkey -v V2 \n" +
+            "       Migrate cloudstack properties (db.properties and server.properties) \n" +
+            "       with new management key and encryptor V2.\n" +
+            "  " + cmdLineSyntax + " -m password -d password -n newmgmtkey -e newdbkey \n" +
+            "       Migrate cloudstack properties and databases with new management key and database secret key.\n" +
+            "  " + cmdLineSyntax + " -m password -d password -n newmgmtkey -e newdbkey -s -v V2 \n" +
+            "       Migrate cloudstack properties with new keys and encryptor V2, but skip database migration.\n" +
+            "  " + cmdLineSyntax + " -m password -d password -l -f \n" +
+            "       Migrate cloudstack properties with new management key (load from $CLOUD_SECRET_KEY_NEW),\n" +
+            "       and migrate database with old db key.\n" +
+            "\nReturn codes:\n" +
+            "  0 - Succeed to change keys and/or migrate databases \n" +
+            "  1 - Fail to parse the command line arguments \n" +
+            "  2 - Fail to validate parameters \n" +
+            "  3 - Fail to migrate database";
+    private static final String oldMSKeyOption = "oldMSKey";
+    private static final String oldDBKeyOption = "oldDBKey";
+    private static final String newMSKeyOption = "newMSKey";
+    private static final String newDBKeyOption = "newDBKey";
+    private static final String encryptorVersionOption = "version";
+    private static final String loadNewMsKeyFromEnvFlag = "load-new-management-key-from-env";
+    private static final String forceDatabaseMigrationFlag = "force-database-migration";
+    private static final String skipDatabaseMigrationFlag = "skip-database-migration";
+    private static final String helpFlag = "help";
 
     public static void main(String[] args) {
-        List<String> argsList = Arrays.asList(args);
-        Iterator<String> iter = argsList.iterator();
-        String oldMSKey = null;
-        String oldDBKey = null;
-        String newMSKey = null;
-        String newDBKey = null;
+        if (args.length == 0 || StringUtils.equalsAny(args[0], "-h", "--help")) {
+            helper.printHelp(width, cmdLineSyntax, header, options, footer, true);
+            System.exit(0);
+        }
+
+        CommandLine cmdLine = null;
+        CommandLineParser parser = new DefaultParser();
+        try {
+            cmdLine = parser.parse(options, args);
+        } catch (ParseException e) {
+            System.out.println(e.getMessage());
+            helper.printHelp(width, cmdLineSyntax, header, options, footer, true);
+            System.exit(1);
+        }
+
+        String oldMSKey = cmdLine.getOptionValue(oldMSKeyOption);
+        String oldDBKey = cmdLine.getOptionValue(oldDBKeyOption);
+        String newMSKey = cmdLine.getOptionValue(newMSKeyOption);
+        String newDBKey = cmdLine.getOptionValue(newDBKeyOption);
+        String newEncryptorVersion = cmdLine.getOptionValue(encryptorVersionOption);
+        boolean loadNewMsKeyFromEnv = cmdLine.hasOption(loadNewMsKeyFromEnvFlag);
+        boolean forced = cmdLine.hasOption(forceDatabaseMigrationFlag);
+        boolean skipped = cmdLine.hasOption(skipDatabaseMigrationFlag);
+
+        if (!validateParameters(oldMSKey, oldDBKey, newMSKey, newDBKey, newEncryptorVersion, loadNewMsKeyFromEnv)) {
+            helper.printHelp(width, cmdLineSyntax, header, options, footer, true);
+            System.exit(2);
+        }
+
+        System.out.println("Started database migration at " + new Date());
+        if (!migrateEverything(oldMSKey, oldDBKey, newMSKey, newDBKey, newEncryptorVersion, loadNewMsKeyFromEnv, forced, skipped)) {
+            System.out.println("Got error during database migration at " + new Date());
+            System.exit(3);
+        }
+        System.out.println("Finished database migration at " + new Date());
+    }
 
-        //Parse command-line args
-        while (iter.hasNext()) {
-            String arg = iter.next();
-            // Old MS Key
-            if (arg.equals("-m")) {
-                oldMSKey = iter.next();
+    private static Options initializeOptions() {
+        Options options = new Options();
+
+        Option oldMSKey = Option.builder("m").longOpt(oldMSKeyOption).argName(oldMSKeyOption).required(true).hasArg().desc("(required) Current Mgmt Secret Key").build();
+        Option oldDBKey = Option.builder("d").longOpt(oldDBKeyOption).argName(oldDBKeyOption).required(true).hasArg().desc("(required) Current DB Secret Key").build();
+        Option newMSKey = Option.builder("n").longOpt(newMSKeyOption).argName(newMSKeyOption).required(false).hasArg().desc("New Mgmt Secret Key").build();
+        Option newDBKey = Option.builder("e").longOpt(newDBKeyOption).argName(newDBKeyOption).required(false).hasArg().desc("New DB Secret Key").build();
+        Option encryptorVersion = Option.builder("v").longOpt(encryptorVersionOption).argName(encryptorVersionOption).required(false).hasArg().desc("New DB Encryptor Version. Options are V1, V2.").build();
+
+        Option loadNewMsKeyFromEnv = Option.builder("l").longOpt(loadNewMsKeyFromEnvFlag).desc("Load new management key from environment variable " + envNewManagementKey).build();
+        Option forceDatabaseMigration = Option.builder("f").longOpt(forceDatabaseMigrationFlag).desc("Force database migration even if DB Secret key is not changed").build();
+        Option skipDatabaseMigration = Option.builder("s").longOpt(skipDatabaseMigrationFlag).desc("Skip database migration even if DB Secret key is changed").build();
+        Option help = Option.builder("h").longOpt(helpFlag).desc("Show help message").build();
+
+        options.addOption(oldMSKey);
+        options.addOption(oldDBKey);
+        options.addOption(newMSKey);
+        options.addOption(newDBKey);
+        options.addOption(encryptorVersion);
+        options.addOption(loadNewMsKeyFromEnv);
+        options.addOption(forceDatabaseMigration);
+        options.addOption(skipDatabaseMigration);
+        options.addOption(help);
+
+        return options;
+    }
+
+    private static HelpFormatter initializeHelper() {
+        HelpFormatter helper = new HelpFormatter();
+
+        helper.setOptionComparator((o1, o2) -> {
+            if (o1.isRequired() && !o2.isRequired()) {
+                return -1;
             }
-            // Old DB Key
-            if (arg.equals("-d")) {
-                oldDBKey = iter.next();
+            if (!o1.isRequired() && o2.isRequired()) {
+                return 1;
             }
-            // New MS Key
-            if (arg.equals("-n")) {
-                newMSKey = iter.next();
+            if (o1.hasArg() && !o2.hasArg()) {
+                return -1;
             }
-            // New DB Key
-            if (arg.equals("-e")) {
-                newDBKey = iter.next();
+            if (!o1.hasArg() && o2.hasArg()) {
+                return 1;
             }
-        }
+            return o1.getOpt().compareTo(o2.getOpt());
+        });
+
+        return helper;
+    }
+
+    private static boolean validateParameters(String oldMSKey, String oldDBKey, String newMSKey, String newDBKey,
+                                              String newEncryptorVersion, boolean loadNewMsKeyFromEnv) {
 
         if (oldMSKey == null || oldDBKey == null) {
-            System.out.println("Existing MS secret key or DB secret key is not provided");
-            usage();
-            return;
+            System.out.println("Existing Management secret key or DB secret key is not provided");
+            return false;
+        }
+
+        if (loadNewMsKeyFromEnv) {
+            if (StringUtils.isNotEmpty(newMSKey)) {
+                System.out.println("The new management key has already been set. Please check if it is set twice.");
+                return false;
+            }
+            newMSKey = System.getenv(envNewManagementKey);
+            if (StringUtils.isEmpty(newMSKey)) {
+                System.out.println("Environment variable " + envNewManagementKey + " is not set or empty");
+                return false;
+            }
         }
 
         if (newMSKey == null && newDBKey == null) {
-            System.out.println("New MS secret key and DB secret are both not provided");
-            usage();
-            return;
+            System.out.println("New Management secret key and DB secret are both not provided");
+            return false;
+        }
+
+        if (newEncryptorVersion != null) {
+            try {
+                CloudStackEncryptor.EncryptorVersion.fromString(newEncryptorVersion);
+            } catch (CloudRuntimeException ex) {
+                System.out.println(ex.getMessage());
+                return false;
+            }
         }
 
+        return true;
+    }
+
+    private static boolean migrateEverything(String oldMSKey, String oldDBKey, String newMSKey, String newDBKey,
+                                             String newEncryptorVersion, boolean loadNewMsKeyFromEnv,
+                                             boolean forced, boolean skipped) {
+
         final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
-        final Properties dbProps;
+        final Properties dbProps = new Properties();
         EncryptionSecretKeyChanger keyChanger = new EncryptionSecretKeyChanger();
-        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
-        keyChanger.initEncryptor(encryptor, oldMSKey);
-        dbProps = new EncryptableProperties(encryptor);
         PropertiesConfiguration backupDBProps = null;
 
         System.out.println("Parsing db.properties file");
-        try(FileInputStream db_prop_fstream = new FileInputStream(dbPropsFile);) {
+        try(FileInputStream db_prop_fstream = new FileInputStream(dbPropsFile)) {
             dbProps.load(db_prop_fstream);
             backupDBProps = new PropertiesConfiguration(dbPropsFile);
         } catch (FileNotFoundException e) {
             System.out.println("db.properties file not found while reading DB secret key" + e.getMessage());
+            return false;
         } catch (IOException e) {
             System.out.println("Error while reading DB secret key from db.properties" + e.getMessage());
+            return false;
         } catch (ConfigurationException e) {
             e.printStackTrace();
+            return false;
         }
 
-        String dbSecretKey = null;
         try {
-            dbSecretKey = dbProps.getProperty("db.cloud.encrypt.secret");
-        } catch (EncryptionOperationNotPossibleException e) {
-            System.out.println("Failed to decrypt existing DB secret key from db.properties. " + e.getMessage());
-            return;
+            EncryptionSecretKeyChecker.initEncryptor(oldMSKey);
+            EncryptionSecretKeyChecker.decryptAnyProperties(dbProps);
+        } catch (CloudRuntimeException e) {
+            System.out.println("Error: Incorrect Management Secret Key");
+            return false;
         }
+        String dbSecretKey = dbProps.getProperty("db.cloud.encrypt.secret");
 
         if (!oldDBKey.equals(dbSecretKey)) {
-            System.out.println("Incorrect MS Secret Key or DB Secret Key");
-            return;
+            System.out.println("Error: Incorrect DB Secret Key");
+            return false;
         }
 
-        System.out.println("Secret key provided matched the key in db.properties");
+        System.out.println("DB Secret key provided matched the key in db.properties");
         final String encryptionType = dbProps.getProperty("db.cloud.encryption.type");
+        final String oldEncryptorVersion = dbProps.getProperty("db.cloud.encryptor.version");
 
+        // validate old and new encryptor versions
+        try {
+            CloudStackEncryptor.EncryptorVersion.fromString(oldEncryptorVersion);
+            CloudStackEncryptor.EncryptorVersion.fromString(newEncryptorVersion);
+        } catch (CloudRuntimeException ex) {
+            System.out.println(ex.getMessage());
+            return false;
+        }
+
+        if (loadNewMsKeyFromEnv) {
+            newMSKey = System.getenv(envNewManagementKey);
+        }
         if (newMSKey == null) {
-            System.out.println("No change in MS Key. Skipping migrating db.properties");
+            newMSKey = oldMSKey;
+            System.out.println("New Management Secret Key is not provided. Skipping migrating db.properties");
         } else {
-            if (!keyChanger.migrateProperties(dbPropsFile, dbProps, newMSKey, newDBKey)) {
+            if (newEncryptorVersion == null && oldEncryptorVersion != null) {
+                newEncryptorVersion = oldEncryptorVersion;
+            }
+            if (!keyChanger.migrateProperties(dbPropsFile, dbProps, newMSKey, (newDBKey != null ? newDBKey : oldDBKey), newEncryptorVersion)) {
                 System.out.println("Failed to update db.properties");
-                return;
-            } else {
-                //db.properties updated successfully
-                if (encryptionType.equals("file")) {
-                    //update key file with new MS key
-                    try (FileWriter fwriter = new FileWriter(keyFile);
-                         BufferedWriter bwriter = new BufferedWriter(fwriter);)
-                    {
-                        bwriter.write(newMSKey);
-                    } catch (IOException e) {
-                        System.out.println(String.format("Please update the file %s manually. Failed to write new secret to file with error %s", keyFile, e.getMessage()));
-                    }
+                return false;
+            }
+            if (!keyChanger.migrateServerProperties(newMSKey)) {
+                System.out.println("Failed to update server.properties");
+                return false;
+            }
+            //db.properties updated successfully
+            if (encryptionType.equals("file")) {
+                //update key file with new MS key
+                try (FileWriter fwriter = new FileWriter(keyFile);
+                     BufferedWriter bwriter = new BufferedWriter(fwriter))
+                {
+                    bwriter.write(newMSKey);
+                } catch (IOException e) {
+                    System.out.printf("Please update the file %s manually. Failed to write new secret to file with error %s%n", keyFile, e.getMessage());
+                    return false;
                 }
             }
         }
 
         boolean success = false;
-        if (newDBKey == null || newDBKey.equals(oldDBKey)) {
+        if ((newDBKey == null || newDBKey.equals(oldDBKey)) && !forced) {
             System.out.println("No change in DB Secret Key. Skipping Data Migration");
+            return true;
+        } else if (skipped) {
+            System.out.println("Skipping Data Migration as '-s' or '--skip-database-migration' is passed");
+            return true;
         } else {
-            EncryptionSecretKeyChecker.initEncryptorForMigration(oldMSKey);
+            EncryptionSecretKeyChecker.initEncryptor(newMSKey);
             try {
-                success = keyChanger.migrateData(oldDBKey, newDBKey);
+                success = keyChanger.migrateData(oldDBKey, newDBKey != null ? newDBKey : oldDBKey, oldEncryptorVersion,
+                        newEncryptorVersion);
             } catch (Exception e) {
                 System.out.println("Error during data migration");
                 e.printStackTrace();
-                success = false;
             }
         }
 
@@ -180,35 +345,87 @@ public class EncryptionSecretKeyChanger {
             if (encryptionType.equals("file")) {
                 //revert secret key in file
                 try (FileWriter fwriter = new FileWriter(keyFile);
-                     BufferedWriter bwriter = new BufferedWriter(fwriter);)
+                     BufferedWriter bwriter = new BufferedWriter(fwriter))
                 {
                     bwriter.write(oldMSKey);
                 } catch (IOException e) {
                     System.out.println("Failed to revert to old secret to file. Please update the file manually");
                 }
             }
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean migrateServerProperties(String newMSKey) {
+        System.out.println("Migrating server.properties..");
+        final File serverPropsFile = PropertiesUtil.findConfigFile("server.properties");
+        final Properties serverProps = new Properties();
+        PropertiesConfiguration newServerProps;
+
+        try(FileInputStream server_prop_fstream = new FileInputStream(serverPropsFile)) {
+            serverProps.load(server_prop_fstream);
+            newServerProps = new PropertiesConfiguration(serverPropsFile);
+        } catch (FileNotFoundException e) {
+            System.out.println("server.properties file not found: " + e.getMessage());
+            return false;
+        } catch (IOException e) {
+            System.out.println("Error while reading server.properties: " + e.getMessage());
+            return false;
+        } catch (ConfigurationException e) {
+            e.printStackTrace();
+            return false;
+        }
+
+        try {
+            EncryptionSecretKeyChecker.decryptAnyProperties(serverProps);
+        } catch (CloudRuntimeException e) {
+            System.out.println(e.getMessage());
+            return false;
+        }
+
+        CloudStackEncryptor msEncryptor = new CloudStackEncryptor(newMSKey, null, getClass());
+
+        try {
+            String encryptionType = serverProps.getProperty("password.encryption.type");
+            if (StringUtils.isEmpty(encryptionType) || encryptionType.equalsIgnoreCase("none")) {
+                System.out.println("Skipping server.properties as password.encryption.type is " + encryptionType);
+                return true;
+            }
+            String keystorePassword = serverProps.getProperty("https.keystore.password");
+            if (StringUtils.isNotEmpty(keystorePassword)) {
+                newServerProps.setProperty("https.keystore.password", "ENC(" + msEncryptor.encrypt(keystorePassword) + ")");
+            }
+            newServerProps.save(serverPropsFile.getAbsolutePath());
+        } catch (Exception e) {
+            e.printStackTrace();
+            return false;
         }
+        System.out.println("Migrating server.properties Done.");
+        return true;
     }
 
-    private boolean migrateProperties(File dbPropsFile, Properties dbProps, String newMSKey, String newDBKey) {
+    private boolean migrateProperties(File dbPropsFile, Properties dbProps, String newMSKey, String newDBKey, String newEncryptorVersion) {
         System.out.println("Migrating db.properties..");
-        StandardPBEStringEncryptor msEncryptor = new StandardPBEStringEncryptor();
-        ;
-        initEncryptor(msEncryptor, newMSKey);
+        CloudStackEncryptor msEncryptor = new CloudStackEncryptor(newMSKey, null, getClass());
 
         try {
             PropertiesConfiguration newDBProps = new PropertiesConfiguration(dbPropsFile);
-            if (newDBKey != null && !newDBKey.isEmpty()) {
+            if (StringUtils.isNotEmpty(newDBKey)) {
                 newDBProps.setProperty("db.cloud.encrypt.secret", "ENC(" + msEncryptor.encrypt(newDBKey) + ")");
             }
             String prop = dbProps.getProperty("db.cloud.password");
-            if (prop != null && !prop.isEmpty()) {
+            if (StringUtils.isNotEmpty(prop)) {
                 newDBProps.setProperty("db.cloud.password", "ENC(" + msEncryptor.encrypt(prop) + ")");
             }
             prop = dbProps.getProperty("db.usage.password");
-            if (prop != null && !prop.isEmpty()) {
+            if (StringUtils.isNotEmpty(prop)) {
                 newDBProps.setProperty("db.usage.password", "ENC(" + msEncryptor.encrypt(prop) + ")");
             }
+            if (newEncryptorVersion != null) {
+                newDBProps.setProperty("db.cloud.encryptor.version", newEncryptorVersion);
+            }
             newDBProps.save(dbPropsFile.getAbsolutePath());
         } catch (Exception e) {
             e.printStackTrace();
@@ -218,10 +435,10 @@ public class EncryptionSecretKeyChanger {
         return true;
     }
 
-    private boolean migrateData(String oldDBKey, String newDBKey) {
+    private boolean migrateData(String oldDBKey, String newDBKey, String oldEncryptorVersion, String newEncryptorVersion) throws SQLException {
         System.out.println("Begin Data migration");
-        initEncryptor(oldEncryptor, oldDBKey);
-        initEncryptor(newEncryptor, newDBKey);
+        oldEncryptor = new CloudStackEncryptor(oldDBKey, oldEncryptorVersion, getClass());
+        newEncryptor = new CloudStackEncryptor(newDBKey, newEncryptorVersion, getClass());
         System.out.println("Initialised Encryptors");
 
         TransactionLegacy txn = TransactionLegacy.open("Migrate");
@@ -231,13 +448,28 @@ public class EncryptionSecretKeyChanger {
             try {
                 conn = txn.getConnection();
             } catch (SQLException e) {
+                System.out.println("Unable to migrate encrypted data in the database due to: " + e.getMessage());
                 throw new CloudRuntimeException("Unable to migrate encrypted data in the database", e);
             }
 
+            // migrate values in configuration
             migrateConfigValues(conn);
+
+            // migrate resource details values
             migrateHostDetails(conn);
-            migrateVNCPassword(conn);
-            migrateUserCredentials(conn);
+            migrateClusterDetails(conn);
+            migrateImageStoreDetails(conn);
+            migrateStoragePoolDetails(conn);
+            migrateScaleIOStoragePoolDetails(conn);
+            migrateUserVmDetails(conn);
+
+            // migrate other encrypted fields
+            migrateTemplateDeployAsIsDetails(conn);
+            migrateImageStoreUrlForCifs(conn);
+            migrateStoragePoolPathForSMB(conn);
+
+            // migrate columns with annotation @Encrypt
+            migrateEncryptedTableColumns(conn);
 
             txn.commit();
         } finally {
@@ -247,125 +479,300 @@ public class EncryptionSecretKeyChanger {
         return true;
     }
 
-    private void initEncryptor(StandardPBEStringEncryptor encryptor, String secretKey) {
-        encryptor.setAlgorithm("PBEWithMD5AndDES");
-        SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig();
-        stringConfig.setPassword(secretKey);
-        encryptor.setConfig(stringConfig);
-    }
-
-    private String migrateValue(String value) {
-        if (value == null || value.isEmpty()) {
+    protected String migrateValue(String value) {
+        if (StringUtils.isEmpty(value)) {
             return value;
         }
         String decryptVal = oldEncryptor.decrypt(value);
         return newEncryptor.encrypt(decryptVal);
     }
 
+    protected String migrateUrlOrPath(String urlOrPath) {
+        if (StringUtils.isEmpty(urlOrPath)) {
+            return urlOrPath;
+        }
+        String[] properties = urlOrPath.split("&");
+        for (String property : properties) {
+            if (property.startsWith("password=")) {
+                String password = property.substring(property.indexOf("=") + 1);
+                password = migrateValue(password);
+                return urlOrPath.replaceAll(property, "password=" + password);
+            }
+        }
+        return urlOrPath;
+    }
+
     private void migrateConfigValues(Connection conn) {
         System.out.println("Begin migrate config values");
-        try(PreparedStatement select_pstmt = conn.prepareStatement("select name, value from configuration where category in ('Hidden', 'Secure')");
-            ResultSet rs = select_pstmt.executeQuery();
-            PreparedStatement update_pstmt = conn.prepareStatement("update configuration set value=? where name=?");
+
+        String tableName = "configuration";
+        String selectSql = "SELECT name, value FROM configuration WHERE category IN ('Hidden', 'Secure')";
+        String updateSql = "UPDATE configuration SET value=? WHERE name=?";
+        migrateValueAndUpdateDatabaseByName(conn, tableName, selectSql, updateSql);
+
+        System.out.println("End migrate config values");
+    }
+
+
+    private void migrateValueAndUpdateDatabaseById(Connection conn, String tableName, String selectSql, String updateSql, boolean isUrlOrPath) {
+        try( PreparedStatement select_pstmt = conn.prepareStatement(selectSql);
+             ResultSet rs = select_pstmt.executeQuery();
+             PreparedStatement update_pstmt = conn.prepareStatement(updateSql)
         ) {
             while (rs.next()) {
-                String name = rs.getString(1);
+                long id = rs.getLong(1);
                 String value = rs.getString(2);
-                if (value == null || value.isEmpty()) {
+                if (StringUtils.isEmpty(value)) {
                     continue;
                 }
-                String encryptedValue = migrateValue(value);
-                update_pstmt.setBytes(1, encryptedValue.getBytes("UTF-8"));
-                update_pstmt.setString(2, name);
+                String encryptedValue = isUrlOrPath ? migrateUrlOrPath(value) : migrateValue(value);
+                update_pstmt.setBytes(1, encryptedValue.getBytes(StandardCharsets.UTF_8));
+                update_pstmt.setLong(2, id);
                 update_pstmt.executeUpdate();
             }
         } catch (SQLException e) {
-            throw new CloudRuntimeException("Unable to update configuration values ", e);
-        } catch (UnsupportedEncodingException e) {
-            throw new CloudRuntimeException("Unable to update configuration values ", e);
+            throwCloudRuntimeException(String.format("Unable to update %s values", tableName), e);
         }
-        System.out.println("End migrate config values");
     }
 
-    private void migrateHostDetails(Connection conn) {
-        System.out.println("Begin migrate host details");
-
-        try( PreparedStatement sel_pstmt = conn.prepareStatement("select id, value from host_details where name = 'password'");
-        ResultSet rs = sel_pstmt.executeQuery();
-        PreparedStatement pstmt = conn.prepareStatement("update host_details set value=? where id=?");
+    private void migrateValueAndUpdateDatabaseByName(Connection conn, String tableName, String selectSql, String updateSql) {
+        try(PreparedStatement select_pstmt = conn.prepareStatement(selectSql);
+            ResultSet rs = select_pstmt.executeQuery();
+            PreparedStatement update_pstmt = conn.prepareStatement(updateSql)
         ) {
             while (rs.next()) {
-                long id = rs.getLong(1);
+                String name = rs.getString(1);
                 String value = rs.getString(2);
-                if (value == null || value.isEmpty()) {
+                if (StringUtils.isEmpty(value)) {
                     continue;
                 }
                 String encryptedValue = migrateValue(value);
-                pstmt.setBytes(1, encryptedValue.getBytes("UTF-8"));
-                pstmt.setLong(2, id);
-                pstmt.executeUpdate();
+                update_pstmt.setBytes(1, encryptedValue.getBytes(StandardCharsets.UTF_8));
+                update_pstmt.setString(2, name);
+                update_pstmt.executeUpdate();
             }
         } catch (SQLException e) {
-            throw new CloudRuntimeException("Unable update host_details values ", e);
-        } catch (UnsupportedEncodingException e) {
-            throw new CloudRuntimeException("Unable update host_details values ", e);
+            throwCloudRuntimeException(String.format("Unable to update %s values", tableName), e);
         }
+    }
+
+    private void migrateHostDetails(Connection conn) {
+        System.out.println("Begin migrate host details");
+        migrateDetails(conn, "host_details", "password");
         System.out.println("End migrate host details");
     }
 
-    private void migrateVNCPassword(Connection conn) {
-        System.out.println("Begin migrate VNC password");
-        try(PreparedStatement  select_pstmt = conn.prepareStatement("select id, vnc_password from vm_instance");
-        ResultSet rs = select_pstmt.executeQuery();
-        PreparedStatement pstmt = conn.prepareStatement("update vm_instance set vnc_password=? where id=?");
+    private void migrateClusterDetails(Connection conn) {
+        System.out.println("Begin migrate cluster details");
+        migrateDetails(conn, "cluster_details", "password");
+        System.out.println("End migrate cluster details");
+    }
+
+    private void migrateImageStoreDetails(Connection conn) {
+        System.out.println("Begin migrate image store details");
+        migrateDetails(conn, "image_store_details", "key", "secretkey");
+        System.out.println("End migrate image store details");
+    }
+
+    private void migrateStoragePoolDetails(Connection conn) {
+        System.out.println("Begin migrate storage pool details");
+        migrateDetails(conn, "storage_pool_details", "password");
+        System.out.println("End migrate storage pool details");
+    }
+
+    private void migrateScaleIOStoragePoolDetails(Connection conn) {
+        System.out.println("Begin migrate storage pool details for ScaleIO");
+        migrateDetails(conn, "storage_pool_details", "powerflex.gw.username", "powerflex.gw.password");
+        System.out.println("End migrate storage pool details for ScaleIO");
+    }
+
+    private void migrateUserVmDetails(Connection conn) {
+        System.out.println("Begin migrate user vm details");
+        migrateDetails(conn, "user_vm_details", "password");
+        System.out.println("End migrate user vm details");
+    }
+
+    private void migrateDetails(Connection conn, String tableName, String... detailNames) {
+        String convertedDetails = Arrays.stream(detailNames).map(detail -> "'" + detail + "'").collect(Collectors.joining(", "));
+        String selectSql = String.format("SELECT id, value FROM %s WHERE name IN (%s)", tableName, convertedDetails);
+        String updateSql = String.format("UPDATE %s SET value=? WHERE id=?", tableName);
+        migrateValueAndUpdateDatabaseById(conn, tableName, selectSql, updateSql, false);
+    }
+
+    private void migrateTemplateDeployAsIsDetails(Connection conn) throws SQLException {
+        System.out.println("Begin migrate user vm deploy_as_is details");
+        if (!ifTableExists(conn.getMetaData(), "user_vm_deploy_as_is_details")) {
+            System.out.printf("Skipped as table %s does not exist\n", "user_vm_deploy_as_is_details");
+            return;
+        }
+        if (!ifTableExists(conn.getMetaData(), "template_deploy_as_is_details")) {
+            System.out.printf("Skipped as table %s does not exist\n", "template_deploy_as_is_details");
+            return;
+        }
+        String sql_template_deploy_as_is_details = "SELECT template_deploy_as_is_details.value " +
+                "FROM template_deploy_as_is_details JOIN vm_instance " +
+                "WHERE template_deploy_as_is_details.template_id = vm_instance.vm_template_id " +
+                "vm_instance.id = %s AND template_deploy_as_is_details.name = '%s' LIMIT 1";
+        try (PreparedStatement sel_pstmt = conn.prepareStatement("SELECT id, vm_id, name, value FROM user_vm_deploy_as_is_details");
+             ResultSet rs = sel_pstmt.executeQuery();
+             PreparedStatement pstmt = conn.prepareStatement("UPDATE user_vm_deploy_as_is_details SET value=? WHERE id=?")
         ) {
             while (rs.next()) {
                 long id = rs.getLong(1);
-                String value = rs.getString(2);
-                if (value == null || value.isEmpty()) {
+                long vmId = rs.getLong(2);
+                String name = rs.getString(3);
+                String value = rs.getString(4);
+                if (StringUtils.isEmpty(value)) {
                     continue;
                 }
-                String encryptedValue = migrateValue(value);
+                String key = name.startsWith("property-") ? name : "property-" + name;
+
+                PreparedStatement pstmt_template_deploy_as_is = conn.prepareStatement(String.format(sql_template_deploy_as_is_details, vmId, key));
+                ResultSet rs_template_deploy_as_is = pstmt_template_deploy_as_is.executeQuery();
+                if (rs_template_deploy_as_is.next()) {
+                    String template_deploy_as_is_detail_value = rs_template_deploy_as_is.getString(1);
+                    OVFPropertyTO property = gson.fromJson(template_deploy_as_is_detail_value, OVFPropertyTO.class);
+                    if (property != null && property.isPassword()) {
+                        String encryptedValue = migrateValue(value);
+                        pstmt.setBytes(1, encryptedValue.getBytes(StandardCharsets.UTF_8));
+                        pstmt.setLong(2, id);
+                        pstmt.executeUpdate();
+                    }
+                }
+            }
+        } catch (SQLException | JsonSyntaxException e) {
+            throwCloudRuntimeException("Unable to update user_vm_deploy_as_is_details values", e);
+        }
+        System.out.println("End migrate user vm deploy_as_is details");
+    }
+
+    private void migrateImageStoreUrlForCifs(Connection conn) {
+        System.out.println("Begin migrate image store url if protocol is cifs");
+
+        String tableName = "image_store";
+        String fieldName = "url";
+        if (getCountOfTable(conn, tableName) == 0) {
+            System.out.printf("Skipped table %s as there is no data in the table\n", tableName);
+            return;
+        }
+        String selectSql = String.format("SELECT id, `%s` FROM %s WHERE protocol = 'cifs'", fieldName, tableName);
+        String updateSql = String.format("UPDATE %s SET `%s`=? WHERE id=?", tableName, fieldName);
+        migrateValueAndUpdateDatabaseById(conn, tableName, selectSql, updateSql, true);
+
+        System.out.println("End migrate image store url if protocol is cifs");
+    }
+
+    private void migrateStoragePoolPathForSMB(Connection conn) {
+        System.out.println("Begin migrate storage pool path if type is SMB");
+
+        String tableName = "storage_pool";
+        String fieldName = "path";
+        if (getCountOfTable(conn, tableName) == 0) {
+            System.out.printf("Skipped table %s as there is no data in the table\n", tableName);
+            return;
+        }
+        String selectSql = String.format("SELECT id, `%s` FROM %s WHERE pool_type = 'SMB'", fieldName, tableName);
+        String updateSql = String.format("UPDATE %s SET `%s`=? WHERE id=?", tableName, fieldName);
+        migrateValueAndUpdateDatabaseById(conn, tableName, selectSql, updateSql, true);
+
+        System.out.println("End migrate storage pool path if type is SMB");
+    }
+
+    private void migrateDatabaseField(Connection conn, String tableName, String fieldName) {
+        System.out.printf("Begin migrate table %s field %s\n", tableName, fieldName);
+
+        String selectSql = String.format("SELECT id, `%s` FROM %s", fieldName, tableName);
+        String updateSql = String.format("UPDATE %s SET `%s`=? WHERE id=?", tableName, fieldName);
+        migrateValueAndUpdateDatabaseById(conn, tableName, selectSql, updateSql, false);
+
+        System.out.printf("Done migrating database field %s.%s\n", tableName, fieldName);
+    }
+
+    protected static Map<String, Set<String>> findEncryptedTableColumns() {
+        Map<String, Set<String>> tableCols = new HashMap<>();
+        Set<Class<?>> vos = ReflectUtil.getClassesWithAnnotation(Table.class, new String[]{"com", "org"});
+        vos.forEach( vo -> {
+            Table tableAnnotation = vo.getAnnotation(Table.class);
+            if (tableAnnotation == null || (tableAnnotation.name() != null && tableAnnotation.name().endsWith("_view"))) {
+                return;
+            }
+            for (Field field : vo.getDeclaredFields()) {
+                if (field.isAnnotationPresent(Encrypt.class)) {
+                    Set<String> encryptedColumns = tableCols.getOrDefault(tableAnnotation.name(), new HashSet<>());
+                    String columnName = field.getName();
+                    if (field.isAnnotationPresent(Column.class)) {
+                        Column columnAnnotation = field.getAnnotation(Column.class);
+                        columnName = columnAnnotation.name();
+                    }
+                    encryptedColumns.add(columnName);
+                    tableCols.put(tableAnnotation.name(), encryptedColumns);
+                }
+            }
+        });
+        return tableCols;
+    }
+
+    private void migrateEncryptedTableColumns(Connection conn) throws SQLException {
+        Map<String, Set<String>> encryptedTableCols = findEncryptedTableColumns();
+        DatabaseMetaData metadata = conn.getMetaData();
+        encryptedTableCols.forEach((table, columns) -> {
+            if (!ifTableExists(metadata, table)) {
+                System.out.printf("Skipped table %s as it does not exist\n", table);
+                return;
+            }
+            if (getCountOfTable(conn, table) == 0) {
+                System.out.printf("Skipped table %s as there is no data in the table\n", table);
+                return;
+            }
+            columns.forEach(column -> {
+                if (!ifTableColumnExists(metadata, table, column)) {
+                    System.out.printf("Skipped column %s in table %s as it does not exist\n", column, table);
+                    return;
+                }
+                migrateDatabaseField(conn, table, column);
+            });
+        });
+    }
 
-                pstmt.setBytes(1, encryptedValue.getBytes("UTF-8"));
-                pstmt.setLong(2, id);
-                pstmt.executeUpdate();
+    private boolean ifTableExists(DatabaseMetaData metadata, String table) {
+        try {
+            ResultSet rs = metadata.getTables(null, null, table, null);
+            if (rs.next()) {
+                return true;
             }
         } catch (SQLException e) {
-            throw new CloudRuntimeException("Unable update vm_instance vnc_password ", e);
-        } catch (UnsupportedEncodingException e) {
-            throw new CloudRuntimeException("Unable update vm_instance vnc_password ", e);
+            throwCloudRuntimeException(String.format("Unable to get table %s", table), e);
         }
-        System.out.println("End migrate VNC password");
+        return false;
     }
 
-    private void migrateUserCredentials(Connection conn) {
-        System.out.println("Begin migrate user credentials");
-        try(PreparedStatement select_pstmt = conn.prepareStatement("select id, secret_key from user");
-        ResultSet rs = select_pstmt.executeQuery();
-        PreparedStatement pstmt = conn.prepareStatement("update user set secret_key=? where id=?");
-        ) {
-            while (rs.next()) {
-                long id = rs.getLong(1);
-                String secretKey = rs.getString(2);
-                if (secretKey == null || secretKey.isEmpty()) {
-                    continue;
-                }
-                String encryptedSecretKey = migrateValue(secretKey);
-                pstmt.setBytes(1, encryptedSecretKey.getBytes("UTF-8"));
-                pstmt.setLong(2, id);
-                pstmt.executeUpdate();
+    private boolean ifTableColumnExists(DatabaseMetaData metadata, String table, String column) {
+        try {
+            ResultSet rs = metadata.getColumns(null, null, table, column);
+            if (rs.next()) {
+                return true;
+            }
+        } catch (SQLException e) {
+            throwCloudRuntimeException(String.format("Unable to get column %s in table %s", column, table), e);
+        }
+        return false;
+    }
+
+    private int getCountOfTable(Connection conn, String table) {
+        try {
+            PreparedStatement pstmt = conn.prepareStatement(String.format("SELECT count(*) FROM %s", table));
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                return rs.getInt(1);
             }
         } catch (SQLException e) {
-            throw new CloudRuntimeException("Unable update user secret key ", e);
-        } catch (UnsupportedEncodingException e) {
-            throw new CloudRuntimeException("Unable update user secret key ", e);
+            throwCloudRuntimeException(String.format("Unable to get count of records in table %s", table), e);
         }
-        System.out.println("End migrate user credentials");
+        return 0;
     }
 
-    private static void usage() {
-        System.out.println("Usage: \tEncryptionSecretKeyChanger \n" + "\t\t-m <Mgmt Secret Key> \n" + "\t\t-d <DB Secret Key> \n" + "\t\t-n [New Mgmt Secret Key] \n"
-            + "\t\t-e [New DB Secret Key]");
+    private static void throwCloudRuntimeException(String msg, Exception e) {
+        System.out.println(msg + " due to: " + e.getMessage());
+        throw new CloudRuntimeException(msg, e);
     }
 }
diff --git a/framework/db/src/main/java/com/cloud/utils/crypt/OVFPropertyTO.java b/framework/db/src/main/java/com/cloud/utils/crypt/OVFPropertyTO.java
new file mode 100644
index 0000000000..1f7a2744d3
--- /dev/null
+++ b/framework/db/src/main/java/com/cloud/utils/crypt/OVFPropertyTO.java
@@ -0,0 +1,130 @@
+//
+// 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 com.cloud.utils.crypt;
+
+/**
+ * This is a copy of ./api/src/main/java/com/cloud/agent/api/to/deployasis/OVFPropertyTO.java
+ */
+public class OVFPropertyTO {
+
+    private String key;
+    private String type;
+    private String value;
+    private String qualifiers;
+    private Boolean userConfigurable;
+    private String label;
+    private String description;
+    private Boolean password;
+    private int index;
+    private String category;
+
+    public OVFPropertyTO() {
+    }
+
+    public OVFPropertyTO(String key, String type, String value, String qualifiers, boolean userConfigurable,
+                       String label, String description, boolean password, int index, String category) {
+        this.key = key;
+        this.type = type;
+        this.value = value;
+        this.qualifiers = qualifiers;
+        this.userConfigurable = userConfigurable;
+        this.label = label;
+        this.description = description;
+        this.password = password;
+        this.index = index;
+        this.category = category;
+    }
+
+    public Long getTemplateId() {
+        return null;
+    }
+
+    public String getKey() {
+        return key;
+    }
+
+    public void setKey(String key) {
+        this.key = key;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getQualifiers() {
+        return qualifiers;
+    }
+
+    public void setQualifiers(String qualifiers) {
+        this.qualifiers = qualifiers;
+    }
+
+    public Boolean isUserConfigurable() {
+        return userConfigurable;
+    }
+
+    public void setUserConfigurable(Boolean userConfigurable) {
+        this.userConfigurable = userConfigurable;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Boolean isPassword() {
+        return password;
+    }
+
+    public void setPassword(Boolean password) {
+        this.password = password;
+    }
+
+    public String getCategory() {
+        return category;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+}
diff --git a/framework/db/src/test/java/com/cloud/utils/crypt/EncryptionSecretKeyChangerTest.java b/framework/db/src/test/java/com/cloud/utils/crypt/EncryptionSecretKeyChangerTest.java
new file mode 100644
index 0000000000..239628160d
--- /dev/null
+++ b/framework/db/src/test/java/com/cloud/utils/crypt/EncryptionSecretKeyChangerTest.java
@@ -0,0 +1,87 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.springframework.test.util.ReflectionTestUtils;
+
+public class EncryptionSecretKeyChangerTest {
+    @Spy
+    EncryptionSecretKeyChanger changer = new EncryptionSecretKeyChanger();
+    @Mock
+    CloudStackEncryptor oldEncryptor;
+    @Mock
+    CloudStackEncryptor newEncryptor;
+
+    private static final String emtpyString = "";
+    private static final String encryptedValue = "encryptedValue";
+    private static final String plainText = "plaintext";
+    private static final String newEncryptedValue = "newEncryptedValue";
+
+    @Before
+    public void setUp() {
+        oldEncryptor = Mockito.mock(CloudStackEncryptor.class);
+        newEncryptor = Mockito.mock(CloudStackEncryptor.class);
+
+        ReflectionTestUtils.setField(changer, "oldEncryptor", oldEncryptor);
+        ReflectionTestUtils.setField(changer, "newEncryptor", newEncryptor);
+
+        Mockito.when(oldEncryptor.decrypt(encryptedValue)).thenReturn(plainText);
+        Mockito.when(newEncryptor.encrypt(plainText)).thenReturn(newEncryptedValue);
+    }
+
+    @Test
+    public void migrateValueTest() {
+        String value = changer.migrateValue(encryptedValue);
+        Assert.assertEquals(newEncryptedValue, value);
+
+        Mockito.verify(oldEncryptor).decrypt(encryptedValue);
+        Mockito.verify(newEncryptor).encrypt(plainText);
+    }
+
+    @Test
+    public void migrateValueTest2() {
+        String value = changer.migrateValue(emtpyString);
+        Assert.assertEquals(emtpyString, value);
+    }
+
+    @Test
+    public void migrateUrlOrPathTest() {
+        String path = emtpyString;
+        Assert.assertEquals(path, changer.migrateUrlOrPath(path));
+
+        path = String.format("password=%s", encryptedValue);
+        Assert.assertEquals(path.replaceAll("password=" + encryptedValue, "password=" + newEncryptedValue), changer.migrateUrlOrPath(path));
+
+        path = String.format("username=user&password=%s", encryptedValue);
+        Assert.assertEquals(path.replaceAll("password=" + encryptedValue, "password=" + newEncryptedValue), changer.migrateUrlOrPath(path));
+
+        path = String.format("username=user&password2=%s", encryptedValue);
+        Assert.assertEquals(path, changer.migrateUrlOrPath(path));
+
+        path = String.format("username=user&password=%s&add=false", encryptedValue);
+        Assert.assertEquals(path.replaceAll("password=" + encryptedValue, "password=" + newEncryptedValue), changer.migrateUrlOrPath(path));
+    }
+}
\ No newline at end of file
diff --git a/packaging/centos7/cloud.spec b/packaging/centos7/cloud.spec
index aabc46ad6b..cc6cf65c62 100644
--- a/packaging/centos7/cloud.spec
+++ b/packaging/centos7/cloud.spec
@@ -299,6 +299,7 @@ ln -sf log4j-cloud.xml  ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/log4j
 
 install python/bindir/cloud-external-ipallocator.py ${RPM_BUILD_ROOT}%{_bindir}/%{name}-external-ipallocator.py
 install -D client/target/pythonlibs/jasypt-1.9.3.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
+install -D utils/target/cloud-utils-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/%{name}-utils.jar
 
 install -D packaging/centos7/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator
 install -D packaging/centos7/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud
@@ -642,6 +643,7 @@ pip3 install --upgrade urllib3
 %attr(0644,root,root) %{python_sitearch}/__pycache__/*
 %attr(0644,root,root) %{python_sitearch}/cloudutils/*
 %attr(0644, root, root) %{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
+%attr(0644, root, root) %{_datadir}/%{name}-common/lib/%{name}-utils.jar
 %{_defaultdocdir}/%{name}-common-%{version}/LICENSE
 %{_defaultdocdir}/%{name}-common-%{version}/NOTICE
 
diff --git a/packaging/centos8/cloud.spec b/packaging/centos8/cloud.spec
index af3b920099..041e4349c4 100644
--- a/packaging/centos8/cloud.spec
+++ b/packaging/centos8/cloud.spec
@@ -292,6 +292,7 @@ ln -sf log4j-cloud.xml  ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/log4j
 
 install python/bindir/cloud-external-ipallocator.py ${RPM_BUILD_ROOT}%{_bindir}/%{name}-external-ipallocator.py
 install -D client/target/pythonlibs/jasypt-1.9.3.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
+install -D utils/target/cloud-utils-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/%{name}-utils.jar
 
 install -D packaging/centos8/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator
 install -D packaging/centos8/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud
@@ -630,6 +631,7 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz
 %attr(0644,root,root) %{python_sitearch}/__pycache__/*
 %attr(0644,root,root) %{python_sitearch}/cloudutils/*
 %attr(0644, root, root) %{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
+%attr(0644, root, root) %{_datadir}/%{name}-common/lib/%{name}-utils.jar
 %{_defaultdocdir}/%{name}-common-%{version}/LICENSE
 %{_defaultdocdir}/%{name}-common-%{version}/NOTICE
 
diff --git a/packaging/suse15/cloud.spec b/packaging/suse15/cloud.spec
index 334ba6ff81..b8b63bd74b 100644
--- a/packaging/suse15/cloud.spec
+++ b/packaging/suse15/cloud.spec
@@ -294,6 +294,7 @@ ln -sf log4j-cloud.xml  ${RPM_BUILD_ROOT}%{_sysconfdir}/%{name}/management/log4j
 
 install python/bindir/cloud-external-ipallocator.py ${RPM_BUILD_ROOT}%{_bindir}/%{name}-external-ipallocator.py
 install -D client/target/pythonlibs/jasypt-1.9.3.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
+install -D utils/target/cloud-utils-%{_maventag}.jar ${RPM_BUILD_ROOT}%{_datadir}/%{name}-common/lib/%{name}-utils.jar
 
 install -D packaging/centos8/cloud-ipallocator.rc ${RPM_BUILD_ROOT}%{_initrddir}/%{name}-ipallocator
 install -D packaging/centos8/cloud.limits ${RPM_BUILD_ROOT}%{_sysconfdir}/security/limits.d/cloud
@@ -624,6 +625,7 @@ pip install --upgrade /usr/share/cloudstack-marvin/Marvin-*.tar.gz
 %attr(0644,root,root) %{python_sitearch}/__pycache__/*
 %attr(0644,root,root) %{python_sitearch}/cloudutils/*
 %attr(0644, root, root) %{_datadir}/%{name}-common/lib/jasypt-1.9.3.jar
+%attr(0644, root, root) %{_datadir}/%{name}-common/lib/%{name}-utils.jar
 %{_defaultdocdir}/%{name}-common-%{version}/LICENSE
 %{_defaultdocdir}/%{name}-common-%{version}/NOTICE
 
diff --git a/pom.xml b/pom.xml
index 601e660129..c5ad00ab9f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,7 @@
 
         <!-- Apache Commons versions -->
         <cs.codec.version>1.15</cs.codec.version>
+        <cs.commons-cli.version>1.5.0</cs.commons-cli.version>
         <cs.commons-collections.version>4.4</cs.commons-collections.version>
         <cs.commons-compress.version>1.21</cs.commons-compress.version>
         <cs.commons-exec.version>1.3</cs.commons-exec.version>
@@ -171,6 +172,7 @@
         <cs.reflections.version>0.9.12</cs.reflections.version>
         <cs.servicemix.version>3.4.4_1</cs.servicemix.version>
         <cs.servlet.version>4.0.1</cs.servlet.version>
+        <cs.tink.version>1.7.0</cs.tink.version>
         <cs.tomcat-embed-core.version>10.0.22</cs.tomcat-embed-core.version>
         <cs.trilead.version>build-217-jenkins-27</cs.trilead.version>
         <cs.vmware.api.version>7.0</cs.vmware.api.version>
diff --git a/scripts/storage/secondary/cloud-install-sys-tmplt b/scripts/storage/secondary/cloud-install-sys-tmplt
index 7ff05b1161..ad976c502c 100755
--- a/scripts/storage/secondary/cloud-install-sys-tmplt
+++ b/scripts/storage/secondary/cloud-install-sys-tmplt
@@ -55,7 +55,7 @@ dbHost="localhost"
 dbUser="root"
 dbPassword=
 dbPort=3306
-jasypt='/usr/share/cloudstack-common/lib/jasypt-1.9.3.jar'
+jarfile='/usr/share/cloudstack-common/lib/cloudstack-utils.jar'
 
 # check if first parameter is not a dash (-) then print the usage block
 if [[ ! $@ =~ ^\-.+ ]]; then
@@ -149,7 +149,7 @@ if [[ -f /etc/cloudstack/management/db.properties ]]; then
   if [[ "$encType" == "file" || "$encType" == "web" ]]; then
     encPassword=$(sed '/^\#/d' /etc/cloudstack/management/db.properties | grep 'db.cloud.password'  | tail -n 1 | cut -d "=" -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'i | sed 's/^ENC(\(.*\))/\1/')
     if [[ ! $encPassword == "" ]]; then
-      dbPassword=(`java -classpath $jasypt org.jasypt.intf.cli.JasyptPBEStringDecryptionCLI decrypt.sh input=$encPassword password=$msKey verbose=false`)
+      dbPassword=(`java -classpath $jarfile com.cloud.utils.crypt.EncryptionCLI -d -i "$encPassword" -p "$msKey"`)
       if [[ ! $dbPassword ]]; then
         failed 2 "Failed to decrypt DB password from db.properties"
       fi
diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
index 96d0f2ac3c..d916d257be 100644
--- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java
@@ -161,7 +161,8 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
         try {
             persistDefaultValues();
             _configDepotAdmin.populateConfigurations();
-        } catch (InternalErrorException e) {
+        } catch (InternalErrorException | CloudRuntimeException e) {
+            s_logger.error("Unhandled configuration exception: " + e.getMessage());
             throw new RuntimeException("Unhandled configuration exception", e);
         }
         return true;
diff --git a/setup/bindir/cloud-migrate-databases.in b/setup/bindir/cloud-migrate-databases.in
index e9a4df37ff..d9a124f963 100644
--- a/setup/bindir/cloud-migrate-databases.in
+++ b/setup/bindir/cloud-migrate-databases.in
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/bin/bash
 
 # Licensed to the Apache Software Foundation (ASF) under one
 # or more contributor license agreements.  See the NOTICE file
@@ -17,271 +17,32 @@
 # specific language governing permissions and limitations
 # under the License.
 
-
-import os,logging,sys
-from optparse import OptionParser
-import mysql.connector
-import subprocess
-import glob
-
-# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
-# ---- We do this so cloud_utils can be looked up in the following order:
-# ---- 1) Sources directory
-# ---- 2) waf configured PYTHONDIR
-# ---- 3) System Python path
-for pythonpath in (
-		"@PYTHONDIR@",
-		os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"),
-	):
-		if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath)
-# ---- End snippet of code ----
-from cloud_utils import check_selinux, CheckFailed, resolves_to_ipv6
-import cloud_utils
-
-# RUN ME LIKE THIS
-# python setup/bindir/cloud-migrate-databases.in --config=client/conf/override/db.properties --resourcedir=setup/db  --dry-run
-# --dry-run makes it so the changes to the database in the context of the migrator are rolled back
-
-# This program / library breaks down as follows:
-#   high-level breakdown:
-#   the module calls main()
-#   main processes command-line options
-#   main() instantiates a migrator with a a list of possible migration steps
-#   migrator discovers and topologically sorts migration steps from the given list
-#   main() run()s the migrator
-#      for each one of the migration steps:
-#          the migrator instantiates the migration step with the context as first parameter
-#          the instantiated migration step saves the context onto itself as self.context
-#          the migrator run()s the instantiated migration step.  within run(), self.context is the context
-#      the migrator commits the migration context to the database (or rollsback if --dry-run is specified)
-#   that is it
-
-# The specific library code is in cloud_utils.py
-# What needs to be implemented is MigrationSteps
-# Specifically in the FromInitialTo21 evolver.
-# What Db20to21MigrationUtil.java does, needs to be done within run() of that class
-# refer to the class docstring to find out how
-# implement them below
-
-class CloudContext(cloud_utils.MigrationContext):
-	def __init__(self,host,port,username,password,database,configdir,resourcedir):
-		self.host = host
-		self.port = port
-		self.username = username
-		self.password = password
-		self.database = database
-		self.configdir = configdir
-		self.resourcedir = resourcedir
-		self.conn = mysql.connector.connect(host=self.host,
-			user=self.username,
-			password=self.password,
-			database=self.database,
-			port=self.port)
-		self.conn.autocommit(False)
-		self.db = self.conn.cursor()
-		def wrapex(func):
-			sqlogger = logging.getLogger("SQL")
-			def f(stmt,parms=None):
-				if parms: sqlogger.debug("%s | with parms %s",stmt,parms)
-				else: sqlogger.debug("%s",stmt)
-				return func(stmt,parms)
-			return f
-		self.db.execute = wrapex(self.db.execute)
-	
-	def __str__(self):
-		return "CloudStack %s database at %s"%(self.database,self.host)
-	
-	def get_schema_level(self):
-		return self.get_config_value('schema.level') or cloud_utils.INITIAL_LEVEL
-	
-	def set_schema_level(self,l):
-		self.db.execute(
-		"INSERT INTO configuration (category,instance,component,name,value,description) VALUES ('Hidden', 'DEFAULT', 'database', 'schema.level', %s, 'The schema level of this database') ON DUPLICATE KEY UPDATE value = %s", (l,l)
-		)
-		self.commit()
-
-	def commit(self):
-		self.conn.commit()
-		#self.conn.close()
-		
-	def close(self):
-		self.conn.close()
-		
-	def get_config_value(self,name):
-		self.db.execute("select value from configuration where name = %s",(name,))
-		try: return self.db.fetchall()[0][0]
-		except IndexError: return
-		
-	def run_sql_resource(self,resource):
-		sqlfiletext = file(os.path.join(self.resourcedir,resource)).read(-1)
-		sqlstatements = sqlfiletext.split(";")
-		for stmt in sqlstatements:
-			if not stmt.strip(): continue # skip empty statements
-			self.db.execute(stmt)
-
-
-class FromInitialTo21NewSchema(cloud_utils.MigrationStep):
-	def __str__(self): return "Altering the database schema"
-	from_level = cloud_utils.INITIAL_LEVEL
-	to_level = "2.1-01"
-	def run(self): self.context.run_sql_resource("schema-20to21.sql")
-
-class From21NewSchemaTo21NewSchemaPlusIndex(cloud_utils.MigrationStep):
-	def __str__(self): return "Altering indexes"
-	from_level = "2.1-01"
-	to_level = "2.1-02"
-	def run(self): self.context.run_sql_resource("index-20to21.sql")
-
-class From21NewSchemaPlusIndexTo21DataMigratedPart1(cloud_utils.MigrationStep):
-	def __str__(self): return "Performing data migration, stage 1"
-	from_level = "2.1-02"
-	to_level = "2.1-03"
-	def run(self):	self.context.run_sql_resource("data-20to21.sql")
-
-class From21step1toTo21datamigrated(cloud_utils.MigrationStep):
-	def __str__(self): return "Performing data migration, stage 2"
-	from_level = "2.1-03"
-	to_level = "2.1-04"
-	
-	def run(self):
-		systemjars = "@SYSTEMJARS@".split()
-		pipe = subprocess.Popen(["build-classpath"]+systemjars,stdout=subprocess.PIPE)
-		systemcp,throwaway = pipe.communicate()
-		systemcp = systemcp.strip()
-		if pipe.wait(): # this means that build-classpath failed miserably
-			systemcp = "@SYSTEMCLASSPATH@"
-		pcp = os.path.pathsep.join( glob.glob( os.path.join ( "@PREMIUMJAVADIR@" , "*" ) ) )
-		mscp = "@MSCLASSPATH@"
-		depscp = "@DEPSCLASSPATH@"
-		migrationxml = "@SERVERSYSCONFDIR@"
-		conf = self.context.configdir
-		cp = os.path.pathsep.join([pcp,systemcp,depscp,mscp,migrationxml,conf])
-		cmd = ["java"]
-		cmd += ["-cp",cp]
-		cmd += ["com.cloud.migration.Db20to21MigrationUtil"]
-		logging.debug("Running command: %s"," ".join(cmd))
-		subprocess.check_call(cmd)
-
-class From21datamigratedTo21postprocessed(cloud_utils.MigrationStep):
-	def __str__(self): return "Postprocessing migrated data"
-	from_level = "2.1-04"
-	to_level = "2.1"
-	def run(self): self.context.run_sql_resource("postprocess-20to21.sql")
-
-class From21To213(cloud_utils.MigrationStep):
-	def __str__(self): return "Dropping obsolete indexes"
-	from_level = "2.1"
-	to_level = "2.1.3"
-	def run(self): self.context.run_sql_resource("index-212to213.sql")
-
-class From213To22data(cloud_utils.MigrationStep):
-	def __str__(self): return "Migrating data"
-	from_level = "2.1.3"
-	to_level = "2.2-01"
-	def run(self): self.context.run_sql_resource("data-21to22.sql")
-
-class From22dataTo22(cloud_utils.MigrationStep):
-	def __str__(self): return "Migrating indexes"
-	from_level = "2.2-01"
-	to_level = "2.2"
-	def run(self): self.context.run_sql_resource("index-21to22.sql")
-
-# command line harness functions
-
-def setup_logging(level):
-	l = logging.getLogger()
-	l.setLevel(level)
-	h = logging.StreamHandler(sys.stderr)
-	l.addHandler(h)
-
-
-def setup_optparse():
-	usage = \
-"""%prog [ options ... ]
-
-This command migrates the CloudStack database."""
-	parser = OptionParser(usage=usage)
-	parser.add_option("-c", "--config", action="store", type="string",dest='configdir',
-		default=os.path.join("@MSCONF@"),
-		help="Configuration directory with a db.properties file, pointing to the CloudStack database")
-	parser.add_option("-r", "--resourcedir", action="store", type="string",dest='resourcedir',
-		default="@SETUPDATADIR@",
-		help="Resource directory with database SQL files used by the migration process")
-	parser.add_option("-d", "--debug", action="store_true", dest='debug',
-		default=False,
-		help="Increase log level from INFO to DEBUG")
-	parser.add_option("-e", "--dump-evolvers", action="store_true", dest='dumpevolvers',
-		default=False,
-		help="Dump evolvers in the order they would be executed, but do not run them")
-	#parser.add_option("-n", "--dry-run", action="store_true", dest='dryrun',
-		#default=False,
-		#help="Run the process as it would normally run, but do not commit the final transaction, so database changes are never saved")
-	parser.add_option("-f", "--start-at-level", action="store", type="string",dest='fromlevel',
-		default=None,
-		help="Rather than discovering the database schema level to start from, start migration from this level.  The special value '-' (a dash without quotes) represents the earliest schema level")
-	parser.add_option("-t", "--end-at-level", action="store", type="string",dest='tolevel',
-		default=None,
-		help="Rather than evolving the database to the most up-to-date level, end migration at this level")
-	return parser
-
-
-def main(*args):
-	"""The entry point of this program"""
-	
-	parser = setup_optparse()
-	opts, args = parser.parse_args(*args)
-	if args: parser.error("This command accepts no parameters")
-
-	if opts.debug: loglevel = logging.DEBUG
-	else: loglevel = logging.INFO
-	setup_logging(loglevel)
-	
-	# FIXME implement
-	opts.dryrun = False
-
-	configdir = opts.configdir
-	resourcedir = opts.resourcedir
-	
-	try:
-		props = cloud_utils.read_properties(os.path.join(configdir,'db.properties'))
-	except (IOError,OSError) as e:
-		logging.error("Cannot read from config file: %s",e)
-		logging.error("You may want to point to a specific config directory with the --config= option")
-		return 2
-	
-	if not os.path.isdir(resourcedir):
-		logging.error("Cannot find directory with SQL files %s",resourcedir)
-		logging.error("You may want to point to a specific resource directory with the --resourcedir= option")
-		return 2
-	
-	host = props["db.cloud.host"]
-	port = int(props["db.cloud.port"])
-	username = props["db.cloud.username"]
-	password = props["db.cloud.password"]
-	database = props["db.cloud.name"]
-	
-	# tell the migrator to load its steps from the globals list
-	migrator = cloud_utils.Migrator(list(globals().values()))
-	
-	if opts.dumpevolvers:
-		print("Evolution steps:")
-		print("	%s	%s	%s"%("From","To","Evolver in charge"))
-		for f,t,e in migrator.get_evolver_chain():
-			print("	%s	%s	%s"%(f,t,e))
-		return
-	
-	#initialize a context with the read configuration
-	context = CloudContext(host=host,port=port,username=username,password=password,database=database,configdir=configdir,resourcedir=resourcedir)
-	try:
-	    try:
-		migrator.run(context,dryrun=opts.dryrun,starting_level=opts.fromlevel,ending_level=opts.tolevel)
-	    finally:
-		context.close()
-	except (cloud_utils.NoMigrationPath,cloud_utils.NoMigrator) as e:
-		logging.error("%s",e)
-		return 4
-
-if __name__ == "__main__":
-	retval = main()
-	if retval: sys.exit(retval)
-	else: sys.exit()
+LOGFILE=/tmp/cloudstack-migrate-databases.log
+
+check_if_svc_active() {
+  svc_name=$1
+  systemctl is-active $svc_name -q
+  if [ $? -eq 0 ];then
+    echo "service $svc_name is still active. Please stop it and retry." |tee -a ${LOGFILE}
+    exit 1
+  fi
+}
+
+if [ "$1" != "" ] && [ "$1" != "-h" ] && [ "$1" != "--help" ];then
+  check_if_svc_active "cloudstack-management"
+  check_if_svc_active "cloudstack-usage"
+fi
+
+java -classpath /etc/cloudstack/management:/usr/share/cloudstack-management/lib/* \
+  com.cloud.utils.crypt.EncryptionSecretKeyChanger \
+  "$@" \
+  > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)
+
+res=$?
+if [ $res -eq 0 ];then
+   rm -f $LOGFILE
+else
+   echo "Failed to migrate databases. You may find more logs in $LOGFILE"
+fi
+
+exit $res
diff --git a/setup/bindir/cloud-setup-databases.in b/setup/bindir/cloud-setup-databases.in
index 0532613dd8..7cb90fcc26 100755
--- a/setup/bindir/cloud-setup-databases.in
+++ b/setup/bindir/cloud-setup-databases.in
@@ -67,7 +67,7 @@ class DBDeployer(object):
     dbDotProperties = {}
     dbDotPropertiesIndex = 0
     encryptionKeyFile = '@MSCONF@/key'
-    encryptionJarPath = '@COMMONLIBDIR@/lib/jasypt-1.9.3.jar'
+    encryptionJarPath = '@COMMONLIBDIR@/lib/cloudstack-utils.jar'
     success = False
     magicString = 'This_is_a_magic_string_i_think_no_one_will_duplicate'
     tmpMysqlFile = os.path.join(os.path.expanduser('~/'), 'cloudstackmysql.tmp.sql')
@@ -391,8 +391,8 @@ for example:
         checkSELinux()
 
     def processEncryptionStuff(self):
-        def encrypt(input):
-            cmd = ['java','-Djava.security.egd=file:/dev/urandom','-classpath','"' + self.encryptionJarPath + '"','org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI', 'encrypt.sh', 'input=\'%s\''%input, 'password=\'%s\''%self.mgmtsecretkey,'verbose=false']
+        def encrypt(value):
+            cmd = ['java','-classpath','"' + self.encryptionJarPath + '"','com.cloud.utils.crypt.EncryptionCLI','-i','"' + value + '"', '-p', '"' + self.mgmtsecretkey + '"', self.encryptorVersion]
             return str(runCmd(cmd)).strip('\r\n')
 
         def saveMgmtServerSecretKey():
@@ -408,6 +408,7 @@ for example:
 
         def encryptDBSecretKey():
             self.putDbProperty('db.cloud.encrypt.secret', formatEncryptResult(encrypt(self.dbsecretkey)))
+            self.putDbProperty("db.cloud.encryptor.version", self.options.encryptorVersion)
 
         def encryptDBPassword():
             dbPassword = self.getDbProperty('db.cloud.password')
@@ -450,7 +451,12 @@ for example:
                 self.info("Using specified cluster management server node IP %s" % self.options.mshostip, True)
 
             self.encryptiontype = self.options.encryptiontype
-            self.mgmtsecretkey = self.options.mgmtsecretkey
+            if self.encryptiontype == "env":
+                self.mgmtsecretkey = os.getenv("CLOUD_SECRET_KEY")
+                if not self.mgmtsecretkey:
+                    self.errorAndExit("Please set environment variable CLOUD_SECRET_KEY if the encryption type is 'env'")
+            else:
+                self.mgmtsecretkey = self.options.mgmtsecretkey
             self.dbsecretkey = self.options.dbsecretkey
             self.isDebug = self.options.debug
             if self.options.dbConfPath:
@@ -464,6 +470,11 @@ for example:
             if self.options.mysqlbinpath:
               self.mysqlBinPath = self.options.mysqlbinpath
 
+            if self.options.encryptorVersion:
+              self.encryptorVersion = "--encryptorversion %s" % self.options.encryptorVersion
+            else:
+              self.encryptorVersion = ""
+
         def parseUserAndPassword(cred):
             stuff = cred.split(':')
             if len(stuff) != 1 and len(stuff) != 2:
@@ -524,8 +535,8 @@ for example:
         def validateParameters():
             if self.options.schemaonly and self.rootuser != None:
                 self.errorAndExit("--schema-only and --deploy-as cannot be passed together\n")
-            if self.encryptiontype != 'file' and self.encryptiontype != 'web':
-                self.errorAndExit('Wrong encryption type %s, --encrypt-type can only be "file" or "web'%self.encryptiontype)
+            if self.encryptiontype != 'file' and self.encryptiontype != 'web' and self.encryptiontype != 'env':
+                self.errorAndExit('Wrong encryption type %s, --encrypt-type can only be "file" or "web" or "env"' % self.encryptiontype)
 
     #---------------------- option parsing and command line checks ------------------------
         usage = """%prog user:[password]@mysqlhost:[port] [--deploy-as=rootuser:[rootpassword]] [--auto=/path/to/server-setup.xml] [-e ENCRYPTIONTYPE] [-m MGMTSECRETKEY] [-k DBSECRETKEY] [--debug]
@@ -577,7 +588,7 @@ for example:
         self.parser.add_option("-a", "--auto", action="store", type="string", dest="serversetup", default="",
                           help="Path to an XML file describing an automated unattended cloud setup")
         self.parser.add_option("-e", "--encrypt-type", action="store", type="string", dest="encryptiontype", default="file",
-                          help="Encryption method used for db password encryption. Valid values are file, web. Default is file.")
+                          help="Encryption method used for db password encryption. Valid values are file, web and env. Default is file.")
         self.parser.add_option("-m", "--managementserver-secretkey", action="store", type="string", dest="mgmtsecretkey", default="password",
                           help="Secret key used to encrypt confidential parameters in db.properties. A string, default is password")
         self.parser.add_option("-k", "--database-secretkey", action="store", type="string", dest="dbsecretkey", default="password",
@@ -588,8 +599,10 @@ for example:
                           help="Region Id for the management server cluster")
         self.parser.add_option("-c", "--db-conf-path", action="store", dest="dbConfPath", help="The path to find db.properties which hold db properties")
         self.parser.add_option("-f", "--db-files-path", action="store", dest="dbFilesPath", help="The path to find sql files to create initial database(s)")
-        self.parser.add_option("-j", "--encryption-jar-path", action="store", dest="encryptionJarPath", help="The path to the jasypt library to be used to encrypt the values in db.properties")
+        self.parser.add_option("-j", "--encryption-jar-path", action="store", dest="encryptionJarPath", help="The cloudstack jar to be used to encrypt the values in db.properties")
         self.parser.add_option("-n", "--encryption-key-file", action="store", dest="encryptionKeyFile", help="The name of the file in which encryption key to be generated")
+        self.parser.add_option("-g", "--encryptor-version", action="store", dest="encryptorVersion", default="",
+                               help="The encryptor version to be used to encrypt the values in db.properties")
         self.parser.add_option("-b", "--mysql-bin-path", action="store", dest="mysqlbinpath", help="The mysql installed bin path")
         (self.options, self.args) = self.parser.parse_args()
         parseCasualCredit()
diff --git a/setup/bindir/cloud-setup-encryption.in b/setup/bindir/cloud-setup-encryption.in
index cd9212a119..880edee888 100755
--- a/setup/bindir/cloud-setup-encryption.in
+++ b/setup/bindir/cloud-setup-encryption.in
@@ -63,7 +63,7 @@ class DBDeployer(object):
     dbDotProperties = {}
     dbDotPropertiesIndex = 0
     encryptionKeyFile = '@MSCONF@/key'
-    encryptionJarPath = '@COMMONLIBDIR@/lib/jasypt-1.9.3.jar'
+    encryptionJarPath = '@COMMONLIBDIR@/lib/cloudstack-utils.jar'
     success = False
     magicString = 'This_is_a_magic_string_i_think_no_one_will_duplicate'
 
@@ -183,8 +183,8 @@ for example:
         self.success = True # At here, we have done successfully and nothing more after this flag is set
                         
     def processEncryptionStuff(self):
-        def encrypt(input):
-            cmd = ['java','-classpath',self.encryptionJarPath,'org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI', 'encrypt.sh', 'input=\'%s\''%input, 'password=%s'%self.mgmtsecretkey,'verbose=false']
+        def encrypt(value):
+            cmd = ['java','-classpath','"' + self.encryptionJarPath + '"','com.cloud.utils.crypt.EncryptionCLI','-i','"' + value + '"', '-p', '"' + self.mgmtsecretkey + '"']
             return runCmd(cmd).strip('\n')
         
         def saveMgmtServerSecretKey():
diff --git a/tools/devcloud4/common/development-installation/files/default/cloud-install-sys-tmplt b/tools/devcloud4/common/development-installation/files/default/cloud-install-sys-tmplt
index 313b5999e8..eb1f193a9c 100755
--- a/tools/devcloud4/common/development-installation/files/default/cloud-install-sys-tmplt
+++ b/tools/devcloud4/common/development-installation/files/default/cloud-install-sys-tmplt
@@ -41,7 +41,8 @@ dbHost=
 dbUser=
 dbPassword=
 name=
-jasypt='/usr/share/cloudstack-common/lib/jasypt-1.9.0.jar'
+jarfile='/usr/share/cloudstack-common/lib/cloudstack-utils.jar'
+
 while getopts 'm:h:f:u:Ft:e:s:o:r:d:n:' OPTION
 do
   case $OPTION in
@@ -134,7 +135,7 @@ then
 	encPassword=$(sed '/^\#/d' /etc/cloudstack/management/db.properties | grep 'db.cloud.password'  | tail -n 1 | cut -d "=" -f2- | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'i | sed 's/^ENC(\(.*\))/\1/')
 	if [ ! $encPassword == "" ]
 	then
-		dbPassword=(`java -classpath $jasypt org.jasypt.intf.cli.JasyptPBEStringDecryptionCLI decrypt.sh input=$encPassword password=$msKey verbose=false`)
+		dbPassword=(`java -classpath $jarfile com.cloud.utils.crypt.EncryptionCLI -d -i "$encPassword" -p "$msKey"`)
 		if [ ! $dbPassword ]
 		then
 			echo "Failed to decrypt DB password from db.properties"
diff --git a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java
index cb9b572373..696cc84d31 100644
--- a/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java
+++ b/usage/src/main/java/com/cloud/usage/UsageManagerImpl.java
@@ -95,6 +95,7 @@ import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.db.QueryBuilder;
 import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.TransactionLegacy;
+import com.cloud.utils.exception.CloudRuntimeException;
 
 import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
 
@@ -208,10 +209,17 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
             s_logger.info("Implementation Version is " + _version);
         }
 
-        Map<String, String> configs = _configDao.getConfiguration(params);
+        Map<String, String> configs;
+        try {
+            configs = _configDao.getConfiguration(params);
 
-        if (params != null) {
-            mergeConfigs(configs, params);
+            if (params != null) {
+                mergeConfigs(configs, params);
+                s_logger.info("configs = " + configs);
+            }
+        } catch (CloudRuntimeException e) {
+            s_logger.error("Unhandled configuration exception: " + e.getMessage());
+            throw new RuntimeException("Unhandled configuration exception", e);
         }
 
         String execTime = configs.get("usage.stats.job.exec.time");
diff --git a/utils/pom.xml b/utils/pom.xml
index db219b080f..1cc46ebac8 100755
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -210,6 +210,16 @@
             <artifactId>nashorn-core</artifactId>
             <version>15.3</version>
         </dependency>
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>${cs.commons-cli.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.crypto.tink</groupId>
+            <artifactId>tink</artifactId>
+            <version>${cs.tink.version}</version>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
@@ -234,6 +244,34 @@
                     </excludes>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.0.0</version>
+                <executions>
+                    <execution>
+                        <id>rebuild-war</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <createDependencyReducedPom>false</createDependencyReducedPom>
+                            <artifactSet>
+                                <includes>
+                                    <include>ch.qos.reload4j</include>
+                                    <include>com.google.crypto.tink:tink</include>
+                                    <include>com.google.protobuf:protobuf-java</include>
+                                    <include>commons-cli:commons-cli</include>
+                                    <include>commons-codec:commons-codec</include>
+                                    <include>org.apache.commons:commons-lang3</include>
+                                    <include>org.jasypt:jasypt</include>
+                                </includes>
+                            </artifactSet>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
     <profiles>
diff --git a/utils/src/main/java/com/cloud/utils/EncryptionUtil.java b/utils/src/main/java/com/cloud/utils/EncryptionUtil.java
index 37cea3499d..4baa58b739 100644
--- a/utils/src/main/java/com/cloud/utils/EncryptionUtil.java
+++ b/utils/src/main/java/com/cloud/utils/EncryptionUtil.java
@@ -18,11 +18,10 @@
  */
 package com.cloud.utils;
 
+import com.cloud.utils.crypt.CloudStackEncryptor;
 import com.cloud.utils.exception.CloudRuntimeException;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.PBEStringEncryptor;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
 
 import javax.crypto.Mac;
 import javax.crypto.spec.SecretKeySpec;
@@ -32,13 +31,10 @@ import java.security.NoSuchAlgorithmException;
 
 public class EncryptionUtil {
     public static final Logger s_logger = Logger.getLogger(EncryptionUtil.class.getName());
-    private static PBEStringEncryptor encryptor;
+    private static CloudStackEncryptor encryptor;
 
     private static void initialize(String key) {
-        StandardPBEStringEncryptor standardPBEStringEncryptor = new StandardPBEStringEncryptor();
-        standardPBEStringEncryptor.setAlgorithm("PBEWITHSHA1ANDDESEDE");
-        standardPBEStringEncryptor.setPassword(key);
-        encryptor = standardPBEStringEncryptor;
+        encryptor = new CloudStackEncryptor(key, null, EncryptionUtil.class);
     }
 
     public static String encodeData(String data, String key) {
diff --git a/utils/src/main/java/com/cloud/utils/SerialVersionUID.java b/utils/src/main/java/com/cloud/utils/SerialVersionUID.java
index 363248c99a..e858726242 100644
--- a/utils/src/main/java/com/cloud/utils/SerialVersionUID.java
+++ b/utils/src/main/java/com/cloud/utils/SerialVersionUID.java
@@ -71,4 +71,5 @@ public interface SerialVersionUID {
     public static final long UnavailableCommandException = Base | 0x2f;
     public static final long OriginDeniedException = Base | 0x30;
     public static final long StorageAccessException = Base | 0x31;
+    public static final long EncryptionException = Base | 0x32;
 }
diff --git a/utils/src/main/java/com/cloud/utils/crypt/AeadBase64Encryptor.java b/utils/src/main/java/com/cloud/utils/crypt/AeadBase64Encryptor.java
new file mode 100644
index 0000000000..f62dff7c6f
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/crypt/AeadBase64Encryptor.java
@@ -0,0 +1,63 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import com.google.crypto.tink.Aead;
+import com.google.crypto.tink.aead.AeadConfig;
+import com.google.crypto.tink.subtle.AesGcmJce;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Base64;
+
+public class AeadBase64Encryptor implements Base64Encryptor {
+    Aead aead = null;
+    private final byte[] aad = new byte[]{};
+
+    public AeadBase64Encryptor(byte[] key) {
+        try {
+            AeadConfig.register();
+            MessageDigest digest = MessageDigest.getInstance("SHA-256");
+            byte[] hash = digest.digest(key);
+            this.aead = new AesGcmJce(hash);
+        } catch (Exception e) {
+            throw new EncryptionException("Failed to initialize AeadBase64Encryptor");
+        }
+    }
+
+    @Override
+    public String encrypt(String plain) {
+        try {
+            return Base64.getEncoder().encodeToString(aead.encrypt(plain.getBytes(StandardCharsets.UTF_8), aad));
+        } catch (Exception ex) {
+            throw new EncryptionException("Failed to encrypt " + plain + ". Error: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public String decrypt(String encrypted) {
+        try {
+            return new String(aead.decrypt(Base64.getDecoder().decode(encrypted), aad));
+        } catch (Exception ex) {
+            throw new EncryptionException("Failed to decrypt " + CloudStackEncryptor.hideValueWithAsterisks(encrypted) + ". Error: " + ex.getMessage());
+        }
+    }
+
+}
diff --git a/utils/src/main/java/com/cloud/utils/crypt/Base64Encryptor.java b/utils/src/main/java/com/cloud/utils/crypt/Base64Encryptor.java
new file mode 100644
index 0000000000..a0b70084e3
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/crypt/Base64Encryptor.java
@@ -0,0 +1,27 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+package com.cloud.utils.crypt;
+
+public interface Base64Encryptor {
+
+    String encrypt(String plain);
+
+    String decrypt(String encrypted);
+}
diff --git a/utils/src/main/java/com/cloud/utils/crypt/CloudStackEncryptor.java b/utils/src/main/java/com/cloud/utils/crypt/CloudStackEncryptor.java
new file mode 100644
index 0000000000..f9616775f9
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/crypt/CloudStackEncryptor.java
@@ -0,0 +1,143 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.exception.CloudRuntimeException;
+
+public class CloudStackEncryptor {
+    public static final Logger s_logger = Logger.getLogger(CloudStackEncryptor.class);
+    private Base64Encryptor encryptor = null;
+    private LegacyBase64Encryptor encryptorV1;
+    private AeadBase64Encryptor encryptorV2;
+    String password;
+    EncryptorVersion version;
+    Class callerClass;
+
+    enum EncryptorVersion {
+        V1, V2;
+
+        public static EncryptorVersion fromString(String version) {
+            if (version != null && version.equalsIgnoreCase("v1")) {
+                return V1;
+            }
+            if (version != null && version.equalsIgnoreCase("v2")) {
+                return V2;
+            }
+            if (StringUtils.isNotEmpty(version)) {
+                throw new CloudRuntimeException(String.format("Invalid encryptor version: %s, valid options are: %s", version,
+                        Arrays.stream(EncryptorVersion.values()).map(EncryptorVersion::name).collect(Collectors.joining(","))));
+            }
+            return null;
+        }
+    }
+
+    public CloudStackEncryptor(String password, String version, Class callerClass) {
+        this.password = password;
+        this.version = EncryptorVersion.fromString(version);
+        this.callerClass = callerClass;
+        initialize();
+    }
+
+    public String encrypt(String plain) {
+        if (StringUtils.isEmpty(plain)) {
+            return plain;
+        }
+        try {
+            if (encryptor == null) {
+                encryptor = encryptorV2;
+                s_logger.debug(String.format("CloudStack will encrypt and decrypt values using default encryptor : %s for class %s",
+                        encryptor.getClass().getSimpleName(), callerClass.getSimpleName()));
+            }
+            return encryptor.encrypt(plain);
+        } catch (EncryptionException e) {
+            throw new CloudRuntimeException("Error encrypting value: ", e);
+        }
+    }
+
+    public String decrypt(String encrypted) {
+        if (StringUtils.isEmpty(encrypted)) {
+            return encrypted;
+        }
+        if (encryptor != null) {
+            try {
+                return encryptor.decrypt(encrypted);
+            } catch (EncryptionException e) {
+                throw new CloudRuntimeException("Error decrypting value: " + hideValueWithAsterisks(encrypted), e);
+            }
+        } else {
+            String result = decrypt(encryptorV2, encrypted);
+            if (result != null) {
+                return result;
+            }
+            result = decrypt(encryptorV1, encrypted);
+            if (result != null) {
+                return result;
+            }
+            throw new CloudRuntimeException("Failed to decrypt value: " + hideValueWithAsterisks(encrypted));
+        }
+    }
+
+    private String decrypt(Base64Encryptor encryptorToUse, String encrypted) {
+        try {
+            String result = encryptorToUse.decrypt(encrypted);
+            s_logger.debug(String.format("CloudStack will encrypt and decrypt values using encryptor : %s for class %s",
+                    encryptorToUse.getClass().getSimpleName(), callerClass.getSimpleName()));
+            encryptor = encryptorToUse;
+            return result;
+        } catch (EncryptionException ex) {
+            s_logger.warn(String.format("Failed to decrypt value using %s: %s", encryptorToUse.getClass().getSimpleName(), hideValueWithAsterisks(encrypted)));
+        }
+        return null;
+    }
+
+    protected static String hideValueWithAsterisks(String value) {
+        if (StringUtils.isEmpty(value)) {
+            return value;
+        }
+        int numChars = value.length() >= 10 ? 5: 1;
+        int numAsterisks = 10 - numChars;
+        return value.substring(0, numChars) + "*".repeat(numAsterisks);
+    }
+
+    protected void initialize() {
+        s_logger.debug("Calling to initialize for class " + callerClass.getName());
+        encryptor = null;
+        if (EncryptorVersion.V1.equals(version)) {
+            encryptorV1 = new LegacyBase64Encryptor(password);
+            encryptor = encryptorV1;
+            s_logger.debug("Initialized with encryptor : " + encryptorV1.getClass().getSimpleName());
+        } else if (EncryptorVersion.V2.equals(version)) {
+            encryptorV2 = new AeadBase64Encryptor(password.getBytes(StandardCharsets.UTF_8));
+            encryptor = encryptorV2;
+            s_logger.debug("Initialized with encryptor : " + encryptorV2.getClass().getSimpleName());
+        } else {
+            encryptorV1 = new LegacyBase64Encryptor(password);
+            encryptorV2 = new AeadBase64Encryptor(password.getBytes(StandardCharsets.UTF_8));
+            s_logger.debug("Initialized with all possible encryptors");
+        }
+    }
+}
diff --git a/utils/src/main/java/com/cloud/utils/crypt/DBEncryptionUtil.java b/utils/src/main/java/com/cloud/utils/crypt/DBEncryptionUtil.java
index 52f2034c0a..571e144952 100644
--- a/utils/src/main/java/com/cloud/utils/crypt/DBEncryptionUtil.java
+++ b/utils/src/main/java/com/cloud/utils/crypt/DBEncryptionUtil.java
@@ -22,16 +22,13 @@ package com.cloud.utils.crypt;
 import java.util.Properties;
 
 import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
 
 import com.cloud.utils.db.DbProperties;
 import com.cloud.utils.exception.CloudRuntimeException;
 
 public class DBEncryptionUtil {
-
     public static final Logger s_logger = Logger.getLogger(DBEncryptionUtil.class);
-    private static StandardPBEStringEncryptor s_encryptor = null;
+    private static CloudStackEncryptor s_encryptor = null;
 
     public static String encrypt(String plain) {
         if (!EncryptionSecretKeyChecker.useEncryption() || (plain == null) || plain.isEmpty()) {
@@ -40,14 +37,7 @@ public class DBEncryptionUtil {
         if (s_encryptor == null) {
             initialize();
         }
-        String encryptedString = null;
-        try {
-            encryptedString = s_encryptor.encrypt(plain);
-        } catch (EncryptionOperationNotPossibleException e) {
-            s_logger.debug("Error while encrypting: " + plain);
-            throw e;
-        }
-        return encryptedString;
+        return s_encryptor.encrypt(plain);
     }
 
     public static String decrypt(String encrypted) {
@@ -58,17 +48,11 @@ public class DBEncryptionUtil {
             initialize();
         }
 
-        String plain = null;
-        try {
-            plain = s_encryptor.decrypt(encrypted);
-        } catch (EncryptionOperationNotPossibleException e) {
-            s_logger.debug("Error while decrypting: " + encrypted);
-            throw e;
-        }
-        return plain;
+        return s_encryptor.decrypt(encrypted);
     }
 
-    private static void initialize() {
+    protected static void initialize() {
+        s_logger.debug("Calling to initialize");
         final Properties dbProps = DbProperties.getDbProperties();
 
         if (EncryptionSecretKeyChecker.useEncryption()) {
@@ -76,12 +60,12 @@ public class DBEncryptionUtil {
             if (dbSecretKey == null || dbSecretKey.isEmpty()) {
                 throw new CloudRuntimeException("Empty DB secret key in db.properties");
             }
+            String dbEncryptorVersion = dbProps.getProperty("db.cloud.encryptor.version");
 
-            s_encryptor = new StandardPBEStringEncryptor();
-            s_encryptor.setAlgorithm("PBEWithMD5AndDES");
-            s_encryptor.setPassword(dbSecretKey);
+            s_encryptor = new CloudStackEncryptor(dbSecretKey, dbEncryptorVersion, DBEncryptionUtil.class);
         } else {
-            throw new CloudRuntimeException("Trying to encrypt db values when encrytion is not enabled");
+            throw new CloudRuntimeException("Trying to encrypt db values when encryption is not enabled");
         }
+        s_logger.debug("initialized");
     }
 }
diff --git a/utils/src/main/java/com/cloud/utils/crypt/EncryptionCLI.java b/utils/src/main/java/com/cloud/utils/crypt/EncryptionCLI.java
new file mode 100644
index 0000000000..e4b9c573af
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/crypt/EncryptionCLI.java
@@ -0,0 +1,79 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class EncryptionCLI {
+    private static final String verboseOption = "verbose";
+    private static final String decryptOption = "decrypt";
+    private static final String inputOption = "input";
+    private static final String passwordOption = "password";
+    private static final String encryptorVersionOption = "encryptorversion";
+
+    public static void main(String[] args) throws ParseException {
+        Options options = new Options();
+        Option verbose = Option.builder("v").longOpt(verboseOption).argName(verboseOption).required(false).desc("Verbose output").hasArg(false).build();
+        Option decrypt = Option.builder("d").longOpt(decryptOption).argName(decryptOption).required(false).desc("Decrypt instead of encrypt").hasArg(false).build();
+        Option input = Option.builder("i").longOpt(inputOption).argName(inputOption).required(true).hasArg().desc("The input string to process").build();
+        Option password = Option.builder("p").longOpt(passwordOption).argName(passwordOption).required(true).hasArg().desc("The encryption password").build();
+        Option encryptorVersion = Option.builder("e").longOpt(encryptorVersionOption).argName(encryptorVersionOption).required(false).hasArg().desc("The encryptor version").build();
+
+        options.addOption(verbose);
+        options.addOption(decrypt);
+        options.addOption(input);
+        options.addOption(password);
+        options.addOption(encryptorVersion);
+
+        CommandLine cmdLine = null;
+        CommandLineParser parser = new DefaultParser();
+        HelpFormatter helper = new HelpFormatter();
+        try {
+            cmdLine = parser.parse(options, args);
+        } catch (ParseException ex) {
+            System.out.println(ex.getMessage());
+            helper.printHelp("Usage:", options);
+            System.exit(1);
+        }
+
+        CloudStackEncryptor encryptor = new CloudStackEncryptor(cmdLine.getOptionValue(passwordOption), cmdLine.getOptionValue(encryptorVersion), EncryptionCLI.class);
+
+        String result;
+        String inString = cmdLine.getOptionValue(inputOption);
+        if (cmdLine.hasOption(decryptOption)) {
+            result = encryptor.decrypt(inString);
+        } else {
+            result = encryptor.encrypt(inString);
+        }
+
+        if (cmdLine.hasOption(verboseOption)) {
+            System.out.printf("Input: %s\n", inString);
+            System.out.printf("Encrypted: %s\n", result);
+        } else {
+            System.out.printf("%s\n", result);
+        }
+    }
+}
diff --git a/utils/src/main/java/com/cloud/utils/crypt/EncryptionException.java b/utils/src/main/java/com/cloud/utils/crypt/EncryptionException.java
new file mode 100644
index 0000000000..fb13d48e3b
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/crypt/EncryptionException.java
@@ -0,0 +1,34 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import com.cloud.utils.SerialVersionUID;
+
+public class EncryptionException extends RuntimeException {
+    private static final long serialVersionUID = SerialVersionUID.EncryptionException;
+
+    public EncryptionException(String message) {
+        super(message);
+    }
+
+    public EncryptionException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/utils/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java b/utils/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java
index ef17f7b5e7..4390bb5570 100644
--- a/utils/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java
+++ b/utils/src/main/java/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java
@@ -31,8 +31,6 @@ import java.util.Properties;
 import javax.annotation.PostConstruct;
 
 import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
 
 import com.cloud.utils.db.DbProperties;
 import com.cloud.utils.exception.CloudRuntimeException;
@@ -45,7 +43,7 @@ public class EncryptionSecretKeyChecker {
     private static final String s_altKeyFile = "key";
     private static final String s_keyFile = "key";
     private static final String s_envKey = "CLOUD_SECRET_KEY";
-    private static StandardPBEStringEncryptor s_encryptor = new StandardPBEStringEncryptor();
+    private static CloudStackEncryptor s_encryptor = null;
     private static boolean s_useEncryption = false;
 
     @PostConstruct
@@ -69,11 +67,8 @@ public class EncryptionSecretKeyChecker {
             return;
         }
 
-        s_encryptor.setAlgorithm("PBEWithMD5AndDES");
         String secretKey = null;
 
-        SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig();
-
         if (encryptionType.equals("file")) {
             InputStream is = this.getClass().getClassLoader().getResourceAsStream(s_keyFile);
             if (is == null) {
@@ -122,12 +117,14 @@ public class EncryptionSecretKeyChecker {
             throw new CloudRuntimeException("Invalid encryption type: " + encryptionType);
         }
 
-        stringConfig.setPassword(secretKey);
-        s_encryptor.setConfig(stringConfig);
-        s_useEncryption = true;
+        if (secretKey == null) {
+            throw new CloudRuntimeException("null secret key is found when setting up server encryption");
+        }
+
+        initEncryptor(secretKey);
     }
 
-    public static StandardPBEStringEncryptor getEncryptor() {
+    public static CloudStackEncryptor getEncryptor() {
         return s_encryptor;
     }
 
@@ -135,12 +132,36 @@ public class EncryptionSecretKeyChecker {
         return s_useEncryption;
     }
 
-    //Initialize encryptor for migration during secret key change
-    public static void initEncryptorForMigration(String secretKey) {
-        s_encryptor.setAlgorithm("PBEWithMD5AndDES");
-        SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig();
-        stringConfig.setPassword(secretKey);
-        s_encryptor.setConfig(stringConfig);
+    public static void initEncryptor(String secretKey) {
+        s_encryptor = new CloudStackEncryptor(secretKey, null, EncryptionSecretKeyChecker.class);
         s_useEncryption = true;
     }
+
+    public static void resetEncryptor() {
+        s_encryptor = null;
+        s_useEncryption = false;
+    }
+
+    protected static String decryptPropertyIfNeeded(String value) {
+        if (s_encryptor == null) {
+            throw new CloudRuntimeException("encryptor not initialized");
+        }
+
+        if (value.startsWith("ENC(") && value.endsWith(")")) {
+            String inner = value.substring("ENC(".length(), value.length() - ")".length());
+            return s_encryptor.decrypt(inner);
+        }
+        return value;
+    }
+
+    public static void decryptAnyProperties(Properties properties) {
+        if (s_encryptor == null) {
+            throw new CloudRuntimeException("encryptor not initialized");
+        }
+
+        for(Object prop : properties.keySet()) {
+            String value = (String) properties.get(prop);
+            properties.replace(prop, decryptPropertyIfNeeded(value));
+        }
+    }
 }
diff --git a/utils/src/main/java/com/cloud/utils/crypt/LegacyBase64Encryptor.java b/utils/src/main/java/com/cloud/utils/crypt/LegacyBase64Encryptor.java
new file mode 100644
index 0000000000..fb3dfbf549
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/crypt/LegacyBase64Encryptor.java
@@ -0,0 +1,58 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
+import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
+
+public class LegacyBase64Encryptor implements Base64Encryptor {
+    StandardPBEStringEncryptor encryptor;
+
+    public LegacyBase64Encryptor(String password) {
+        try {
+            encryptor = new StandardPBEStringEncryptor();
+            encryptor.setAlgorithm("PBEWithMD5AndDES");
+            encryptor.setPassword(password);
+            SimpleStringPBEConfig stringConfig = new SimpleStringPBEConfig();
+            encryptor.setConfig(stringConfig);
+        } catch (Exception e) {
+            throw new EncryptionException("Failed to initialize LegacyBase64Encryptor");
+        }
+    }
+
+    @Override
+    public String encrypt(String plain) {
+        try {
+            return encryptor.encrypt(plain);
+        } catch (Exception ex) {
+            throw new EncryptionException("Failed to encrypt " + plain + ". Error: " + ex.getMessage());
+        }
+    }
+
+    @Override
+    public String decrypt(String encrypted) {
+        try {
+            return encryptor.decrypt(encrypted);
+        } catch (Exception ex) {
+            throw new EncryptionException("Failed to decrypt " + CloudStackEncryptor.hideValueWithAsterisks(encrypted) + ". Error: " + ex.getMessage());
+        }
+    }
+
+}
diff --git a/utils/src/main/java/com/cloud/utils/db/DbProperties.java b/utils/src/main/java/com/cloud/utils/db/DbProperties.java
index d99e6c011b..3851501e74 100644
--- a/utils/src/main/java/com/cloud/utils/db/DbProperties.java
+++ b/utils/src/main/java/com/cloud/utils/db/DbProperties.java
@@ -27,14 +27,11 @@ import java.util.Properties;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.properties.EncryptableProperties;
 
 import com.cloud.utils.PropertiesUtil;
 import com.cloud.utils.crypt.EncryptionSecretKeyChecker;
 
 public class DbProperties {
-
     private static final Logger log = Logger.getLogger(DbProperties.class);
 
     private static Properties properties = new Properties();
@@ -46,11 +43,12 @@ public class DbProperties {
         checker.check(dbProps, dbEncryptionType);
 
         if (EncryptionSecretKeyChecker.useEncryption()) {
+            log.debug("encryptionsecretkeychecker using encryption");
+            EncryptionSecretKeyChecker.decryptAnyProperties(dbProps);
             return dbProps;
         } else {
-            EncryptableProperties encrProps = new EncryptableProperties(EncryptionSecretKeyChecker.getEncryptor());
-            encrProps.putAll(dbProps);
-            return encrProps;
+            log.debug("encryptionsecretkeychecker not using encryption");
+            return dbProps;
         }
     }
 
@@ -81,12 +79,10 @@ public class DbProperties {
                 checker.check(dbProps, dbEncryptionType);
 
                 if (EncryptionSecretKeyChecker.useEncryption()) {
-                    StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor();
-                    EncryptableProperties encrDbProps = new EncryptableProperties(encryptor);
-                    encrDbProps.putAll(dbProps);
-                    dbProps = encrDbProps;
+                    EncryptionSecretKeyChecker.decryptAnyProperties(dbProps);
                 }
             } catch (IOException e) {
+                log.error(String.format("Failed to load DB properties: %s", e.getMessage()), e);
                 throw new IllegalStateException("Failed to load db.properties", e);
             } finally {
                 IOUtils.closeQuietly(is);
@@ -94,6 +90,8 @@ public class DbProperties {
 
             properties = dbProps;
             loaded = true;
+        } else {
+            log.debug("DB properties were already loaded");
         }
 
         return properties;
diff --git a/utils/src/main/java/com/cloud/utils/server/ServerProperties.java b/utils/src/main/java/com/cloud/utils/server/ServerProperties.java
index 4eabc5f99f..b1a845cc50 100644
--- a/utils/src/main/java/com/cloud/utils/server/ServerProperties.java
+++ b/utils/src/main/java/com/cloud/utils/server/ServerProperties.java
@@ -19,8 +19,6 @@ package com.cloud.utils.server;
 import com.cloud.utils.crypt.EncryptionSecretKeyChecker;
 import org.apache.commons.io.IOUtils;
 import org.apache.log4j.Logger;
-import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
-import org.jasypt.properties.EncryptableProperties;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -43,10 +41,7 @@ public class ServerProperties {
                 checker.check(serverProps, passwordEncryptionType);
 
                 if (EncryptionSecretKeyChecker.useEncryption()) {
-                    StandardPBEStringEncryptor encryptor = EncryptionSecretKeyChecker.getEncryptor();
-                    EncryptableProperties encrServerProps = new EncryptableProperties(encryptor);
-                    encrServerProps.putAll(serverProps);
-                    serverProps = encrServerProps;
+                    EncryptionSecretKeyChecker.decryptAnyProperties(serverProps);
                 }
             } catch (IOException e) {
                 throw new IllegalStateException("Failed to load server.properties", e);
diff --git a/utils/src/test/java/com/cloud/utils/crypt/EncryptionSecretKeyCheckerTest.java b/utils/src/test/java/com/cloud/utils/crypt/EncryptionSecretKeyCheckerTest.java
new file mode 100644
index 0000000000..8f7e4082c5
--- /dev/null
+++ b/utils/src/test/java/com/cloud/utils/crypt/EncryptionSecretKeyCheckerTest.java
@@ -0,0 +1,58 @@
+//
+// 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 com.cloud.utils.crypt;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Properties;
+
+public class EncryptionSecretKeyCheckerTest {
+    @Before
+    public void setup() {
+        EncryptionSecretKeyChecker.initEncryptor("managementkey");
+    }
+
+    @After
+    public void tearDown() {
+        EncryptionSecretKeyChecker.resetEncryptor();
+    }
+
+    @Test
+    public void decryptPropertyIfNeededTest() {
+        String rawValue = "ENC(iYVsCZXiGiC6SzZLMNBvBL93hoUpntxkuRjyaqC8L+JYKXw=)";
+        String result = EncryptionSecretKeyChecker.decryptPropertyIfNeeded(rawValue);
+        Assert.assertEquals("encthis", result);
+    }
+
+    @Test
+    public void decryptAnyPropertiesTest() {
+        Properties props = new Properties();
+        props.setProperty("db.cloud.encrypt.secret", "ENC(iYVsCZXiGiC6SzZLMNBvBL93hoUpntxkuRjyaqC8L+JYKXw=)");
+        props.setProperty("other.unencrypted", "somevalue");
+
+        EncryptionSecretKeyChecker.decryptAnyProperties(props);
+
+        Assert.assertEquals("encthis", props.getProperty("db.cloud.encrypt.secret"));
+        Assert.assertEquals("somevalue", props.getProperty("other.unencrypted"));
+    }
+}
\ No newline at end of file