View Javadoc
1   /**
2    * This Source Code Form is subject to the terms of the Mozilla Public
3    * License, v. 2.0. If a copy of the MPL was not distributed with this
4    * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5    *
6    * If it is not possible or desirable to put the notice in a particular
7    * file, then You may include the notice in a location (such as a LICENSE
8    * file in a relevant directory) where a recipient would be likely to look
9    * for such a notice.
10   *
11   * 
12   */
13  /*  ---------------------------------------------------------------------------
14   *  U.S. Government, Department of the Army
15   *  Army Materiel Command
16   *  Research Development Engineering Command
17   *  Communications Electronics Research Development and Engineering Center
18   *  ---------------------------------------------------------------------------
19   */
20  package org.miloss.fgsms.common;
21  
22  import javax.crypto.*;
23  import javax.crypto.spec.*;
24  import java.io.*;
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.net.URL;
28  import java.security.GeneralSecurityException;
29  import org.apache.log4j.Level;
30  import org.miloss.fgsms.common.Logger;
31  import org.miloss.fgsms.common.codec.Base64;
32  
33  ;
34  
35  /**
36   * This program uses a AES key, retrieves its raw bytes, and then reinstantiates
37   * a AES key from the key bytes. The reinstantiated key is used to initialize a
38   * AES cipher for encryption and decryption. source :
39   * http://java.sun.com/developer/technicalArticles/Security/AES/AES_v1.html
40   *
41   * This is the encryption/decryption module of fgsms. used for passwords and
42   * certain database columns
43   */
44  public class AES {
45  
46      public final static boolean isJCEInstalled;
47  
48      public static final String logname = "fgsms.Utility";
49      public static final Logger log = Logger.getLogger(logname);
50  
51      //pist don't tell anyone, but this is the default key in case all other mechanisms fail to load a valid key
52      // private final static String something256 = "gaPVPK6SONri9LCeWJlB2cUMPQL8a3JLegXaJObWj0Q=:F9798Q6FY+YNEaZz5xa6c8OkXk9yp8BcBpX/5avR/Oc=";
53      private final static String something128 = "m0b3b70CH0P4Ua7rxksnRw==:o1QNVJ/KY453VEB3VsQn39tQpu5yKovGAH/LQvyzawI=";
54  
55      /**
56       * Generate a new AES 256 bit encryption key. Once generated, this key can
57       * be used to replace the default key.
58       *
59       * @return
60       */
61      public static String GEN() {
62  
63          return GEN((short) 128);
64      }
65  
66      /**
67       * Generate a new AES 256 bit encryption key. Once generated, this key can
68       * be used to replace the default key.
69       *
70       * @param keysize must be a supported key size, using 128 or 256
71       * @return
72       */
73      public static String GEN(final short keysize) {
74          try {
75              return AesCbcWithIntegrity.generateKey().toString();
76          } catch (GeneralSecurityException ex) {
77              log.log(Level.ERROR, "There was an error generating key, this could indicate that you're making a 256 bit key on a system that does not have the Java Crypto Extensions installed. . Is JCE installed? " + (isJCEInstalled ? "yes " : "no ") + ex.getMessage());
78              log.log(Level.DEBUG, "error generating key, this could indicate that you're making a 256 bit key on a system that does not have the Java Crypto Extensions installed. " + ex.getMessage(), ex);
79          }
80          return null;
81      }
82  
83      private URI getUrl(String FileName) {
84          URL pcsurl = null;
85          if (pcsurl == null) {
86              try {
87                  pcsurl = Thread.currentThread().getContextClassLoader().getResource(FileName);
88                  log.log(Level.DEBUG, "Loading encryption key from " + pcsurl.toString());
89              } catch (Exception ex) {
90                  log.log(Level.DEBUG, "not found", ex);
91              }
92          }
93          if (pcsurl == null) {
94              try {
95                  pcsurl = Thread.currentThread().getContextClassLoader().getResource("/" + FileName);
96                  log.log(Level.DEBUG, "Loading encryption key from " + pcsurl.toString());
97              } catch (Exception ex) {
98                  log.log(Level.DEBUG, "not found", ex);
99              }
100         }
101 
102         if (pcsurl == null) {
103             try {
104                 pcsurl = new URL(FileName);
105                 log.log(Level.DEBUG, "Loading encryption key from " + pcsurl.toString());
106             } catch (Exception ex) {
107                 log.log(Level.DEBUG, "not found", ex);
108             }
109         }
110 
111         if (pcsurl == null) {
112             try {
113                 pcsurl = AES.class.getClassLoader().getResource(FileName);
114                 log.log(Level.DEBUG, "Loading encryption key from " + pcsurl.toString());
115             } catch (Exception ex) {
116                 log.log(Level.DEBUG, "not found", ex);
117             }
118         }
119         if (pcsurl == null) {
120             try {
121                 pcsurl = AES.class.getClassLoader().getResource("/" + FileName);
122                 log.log(Level.DEBUG, "Loading encryption key from " + pcsurl.toString());
123             } catch (Exception ex) {
124                 log.log(Level.DEBUG, "not found", ex);
125             }
126         }
127         try {
128             if (pcsurl != null) {
129                 return pcsurl.toURI();
130             }
131         } catch (URISyntaxException ex) {
132             log.log(Level.DEBUG, null, ex);
133         }
134         return null;
135     }
136 
137     private static String readAllText(File file) {
138         if (file == null || !file.exists()) {
139             log.log(Level.WARN, "Referenced key does not exist" + file.getAbsolutePath());
140             return null;
141         }
142         FileInputStream stream = null;
143         try {
144             stream = new FileInputStream(file);
145             String str = readAllText(stream);
146             stream.close();
147             return (str);
148         } catch (Exception e) {
149             log.log(Level.DEBUG, "error reading key", e);
150             return null;
151         } finally {
152             if (stream != null) {
153                 try {
154                     stream.close();
155                 } catch (Exception ex) {
156                     log.log(Level.DEBUG, "error reading key", ex);
157                 }
158             }
159         }
160 
161     }
162 
163     private static String readAllText(InputStream stream) {
164         try {
165             int size = 1024;
166             byte chars[] = new byte[size];
167             int k = stream.read(chars);
168             StringBuilder str = new StringBuilder();
169             while (k > 0) {
170 
171                 for (int i = 0; i < k; i++) {
172                     str.append((char) chars[i]);
173                 }
174                 k = stream.read(chars);
175             }
176             stream.close();
177             return (str.toString());
178         } catch (Exception e) {
179             log.log(Level.DEBUG, "error reading key", e);
180             return null;
181         }
182     }
183 
184     /* 
185      * 
186      * note we don't actually store the key in memory at any point in time
187      * double edged sword, 1) increases IO, 2) decreases risk
188      */
189     private static String loadKey() {
190         String key = null;
191         if (System.getenv("fgsms.keyFile") != null) {
192             //attempt load from system environment variable, kind of risky but ok
193             key = readAllText(new File(System.getenv("fgsms.keyFile")));
194         } else if (key == null && System.getProperty("fgsms.keyFile") != null) {
195             //attempt to load from system property
196             key = readAllText(new File(System.getProperty("fgsms.keyFile")));
197         } else if (key == null) {
198 
199             //if (isJCEInstalled)
200             {
201                 //prefer the strong encryption, if it's available
202                 try {
203                     File f = new File(new AES().getUrl("fgsms-aes128.key"));
204                     key = readAllText(f);
205                 } catch (Exception ex) {
206                     log.log(Level.DEBUG,null,ex);
207                 }
208                 if (key == null) {
209                     try {
210                         File f = new File(AES.class.getResource("fgsms-aes128.key").toURI());
211                         key = readAllText(f);
212                     } catch (Exception ex) {
213                         log.log(Level.DEBUG,null,ex);
214                     }
215                 }
216                 if (key == null) {
217                     try {
218                         InputStream is = AES.class.getResourceAsStream("fgsms-aes128.key");
219                         key = readAllText(is);
220                         is.close();
221                     } catch (Exception ex) {
222                         log.log(Level.DEBUG,null,ex);
223                     }
224                 }
225                 //try to load as a class resource
226 
227             }
228             /*else {
229                 try {
230                     File f = new File(new AES().getUrl("org/miloss/fgsms/common/aes128.key"));
231                     key = readAllText(f);
232                 } catch (Exception e) {
233 
234                 }
235             }*/
236         }
237         if (key != null) {
238             log.log(Level.DEBUG, "key loaded from file");
239             return (key);
240         } else {
241             log.log(Level.WARN, "Could not load the key, using hard coded default key instead. This should be considered a security risk.");
242 
243             return (something128);
244 
245         }
246     }
247 
248     public static String EN(final String cleartext) throws Exception {
249         return EN(cleartext, (loadKey()));
250     }
251 
252     public static String EN(final String cleartext, final String key) throws Exception {
253         AesCbcWithIntegrity.SecretKeys skey = AesCbcWithIntegrity.keys(key);
254         AesCbcWithIntegrity.CipherTextIvMac encrypt = AesCbcWithIntegrity.encrypt(cleartext, skey);
255         return encrypt.toString();
256     }
257 
258     public static String DE(final String ciphertext) throws Exception {
259 
260         return DE(ciphertext, (loadKey()));
261     }
262 
263     public static String DE(final String ciphertext, final String key) throws Exception {
264         AesCbcWithIntegrity.SecretKeys skey = AesCbcWithIntegrity.keys(key);
265         AesCbcWithIntegrity.CipherTextIvMac civ = new AesCbcWithIntegrity.CipherTextIvMac(ciphertext);
266         return AesCbcWithIntegrity.decryptString(civ, skey);
267     }
268 
269     /**
270      * return true is the supplied key is a valid aes key
271      *
272      * @param key
273      * @return
274      */
275     public static boolean validateKey(final String key) {
276         try {
277             String src = "abcdefghijklmopqrstuvwxyz123567890!@#$%^&*()_+{}|:\">?<,";
278             String x = EN(src, key);
279             String y = DE(x, key);
280             //if the sample text is encryptable and decryptable, and it was actually encrypted
281             return y.equals(src) && !x.equals(y);
282         } catch (Throwable ex) {
283 //            log.log(Level.WARN, null, ex);
284             return false;
285         }
286     }
287 
288     /*
289      * public static OutputStream DE(InputStream ciphertext) throws Exception {
290      * byte[] raw =//skey.getEncoded(); hexToBytes(something); // SecretKeySpec
291      * skeySpec = new SecretKeySpec(raw, "AES"); Cipher cipher =
292      * Cipher.getInstance("AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec);
293      * CipherInputStream cis = new CipherInputStream(ciphertext, cipher);
294      * cis.read(raw);
295      *
296      * byte[] original = cipher.doFinal(hexToBytes(ciphertext)); return new
297      * String(original); }
298      */
299     static {
300         String key = GEN((short) 256);
301         isJCEInstalled = validateKey(key);
302     }
303 }