View Javadoc

1   /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2    *
3    * Licensed under the Apache License, Version 2.0 (the "License");
4    * you may not use this file except in compliance with the License.
5    * You may obtain a copy of the License at
6    *
7    *     http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  
16  package org.acegisecurity.util;
17  
18  import java.io.UnsupportedEncodingException;
19  import java.security.spec.KeySpec;
20  
21  import javax.crypto.Cipher;
22  import javax.crypto.SecretKey;
23  import javax.crypto.SecretKeyFactory;
24  import javax.crypto.spec.DESedeKeySpec;
25  
26  import org.acegisecurity.AcegiSecurityException;
27  import org.apache.commons.codec.binary.Base64;
28  import org.springframework.util.Assert;
29  
30  /**
31   * A static utility class that can encrypt and decrypt text.
32   *
33   * <p>This class is useful if you have simple needs and wish to use the DESede
34   * encryption cipher. More sophisticated requirements will need to use the
35   * Java crypto libraries directly.
36   *
37   * @author Alan Stewart
38   * @author Ben Alex
39   * @version $Id: EncryptionUtils.java 1784 2007-02-24 21:00:24Z luke_t $
40   */
41  public final class EncryptionUtils {
42  
43      /**
44       * This is a static class that should not be instantiated.
45       */
46      private EncryptionUtils() {}
47  
48      /**
49       * Converts a String into a byte array using UTF-8, falling back to the
50       * platform's default character set if UTF-8 fails.
51       *
52       * @param input the input (required)
53       * @return a byte array representation of the input string
54       */
55      public static byte[] stringToByteArray(String input) {
56          Assert.hasLength(input, "Input required");
57          try {
58              return input.getBytes("UTF-8");
59          } catch (UnsupportedEncodingException fallbackToDefault) {
60              return input.getBytes();
61          }
62      }
63  
64      /**
65       * Converts a byte array into a String using UTF-8, falling back to the
66       * platform's default character set if UTF-8 fails.
67       *
68       * @param byteArray the byte array to convert (required)
69       * @return a string representation of the byte array
70       */
71      public static String byteArrayToString(byte[] byteArray) {
72          Assert.notNull(byteArray, "ByteArray required");
73          Assert.isTrue(byteArray.length > 0, "ByteArray cannot be empty");
74          try {
75              return new String(byteArray, "UTF8");
76          } catch (final UnsupportedEncodingException e) {
77              return new String(byteArray);
78          }
79      }
80  
81      private static byte[] cipher(String key, byte[] passedBytes, int cipherMode) throws EncryptionException {
82          try {
83              final KeySpec keySpec = new DESedeKeySpec(stringToByteArray(key));
84              final SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DESede");
85              final Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
86              final SecretKey secretKey = keyFactory.generateSecret(keySpec);
87              cipher.init(cipherMode, secretKey);
88              return cipher.doFinal(passedBytes);
89          } catch (final Exception e) {
90              throw new EncryptionException(e.getMessage(), e);
91          }
92      }
93  
94      /**
95       * Encrypts the inputString using the key.
96       *
97       * @param key at least 24 character long key (required)
98       * @param inputString the string to encrypt (required)
99       * @return the encrypted version of the inputString
100      * @throws EncryptionException in the event of an encryption failure
101      */
102     public static String encrypt(String key, String inputString) throws EncryptionException {
103         isValidKey(key);
104         final byte[] cipherText = cipher(key, stringToByteArray(inputString), Cipher.ENCRYPT_MODE);
105         return byteArrayToString(Base64.encodeBase64(cipherText));
106     }
107 
108     /**
109      * Encrypts the inputBytes using the key.
110      *
111      * @param key at least 24 character long key (required)
112      * @param inputBytes the bytes to encrypt (required)
113      * @return the encrypted version of the inputBytes
114      * @throws EncryptionException in the event of an encryption failure
115      */
116     public static byte[] encrypt(String key, byte[] inputBytes) throws EncryptionException {
117         isValidKey(key);
118         return Base64.encodeBase64(cipher(key, inputBytes, Cipher.ENCRYPT_MODE));
119     }
120 
121     /**
122      * Decrypts the inputString using the key.
123      *
124      * @param key the key used to originally encrypt the string (required)
125      * @param inputString the encrypted string (required)
126      * @return the decrypted version of inputString
127      * @throws EncryptionException in the event of an encryption failure
128      */
129     public static String decrypt(String key, String inputString) throws EncryptionException {
130         Assert.hasText(key, "A key is required to attempt decryption");
131         final byte[] cipherText = cipher(key, Base64.decodeBase64(stringToByteArray(inputString)), Cipher.DECRYPT_MODE);
132         return byteArrayToString(cipherText);
133     }
134 
135     /**
136      * Decrypts the inputBytes using the key.
137      *
138      * @param key the key used to originally encrypt the string (required)
139      * @param inputBytes the encrypted bytes (required)
140      * @return the decrypted version of inputBytes
141      * @throws EncryptionException in the event of an encryption failure
142      */
143     public static byte[] decrypt(String key, byte[] inputBytes) throws EncryptionException {
144         Assert.hasText(key, "A key is required to attempt decryption");
145         return cipher(key, Base64.decodeBase64(inputBytes), Cipher.DECRYPT_MODE);
146     }
147 
148     private static void isValidKey(String key) {
149         Assert.hasText(key, "A key to perform the encryption is required");
150         Assert.isTrue(key.length() >= 24, "Key must be at least 24 characters long");
151     }
152 
153     public static class EncryptionException extends AcegiSecurityException {
154         private static final long serialVersionUID = 1L;
155 
156         public EncryptionException(String message, Throwable t) {
157             super(message, t);
158         }
159 
160         public EncryptionException(String message) {
161             super(message);
162         }
163     }
164 }