View Javadoc

1   // $Id: Encrypter.java,v 1.6 2007/12/31 22:37:00 dkeener Exp $
2   
3   package com.keenertech.antimatter;
4   
5   import java.io.UnsupportedEncodingException;
6   import java.io.IOException;
7   import java.security.NoSuchAlgorithmException;
8   import java.security.InvalidKeyException;
9   import javax.crypto.Cipher;
10  import javax.crypto.SecretKey;
11  import javax.crypto.IllegalBlockSizeException;
12  import javax.crypto.BadPaddingException;
13  import javax.crypto.NoSuchPaddingException;
14  import javax.crypto.spec.SecretKeySpec;
15  import org.apache.commons.codec.binary.Base64;
16  
17  // Replaced unsupported Sun Base64 functionality with Apache version
18  // import sun.misc.BASE64Encoder;
19  // import sun.misc.BASE64Decoder;
20  
21  /**
22   * The Encrypter class provides for the encryption and decryption
23   * of text strings using the robust and well-supported encryption algorithms
24   * supported by JCE (Java Cryptographic Extensions), an implementation of
25   * the JCA (Java Cryptography Architecture). The JCE is included with the
26   * Java JDK, and should thus be readily available within most development
27   * environments. Encrypted text can be binary and is thus further encoded 
28   * in base64 for easy transport as strings.
29   *
30   * <p>The Encrypter class should meet most encryption/decryption needs,
31   * but has not particularly been designed with speed in mind. It is ideal 
32   * for encrypting vital fields, such as passwords, for use in configuration 
33   * files or configuration fields in a database.
34   *
35   * <p>The following code provides an example of how to use the class to
36   * first encrypt and then decrypt a string:</p>
37   *
38   * <pre>
39   *       String strContent = "clarke";
40   *
41   *       // Encrypt the string using the DES algorithm, with the key provided
42   *       Encrypter objEncrypter = new Encrypter("A345B2Nu", "DES");
43   *       String strEncrypted = objEncrypter.encrypt(strContent);
44   *
45   *       // Now create another Encrypter instance and decrypt the string
46   *       Encrypter objEncrypter2 = new Encrypter("A345B2Nu", "DES");
47   *       String strDecrypted = objEncrypter2.decrypt(strEncrypted);
48   * </pre>
49   *
50   * <p>Note: The standard encryption/decryption code examples available on the web
51   * depend on the Base64 encoding/decoding functionality provided by the
52   * sun.misc.Base64Encoder and sun.misc.Base64Decoder classes. These are
53   * <strong>unsupported</strong> classes; they are also not guaranteed to work on other
54   * platforms. Accordingly, the Encrypter class uses the base64 functionality provided
55   * by the Apache commons-codec class.
56   *
57   * @author David Keener
58   * @version $Id: Encrypter.java,v 1.6 2007/12/31 22:37:00 dkeener Exp $
59   *
60   * @todo Should add more documentation for the different types of supported encryption
61   *       algorithms (the key types).
62   * @todo Should add some validations for the key types provided to the class.
63   */
64  
65  public class Encrypter
66  {
67     // Default definitions  
68     private Cipher         m_ecipher = null;
69     private Cipher         m_dcipher = null;
70     private String         m_keytype = "DES";
71     private SecretKeySpec  m_secretkey = null;
72     
73  
74     /**
75      * Default constructor. The internal Cipher will need to be set
76      * using the setCipher() method before the class instance can
77      * be used for encryption or decryption.
78      */
79  
80     protected Encrypter() 
81     {
82     } // End Method
83     
84     /**
85      * This constructor creates a Encrypter class with all parameters
86      * necessary for the class to be used for encryption and 
87      * decryption operations.
88      *
89      * @param sSecretKey    The secret key, as a string.
90      * @param sKeyType      The type of key. The most common key type
91      *                      is "DES".
92      */
93     
94     public Encrypter(String sSecretKey, String sKeyType)
95     {
96        SecretKeySpec secretKey = null;
97        
98        if (sSecretKey != null)
99        {
100          secretKey = generateSecretKey(sSecretKey);
101          setKeyType(sKeyType);
102          setCipher(secretKey);
103       }
104       else
105       {
106          System.out.println("Encrypter: ERROR: Invalid string key provided to constructor.");
107       }
108    } // End Method
109 
110    /**
111     * This constructor creates an Encrypter class with all parameters
112     * necessary for the class to be used for encryption and 
113     * decryption operations.
114     *
115     * @param secretKey The secret key, as a SecretKey object, as defined in the
116     *        javax.crypto.SecretKey class.
117     *
118     * @param sKeyType The type of key. The most common key type is
119     *        "DES".
120     */
121    
122    public Encrypter(SecretKey secretKey, String sKeyType)
123    {
124       setKeyType(sKeyType);
125       setCipher(secretKey);
126    } // End Method
127 
128    /**
129     * This method stores the key type internally. The most commonly used
130     * encryption type is "DES".
131     *
132     * @param sKeyType  The key type, as a string.
133     */  
134    
135    public void setKeyType(String sKeyType)
136    {
137       if (sKeyType != null)
138       {
139          m_keytype = sKeyType;
140       }
141       else
142       {
143          System.out.println("Encrypter.setKeyType: ERROR: Unable to set undefined key type.");
144       }
145    } // End Method
146    
147    /**
148     * This method is used to generate a SecretKey object based on an incoming
149     * secret key string. This key is required in order to perform encryption
150     * and decryption operations. 
151     *
152     * @param sSecretKey The secrey key, as a string.
153     *
154     * @return Returns a SecretKey object generated using the secrey key
155     *         string parameter and the key type defined in the class. Note that
156     *         the key type defaults to "DES" if it has not been previously
157     *         set using the setKeyType method.
158     */
159    
160    public SecretKeySpec generateSecretKey(String sSecretKey)
161    {
162       SecretKeySpec secretKey = null;
163 
164       if (sSecretKey == null)
165       {
166          System.out.println("Encrypter.generateSecretKey: ERROR: Unable to generate secret key for undefined string.");
167       }
168       else if (sSecretKey.length() != 8)
169       {
170          System.out.println("Encrypter.generateSecretKey: ERROR: Secret Key: " + sSecretKey + " is not 8 bytes long.");
171       }
172       else
173       {
174          secretKey = new SecretKeySpec(sSecretKey.getBytes(), m_keytype);
175       }
176 
177       return secretKey;
178    } // End Method
179 
180    /**
181     * This method is used to create the internal Cipher object that will be 
182     * used to perform encryption and decryption operations. The method requires
183     * a SecretKey object as a parameter, which can be geberated using the
184     * generateSecretKey method if necessary.
185     *
186     * @param secretKey A SecretKey object.
187     */
188    
189    public void setCipher(SecretKey secretKey)
190    {
191       if (secretKey == null)
192       {
193          System.out.println("Encrypter.setCipher: ERROR: Unable to set the cipher for an undefined secret key.");
194       }
195       else if (m_keytype == null)
196       {
197          System.out.println("Encrypter.setCipher: ERROR: Unable to set the cipher while key type is undefined.");
198       }
199       else try
200       {
201          m_ecipher = Cipher.getInstance(m_keytype);
202          m_dcipher = Cipher.getInstance(m_keytype);
203          m_ecipher.init(Cipher.ENCRYPT_MODE, secretKey);
204          m_dcipher.init(Cipher.DECRYPT_MODE, secretKey);
205       }
206       catch (NoSuchPaddingException e)
207       {
208          System.out.println("Encrypter.setCipher: ERROR: Unable to set ciphers because " + e);
209          e.printStackTrace();
210       }
211       catch (NoSuchAlgorithmException e)
212       {
213          System.out.println("Encrypter.setCipher: ERROR: Unable to set ciphers because " + e);
214          e.printStackTrace();
215       }
216       catch (InvalidKeyException e)
217       {
218          System.out.println("Encrypter.setCipher: ERROR: Unable to set ciphers because " + e);
219          e.printStackTrace();
220       }
221    } // End Method
222 
223    /**
224     * This method encrypts a string using an internal Cipher created either by an
225     * appropriate constructor or by previously calling the setCipher method.
226     *
227     * @param sString The string to be encrypted.
228     *
229     * @return Returns the encrypted string.
230     */
231    
232    public String encrypt(String sString)
233    {
234       String sEncrypted = null;
235 
236       if (sString == null)
237       {
238          System.out.println("Encrypter.Encrypt: ERROR: Unable to encrypt undefined string");
239       }
240       else if (m_ecipher == null)
241       {
242          System.out.println("Encrypter.Encrypt: ERROR: Unable to encrypt string: " + sString
243             + " while encryption cypher is not defined.");
244       }
245       else try
246       {
247          byte[] bUtf8 = null;
248          byte[] bEnc = null;
249 
250          // Encode the string into bytes using utf-8
251          bUtf8 = sString.getBytes("UTF8");
252 
253          // Encrypt
254          bEnc = m_ecipher.doFinal(bUtf8);
255 
256          // Encode bytes to base64 to get a string
257          sEncrypted = new String(Base64.encodeBase64(bEnc));
258          // sEncrypted = new BASE64Encoder().encode(bEnc);  -- Replaced the Sun version
259       }
260       catch (Exception e)
261       {
262          System.out.println("Encrypter.Encrypt: ERROR: Unable to encrypt string: " + sString + " because " + e);
263          e.printStackTrace();
264       }
265 
266       return sEncrypted;
267    } // End Method
268 
269   /**
270     * This method decrypts a string using an internal Cipher created either by an
271     * appropriate constructor or by previously calling the setCipher method.
272     *
273     * @param sString The string to be decrypted.
274     *
275     * @return Returns the decrypted string.
276     */
277    
278    public String decrypt(String sString)
279    {
280       String sDecrypted = null;
281       byte[] bDec = null;
282 
283       if (sString == null)
284       {
285          System.out.println("Encrypter.Decrypt: ERROR: Unable to decrypt undefined string");
286       }
287       else if (m_dcipher == null)
288       {
289          System.out.println("Encrypter.Decrypt: ERROR: Unable to decrypt string: " + sString
290             + " while decryption cypher is not defined.");
291       }
292       else try
293       {
294          byte[] bUtf8 = null;
295 
296          // Decode base64 to get bytes
297          bDec = Base64.decodeBase64(sString.getBytes("UTF8"));
298          // bDec = new BASE64Decoder().decodeBuffer(sString);    -- Replaced Sun version
299 
300          // Decrypt
301          bUtf8 = m_dcipher.doFinal(bDec);
302 
303          // Decode using utf-8
304          sDecrypted = new String(bUtf8, "UTF8");
305       }
306       catch (BadPaddingException e)
307       {
308          System.out.println("Encrypter.Decrypt: ERROR: Unable to decrypt string: (" + sString.length()
309             + ") " + sString + "  byte Decoder: " + bDec + " because " + e);
310          e.printStackTrace();
311       }
312       catch (IllegalBlockSizeException e)
313       {
314          System.out.println("Encrypter.Decrypt: ERROR: Unable to decrypt string: (" + sString.length()
315             + ") " + sString + "  byte Decoder: " + bDec + " because " + e);
316          e.printStackTrace();
317       }
318       catch (UnsupportedEncodingException e)
319       {
320          System.out.println("Encrypter.Decrypt: ERROR: Unable to decrypt string: (" + sString.length()
321             + ") " + sString + "  byte Decoder: " + bDec + " because " + e);
322          e.printStackTrace();
323       }
324       catch (IOException e)
325       {
326          System.out.println("Encrypter.Decrypt: ERROR: Unable to decrypt string: (" + sString.length()
327             + ") " + sString + "  byte Decoder: " + bDec + " because " + e);
328          e.printStackTrace();
329       }
330 
331       return sDecrypted;
332    } // End Method
333    
334    /**
335     * This main() method can be used to encrypt or decrypt a string using an optional
336     * key string provided by the user. If no key is provided, then the default key will 
337     * be used. If provided, the key string should be 8 bytes long. Since this main 
338     * program is primarily for demonstration purposes, it uses the DES encryption 
339     * mechanism, which is useful but certainly not the strongest encryption possible. 
340     *
341     * <p>To execute the main program, first make sure that the Antimatter jar is in the
342     * CLASSPATH. Then, execute the main method from the jar file as shown below:
343     *
344     * <p>&nbsp;&nbsp;&nbsp;java com.keenertech.antimatter.Encrypter [arguments]
345     *
346     * <p>The main program expects one of the following sets of arguments:
347     *
348     * <p><ul>
349     * <li>1 &lt;key&gt; &lt;string to be encrypted&gt;</li>
350     * <li>2 &lt;key&gt; &lt;string to be decrypted&gt;</li>
351     * <li>3 &lt;string to be encrypted&gt;</li>
352     * <li>4 &lt;string to be decrypted&gt;</li>
353     *
354     * @todo Create a better argument-handling scheme for the main() method. It would
355     *       be better to have a "-e" or -"d" option to select encrypt/decrypt, and
356     *       then interpret the other arguments depending on whether a string is
357     *       provided, or a key and a string.
358     */
359    
360    public static void main(String[] args)
361    {
362       String  DEFAULT_KEY = "A345B2Nu";
363       String  DEFAULT_KEY_TYPE = "DES";
364       int status = 0;
365       boolean bShowSyntax = false;
366 
367       // Show syntax, if no arguments are given
368       if (args.length == 0) 
369       {
370          bShowSyntax = true;
371       }
372       // If not a valid
373       else
374       {
375          int iOption = 0;
376 
377       // Get the option value
378       try
379       {
380          iOption = Integer.parseInt(args[0]);
381       }
382       catch (Exception e)
383       {
384          iOption = 0;
385       }
386 
387       // If the value is invalid, show sytax
388       if (iOption <= 0)
389       {
390          System.out.println("Option: " + args[0] + " is not a valid option.");
391          bShowSyntax = true;
392       }
393       else if (iOption == 1)
394       {
395          // Show the syntax, if not enough options were given
396          if (args.length <= 2) 
397          {
398             bShowSyntax = true;
399          }
400          else
401          {
402             String sString = null;
403 
404             // Encrypt the string
405             Encrypter objEncrypter = new Encrypter(args[1], DEFAULT_KEY_TYPE);
406             sString = objEncrypter.encrypt(args[2]);
407 
408             System.out.println("\n    String:    " + args[2]);
409             System.out.println("    Key:       " + args[1]);
410             System.out.println("    Encrypted: " + sString);
411          }
412       }
413       else if (iOption == 2)
414       {
415          // Show the syntax, if not enough options were given
416          if (args.length <= 2) 
417          {
418             bShowSyntax = true;
419          }
420          else
421          {
422             String sString = null;
423 
424             // Decrypt the string
425             Encrypter objEncrypter = new Encrypter(args[1], DEFAULT_KEY_TYPE);
426             sString = objEncrypter.decrypt(args[2]);
427 
428             System.out.println("\n    String:    " + args[2]);
429             System.out.println("    Key:       " + args[1]);
430             System.out.println("    Decrypted: " + sString);
431          }
432       }
433       else if (iOption == 3)
434       {
435          // Show the syntax, if not enough options were given
436          if (args.length <= 1) 
437          {
438             bShowSyntax = true;
439          }
440          else
441          {
442             String sString = null;
443 
444             // Encrypt the string
445             Encrypter objEncrypter = new Encrypter(DEFAULT_KEY, DEFAULT_KEY_TYPE);
446             sString = objEncrypter.encrypt(args[1]);
447 
448             System.out.println("\n    String:    " + args[1]);
449             System.out.println("    Encrypted: " + sString);
450          }
451       }
452       else if (iOption == 4)
453       {
454          // Show the syntax, if not enough options were given
455          if (args.length <= 1) 
456          {
457             bShowSyntax = true;
458          }
459          else
460          {
461             String sString = null;
462 
463             // Decrypt the string
464             Encrypter objEncrypter = new Encrypter(DEFAULT_KEY, DEFAULT_KEY_TYPE);
465             sString = objEncrypter.decrypt(args[1]);
466 
467             System.out.println("\n    String:    " + args[1]);
468             System.out.println("    Decrypted: " + sString);
469          }
470       }
471       else
472       {
473          System.out.println("Option: " + args[0] + " is not a valid option.");
474          bShowSyntax = true;
475       }
476    }
477 
478    if (bShowSyntax)
479    {
480       System.out.println("\n Syntax: decoder <command> [option arguments]\n\n"
481          + "           1: Encrypt: decoder 1 <key> <string>\n"
482          + "           2: Decrypt: decoder 2 <key> <string>\n"
483          + "           3: Encrypt: decoder 3 <string>\n"
484          + "           4: Decrypt: decoder 4 <string>\n");
485 
486       status ++;
487    }
488 
489    // System.out.println("status: " + status);
490 
491    // Exit with specified status code, if in error
492    if (status != 0) 
493    {
494       Runtime.getRuntime().exit(status);
495    }
496 
497    return;
498    } // End Main
499    
500 } // End Class