/** * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2012], VMware, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. * */ package org.hyperic.util; import org.hyperic.util.file.FileUtil; import org.hyperic.util.security.SecurityUtil; import java.io.*; import java.util.*; /** * A set of utilities to use when encrypting/decryption properties. */ public class PropertyEncryptionUtil { /** * When encrypting properties, the encryption key is saved as a serialized object to the disk. However, since an * encryption key is a string, it is easily extracted from the binary (serialized) file. Therefore, before * serialization the key is encrypted using this obvious value is the key. */ private static final String KEY_ENCRYPTION_KEY = "Security Kung-Fu"; private static final String ENCRYPTION_KEY_PROP = "k"; private static final String LOCK_FILE_NAME = System.getProperty("java.io.tmpdir") + "/agent.encrypt.lock"; /** * Read the properties encryption key from a file. * * @param fileName the path and name of the file into which the encryption key is serialized. * @return the encryption key. * @throws PropertyUtilException if the file doesn't exist, the provided file name is invalid, * or if the deserialization fails. */ public static synchronized String getPropertyEncryptionKey(String fileName) throws PropertyUtilException { // Validate the file-name if (fileName == null || fileName.trim().length() < 1) { throw new PropertyUtilException("Illegal Argument: fileName [" + fileName + "]"); } // Create a new file instance. File encryptionKeyFile = new File(fileName); // Make sure that the file exists. if (!encryptionKeyFile.exists()) { throw new PropertyUtilException("The encryption key file [" + fileName + "] doesn't exist"); } String encryptionKey; try { // Read the properties file Properties props = new Properties(); props.load(new FileInputStream(encryptionKeyFile)); // Get the encrypted key. String encryptedKey = props.getProperty(ENCRYPTION_KEY_PROP); if (encryptedKey != null) { // Decrypt the hey. encryptionKey = SecurityUtil.decrypt( SecurityUtil.DEFAULT_ENCRYPTION_ALGORITHM, KEY_ENCRYPTION_KEY, encryptedKey); } else { throw new PropertyUtilException("Invalid properties encryption key"); } } catch (Exception exc) { throw new PropertyUtilException(exc); } // Return the key. return encryptionKey; } // EOM /** * Make sure that the values all secure properties (in the given property file) get encrypted. * * @param propsFileName the name of the properties file to encrypt. * @param encryptionKeyFileName the name of the encryption key file. * @param secureProps a set of names of secure properties. * @throws PropertyUtilException */ public static synchronized void ensurePropertiesEncryption( String propsFileName, String encryptionKeyFileName, Set<String> secureProps) throws PropertyUtilException { // Wait while another thread is executing this process. int tries = 10; while (!lock() && tries > 0) { tries--; try { Thread.sleep(1000); } catch (InterruptedException ignore) { /* ignore */ } } if (tries <=0) { throw new PropertyUtilException(LOCK_FILE_NAME + " is locked. can't continue."); } try { // Make sure that the properties file exists. if (propsFileName == null || propsFileName.trim().length() < 1) { throw new PropertyUtilException("Illegal Argument: propsFileName [" + propsFileName + "]"); } // Make sure that the encryption key name is valid. if (encryptionKeyFileName == null || encryptionKeyFileName.trim().length() < 1) { throw new PropertyUtilException("Illegal Argument: encryptionKeyFileName [" + encryptionKeyFileName + "]"); } // If there's nothing to secure then return if (secureProps == null || secureProps.size() < 1) { return; } // Load the properties from the filesystem. Properties props = PropertyUtil.loadProperties(propsFileName); // Check if the properties are already encrypted. boolean alreadyEncrypted = isAlreadyEncrypted(props); // 'Reference' the encryption-key file. File encryptionKeyFile = new File(encryptionKeyFileName); // The properties encryption key. String encryptionKey; // If the encryption key file exists then load it. If it doesn't exist then create one, only if the properties // aren't already encrypted. If the properties are encrypted then throw an exception (we can't do much with // encrypted properties if the encryption key is missing). if (encryptionKeyFile.exists()) { encryptionKey = getPropertyEncryptionKey(encryptionKeyFileName); } else { if (alreadyEncrypted) { // The encryption key is new, but the properties are already encrypted. throw new PropertyUtilException("The properties are already encrypted, but the encryption key is missing"); } encryptionKey = createAndStorePropertyEncryptionKey(encryptionKeyFileName); } // Collect all the properties that should be encrypted but still aren't Map<String, String> unEncProps = new HashMap<String, String>(); for (Enumeration<?> propKeys = props.propertyNames(); propKeys.hasMoreElements(); ) { String key = (String) propKeys.nextElement(); String value = props.getProperty(key); if (value != null && secureProps.contains(key) && !SecurityUtil.isMarkedEncrypted(value)) { unEncProps.put(key, value); } } // Encrypt secure properties. if (unEncProps.size() > 0) { PropertyUtil.storeProperties(propsFileName, encryptionKey, unEncProps); } } finally { //Delete the lock file to lock file that prevent parallel processing. unlock(false); } } // EOM /** * Prevents from other threads to change to properties file. * * @return true if lock was successful; false otherwise. */ static boolean lock() { File lockFile = new File(LOCK_FILE_NAME); try { return lockFile.createNewFile(); } catch (IOException ignore) { return false; } } /** * Remove the lock. * * @return true if the lock was successfully removed; false otherwise. */ public static boolean unlock(boolean shouldLog) { File lockFile = new File(LOCK_FILE_NAME); boolean res = lockFile.delete(); if (res && shouldLog){ System.out.println(LOCK_FILE_NAME + " was deleted.");//not sure we have log file at this stage, so using system.out. } return res; } /** * Creates a new encryption key and saves it as a serialized object to the files system. * * @param fileName the path and name of the file into which the encryption key is serialized. * @return the encryption key. * @throws PropertyUtilException if the file already exists, the provided file name is invalid, * or the object serialization fails. */ static synchronized String createAndStorePropertyEncryptionKey(String fileName) throws PropertyUtilException { // Validate the file-name if (fileName == null || fileName.trim().length() < 1) { throw new PropertyUtilException("Illegal Argument: fileName [" + fileName + "]"); } // Create a new file instance. File encryptionKeyFile = new File(fileName); // Check if the file already exists. Overriding an encryption file isn't allowed. if (encryptionKeyFile.exists()) { throw new PropertyUtilException("Attempt to override an encryption key file [" + fileName + "]"); } String encryptionKey; try { // Create the encryption key. encryptionKey = SecurityUtil.generateRandomToken(); // Encrypt the key and save it ot the disk. String encryptedKey = SecurityUtil.encrypt( SecurityUtil.DEFAULT_ENCRYPTION_ALGORITHM, KEY_ENCRYPTION_KEY, encryptionKey); // Store the key Properties props = new Properties(); props.put(ENCRYPTION_KEY_PROP, encryptedKey); props.store(new FileOutputStream(fileName), null); // set read/write permissions to be given to the owner only File encKeyFile = new File(fileName); FileUtil.setReadWriteOnlyByOwner(encKeyFile); } catch (Exception exc) { throw new PropertyUtilException(exc); } // Return the key return encryptionKey; } // EOM /** * Check if the provided properties are already encrypted (the secure ones). * * @param props the properties to check * @return true if an encrypted property is found; otherwise false. */ private static boolean isAlreadyEncrypted(Properties props) { // Iterate the values for (Object value : props.values()) { // Check for encryption. if (SecurityUtil.isMarkedEncrypted((String) value)) { // Encryption found -- return (true) without completing the iteration. return true; } } // Non of the values is encrypted -- return false. return false; } // EOM }