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
.InvalidAlgorithmParameterException
;
8 import java
.security
.InvalidKeyException
;
9 import java
.security
.NoSuchAlgorithmException
;
11 import javax
.crypto
.BadPaddingException
;
12 import javax
.crypto
.Cipher
;
13 import javax
.crypto
.CipherInputStream
;
14 import javax
.crypto
.CipherOutputStream
;
15 import javax
.crypto
.IllegalBlockSizeException
;
16 import javax
.crypto
.NoSuchPaddingException
;
17 import javax
.crypto
.SecretKey
;
18 import javax
.crypto
.spec
.IvParameterSpec
;
19 import javax
.crypto
.spec
.SecretKeySpec
;
20 import javax
.net
.ssl
.SSLException
;
23 * Small utility class to do AES encryption/decryption.
25 * For the moment, it is multi-thread compatible, but beware:
27 * <li>The encrypt/decrypt calls are serialized</li>
28 * <li>The streams are independent and thus parallel</li>
31 * Do not assume it is actually secure until you checked the code...
35 public class CryptUtils
{
36 static private final String AES_NAME
= "AES/CFB8/NoPadding";
38 private Cipher ecipher
;
39 private Cipher dcipher
;
40 private SecretKey key
;
43 * Small and lazy-easy way to initialize a 128 bits key with
46 * <b>Some</b> part of the key will be used to generate a 128 bits key and
47 * initialize the {@link CryptUtils}; even NULL will generate something.
49 * <b>This is most probably not secure. Do not use if you actually care
53 * the {@link String} to use as a base for the key, can be NULL
55 public CryptUtils(String key
) {
58 } catch (InvalidKeyException e
) {
59 // We made sure that the key is correct, so nothing here
65 * Create a new instance of {@link CryptUtils} with the given 128 bytes key.
67 * The key <b>must</b> be exactly 128 bytes long.
70 * the 128 bits (32 bytes) of the key
72 * @throws InvalidKeyException
73 * if the key is not an array of 128 bytes
75 public CryptUtils(byte[] bytes32
) throws InvalidKeyException
{
80 * Wrap the given {@link InputStream} so it is transparently encrypted by
81 * the current {@link CryptUtils}.
84 * the {@link InputStream} to wrap
85 * @return the auto-encode {@link InputStream}
87 public InputStream
encrypt(InputStream in
) {
88 Cipher ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
89 return new CipherInputStream(in
, ecipher
);
93 * Wrap the given {@link InputStream} so it is transparently encrypted by
94 * the current {@link CryptUtils} and encoded in base64.
97 * the {@link InputStream} to wrap
99 * TRUE to also uncompress the data from a GZIP format; take care
100 * about this flag, as it could easily cause errors in the
101 * returned content or an {@link IOException}
103 * @return the auto-encode {@link InputStream}
105 * @throws IOException
106 * in case of I/O error
108 public InputStream
encrypt64(InputStream in
, boolean zip
)
110 return StringUtils
.base64(encrypt(in
), zip
, false);
114 * Wrap the given {@link OutputStream} so it is transparently encrypted by
115 * the current {@link CryptUtils}.
118 * the {@link OutputStream} to wrap
120 * @return the auto-encode {@link OutputStream}
122 public OutputStream
encrypt(OutputStream out
) {
123 Cipher ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
124 return new CipherOutputStream(out
, ecipher
);
128 * Wrap the given {@link OutputStream} so it is transparently encrypted by
129 * the current {@link CryptUtils} and encoded in base64.
132 * the {@link OutputStream} to wrap
134 * TRUE to also uncompress the data from a GZIP format; take care
135 * about this flag, as it could easily cause errors in the
136 * returned content or an {@link IOException}
138 * @return the auto-encode {@link OutputStream}
140 * @throws IOException
141 * in case of I/O error
143 public OutputStream
encrypt64(OutputStream out
, boolean zip
)
145 return encrypt(StringUtils
.base64(out
, zip
, false));
149 * Wrap the given {@link OutputStream} so it is transparently decoded by the
150 * current {@link CryptUtils}.
153 * the {@link InputStream} to wrap
155 * @return the auto-decode {@link InputStream}
157 public InputStream
decrypt(InputStream in
) {
158 Cipher dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
159 return new CipherInputStream(in
, dcipher
);
163 * Wrap the given {@link OutputStream} so it is transparently decoded by the
164 * current {@link CryptUtils} and decoded from base64.
167 * the {@link InputStream} to wrap
169 * TRUE to also uncompress the data from a GZIP format; take care
170 * about this flag, as it could easily cause errors in the
171 * returned content or an {@link IOException}
173 * @return the auto-decode {@link InputStream}
175 * @throws IOException
176 * in case of I/O error
178 public InputStream
decrypt64(InputStream in
, boolean zip
)
180 return decrypt(StringUtils
.unbase64(in
, zip
));
184 * Wrap the given {@link OutputStream} so it is transparently decoded by the
185 * current {@link CryptUtils}.
188 * the {@link OutputStream} to wrap
189 * @return the auto-decode {@link OutputStream}
191 public OutputStream
decrypt(OutputStream out
) {
192 Cipher dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
193 return new CipherOutputStream(out
, dcipher
);
197 * Wrap the given {@link OutputStream} so it is transparently decoded by the
198 * current {@link CryptUtils} and decoded from base64.
201 * the {@link OutputStream} to wrap
203 * TRUE to also uncompress the data from a GZIP format; take care
204 * about this flag, as it could easily cause errors in the
205 * returned content or an {@link IOException}
207 * @return the auto-decode {@link OutputStream}
209 * @throws IOException
210 * in case of I/O error
212 public OutputStream
decrypt64(OutputStream out
, boolean zip
)
214 return StringUtils
.unbase64(decrypt(out
), zip
);
218 * This method required an array of 128 bytes.
221 * the array, which <b>must</b> be of 128 bits (32 bytes)
223 * @throws InvalidKeyException
224 * if the key is not an array of 128 bits (32 bytes)
226 private void init(byte[] bytes32
) throws InvalidKeyException
{
227 if (bytes32
== null || bytes32
.length
!= 32) {
228 throw new InvalidKeyException(
229 "The size of the key must be of 128 bits (32 bytes), it is: "
230 + (bytes32
== null ?
"null" : "" + bytes32
.length
)
234 key
= new SecretKeySpec(bytes32
, "AES");
235 ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
236 dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
240 * Create a new {@link Cipher}of the given mode (see
241 * {@link Cipher#ENCRYPT_MODE} and {@link Cipher#ENCRYPT_MODE}).
244 * the mode ({@link Cipher#ENCRYPT_MODE} or
245 * {@link Cipher#ENCRYPT_MODE})
247 * @return the new {@link Cipher}
249 private Cipher
newCipher(int mode
) {
251 byte[] iv
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
252 IvParameterSpec ivspec
= new IvParameterSpec(iv
);
253 Cipher cipher
= Cipher
.getInstance(AES_NAME
);
254 cipher
.init(mode
, key
, ivspec
);
256 } catch (NoSuchAlgorithmException e
) {
257 // Every implementation of the Java platform is required to support
258 // this standard Cipher transformation with 128 bits keys
260 } catch (NoSuchPaddingException e
) {
261 // Every implementation of the Java platform is required to support
262 // this standard Cipher transformation with 128 bits keys
264 } catch (InvalidKeyException e
) {
265 // Every implementation of the Java platform is required to support
266 // this standard Cipher transformation with 128 bits keys
268 } catch (InvalidAlgorithmParameterException e
) {
280 * the data to encrypt
282 * @return the encrypted data
284 * @throws SSLException
285 * in case of I/O error (i.e., the data is not what you assumed
288 public byte[] encrypt(byte[] data
) throws SSLException
{
289 synchronized (ecipher
) {
291 return ecipher
.doFinal(data
);
292 } catch (IllegalBlockSizeException e
) {
293 throw new SSLException(e
);
294 } catch (BadPaddingException e
) {
295 throw new SSLException(e
);
304 * the data to encrypt
306 * @return the encrypted data
308 * @throws SSLException
309 * in case of I/O error (i.e., the data is not what you assumed
312 public byte[] encrypt(String data
) throws SSLException
{
314 return encrypt(data
.getBytes("UTF8"));
315 } catch (UnsupportedEncodingException e
) {
316 // UTF-8 is required in all confirm JVMs
323 * Encrypt the data, then encode it into Base64.
326 * the data to encrypt
328 * TRUE to also compress the data in GZIP format; remember that
329 * compressed and not-compressed content are different; you need
330 * to know which is which when decoding
332 * @return the encrypted data, encoded in Base64
334 * @throws SSLException
335 * in case of I/O error (i.e., the data is not what you assumed
338 public String
encrypt64(String data
, boolean zip
) throws SSLException
{
340 return encrypt64(data
.getBytes("UTF8"), zip
);
341 } catch (UnsupportedEncodingException e
) {
342 // UTF-8 is required in all confirm JVMs
349 * Encrypt the data, then encode it into Base64.
352 * the data to encrypt
354 * TRUE to also compress the data in GZIP format; remember that
355 * compressed and not-compressed content are different; you need
356 * to know which is which when decoding
358 * @return the encrypted data, encoded in Base64
360 * @throws SSLException
361 * in case of I/O error (i.e., the data is not what you assumed
364 public String
encrypt64(byte[] data
, boolean zip
) throws SSLException
{
366 return StringUtils
.base64(encrypt(data
), zip
);
367 } catch (IOException e
) {
368 // not exactly true, but we consider here that this error is a crypt
369 // error, not a normal I/O error
370 throw new SSLException(e
);
375 * Decode the data which is assumed to be encrypted with the same utilities.
378 * the encrypted data to decode
380 * @return the original, decoded data
382 * @throws SSLException
383 * in case of I/O error
385 public byte[] decrypt(byte[] data
) throws SSLException
{
386 synchronized (dcipher
) {
388 return dcipher
.doFinal(data
);
389 } catch (IllegalBlockSizeException e
) {
390 throw new SSLException(e
);
391 } catch (BadPaddingException e
) {
392 throw new SSLException(e
);
398 * Decode the data which is assumed to be encrypted with the same utilities
399 * and to be a {@link String}.
402 * the encrypted data to decode
404 * @return the original, decoded data,as a {@link String}
406 * @throws SSLException
407 * in case of I/O error
409 public String
decrypts(byte[] data
) throws SSLException
{
411 return new String(decrypt(data
), "UTF-8");
412 } catch (UnsupportedEncodingException e
) {
413 // UTF-8 is required in all confirm JVMs
420 * Decode the data which is assumed to be encrypted with the same utilities
421 * and is a Base64 encoded value.
424 * the encrypted data to decode in Base64 format
426 * TRUE to also uncompress the data from a GZIP format
427 * automatically; if set to FALSE, zipped data can be returned
429 * @return the original, decoded data
431 * @throws SSLException
432 * in case of I/O error
434 public byte[] decrypt64(String data
, boolean zip
) throws SSLException
{
436 return decrypt(StringUtils
.unbase64(data
, zip
));
437 } catch (IOException e
) {
438 // not exactly true, but we consider here that this error is a crypt
439 // error, not a normal I/O error
440 throw new SSLException(e
);
445 * Decode the data which is assumed to be encrypted with the same utilities
446 * and is a Base64 encoded value, then convert it into a String (this method
447 * assumes the data <b>was</b> indeed a UTF-8 encoded {@link String}).
450 * the encrypted data to decode in Base64 format
452 * TRUE to also uncompress the data from a GZIP format
453 * automatically; if set to FALSE, zipped data can be returned
455 * @return the original, decoded data
457 * @throws SSLException
458 * in case of I/O error
460 public String
decrypt64s(String data
, boolean zip
) throws SSLException
{
462 return new String(decrypt(StringUtils
.unbase64(data
, zip
)), "UTF-8");
463 } catch (UnsupportedEncodingException e
) {
464 // UTF-8 is required in all confirm JVMs
467 } catch (IOException e
) {
468 // not exactly true, but we consider here that this error is a crypt
469 // error, not a normal I/O error
470 throw new SSLException(e
);
475 * This is probably <b>NOT</b> secure!
478 * some {@link String} input
480 * @return a 128 bits key computed from the given input
482 static private byte[] key2key(String input
) {
483 return StringUtils
.getMd5Hash("" + input
).getBytes();