CryptUtils: clear the key array after use
[nikiroo-utils.git] / src / be / nikiroo / utils / CryptUtils.java
CommitLineData
52e0732e
NR
1package be.nikiroo.utils;
2
3import java.io.IOException;
d20c8d77
NR
4import java.io.InputStream;
5import java.io.OutputStream;
9fb03c36 6import java.io.UnsupportedEncodingException;
52e0732e
NR
7import java.security.InvalidKeyException;
8import java.security.NoSuchAlgorithmException;
9
10import javax.crypto.BadPaddingException;
11import javax.crypto.Cipher;
d20c8d77
NR
12import javax.crypto.CipherInputStream;
13import javax.crypto.CipherOutputStream;
52e0732e
NR
14import javax.crypto.IllegalBlockSizeException;
15import javax.crypto.NoSuchPaddingException;
16import javax.crypto.SecretKey;
17import javax.crypto.spec.SecretKeySpec;
9fb03c36 18import javax.net.ssl.SSLException;
52e0732e
NR
19
20/**
21 * Small utility class to do AES encryption/decryption.
22 * <p>
23 * Do not assume it is actually secure until you checked the code...
24 *
25 * @author niki
26 */
27public class CryptUtils {
28 private Cipher ecipher;
29 private Cipher dcipher;
30
31 /**
32 * Small and leazy way to initialize a 128 bits key with {@link CryptUtils}.
33 * <p>
34 * <b>Some</b> part of the key will be used to generate a 128 bits key and
35 * initialize the {@link CryptUtils}; even NULL will generate something.
36 * <p>
37 * <b>This is most probably not secure. Do not use if you actually care
38 * about security.</b>
39 *
40 * @param key
41 * the {@link String} to use as a base for the key, can be NULL
42 */
43 public CryptUtils(String key) {
44 try {
272f5c86
NR
45 byte[] bytes32 = key2key(key);
46 init(bytes32);
47 for (int i = 0 ; i < bytes32.length ; i++) {
48 bytes32[i] = 0;
49 }
52e0732e
NR
50 } catch (InvalidKeyException e) {
51 // We made sure that the key is correct, so nothing here
52 e.printStackTrace();
53 }
54 }
55
56 /**
57 * Create a new instance of {@link CryptUtils} with the given 128 bytes key.
58 * <p>
59 * The key <b>must</b> be exactly 128 bytes long.
60 *
61 * @param bytes32
62 * the 128 bits (32 bytes) of the key
63 *
64 * @throws InvalidKeyException
65 * if the key is not an array of 128 bytes
66 */
67 public CryptUtils(byte[] bytes32) throws InvalidKeyException {
68 init(bytes32);
272f5c86
NR
69 for (int i = 0 ; i < bytes32.length ; i++) {
70 bytes32[i] = 0;
71 }
52e0732e
NR
72 }
73
d20c8d77
NR
74 /**
75 * Wrap the given {@link InputStream} so it is transparently encrypted by
76 * the current {@link CryptUtils}.
77 *
78 * @param in
79 * the {@link InputStream} to wrap
80 * @return the auto-encode {@link InputStream}
81 */
82 public InputStream encryptInputStream(InputStream in) {
83 return new CipherInputStream(in, ecipher);
84 }
85
86 /**
87 * Wrap the given {@link OutputStream} so it is transparently encrypted by
88 * the current {@link CryptUtils}.
89 *
90 * @param in
91 * the {@link OutputStream} to wrap
92 * @return the auto-encode {@link OutputStream}
93 */
94 public OutputStream encryptOutpuStream(OutputStream out) {
95 return new CipherOutputStream(out, ecipher);
96 }
97
98 /**
99 * Wrap the given {@link OutStream} so it is transparently decoded by the
100 * current {@link CryptUtils}.
101 *
102 * @param in
103 * the {@link InputStream} to wrap
104 * @return the auto-decode {@link InputStream}
105 */
106 public InputStream decryptInputStream(InputStream in) {
107 return new CipherInputStream(in, dcipher);
108 }
109
110 /**
111 * Wrap the given {@link OutStream} so it is transparently decoded by the
112 * current {@link CryptUtils}.
113 *
114 * @param out
115 * the {@link OutputStream} to wrap
116 * @return the auto-decode {@link OutputStream}
117 */
118 public OutputStream decryptOutputStream(OutputStream out) {
119 return new CipherOutputStream(out, dcipher);
120 }
121
52e0732e
NR
122 /**
123 * This method required an array of 128 bytes.
124 *
125 * @param bytes32
126 * the array, which <b>must</b> be of 128 bits (32 bytes)
127 *
128 * @throws InvalidKeyException
129 * if the key is not an array of 128 bits (32 bytes)
130 */
131 private void init(byte[] bytes32) throws InvalidKeyException {
132 if (bytes32 == null || bytes32.length != 32) {
133 throw new InvalidKeyException(
134 "The size of the key must be of 128 bits (32 bytes), it is: "
135 + (bytes32 == null ? "null" : "" + bytes32.length)
136 + " bytes");
137 }
138
139 SecretKey key = new SecretKeySpec(bytes32, "AES");
140 try {
141 ecipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
142 dcipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
143 ecipher.init(Cipher.ENCRYPT_MODE, key);
144 dcipher.init(Cipher.DECRYPT_MODE, key);
145 } catch (NoSuchAlgorithmException e) {
146 // Every implementation of the Java platform is required to support
147 // this standard Cipher transformation with 128 bits keys
148 e.printStackTrace();
149 } catch (NoSuchPaddingException e) {
150 // Every implementation of the Java platform is required to support
151 // this standard Cipher transformation with 128 bits keys
152 e.printStackTrace();
153 } catch (InvalidKeyException e) {
154 // Every implementation of the Java platform is required to support
155 // this standard Cipher transformation with 128 bits keys
156 e.printStackTrace();
157 }
158 }
159
160 /**
161 * Encrypt the data.
162 *
163 * @param data
164 * the data to encrypt
165 *
166 * @return the encrypted data
167 *
9fb03c36 168 * @throws SSLException
52e0732e
NR
169 * in case of I/O error (i.e., the data is not what you assumed
170 * it was)
171 */
9fb03c36 172 public byte[] encrypt(byte[] data) throws SSLException {
52e0732e
NR
173 try {
174 return ecipher.doFinal(data);
175 } catch (IllegalBlockSizeException e) {
9fb03c36 176 throw new SSLException(e);
52e0732e 177 } catch (BadPaddingException e) {
9fb03c36 178 throw new SSLException(e);
52e0732e
NR
179 }
180 }
181
182 /**
183 * Encrypt the data.
184 *
185 * @param data
186 * the data to encrypt
187 *
188 * @return the encrypted data
189 *
9fb03c36 190 * @throws SSLException
52e0732e
NR
191 * in case of I/O error (i.e., the data is not what you assumed
192 * it was)
193 */
9fb03c36
NR
194 public byte[] encrypt(String data) throws SSLException {
195 try {
196 return encrypt(data.getBytes("UTF8"));
197 } catch (UnsupportedEncodingException e) {
198 // UTF-8 is required in all confirm JVMs
199 e.printStackTrace();
200 return null;
201 }
52e0732e
NR
202 }
203
204 /**
205 * Encrypt the data, then encode it into Base64.
206 *
207 * @param data
208 * the data to encrypt
209 * @param zip
210 * TRUE to also compress the data in GZIP format; remember that
211 * compressed and not-compressed content are different; you need
212 * to know which is which when decoding
213 *
214 * @return the encrypted data, encoded in Base64
215 *
9fb03c36 216 * @throws SSLException
52e0732e
NR
217 * in case of I/O error (i.e., the data is not what you assumed
218 * it was)
219 */
9fb03c36
NR
220 public String encrypt64(String data, boolean zip) throws SSLException {
221 try {
222 return encrypt64(data.getBytes("UTF8"), zip);
223 } catch (UnsupportedEncodingException e) {
224 // UTF-8 is required in all confirm JVMs
225 e.printStackTrace();
226 return null;
227 }
52e0732e
NR
228 }
229
230 /**
231 * Encrypt the data, then encode it into Base64.
232 *
233 * @param data
234 * the data to encrypt
235 * @param zip
236 * TRUE to also compress the data in GZIP format; remember that
237 * compressed and not-compressed content are different; you need
238 * to know which is which when decoding
239 *
240 * @return the encrypted data, encoded in Base64
241 *
9fb03c36 242 * @throws SSLException
52e0732e
NR
243 * in case of I/O error (i.e., the data is not what you assumed
244 * it was)
245 */
9fb03c36
NR
246 public String encrypt64(byte[] data, boolean zip) throws SSLException {
247 try {
248 return StringUtils.base64(encrypt(data), zip);
249 } catch (IOException e) {
250 // not exactly true, but we consider here that this error is a crypt
251 // error, not a normal I/O error
252 throw new SSLException(e);
253 }
52e0732e
NR
254 }
255
256 /**
257 * Decode the data which is assumed to be encrypted with the same utilities.
258 *
259 * @param data
260 * the encrypted data to decode
261 *
262 * @return the original, decoded data
263 *
9fb03c36 264 * @throws SSLException
52e0732e
NR
265 * in case of I/O error
266 */
9fb03c36 267 public byte[] decrypt(byte[] data) throws SSLException {
52e0732e
NR
268 try {
269 return dcipher.doFinal(data);
270 } catch (IllegalBlockSizeException e) {
9fb03c36 271 throw new SSLException(e);
52e0732e 272 } catch (BadPaddingException e) {
9fb03c36 273 throw new SSLException(e);
52e0732e
NR
274 }
275 }
276
277 /**
278 * Decode the data which is assumed to be encrypted with the same utilities
ed3aa598
NR
279 * and to be a {@link String}.
280 *
281 * @param data
282 * the encrypted data to decode
283 *
284 * @return the original, decoded data,as a {@link String}
285 *
286 * @throws SSLException
287 * in case of I/O error
288 */
289 public String decrypts(byte[] data) throws SSLException {
290 try {
291 return new String(decrypt(data), "UTF-8");
292 } catch (UnsupportedEncodingException e) {
293 // UTF-8 is required in all confirm JVMs
294 e.printStackTrace();
295 return null;
296 }
297 }
298
299 /**
300 * Decode the data which is assumed to be encrypted with the same utilities
52e0732e
NR
301 * and is a Base64 encoded value.
302 *
303 * @param data
304 * the encrypted data to decode in Base64 format
305 * @param zip
306 * TRUE to also uncompress the data from a GZIP format
307 * automatically; if set to FALSE, zipped data can be returned
308 *
309 * @return the original, decoded data
310 *
9fb03c36 311 * @throws SSLException
52e0732e
NR
312 * in case of I/O error
313 */
9fb03c36
NR
314 public byte[] decrypt64(String data, boolean zip) throws SSLException {
315 try {
316 return decrypt(StringUtils.unbase64(data, zip));
317 } catch (IOException e) {
318 // not exactly true, but we consider here that this error is a crypt
319 // error, not a normal I/O error
320 throw new SSLException(e);
321 }
52e0732e
NR
322 }
323
324 /**
325 * Decode the data which is assumed to be encrypted with the same utilities
326 * and is a Base64 encoded value, then convert it into a String (this method
327 * assumes the data <b>was</b> indeed a UTF-8 encoded {@link String}).
328 *
329 * @param data
330 * the encrypted data to decode in Base64 format
331 * @param zip
332 * TRUE to also uncompress the data from a GZIP format
333 * automatically; if set to FALSE, zipped data can be returned
334 *
335 * @return the original, decoded data
336 *
9fb03c36 337 * @throws SSLException
52e0732e
NR
338 * in case of I/O error
339 */
9fb03c36
NR
340 public String decrypt64s(String data, boolean zip) throws SSLException {
341 try {
342 return new String(decrypt(StringUtils.unbase64(data, zip)), "UTF-8");
343 } catch (UnsupportedEncodingException e) {
344 // UTF-8 is required in all confirm JVMs
345 e.printStackTrace();
346 return null;
347 } catch (IOException e) {
348 // not exactly true, but we consider here that this error is a crypt
349 // error, not a normal I/O error
350 throw new SSLException(e);
351 }
52e0732e
NR
352 }
353
354 /**
355 * This is probably <b>NOT</b> secure!
356 *
357 * @param input
358 * some {@link String} input
359 *
360 * @return a 128 bits key computed from the given input
361 */
362 static private byte[] key2key(String input) {
b1ed544b 363 return StringUtils.getMd5Hash("" + input).getBytes();
52e0732e
NR
364 }
365}