aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--utils/pom.xml4
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/security/CryptoUtils.java259
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/security/CryptoUtilsTest.java120
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