1 /*-
2 * #%L
3 * Secured Properties
4 * ===============================================================
5 * Copyright (C) 2016 Brabenetz Harald, Austria
6 * ===============================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * #L%
19 */
20 package net.brabenetz.lib.securedproperties;
21
22 import net.brabenetz.lib.securedproperties.core.Encryption;
23 import net.brabenetz.lib.securedproperties.core.SecretContainer;
24 import net.brabenetz.lib.securedproperties.core.SecretContainerStore;
25 import net.brabenetz.lib.securedproperties.utils.SecuredPropertiesUtils;
26 import org.apache.commons.lang3.StringUtils;
27 import org.apache.commons.lang3.tuple.Pair;
28
29 import java.io.File;
30 import java.util.HashMap;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.Properties;
34 import java.util.stream.Collectors;
35
36 /**
37 * Encrypt and decrypt secret values in properties files with a secret key.
38 * <p>
39 * <b>Example:</b> <br>
40 * The Property file "myConfiguration.properties":
41 *
42 * <pre>
43 * mySecretPassword = test
44 * </pre>
45 *
46 * The Java code:
47 *
48 * <pre>
49 * // prepare custom config
50 * final SecuredPropertiesConfig config = new SecuredPropertiesConfig().withSecretFile(new File("G:/mysecret.key")).initDefault();
51 *
52 * // auto-encrypt values in the property-file:
53 * SecuredProperties.encryptNonEncryptedValues(config,
54 * new File("myConfiguration.properties"), // The Property File
55 * "mySecretPassword"); // the property-key from "myConfiguration.properties"
56 *
57 * // read encrypted values from the property-file
58 * String secretValue = SecuredProperties.getSecretValue(config,
59 * new File("myConfiguration.properties"), // The Property File
60 * "mySecretPassword"); // the property-key from "myConfiguration.properties"
61 * </pre>
62 *
63 * will return "test" as secretValue and automatically encrypt the value in the property file. After the first run the Property file will looks similar to the
64 * following:
65 *
66 * <pre>
67 * mySecretPassword = {wVtvW8lQrwCf8MA9sadwww==}
68 * </pre>
69 *
70 * This encrypted password can now be read only in combination with the secret file "G:/mysecret.key"
71 */
72 public final class SecuredProperties {
73
74 private SecuredProperties() {
75 super();
76 }
77
78 /**
79 *
80 * @see SecuredProperties
81 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
82 * @param propertyFile The Properties file to with the encrypted value
83 * @param key The Property-Key of the encrypted value.
84 * @return The decrypted plain-text value.
85 */
86 public static String getSecretValue(final SecuredPropertiesConfig config, final File propertyFile, final String key) {
87 return getSecretValues(config, propertyFile, key).get(key);
88 }
89
90 /**
91 * @see SecuredProperties
92 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
93 * @param propertyFiles A list of Property file to with the encrypted value
94 * @param key The Property-Key of the encrypted value.
95 * @return The decrypted plain-text value.
96 */
97 public static String getSecretValue(final SecuredPropertiesConfig config, final File[] propertyFiles, final String key) {
98 return getSecretValues(config, propertyFiles, key).get(key);
99 }
100
101 /**
102 * @see SecuredProperties
103 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
104 * @param propertyFile The Properties file to with the encrypted value
105 * @param keys The Property-Keys of the encrypted value.
106 * @return A Map with the decrypted plain-text values per key.
107 */
108 public static Map<String, String> getSecretValues(
109 final SecuredPropertiesConfig config, final File propertyFile, final String... keys) {
110 return getSecretValues(config, new File[] {propertyFile}, keys);
111 }
112
113 /**
114 * @see SecuredProperties
115 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
116 * @param propertyFiles A list of Property file to with the encrypted value
117 * @param keys The Property-Keys of the encrypted value.
118 * @return A Map with the decrypted plain-text values per key.
119 */
120 public static Map<String, String> getSecretValues(
121 final SecuredPropertiesConfig config, final File[] propertyFiles, final String... keys) {
122
123 Map<String, String> result = new HashMap<>();
124 final SecretContainer secretContainer = getSecretContainer(config);
125
126 for (File propertyFile : propertyFiles) {
127 if (!propertyFile.exists()) {
128 continue;
129 }
130 final Properties properties = SecuredPropertiesUtils.readProperties(propertyFile);
131 for (String key : keys) {
132
133 String value = properties.getProperty(key);
134 if (Encryption.isEncryptedValue(value)) {
135 // read and decrypt value
136 result.put(key, Encryption.decrypt(secretContainer.getAlgorithm(), secretContainer.getSecretKey(), config.getSaltLength(), value));
137 } else {
138 result.put(key, value);
139 }
140 }
141
142 }
143
144 return result;
145
146 }
147
148 /**
149 * @see SecuredProperties
150 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
151 * @param propertyFile The Properties file to with the encrypted value
152 * @param keys The Property-Keys of the encrypted value.
153 */
154 public static void encryptNonEncryptedValues(
155 final SecuredPropertiesConfig config, final File propertyFile, final String... keys) {
156 encryptNonEncryptedValues(config, new File[] {propertyFile}, keys);
157 }
158
159 /**
160 * @see SecuredProperties
161 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
162 * @param propertyFiles A list of Property file to with the encrypted value
163 * @param keys The Property-Keys of the encrypted value.
164 */
165 public static void encryptNonEncryptedValues(
166 final SecuredPropertiesConfig config, final File[] propertyFiles, final String... keys) {
167
168 final SecretContainer secretContainer = getSecretContainer(config);
169 for (File propertyFile : propertyFiles) {
170 if (!propertyFile.exists()) {
171 continue;
172 }
173 Map<String, String> unencryptedValues = new HashMap<>();
174 final Properties properties = SecuredPropertiesUtils.readProperties(propertyFile);
175 for (String key : keys) {
176
177 String value = properties.getProperty(key);
178 if (!Encryption.isEncryptedValue(value) && StringUtils.isNotEmpty(value)) {
179 // replace value with encrypted in property file.
180 unencryptedValues.put(key, value);
181 }
182 }
183
184 if (!unencryptedValues.isEmpty()) {
185 Map<String, String> encryptedValues = encryptValues(config, secretContainer, unencryptedValues);
186 Pair<String, String>[] newProperties = encryptedValues.entrySet().stream()
187 .map(e -> Pair.of(e.getKey(), e.getValue()))
188 .collect(Collectors.toSet())
189 .toArray(new Pair[encryptedValues.size()]);
190 SecuredPropertiesUtils.replaceSecretValue(propertyFile, newProperties);
191 }
192 }
193 }
194
195 private static Map<String, String> encryptValues(final SecuredPropertiesConfig config, final SecretContainer secretContainer,
196 final Map<String, String> unencryptedValues) {
197
198 Map<String, String> encryptedValues = new HashMap<>();
199 for (Entry<String, String> entry : unencryptedValues.entrySet()) {
200 encryptedValues.put(entry.getKey(), Encryption.encrypt(
201 secretContainer.getAlgorithm(), secretContainer.getSecretKey(), config.getSaltLength(), entry.getValue()));
202 }
203 return encryptedValues;
204 }
205
206 /**
207 * Checks if the given String looks like an encrypted value.
208 */
209 public static boolean isEncryptedValue(final String maybeEncryptedValue) {
210 return Encryption.isEncryptedValue(maybeEncryptedValue);
211 }
212
213 /**
214 * Encrypt the given value (will create the secret key if not already exist).
215 *
216 * @param config
217 * the {@link SecuredPropertiesConfig} to control custom behavior.
218 * @param plainTextValue
219 * The value to encrypt
220 * @return the encrypted value.
221 */
222 public static String encrypt(final SecuredPropertiesConfig config, final String plainTextValue) {
223 final SecretContainer secretContainer = getSecretContainer(config);
224
225 return Encryption.encrypt(secretContainer.getAlgorithm(), secretContainer.getSecretKey(), config.getSaltLength(), plainTextValue);
226 }
227
228 /**
229 * Encrypt the given password (will create the secret key if not already exist).
230 *
231 * @param config the {@link SecuredPropertiesConfig} to control custom behavior.
232 * @param encryptedPassword The password to encrypt
233 * @return the encrypted value.
234 */
235 public static String decrypt(final SecuredPropertiesConfig config, final String encryptedPassword) {
236 final SecretContainer secretContainer = getSecretContainer(config);
237
238 return Encryption.decrypt(secretContainer.getAlgorithm(), secretContainer.getSecretKey(), config.getSaltLength(), encryptedPassword);
239
240 }
241
242 private static SecretContainer getSecretContainer(final SecuredPropertiesConfig config) {
243
244 return SecretContainerStore.getSecretContainer(config.getSecretFile(), config.isAutoCreateSecretKey(), config.getAllowedAlgorithm());
245 }
246 }