aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/org/onap/clamp/clds/util/CryptoUtils.java
blob: f3ccbc850d10f1513bad9d46e812c24e96112dc5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*-
 * ============LICENSE_START=======================================================
 * ONAP CLAMP
 * ================================================================================
 * Copyright (C) 2017-2018 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.clamp.clds.util;

import com.att.eelf.configuration.EELFLogger;
import com.att.eelf.configuration.EELFManager;
import com.google.common.base.Charsets;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Properties;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.ArrayUtils;

/**
 * CryptoUtils for encrypting/decrypting string based on a Key defined in
 * application.properties (Spring config file).
 */
public final class CryptoUtils {

    /**
     * Used to log CryptoUtils class.
     */
    private static final EELFLogger logger = EELFManager.getInstance().getLogger(CryptoUtils.class);
    // Openssl commands:
    // Encrypt: echo -n "123456" | openssl aes-128-cbc -e -K <Private Hex key>
    // -iv <16 Bytes iv (HEX), be careful it's 32 Hex Chars> | xxd -u -g100
    // Final result is to put in properties file is: IV + Outcome of openssl
    // command
    // ************************************************************
    // Decrypt: echo -n 'Encrypted string' | xxd -r -ps | openssl aes-128-cbc -d
    // -K
    // <Private Hex Key> -iv <16 Bytes IV extracted from Encrypted String, be
    // careful it's 32 Hex Chars>
    /**
     * Definition of encryption algorithm.
     */
    private static final String ALGORITHM = "AES";

    /**
     * AES Encryption Key environment variable for external configuration.
     */
    private static final String AES_ENCRYPTION_KEY = "AES_ENCRYPTION_KEY";

    /**
     * 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;
    /**
     * Key to read in the key.properties file.
     */
    private static final String KEY_PARAM = "org.onap.clamp.encryption.aes.key";
    private static final String PROPERTIES_FILE_NAME = "clds/key.properties";
    /**
     * The SecretKeySpec created from the Base 64 String key.
     */
    private static final SecretKeySpec SECRET_KEY_SPEC = readSecretKeySpec(PROPERTIES_FILE_NAME);

    /**
     * Private constructor to avoid creating instances of util class.
     */
    private CryptoUtils() {
    }

    /**
     * Encrypt a value based on the Clamp Encryption Key.
     * 
     * @param value The value to encrypt
     * @return The encrypted string
     * @throws GeneralSecurityException     In case of issue with the encryption
     * @throws UnsupportedEncodingException In case of issue with the charset
     *                                      conversion
     */
    public static String encrypt(String value) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance(ALGORITHM_DETAILS, "SunJCE");
        byte[] iv = new byte[IV_BLOCK_SIZE_IN_BYTES];
        SecureRandom.getInstance("SHA1PRNG").nextBytes(iv);
        IvParameterSpec ivspec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, SECRET_KEY_SPEC, ivspec);
        return Hex.encodeHexString(ArrayUtils.addAll(iv, cipher.doFinal(value.getBytes(Charsets.UTF_8))));
    }

    /**
     * Decrypt a value based on the Clamp Encryption Key.
     * 
     * @param message The encrypted string that must be decrypted using the Clamp
     *                Encryption Key
     * @return The String decrypted
     * @throws GeneralSecurityException In case of issue with the encryption
     * @throws DecoderException         In case of issue to decode the HexString
     */
    public static String decrypt(String message) throws GeneralSecurityException, DecoderException {
        byte[] encryptedMessage = Hex.decodeHex(message.toCharArray());
        Cipher cipher = Cipher.getInstance(ALGORITHM_DETAILS, "SunJCE");
        IvParameterSpec ivspec = new IvParameterSpec(ArrayUtils.subarray(encryptedMessage, 0, IV_BLOCK_SIZE_IN_BYTES));
        byte[] realData = ArrayUtils.subarray(encryptedMessage, IV_BLOCK_SIZE_IN_BYTES, encryptedMessage.length);
        cipher.init(Cipher.DECRYPT_MODE, SECRET_KEY_SPEC, ivspec);
        byte[] decrypted = cipher.doFinal(realData);
        return new String(decrypted);
    }

    /**
     * Method used to generate the SecretKeySpec from a Base64 String.
     * 
     * @param keyString The key as a string in Base 64
     * @return The SecretKeySpec created
     * @throws DecoderException In case of issues with the decoding of Base64
     */
    private static SecretKeySpec getSecretKeySpec(String keyString) throws DecoderException {
        byte[] key = Hex.decodeHex(keyString.toCharArray());
        return new SecretKeySpec(key, ALGORITHM);
    }

    /**
     * Reads SecretKeySpec from file specified by propertiesFileName.
     *
     * @param propertiesFileName File name with properties
     * @return SecretKeySpec secret key spec read from propertiesFileName
     */
    private static SecretKeySpec readSecretKeySpec(String propertiesFileName) {
        Properties props = new Properties();
        try {
            // Workaround fix to make encryption key configurable
            // System environment variable takes precedence for over clds/key.properties
            String encryptionKey = System.getenv(AES_ENCRYPTION_KEY);
            if (encryptionKey != null && encryptionKey.trim().length() > 0) {
                return getSecretKeySpec(encryptionKey);
            } else {
                props.load(ResourceFileUtil.getResourceAsStream(propertiesFileName));
                return getSecretKeySpec(props.getProperty(KEY_PARAM));
            }
        } catch (IOException | DecoderException e) {
            logger.error("Exception occurred during the key reading", e);
            return null;
        }
    }
}