diff --git a/deps/.classpath b/deps/.classpath
index 9e74253e89f..b9eb504b959 100755
--- a/deps/.classpath
+++ b/deps/.classpath
@@ -44,5 +44,7 @@
+
+
diff --git a/deps/cloud-commons-configuration-1.8.jar b/deps/cloud-commons-configuration-1.8.jar
new file mode 100644
index 00000000000..ae9ae9969b6
Binary files /dev/null and b/deps/cloud-commons-configuration-1.8.jar differ
diff --git a/deps/cloud-commons-lang-2.6.jar b/deps/cloud-commons-lang-2.6.jar
new file mode 100644
index 00000000000..98467d3a653
Binary files /dev/null and b/deps/cloud-commons-lang-2.6.jar differ
diff --git a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java
new file mode 100755
index 00000000000..55d4913cff5
--- /dev/null
+++ b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChanger.java
@@ -0,0 +1,428 @@
+/**
+ * Copyright (C) 2012 Citrix Systems, Inc. All rights reserved
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.utils.crypt;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.sql.Connection;
+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.Properties;
+
+import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
+import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
+import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
+import org.jasypt.properties.EncryptableProperties;
+
+import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+
+/*
+ * 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
+ * DB data migrated using new DB secret key
+ */
+public class EncryptionSecretKeyChanger {
+
+ private StandardPBEStringEncryptor oldEncryptor = new StandardPBEStringEncryptor();
+ private StandardPBEStringEncryptor newEncryptor = new StandardPBEStringEncryptor();
+ private static final String keyFile = "/etc/cloud/management/key";
+
+ public static void main(String[] args){
+ List argsList = Arrays.asList(args);
+ Iterator iter = argsList.iterator();
+ String oldMSKey = null;
+ String oldDBKey = null;
+ String newMSKey = null;
+ String newDBKey = null;
+
+ //Parse command-line args
+ while (iter.hasNext()) {
+ String arg = iter.next();
+ // Old MS Key
+ if (arg.equals("-m")) {
+ oldMSKey = iter.next();
+ }
+ // Old DB Key
+ if (arg.equals("-d")) {
+ oldDBKey = iter.next();
+ }
+ // New MS Key
+ if (arg.equals("-n")) {
+ newMSKey = iter.next();
+ }
+ // New DB Key
+ if (arg.equals("-e")) {
+ newDBKey = iter.next();
+ }
+ }
+
+ if(oldMSKey == null || oldDBKey ==null){
+ System.out.println("Existing MS secret key or DB secret key is not provided");
+ usage();
+ return;
+ }
+
+ if(newMSKey == null && newDBKey ==null){
+ System.out.println("New MS secret key and DB secret are both not provided");
+ usage();
+ return;
+ }
+
+ final File dbPropsFile = PropertiesUtil.findConfigFile("db.properties");
+ final Properties dbProps;
+ 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 {
+ dbProps.load(new FileInputStream(dbPropsFile));
+ backupDBProps = new PropertiesConfiguration(dbPropsFile);
+ } catch (FileNotFoundException e) {
+ System.out.println("db.properties file not found while reading DB secret key" +e.getMessage());
+ } catch (IOException e) {
+ System.out.println("Error while reading DB secret key from db.properties" +e.getMessage());
+ } catch (ConfigurationException e) {
+ e.printStackTrace();
+ }
+
+ 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;
+ }
+
+ if(!oldDBKey.equals(dbSecretKey)){
+ System.out.println("Incorrect MS Secret Key or DB Secret Key");
+ return;
+ }
+
+ System.out.println("Secret key provided matched the key in db.properties");
+ final String encryptionType = dbProps.getProperty("db.cloud.encryption.type");
+
+ if(newMSKey == null){
+ System.out.println("No change in MS Key. Skipping migrating db.properties");
+ } else {
+ if(!keyChanger.migrateProperties(dbPropsFile, dbProps, newMSKey, newDBKey)){
+ 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);
+ bwriter.close();
+ } catch (IOException e) {
+ System.out.println("Failed to write new secret to file. Please update the file manually");
+ }
+ }
+ }
+ }
+
+ boolean success = false;
+ if(newDBKey == null || newDBKey.equals(oldDBKey)){
+ System.out.println("No change in DB Secret Key. Skipping Data Migration");
+ } else {
+ EncryptionSecretKeyChecker.initEncryptorForMigration(oldMSKey);
+ try {
+ success = keyChanger.migrateData(oldDBKey, newDBKey);
+ } catch (Exception e) {
+ System.out.println("Error during data migration");
+ e.printStackTrace();
+ success = false;
+ }
+ }
+
+ if(success){
+ System.out.println("Successfully updated secret key(s)");
+ }
+ else {
+ System.out.println("Data Migration failed. Reverting db.properties");
+ //revert db.properties
+ try {
+ backupDBProps.save();
+ } catch (ConfigurationException e) {
+ e.printStackTrace();
+ }
+ if(encryptionType.equals("file")){
+ //revert secret key in file
+ try {
+ FileWriter fwriter = new FileWriter(keyFile);
+ BufferedWriter bwriter = new BufferedWriter(fwriter);
+ bwriter.write(oldMSKey);
+ bwriter.close();
+ } catch (IOException e) {
+ System.out.println("Failed to revert to old secret to file. Please update the file manually");
+ }
+ }
+ }
+ }
+
+ private boolean migrateProperties(File dbPropsFile, Properties dbProps, String newMSKey, String newDBKey){
+ System.out.println("Migrating db.properties..");
+ StandardPBEStringEncryptor msEncryptor = new StandardPBEStringEncryptor();;
+ initEncryptor(msEncryptor, newMSKey);
+
+ try {
+ PropertiesConfiguration newDBProps = new PropertiesConfiguration(dbPropsFile);
+ if(newDBKey!=null && !newDBKey.isEmpty()){
+ newDBProps.setProperty("db.cloud.encrypt.secret", "ENC("+msEncryptor.encrypt(newDBKey)+")");
+ }
+ String prop = dbProps.getProperty("db.cloud.password");
+ if(prop!=null && !prop.isEmpty()){
+ newDBProps.setProperty("db.cloud.password", "ENC("+msEncryptor.encrypt(prop)+")");
+ }
+ prop = dbProps.getProperty("db.usage.password");
+ if(prop!=null && !prop.isEmpty()){
+ newDBProps.setProperty("db.usage.password", "ENC("+msEncryptor.encrypt(prop)+")");
+ }
+ newDBProps.save(dbPropsFile.getAbsolutePath());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ System.out.println("Migrating db.properties Done.");
+ return true;
+ }
+
+ private boolean migrateData(String oldDBKey, String newDBKey){
+ System.out.println("Begin Data migration");
+ initEncryptor(oldEncryptor, oldDBKey);
+ initEncryptor(newEncryptor, newDBKey);
+ System.out.println("Initialised Encryptors");
+
+ Transaction txn = Transaction.open("Migrate");
+ txn.start();
+ try {
+ Connection conn;
+ try {
+ conn = txn.getConnection();
+ } catch (SQLException e) {
+ throw new CloudRuntimeException("Unable to migrate encrypted data in the database", e);
+ }
+
+ migrateConfigValues(conn);
+ migrateHostDetails(conn);
+ migrateVNCPassword(conn);
+ migrateUserCredentials(conn);
+
+ txn.commit();
+ } finally {
+ txn.close();
+ }
+ System.out.println("End Data migration");
+ 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()){
+ return value;
+ }
+ String decryptVal = oldEncryptor.decrypt(value);
+ return newEncryptor.encrypt(decryptVal);
+ }
+
+ private void migrateConfigValues(Connection conn) {
+ System.out.println("Begin migrate config values");
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try {
+ pstmt = conn.prepareStatement("select name, value from configuration where category = 'Hidden'");
+ rs = pstmt.executeQuery();
+ while (rs.next()) {
+ String name = rs.getString(1);
+ String value = rs.getString(2);
+ if(value == null || value.isEmpty()){
+ continue;
+ }
+ String encryptedValue = migrateValue(value);
+ pstmt = conn.prepareStatement("update configuration set value=? where name=?");
+ pstmt.setBytes(1, encryptedValue.getBytes("UTF-8"));
+ pstmt.setString(2, name);
+ 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);
+ } finally {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+
+ if (pstmt != null) {
+ pstmt.close();
+ }
+ } catch (SQLException e) {
+ }
+ }
+ System.out.println("End migrate config values");
+ }
+
+ private void migrateHostDetails(Connection conn) {
+ System.out.println("Begin migrate host details");
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try {
+ pstmt = conn.prepareStatement("select id, value from host_details where name = 'password'");
+ rs = pstmt.executeQuery();
+ while (rs.next()) {
+ long id = rs.getLong(1);
+ String value = rs.getString(2);
+ if(value == null || value.isEmpty()){
+ continue;
+ }
+ String encryptedValue = migrateValue(value);
+ pstmt = conn.prepareStatement("update host_details set value=? where id=?");
+ pstmt.setBytes(1, encryptedValue.getBytes("UTF-8"));
+ pstmt.setLong(2, id);
+ 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);
+ } finally {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+
+ if (pstmt != null) {
+ pstmt.close();
+ }
+ } catch (SQLException e) {
+ }
+ }
+ System.out.println("End migrate host details");
+ }
+
+ private void migrateVNCPassword(Connection conn) {
+ System.out.println("Begin migrate VNC password");
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try {
+ pstmt = conn.prepareStatement("select id, vnc_password from vm_instance");
+ rs = pstmt.executeQuery();
+ while (rs.next()) {
+ long id = rs.getLong(1);
+ String value = rs.getString(2);
+ if(value == null || value.isEmpty()){
+ continue;
+ }
+ String encryptedValue = migrateValue(value);
+ pstmt = conn.prepareStatement("update vm_instance set vnc_password=? where id=?");
+ pstmt.setBytes(1, encryptedValue.getBytes("UTF-8"));
+ pstmt.setLong(2, id);
+ pstmt.executeUpdate();
+ }
+ } 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);
+ } finally {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+
+ if (pstmt != null) {
+ pstmt.close();
+ }
+ } catch (SQLException e) {
+ }
+ }
+ System.out.println("End migrate VNC password");
+ }
+
+ private void migrateUserCredentials(Connection conn) {
+ System.out.println("Begin migrate user credentials");
+ PreparedStatement pstmt = null;
+ ResultSet rs = null;
+ try {
+ pstmt = conn.prepareStatement("select id, secret_key from user");
+ rs = pstmt.executeQuery();
+ while (rs.next()) {
+ long id = rs.getLong(1);
+ String secretKey = rs.getString(2);
+ if(secretKey == null || secretKey.isEmpty()){
+ continue;
+ }
+ String encryptedSecretKey = migrateValue(secretKey);
+ pstmt = conn.prepareStatement("update user set secret_key=? where id=?");
+ pstmt.setBytes(1, encryptedSecretKey.getBytes("UTF-8"));
+ pstmt.setLong(2, id);
+ pstmt.executeUpdate();
+ }
+ } catch (SQLException e) {
+ throw new CloudRuntimeException("Unable update user secret key ", e);
+ } catch (UnsupportedEncodingException e) {
+ throw new CloudRuntimeException("Unable update user secret key ", e);
+ } finally {
+ try {
+ if (rs != null) {
+ rs.close();
+ }
+
+ if (pstmt != null) {
+ pstmt.close();
+ }
+ } catch (SQLException e) {
+ }
+ }
+ System.out.println("End migrate user credentials");
+ }
+
+ private static void usage(){
+ System.out.println("Usage: \tEncryptionSecretKeyChanger \n" +
+ "\t\t-m \n" +
+ "\t\t-d \n" +
+ "\t\t-n [New Mgmt Secret Key] \n" +
+ "\t\t-e [New DB Secret Key]");
+ }
+}
diff --git a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java
index b5bc5e4c504..68a04e7e345 100755
--- a/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java
+++ b/utils/src/com/cloud/utils/crypt/EncryptionSecretKeyChecker.java
@@ -136,4 +136,13 @@ public class EncryptionSecretKeyChecker implements SystemIntegrityChecker {
public static boolean useEncryption(){
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);
+ s_useEncryption = true;
+ }
}