diff options
author | Chou, Joseph <jc2555@att.com> | 2019-03-07 11:49:23 -0500 |
---|---|---|
committer | Chou, Joseph <jc2555@att.com> | 2019-03-12 09:57:32 -0400 |
commit | d574a2fc71ad43f8bc025ea9cc23ca718bc66570 (patch) | |
tree | fff476156f8bcdffa96fdd6b342d21ef0e7b539d | |
parent | 23a3dc4ece2f1533fe1d6b627b5db05e7754a70c (diff) |
ONAP password encryption tool
Migrate ECOMP Policy password encryption tool to ONAP
Issue-ID: POLICY-1561
Change-Id: I9020efb7698b95c36c4ebff842a318bf8beefc69
Signed-off-by: Joseph Chou <jc2555@att.com>
3 files changed, 383 insertions, 0 deletions
diff --git a/utils/pom.xml b/utils/pom.xml index 3263b7b9..3faf9121 100644 --- a/utils/pom.xml +++ b/utils/pom.xml @@ -41,6 +41,10 @@ <artifactId>commons-lang3</artifactId> </dependency> <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>test</scope> diff --git a/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java b/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java new file mode 100644 index 00000000..ade8b467 --- /dev/null +++ b/utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java @@ -0,0 +1,259 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.security; + +import com.google.common.base.Charsets; + +import java.security.GeneralSecurityException; + +import java.security.SecureRandom; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.xml.bind.DatatypeConverter; + +import org.apache.commons.lang3.ArrayUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AES Encryption Utilities. + */ +public class CryptoUtils { + private static Logger logger = LoggerFactory.getLogger(CryptoUtils.class); + + /** + * Definition of encryption algorithm. + */ + private static final String ALGORITHM = "AES"; + + /** + * Detailed definition of encryption algorithm. + */ + private static final String ALGORITHM_DETAILS = ALGORITHM + "/CBC/PKCS5PADDING"; + + private static final int IV_BLOCK_SIZE_IN_BITS = 128; + + /** + * An Initial Vector of 16 Bytes, so 32 Hexadecimal Chars. + */ + private static final int IV_BLOCK_SIZE_IN_BYTES = IV_BLOCK_SIZE_IN_BITS / 8; + + private static int validSize = (2 * IV_BLOCK_SIZE_IN_BYTES) + 4; + + private SecretKeySpec secretKeySpec; + + private static final String RANDOM_NUMBER_GENERATOR = "SHA1PRNG"; + + /** + * This method is used as the main entry point when testing. + * + */ + public static void main(String[] args) { + if (args.length == 3) { + if (args[0].equals("enc")) { + String encryptedValue = encrypt(args[1], args[2]); + logger.info("original value: " + args[1] + " encrypted value: " + encryptedValue); + } else if (args[0].equals("dec")) { + String decryptedValue = decrypt(args[1], args[2]); + logger.info("original value: " + args[1] + " decrypted value: " + decryptedValue); + } else { + logger.info("Unknown request: " + args[0]); + } + } else { + logger.info("Usage : CryptoUtils enc/dec password secretKey"); + logger.info("Example: CryptoUtils enc HelloWorld 1234"); + logger.info("Example: CryptoUtils dec enc:112233 1234"); + } + } + + public CryptoUtils(SecretKeySpec secretKeySpec) { + this.secretKeySpec = secretKeySpec; + } + + /** + * CryptoUtils - encryption tool constructor. + * @param secretKey + * AES supports 128, 192 or 256-bit long key size, it can be plain text or generated with key generator + */ + public CryptoUtils(String secretKey) { + this.secretKeySpec = readSecretKeySpec(secretKey); + } + + /** + * Encrypt a value based on the Policy Encryption Key. + * Equivalent openssl command: echo -n "123456" | openssl aes-128-cbc -e -K PrivateHexkey + * -iv 16BytesIV | xxd -u -g100 + * + * <p>Final result is to put in properties file is: IV + Outcome of openssl command + * + * @param value + * The plain text string + * @return The encrypted String + * @throws GeneralSecurityException + * In case of issue with the encryption + */ + public String encrypt(String value) throws GeneralSecurityException { + return encryptValue(value, secretKeySpec); + } + + /** + * Encrypt a value based on the Policy Encryption Key. + * + * @param value + * The plain text string + * @param secretKey + * The secret key + * @return The encrypted String + */ + public static String encrypt(String value, String secretKey) { + SecretKeySpec keySpec = readSecretKeySpec(secretKey); + return encryptValue(value, keySpec); + } + + private static String encryptValue(String value, SecretKeySpec keySpec) { + if (value == null || value.isEmpty()) { + logger.debug("Empty/null value passed in for decryption"); + return value; + } + if (isEncrypted(value)) { + return value; + } + try { + Cipher cipher = Cipher.getInstance(ALGORITHM_DETAILS); + byte[] iv = new byte[IV_BLOCK_SIZE_IN_BYTES]; + SecureRandom.getInstance(RANDOM_NUMBER_GENERATOR).nextBytes(iv); + IvParameterSpec ivspec = new IvParameterSpec(iv); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivspec); + + return "enc:" + DatatypeConverter.printBase64Binary( + ArrayUtils.addAll(iv, cipher.doFinal(value.getBytes(Charsets.UTF_8)))); + } catch (Exception e) { + logger.error("Could not encrypt value - exception: ", e); + return value; + } + } + + /** + * Decrypt a value based on the Policy Encryption Key if string begin with 'enc:'. + * Equivalent openssl command: echo -n 'Encrypted string' | xxd -r -ps | openssl aes-128-cbc -d + * -K PrivateHexKey -iv 16BytesIVFromEncryptedString + * + * @param value + * The encrypted string that must be decrypted using the Policy Encryption Key + * @return The String decrypted if string begin with 'enc:' + * @throws GeneralSecurityException + * In case of issue with the encryption + */ + public String decrypt(String value) throws GeneralSecurityException { + return decryptValue(value, secretKeySpec); + } + + /** + * Decrypt a value based on the Policy Encryption Key if string begin with 'enc:'. + * + * @param value + * The encrypted string that must be decrypted using the Policy Encryption Key + * @param secretKey + * The secret key + * @return The String decrypted if string begin with 'enc:' + */ + public static String decrypt(String value, String secretKey) { + SecretKeySpec keySpec = readSecretKeySpec(secretKey); + if (keySpec != null) { + return decryptValue(value, keySpec); + } else { + return value; + } + } + + private static String decryptValue(String value, SecretKeySpec keySpec) { + if (value == null || value.isEmpty() || !isEncrypted(value)) { + return value; + } + if (value.length() < validSize) { + throw new IllegalArgumentException("Invalid size on input value"); + } + try { + String pureValue = value.substring(4); + byte[] encryptedValue = DatatypeConverter.parseBase64Binary(pureValue); + + Cipher cipher = Cipher.getInstance(ALGORITHM_DETAILS); + IvParameterSpec ivspec = new IvParameterSpec( + ArrayUtils.subarray(encryptedValue, 0, IV_BLOCK_SIZE_IN_BYTES)); + byte[] realData = ArrayUtils.subarray(encryptedValue, IV_BLOCK_SIZE_IN_BYTES, encryptedValue.length); + + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivspec); + byte[] decrypted = cipher.doFinal(realData); + return new String(decrypted, Charsets.UTF_8); + } catch (Exception e) { + logger.error("Could not decrypt value - exception: ", e); + } + return value; + } + + /** + * Method used to generate the SecretKeySpec from a Hex String. + * + * @param keyString + * The key as a string in base64 String + * @return The SecretKeySpec created + * @throws DecoderException + * In case of issues with the decoding of base64 String + */ + private static SecretKeySpec getSecretKeySpec(String keyString) { + byte[] key = DatatypeConverter.parseBase64Binary(keyString); + return new SecretKeySpec(key, ALGORITHM); + } + + /** + * Get Secret Key Spec from user provided secret key string. + * + * @param secretKey + * user provided secretKey String + * @return SecretKeySpec secret key spec read from getSecretKeySpec + */ + private static SecretKeySpec readSecretKeySpec(String secretKey) { + if (secretKey != null && !secretKey.isEmpty()) { + SecretKeySpec keySpec = null; + try { + keySpec = getSecretKeySpec(secretKey); + return keySpec; + } catch (Exception e) { + logger.error("Invalid key - exception: ", e); + return null; + } + } else { + logger.error("Secretkey can not be null or empty"); + return null; + } + } + /** + * Check if string is encrypted by verify if string prefix with 'enc:'. + * + * @param value + * The encrypted string or plain text value + * @return boolean value indicate if string prefix with enc: or not + */ + public static Boolean isEncrypted(String value) { + return (value != null && value.startsWith("enc:")); + } +}
\ No newline at end of file diff --git a/utils/src/test/java/org/onap/policy/common/utils/security/CryptoUtilsTest.java b/utils/src/test/java/org/onap/policy/common/utils/security/CryptoUtilsTest.java new file mode 100644 index 00000000..fd3daee8 --- /dev/null +++ b/utils/src/test/java/org/onap/policy/common/utils/security/CryptoUtilsTest.java @@ -0,0 +1,120 @@ +/*- + * ============LICENSE_START======================================================= + * ONAP + * ================================================================================ + * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * Licensed 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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.common.utils.security; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.security.GeneralSecurityException; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Unit test for simple App. + */ + +public class CryptoUtilsTest { + private static Logger logger = LoggerFactory.getLogger(CryptoUtilsTest.class); + private final String pass = "HelloWorld"; + private final String secretKey = "12345678901234567890123456789012"; + private final String encryptedPass = "enc:8XxseP5W5ODxzPrReNKd9JBYLv0iiAzy9BHnMKau5yg="; + + @Test + public void testEncrypt() throws GeneralSecurityException { + logger.info("testEncrypt:"); + CryptoUtils cryptoUtils = new CryptoUtils(secretKey); + String encryptedValue = cryptoUtils.encrypt(pass); + logger.info("original value : " + pass + " encrypted value: " + encryptedValue); + + String decryptedValue = cryptoUtils.decrypt(encryptedValue); + logger.info("encrypted value: " + encryptedValue + " decrypted value : " + decryptedValue); + assertEquals(pass, decryptedValue); + } + + @Test + public void testDecrypt() throws GeneralSecurityException { + logger.info("testDecrypt:"); + CryptoUtils cryptoUtils = new CryptoUtils(secretKey); + String decryptedValue = cryptoUtils.decrypt(encryptedPass); + logger.info("encrypted value: " + encryptedPass + " decrypted value : " + decryptedValue); + assertEquals(pass, decryptedValue); + } + + @Test + public void testStaticEncrypt() { + logger.info("testStaticEncrypt:"); + String encryptedValue = CryptoUtils.encrypt(pass, secretKey); + logger.info("original value : " + pass + " encrypted value: " + encryptedValue); + + String decryptedValue = CryptoUtils.decrypt(encryptedValue, secretKey); + logger.info("encrypted value: " + encryptedValue + " decrypted value : " + decryptedValue); + assertEquals(pass, decryptedValue); + } + + @Test + public void testStaticDecrypt() { + logger.info("testStaticDecrypt:"); + String decryptedValue = CryptoUtils.decrypt(encryptedPass, secretKey); + logger.info("encrypted value: " + encryptedPass + " decrypted value : " + decryptedValue); + assertEquals(pass, decryptedValue); + } + + @Test + public void testBadInputs() { + String badKey = CryptoUtils.encrypt(pass, "test"); + assertEquals(pass, badKey); + + String badDecrypt = CryptoUtils.decrypt(encryptedPass, ""); + assertEquals(encryptedPass, badDecrypt); + + String emptyValue = CryptoUtils.encrypt(new String(), secretKey); + assertEquals("", emptyValue); + + String emptyDecrypt = CryptoUtils.decrypt(new String(), secretKey); + assertEquals("", emptyDecrypt); + + String nullValue = CryptoUtils.encrypt(null, secretKey); + assertNull(nullValue); + + String nullDecrypt = CryptoUtils.decrypt(null, secretKey); + assertNull(nullDecrypt); + } + + @Test + public void testAll() { + logger.info("testAll:"); + String encryptedValue = CryptoUtils.encrypt(pass, secretKey); + logger.info("original value : " + pass + " encrypted value: " + encryptedValue); + + String encryptedAgain = CryptoUtils.encrypt(encryptedValue, secretKey); + + assertEquals(encryptedValue, encryptedAgain); + + String decryptedValue = CryptoUtils.decrypt(encryptedAgain, secretKey); + logger.info("encrypted value: " + encryptedAgain + " decrypted value : " + decryptedValue); + assertEquals(pass, decryptedValue); + + String decryptedAgain = CryptoUtils.decrypt(decryptedValue, secretKey); + assertEquals(decryptedValue, decryptedAgain); + } +}
\ No newline at end of file |