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
;
22 import be
.nikiroo
.utils
.streams
.Base64InputStream
;
23 import be
.nikiroo
.utils
.streams
.Base64OutputStream
;
26 * Small utility class to do AES encryption/decryption.
28 * For the moment, it is multi-thread compatible, but beware:
30 * <li>The encrypt/decrypt calls are serialized</li>
31 * <li>The streams are independent and thus parallel</li>
34 * Do not assume it is actually secure until you checked the code...
38 public class CryptUtils
{
39 static private final String AES_NAME
= "AES/CFB8/NoPadding";
41 private Cipher ecipher
;
42 private Cipher dcipher
;
43 private SecretKey key
;
46 * Small and lazy-easy way to initialize a 128 bits key with
49 * <b>Some</b> part of the key will be used to generate a 128 bits key and
50 * initialize the {@link CryptUtils}; even NULL will generate something.
52 * <b>This is most probably not secure. Do not use if you actually care
56 * the {@link String} to use as a base for the key, can be NULL
58 public CryptUtils(String key
) {
61 } catch (InvalidKeyException e
) {
62 // We made sure that the key is correct, so nothing here
68 * Create a new instance of {@link CryptUtils} with the given 128 bytes key.
70 * The key <b>must</b> be exactly 128 bytes long.
73 * the 128 bits (32 bytes) of the key
75 * @throws InvalidKeyException
76 * if the key is not an array of 128 bytes
78 public CryptUtils(byte[] bytes32
) throws InvalidKeyException
{
83 * Wrap the given {@link InputStream} so it is transparently encrypted by
84 * the current {@link CryptUtils}.
87 * the {@link InputStream} to wrap
88 * @return the auto-encode {@link InputStream}
90 public InputStream
encrypt(InputStream in
) {
91 Cipher ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
92 return new CipherInputStream(in
, ecipher
);
96 * Wrap the given {@link InputStream} so it is transparently encrypted by
97 * the current {@link CryptUtils} and encoded in base64.
100 * the {@link InputStream} to wrap
102 * @return the auto-encode {@link InputStream}
104 * @throws IOException
105 * in case of I/O error
107 public InputStream
encrypt64(InputStream in
) throws IOException
{
108 return new Base64InputStream(encrypt(in
), true);
112 * Wrap the given {@link OutputStream} so it is transparently encrypted by
113 * the current {@link CryptUtils}.
116 * the {@link OutputStream} to wrap
118 * @return the auto-encode {@link OutputStream}
120 public OutputStream
encrypt(OutputStream out
) {
121 Cipher ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
122 return new CipherOutputStream(out
, ecipher
);
126 * Wrap the given {@link OutputStream} so it is transparently encrypted by
127 * the current {@link CryptUtils} and encoded in base64.
130 * the {@link OutputStream} to wrap
132 * @return the auto-encode {@link OutputStream}
134 * @throws IOException
135 * in case of I/O error
137 public OutputStream
encrypt64(OutputStream out
) throws IOException
{
138 return encrypt(new Base64OutputStream(out
, true));
142 * Wrap the given {@link OutputStream} so it is transparently decoded by the
143 * current {@link CryptUtils}.
146 * the {@link InputStream} to wrap
148 * @return the auto-decode {@link InputStream}
150 public InputStream
decrypt(InputStream in
) {
151 Cipher dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
152 return new CipherInputStream(in
, dcipher
);
156 * Wrap the given {@link OutputStream} so it is transparently decoded by the
157 * current {@link CryptUtils} and decoded from base64.
160 * the {@link InputStream} to wrap
162 * @return the auto-decode {@link InputStream}
164 * @throws IOException
165 * in case of I/O error
167 public InputStream
decrypt64(InputStream in
) throws IOException
{
168 return decrypt(new Base64InputStream(in
, false));
172 * Wrap the given {@link OutputStream} so it is transparently decoded by the
173 * current {@link CryptUtils}.
176 * the {@link OutputStream} to wrap
177 * @return the auto-decode {@link OutputStream}
179 public OutputStream
decrypt(OutputStream out
) {
180 Cipher dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
181 return new CipherOutputStream(out
, dcipher
);
185 * Wrap the given {@link OutputStream} so it is transparently decoded by the
186 * current {@link CryptUtils} and decoded from base64.
189 * the {@link OutputStream} to wrap
191 * @return the auto-decode {@link OutputStream}
193 * @throws IOException
194 * in case of I/O error
196 public OutputStream
decrypt64(OutputStream out
) throws IOException
{
197 return new Base64OutputStream(decrypt(out
), false);
201 * This method required an array of 128 bytes.
204 * the array, which <b>must</b> be of 128 bits (32 bytes)
206 * @throws InvalidKeyException
207 * if the key is not an array of 128 bits (32 bytes)
209 private void init(byte[] bytes32
) throws InvalidKeyException
{
210 if (bytes32
== null || bytes32
.length
!= 32) {
211 throw new InvalidKeyException(
212 "The size of the key must be of 128 bits (32 bytes), it is: "
213 + (bytes32
== null ?
"null" : "" + bytes32
.length
)
217 key
= new SecretKeySpec(bytes32
, "AES");
218 ecipher
= newCipher(Cipher
.ENCRYPT_MODE
);
219 dcipher
= newCipher(Cipher
.DECRYPT_MODE
);
223 * Create a new {@link Cipher}of the given mode (see
224 * {@link Cipher#ENCRYPT_MODE} and {@link Cipher#ENCRYPT_MODE}).
227 * the mode ({@link Cipher#ENCRYPT_MODE} or
228 * {@link Cipher#ENCRYPT_MODE})
230 * @return the new {@link Cipher}
232 private Cipher
newCipher(int mode
) {
234 byte[] iv
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
235 IvParameterSpec ivspec
= new IvParameterSpec(iv
);
236 Cipher cipher
= Cipher
.getInstance(AES_NAME
);
237 cipher
.init(mode
, key
, ivspec
);
239 } catch (NoSuchAlgorithmException e
) {
240 // Every implementation of the Java platform is required to support
241 // this standard Cipher transformation with 128 bits keys
243 } catch (NoSuchPaddingException e
) {
244 // Every implementation of the Java platform is required to support
245 // this standard Cipher transformation with 128 bits keys
247 } catch (InvalidKeyException e
) {
248 // Every implementation of the Java platform is required to support
249 // this standard Cipher transformation with 128 bits keys
251 } catch (InvalidAlgorithmParameterException e
) {
263 * the data to encrypt
265 * @return the encrypted data
267 * @throws SSLException
268 * in case of I/O error (i.e., the data is not what you assumed
271 public byte[] encrypt(byte[] data
) throws SSLException
{
272 synchronized (ecipher
) {
274 return ecipher
.doFinal(data
);
275 } catch (IllegalBlockSizeException e
) {
276 throw new SSLException(e
);
277 } catch (BadPaddingException e
) {
278 throw new SSLException(e
);
287 * the data to encrypt
289 * @return the encrypted data
291 * @throws SSLException
292 * in case of I/O error (i.e., the data is not what you assumed
295 public byte[] encrypt(String data
) throws SSLException
{
296 return encrypt(StringUtils
.getBytes(data
));
300 * Encrypt the data, then encode it into Base64.
303 * the data to encrypt
305 * TRUE to also compress the data in GZIP format; remember that
306 * compressed and not-compressed content are different; you need
307 * to know which is which when decoding
309 * @return the encrypted data, encoded in Base64
311 * @throws SSLException
312 * in case of I/O error (i.e., the data is not what you assumed
315 public String
encrypt64(String data
) throws SSLException
{
316 return encrypt64(StringUtils
.getBytes(data
));
320 * Encrypt the data, then encode it into Base64.
323 * the data to encrypt
325 * @return the encrypted data, encoded in Base64
327 * @throws SSLException
328 * in case of I/O error (i.e., the data is not what you assumed
331 public String
encrypt64(byte[] data
) throws SSLException
{
333 return StringUtils
.base64(encrypt(data
));
334 } catch (IOException e
) {
335 // not exactly true, but we consider here that this error is a crypt
336 // error, not a normal I/O error
337 throw new SSLException(e
);
342 * Decode the data which is assumed to be encrypted with the same utilities.
345 * the encrypted data to decode
347 * @return the original, decoded data
349 * @throws SSLException
350 * in case of I/O error
352 public byte[] decrypt(byte[] data
) throws SSLException
{
353 synchronized (dcipher
) {
355 return dcipher
.doFinal(data
);
356 } catch (IllegalBlockSizeException e
) {
357 throw new SSLException(e
);
358 } catch (BadPaddingException e
) {
359 throw new SSLException(e
);
365 * Decode the data which is assumed to be encrypted with the same utilities
366 * and to be a {@link String}.
369 * the encrypted data to decode
371 * @return the original, decoded data,as a {@link String}
373 * @throws SSLException
374 * in case of I/O error
376 public String
decrypts(byte[] data
) throws SSLException
{
378 return new String(decrypt(data
), "UTF-8");
379 } catch (UnsupportedEncodingException e
) {
380 // UTF-8 is required in all confirm JVMs
387 * Decode the data which is assumed to be encrypted with the same utilities
388 * and is a Base64 encoded value.
391 * the encrypted data to decode in Base64 format
393 * TRUE to also uncompress the data from a GZIP format
394 * automatically; if set to FALSE, zipped data can be returned
396 * @return the original, decoded data
398 * @throws SSLException
399 * in case of I/O error
401 public byte[] decrypt64(String data
) throws SSLException
{
403 return decrypt(StringUtils
.unbase64(data
));
404 } catch (IOException e
) {
405 // not exactly true, but we consider here that this error is a crypt
406 // error, not a normal I/O error
407 throw new SSLException(e
);
412 * Decode the data which is assumed to be encrypted with the same utilities
413 * and is a Base64 encoded value, then convert it into a String (this method
414 * assumes the data <b>was</b> indeed a UTF-8 encoded {@link String}).
417 * the encrypted data to decode in Base64 format
419 * TRUE to also uncompress the data from a GZIP format
420 * automatically; if set to FALSE, zipped data can be returned
422 * @return the original, decoded data
424 * @throws SSLException
425 * in case of I/O error
427 public String
decrypt64s(String data
) throws SSLException
{
429 return new String(decrypt(StringUtils
.unbase64(data
)), "UTF-8");
430 } catch (UnsupportedEncodingException e
) {
431 // UTF-8 is required in all confirm JVMs
434 } catch (IOException e
) {
435 // not exactly true, but we consider here that this error is a crypt
436 // error, not a normal I/O error
437 throw new SSLException(e
);
442 * This is probably <b>NOT</b> secure!
445 * some {@link String} input
447 * @return a 128 bits key computed from the given input
449 static private byte[] key2key(String input
) {
450 return StringUtils
.getMd5Hash("" + input
).getBytes();