View Javadoc
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 }