Minor Improvement
[clamp.git] / src / main / java / org / onap / clamp / clds / util / CryptoUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP CLAMP
4  * ================================================================================
5  * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights
6  *                             reserved.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END============================================
20  * ===================================================================
21  * 
22  */
23
24 package org.onap.clamp.clds.util;
25
26 import com.att.eelf.configuration.EELFLogger;
27 import com.att.eelf.configuration.EELFManager;
28 import com.google.common.base.Charsets;
29
30 import java.io.IOException;
31 import java.io.UnsupportedEncodingException;
32 import java.security.GeneralSecurityException;
33 import java.security.SecureRandom;
34 import java.util.Properties;
35
36 import javax.crypto.Cipher;
37 import javax.crypto.spec.IvParameterSpec;
38 import javax.crypto.spec.SecretKeySpec;
39
40 import org.apache.commons.codec.DecoderException;
41 import org.apache.commons.codec.binary.Hex;
42 import org.apache.commons.lang3.ArrayUtils;
43
44 /**
45  * CryptoUtils for encrypting/decrypting string based on a Key defined in
46  * application.properties (Spring config file).
47  */
48 public final class CryptoUtils {
49
50     /**
51      * Used to log CryptoUtils class.
52      */
53     private static final EELFLogger logger = EELFManager.getInstance().getLogger(CryptoUtils.class);
54     // Openssl commands:
55     // Encrypt: echo -n "123456" | openssl aes-128-cbc -e -K <Private Hex key>
56     // -iv <16 Bytes iv (HEX), be careful it's 32 Hex Chars> | xxd -u -g100
57     // Final result is to put in properties file is: IV + Outcome of openssl
58     // command
59     // ************************************************************
60     // Decrypt: echo -n 'Encrypted string' | xxd -r -ps | openssl aes-128-cbc -d
61     // -K
62     // <Private Hex Key> -iv <16 Bytes IV extracted from Encrypted String, be
63     // careful it's 32 Hex Chars>
64     /**
65      * Definition of encryption algorithm.
66      */
67     private static final String ALGORITHM = "AES";
68     
69     /**
70      * AES Encryption Key environment variable for external configuration
71      */
72     private static final String AES_ENCRYPTION_KEY = "AES_ENCRYPTION_KEY";
73     
74     /**
75      * Detailed definition of encryption algorithm.
76      */
77     private static final String ALGORITHM_DETAILS = ALGORITHM + "/CBC/PKCS5PADDING";
78     private static final int IV_BLOCK_SIZE_IN_BITS = 128;
79     /**
80      * An Initial Vector of 16 Bytes, so 32 Hexadecimal Chars.
81      */
82     private static final int IV_BLOCK_SIZE_IN_BYTES = IV_BLOCK_SIZE_IN_BITS / 8;
83     /**
84      * Key to read in the key.properties file.
85      */
86     private static final String KEY_PARAM = "org.onap.clamp.encryption.aes.key";
87     private static final String PROPERTIES_FILE_NAME = "clds/key.properties";
88     /**
89      * The SecretKeySpec created from the Base 64 String key.
90      */
91     private static final SecretKeySpec SECRET_KEY_SPEC = readSecretKeySpec(PROPERTIES_FILE_NAME);
92
93     /**
94      * Private constructor to avoid creating instances of util class.
95      */
96     private CryptoUtils() {
97     }
98
99     /**
100      * Encrypt a value based on the Clamp Encryption Key.
101      * 
102      * @param value
103      *            The value to encrypt
104      * @return The encrypted string
105      * @throws GeneralSecurityException
106      *             In case of issue with the encryption
107      * @throws UnsupportedEncodingException
108      *             In case of issue with the charset conversion
109      */
110     public static String encrypt(String value) throws GeneralSecurityException {
111         Cipher cipher = Cipher.getInstance(ALGORITHM_DETAILS, "SunJCE");
112         byte[] iv = new byte[IV_BLOCK_SIZE_IN_BYTES];
113         SecureRandom.getInstance("SHA1PRNG").nextBytes(iv);
114         IvParameterSpec ivspec = new IvParameterSpec(iv);
115         cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY_SPEC, ivspec);
116         return Hex.encodeHexString(ArrayUtils.addAll(iv, cipher.doFinal(value.getBytes(Charsets.UTF_8))));
117     }
118
119     /**
120      * Decrypt a value based on the Clamp Encryption Key.
121      * 
122      * @param message
123      *            The encrypted string that must be decrypted using the Clamp
124      *            Encryption Key
125      * @return The String decrypted
126      * @throws GeneralSecurityException
127      *             In case of issue with the encryption
128      * @throws DecoderException
129      *             In case of issue to decode the HexString
130      */
131     public static String decrypt(String message) throws GeneralSecurityException, DecoderException {
132         byte[] encryptedMessage = Hex.decodeHex(message.toCharArray());
133         Cipher cipher = Cipher.getInstance(ALGORITHM_DETAILS, "SunJCE");
134         IvParameterSpec ivspec = new IvParameterSpec(ArrayUtils.subarray(encryptedMessage, 0, IV_BLOCK_SIZE_IN_BYTES));
135         byte[] realData = ArrayUtils.subarray(encryptedMessage, IV_BLOCK_SIZE_IN_BYTES, encryptedMessage.length);
136         cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY_SPEC, ivspec);
137         byte[] decrypted = cipher.doFinal(realData);
138         return new String(decrypted);
139     }
140
141     /**
142      * Method used to generate the SecretKeySpec from a Base64 String.
143      * 
144      * @param keyString
145      *            The key as a string in Base 64
146      * @return The SecretKeySpec created
147      * @throws DecoderException
148      *             In case of issues with the decoding of Base64
149      */
150     private static SecretKeySpec getSecretKeySpec(String keyString) throws DecoderException {
151         byte[] key = Hex.decodeHex(keyString.toCharArray());
152         return new SecretKeySpec(key, ALGORITHM);
153     }
154
155     /**
156      * Reads SecretKeySpec from file specified by propertiesFileName
157      *
158      * @param propertiesFileName
159      *            File name with properties
160      * @return SecretKeySpec secret key spec read from propertiesFileName
161      */
162     private static SecretKeySpec readSecretKeySpec(String propertiesFileName) {
163         Properties props = new Properties();
164         try {
165                 //Workaround fix to make encryption key configurable.
166                 //System environment variable takes precedence for over clds/key.properties
167                 String encryptionKey = System.getenv(AES_ENCRYPTION_KEY);
168                 if(encryptionKey != null && encryptionKey.trim().length() > 0) {
169                         return getSecretKeySpec(encryptionKey);
170                 } else {
171                         props.load(ResourceFileUtil.getResourceAsStream(propertiesFileName));
172                 return getSecretKeySpec(props.getProperty(KEY_PARAM));
173                 }
174         } catch (IOException | DecoderException e) {
175             logger.error("Exception occurred during the key reading", e);
176             return null;
177         }
178     }
179 }