1 package be
.nikiroo
.utils
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.io
.OutputStream
;
6 import java
.io
.UnsupportedEncodingException
;
7 import java
.security
.InvalidKeyException
;
8 import java
.security
.NoSuchAlgorithmException
;
10 import javax
.crypto
.BadPaddingException
;
11 import javax
.crypto
.Cipher
;
12 import javax
.crypto
.CipherInputStream
;
13 import javax
.crypto
.CipherOutputStream
;
14 import javax
.crypto
.IllegalBlockSizeException
;
15 import javax
.crypto
.NoSuchPaddingException
;
16 import javax
.crypto
.SecretKey
;
17 import javax
.crypto
.spec
.SecretKeySpec
;
18 import javax
.net
.ssl
.SSLException
;
21 * Small utility class to do AES encryption/decryption.
23 * For the moment, it is multi-thread compatible, but beware:
25 * <li>The encrypt/decrypt calls are serialized</li>
26 * <li>The streams are independent and thus parallel</li>
29 * Do not assume it is actually secure until you checked the code...
33 public class CryptUtils
{
34 static private final String AES_NAME
= "AES/ECB/PKCS5Padding";
36 private Cipher ecipher
;
37 private Cipher dcipher
;
38 private SecretKey key
;
41 * Small and lazy-easy way to initialize a 128 bits key with {@link CryptUtils}.
43 * <b>Some</b> part of the key will be used to generate a 128 bits key and
44 * initialize the {@link CryptUtils}; even NULL will generate something.
46 * <b>This is most probably not secure. Do not use if you actually care
50 * the {@link String} to use as a base for the key, can be NULL
52 public CryptUtils(String key
) {
55 } catch (InvalidKeyException e
) {
56 // We made sure that the key is correct, so nothing here
62 * Create a new instance of {@link CryptUtils} with the given 128 bytes key.
64 * The key <b>must</b> be exactly 128 bytes long.
67 * the 128 bits (32 bytes) of the key
69 * @throws InvalidKeyException
70 * if the key is not an array of 128 bytes
72 public CryptUtils(byte[] bytes32
) throws InvalidKeyException
{
77 * Wrap the given {@link InputStream} so it is transparently encrypted by
78 * the current {@link CryptUtils}.
81 * the {@link InputStream} to wrap
82 * @return the auto-encode {@link InputStream}
84 public InputStream
encryptInputStream(InputStream in
) {
85 Cipher ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
86 return new CipherInputStream(in
, ecipher
);
90 * Wrap the given {@link OutputStream} so it is transparently encrypted by
91 * the current {@link CryptUtils}.
94 * the {@link OutputStream} to wrap
95 * @return the auto-encode {@link OutputStream}
97 public OutputStream
encryptOutpuStream(OutputStream out
) {
98 Cipher ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
99 return new CipherOutputStream(out
, ecipher
);
103 * Wrap the given {@link OutStream} so it is transparently decoded by the
104 * current {@link CryptUtils}.
107 * the {@link InputStream} to wrap
108 * @return the auto-decode {@link InputStream}
110 public InputStream
decryptInputStream(InputStream in
) {
111 Cipher dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
112 return new CipherInputStream(in
, dcipher
);
116 * Wrap the given {@link OutStream} so it is transparently decoded by the
117 * current {@link CryptUtils}.
120 * the {@link OutputStream} to wrap
121 * @return the auto-decode {@link OutputStream}
123 public OutputStream
decryptOutputStream(OutputStream out
) {
124 Cipher dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
125 return new CipherOutputStream(out
, dcipher
);
129 * This method required an array of 128 bytes.
132 * the array, which <b>must</b> be of 128 bits (32 bytes)
134 * @throws InvalidKeyException
135 * if the key is not an array of 128 bits (32 bytes)
137 private void init(byte[] bytes32
) throws InvalidKeyException
{
138 if (bytes32
== null || bytes32
.length
!= 32) {
139 throw new InvalidKeyException(
140 "The size of the key must be of 128 bits (32 bytes), it is: "
141 + (bytes32
== null ?
"null" : "" + bytes32
.length
)
145 key
= new SecretKeySpec(bytes32
, "AES");
146 ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
147 dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
151 * Create a new {@link Cipher}of the given mode (see
152 * {@link Cipher#ENCRYPT_MODE} and {@link Cipher#ENCRYPT_MODE}).
155 * the mode ({@link Cipher#ENCRYPT_MODE} or
156 * {@link Cipher#ENCRYPT_MODE})
158 * @return the new {@link Cipher}
160 private Cipher
newCipher(int mode
) {
162 Cipher cipher
= Cipher
.getInstance(AES_NAME
);
163 cipher
.init(mode
, key
);
165 } catch (NoSuchAlgorithmException e
) {
166 // Every implementation of the Java platform is required to support
167 // this standard Cipher transformation with 128 bits keys
169 } catch (NoSuchPaddingException e
) {
170 // Every implementation of the Java platform is required to support
171 // this standard Cipher transformation with 128 bits keys
173 } catch (InvalidKeyException e
) {
174 // Every implementation of the Java platform is required to support
175 // this standard Cipher transformation with 128 bits keys
186 * the data to encrypt
188 * @return the encrypted data
190 * @throws SSLException
191 * in case of I/O error (i.e., the data is not what you assumed
194 public byte[] encrypt(byte[] data
) throws SSLException
{
195 synchronized (ecipher
) {
197 return ecipher
.doFinal(data
);
198 } catch (IllegalBlockSizeException e
) {
199 throw new SSLException(e
);
200 } catch (BadPaddingException e
) {
201 throw new SSLException(e
);
210 * the data to encrypt
212 * @return the encrypted data
214 * @throws SSLException
215 * in case of I/O error (i.e., the data is not what you assumed
218 public byte[] encrypt(String data
) throws SSLException
{
220 return encrypt(data
.getBytes("UTF8"));
221 } catch (UnsupportedEncodingException e
) {
222 // UTF-8 is required in all confirm JVMs
229 * Encrypt the data, then encode it into Base64.
232 * the data to encrypt
234 * TRUE to also compress the data in GZIP format; remember that
235 * compressed and not-compressed content are different; you need
236 * to know which is which when decoding
238 * @return the encrypted data, encoded in Base64
240 * @throws SSLException
241 * in case of I/O error (i.e., the data is not what you assumed
244 public String
encrypt64(String data
, boolean zip
) throws SSLException
{
246 return encrypt64(data
.getBytes("UTF8"), zip
);
247 } catch (UnsupportedEncodingException e
) {
248 // UTF-8 is required in all confirm JVMs
255 * Encrypt the data, then encode it into Base64.
258 * the data to encrypt
260 * TRUE to also compress the data in GZIP format; remember that
261 * compressed and not-compressed content are different; you need
262 * to know which is which when decoding
264 * @return the encrypted data, encoded in Base64
266 * @throws SSLException
267 * in case of I/O error (i.e., the data is not what you assumed
270 public String
encrypt64(byte[] data
, boolean zip
) throws SSLException
{
272 return StringUtils
.base64(encrypt(data
), zip
);
273 } catch (IOException e
) {
274 // not exactly true, but we consider here that this error is a crypt
275 // error, not a normal I/O error
276 throw new SSLException(e
);
281 * Decode the data which is assumed to be encrypted with the same utilities.
284 * the encrypted data to decode
286 * @return the original, decoded data
288 * @throws SSLException
289 * in case of I/O error
291 public byte[] decrypt(byte[] data
) throws SSLException
{
292 synchronized (dcipher
) {
294 return dcipher
.doFinal(data
);
295 } catch (IllegalBlockSizeException e
) {
296 throw new SSLException(e
);
297 } catch (BadPaddingException e
) {
298 throw new SSLException(e
);
304 * Decode the data which is assumed to be encrypted with the same utilities
305 * and to be a {@link String}.
308 * the encrypted data to decode
310 * @return the original, decoded data,as a {@link String}
312 * @throws SSLException
313 * in case of I/O error
315 public String
decrypts(byte[] data
) throws SSLException
{
317 return new String(decrypt(data
), "UTF-8");
318 } catch (UnsupportedEncodingException e
) {
319 // UTF-8 is required in all confirm JVMs
326 * Decode the data which is assumed to be encrypted with the same utilities
327 * and is a Base64 encoded value.
330 * the encrypted data to decode in Base64 format
332 * TRUE to also uncompress the data from a GZIP format
333 * automatically; if set to FALSE, zipped data can be returned
335 * @return the original, decoded data
337 * @throws SSLException
338 * in case of I/O error
340 public byte[] decrypt64(String data
, boolean zip
) throws SSLException
{
342 return decrypt(StringUtils
.unbase64(data
, zip
));
343 } catch (IOException e
) {
344 // not exactly true, but we consider here that this error is a crypt
345 // error, not a normal I/O error
346 throw new SSLException(e
);
351 * Decode the data which is assumed to be encrypted with the same utilities
352 * and is a Base64 encoded value, then convert it into a String (this method
353 * assumes the data <b>was</b> indeed a UTF-8 encoded {@link String}).
356 * the encrypted data to decode in Base64 format
358 * TRUE to also uncompress the data from a GZIP format
359 * automatically; if set to FALSE, zipped data can be returned
361 * @return the original, decoded data
363 * @throws SSLException
364 * in case of I/O error
366 public String
decrypt64s(String data
, boolean zip
) throws SSLException
{
368 return new String(decrypt(StringUtils
.unbase64(data
, zip
)), "UTF-8");
369 } catch (UnsupportedEncodingException e
) {
370 // UTF-8 is required in all confirm JVMs
373 } catch (IOException e
) {
374 // not exactly true, but we consider here that this error is a crypt
375 // error, not a normal I/O error
376 throw new SSLException(e
);
381 * This is probably <b>NOT</b> secure!
384 * some {@link String} input
386 * @return a 128 bits key computed from the given input
388 static private byte[] key2key(String input
) {
389 return StringUtils
.getMd5Hash("" + input
).getBytes();