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> 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 <key> <string to be encrypted></li>
350 * <li>2 <key> <string to be decrypted></li>
351 * <li>3 <string to be encrypted></li>
352 * <li>4 <string to be decrypted></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