CryptUtils: add I/O streams
[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 {
45 init(key2key(key));
46 } catch (InvalidKeyException e) {
47 // We made sure that the key is correct, so nothing here
48 e.printStackTrace();
49 }
50 }
51
52 /**
53 * Create a new instance of {@link CryptUtils} with the given 128 bytes key.
54 * <p>
55 * The key <b>must</b> be exactly 128 bytes long.
56 *
57 * @param bytes32
58 * the 128 bits (32 bytes) of the key
59 *
60 * @throws InvalidKeyException
61 * if the key is not an array of 128 bytes
62 */
63 public CryptUtils(byte[] bytes32) throws InvalidKeyException {
64 init(bytes32);
65 }
66
d20c8d77
NR
67 /**
68 * Wrap the given {@link InputStream} so it is transparently encrypted by
69 * the current {@link CryptUtils}.
70 *
71 * @param in
72 * the {@link InputStream} to wrap
73 * @return the auto-encode {@link InputStream}
74 */
75 public InputStream encryptInputStream(InputStream in) {
76 return new CipherInputStream(in, ecipher);
77 }
78
79 /**
80 * Wrap the given {@link OutputStream} so it is transparently encrypted by
81 * the current {@link CryptUtils}.
82 *
83 * @param in
84 * the {@link OutputStream} to wrap
85 * @return the auto-encode {@link OutputStream}
86 */
87 public OutputStream encryptOutpuStream(OutputStream out) {
88 return new CipherOutputStream(out, ecipher);
89 }
90
91 /**
92 * Wrap the given {@link OutStream} so it is transparently decoded by the
93 * current {@link CryptUtils}.
94 *
95 * @param in
96 * the {@link InputStream} to wrap
97 * @return the auto-decode {@link InputStream}
98 */
99 public InputStream decryptInputStream(InputStream in) {
100 return new CipherInputStream(in, dcipher);
101 }
102
103 /**
104 * Wrap the given {@link OutStream} so it is transparently decoded by the
105 * current {@link CryptUtils}.
106 *
107 * @param out
108 * the {@link OutputStream} to wrap
109 * @return the auto-decode {@link OutputStream}
110 */
111 public OutputStream decryptOutputStream(OutputStream out) {
112 return new CipherOutputStream(out, dcipher);
113 }
114
52e0732e
NR
115 /**
116 * This method required an array of 128 bytes.
117 *
118 * @param bytes32
119 * the array, which <b>must</b> be of 128 bits (32 bytes)
120 *
121 * @throws InvalidKeyException
122 * if the key is not an array of 128 bits (32 bytes)
123 */
124 private void init(byte[] bytes32) throws InvalidKeyException {
125 if (bytes32 == null || bytes32.length != 32) {
126 throw new InvalidKeyException(
127 "The size of the key must be of 128 bits (32 bytes), it is: "
128 + (bytes32 == null ? "null" : "" + bytes32.length)
129 + " bytes");
130 }
131
132 SecretKey key = new SecretKeySpec(bytes32, "AES");
133 try {
134 ecipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
135 dcipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
136 ecipher.init(Cipher.ENCRYPT_MODE, key);
137 dcipher.init(Cipher.DECRYPT_MODE, key);
138 } catch (NoSuchAlgorithmException e) {
139 // Every implementation of the Java platform is required to support
140 // this standard Cipher transformation with 128 bits keys
141 e.printStackTrace();
142 } catch (NoSuchPaddingException e) {
143 // Every implementation of the Java platform is required to support
144 // this standard Cipher transformation with 128 bits keys
145 e.printStackTrace();
146 } catch (InvalidKeyException e) {
147 // Every implementation of the Java platform is required to support
148 // this standard Cipher transformation with 128 bits keys
149 e.printStackTrace();
150 }
151 }
152
153 /**
154 * Encrypt the data.
155 *
156 * @param data
157 * the data to encrypt
158 *
159 * @return the encrypted data
160 *
9fb03c36 161 * @throws SSLException
52e0732e
NR
162 * in case of I/O error (i.e., the data is not what you assumed
163 * it was)
164 */
9fb03c36 165 public byte[] encrypt(byte[] data) throws SSLException {
52e0732e
NR
166 try {
167 return ecipher.doFinal(data);
168 } catch (IllegalBlockSizeException e) {
9fb03c36 169 throw new SSLException(e);
52e0732e 170 } catch (BadPaddingException e) {
9fb03c36 171 throw new SSLException(e);
52e0732e
NR
172 }
173 }
174
175 /**
176 * Encrypt the data.
177 *
178 * @param data
179 * the data to encrypt
180 *
181 * @return the encrypted data
182 *
9fb03c36 183 * @throws SSLException
52e0732e
NR
184 * in case of I/O error (i.e., the data is not what you assumed
185 * it was)
186 */
9fb03c36
NR
187 public byte[] encrypt(String data) throws SSLException {
188 try {
189 return encrypt(data.getBytes("UTF8"));
190 } catch (UnsupportedEncodingException e) {
191 // UTF-8 is required in all confirm JVMs
192 e.printStackTrace();
193 return null;
194 }
52e0732e
NR
195 }
196
197 /**
198 * Encrypt the data, then encode it into Base64.
199 *
200 * @param data
201 * the data to encrypt
202 * @param zip
203 * TRUE to also compress the data in GZIP format; remember that
204 * compressed and not-compressed content are different; you need
205 * to know which is which when decoding
206 *
207 * @return the encrypted data, encoded in Base64
208 *
9fb03c36 209 * @throws SSLException
52e0732e
NR
210 * in case of I/O error (i.e., the data is not what you assumed
211 * it was)
212 */
9fb03c36
NR
213 public String encrypt64(String data, boolean zip) throws SSLException {
214 try {
215 return encrypt64(data.getBytes("UTF8"), zip);
216 } catch (UnsupportedEncodingException e) {
217 // UTF-8 is required in all confirm JVMs
218 e.printStackTrace();
219 return null;
220 }
52e0732e
NR
221 }
222
223 /**
224 * Encrypt the data, then encode it into Base64.
225 *
226 * @param data
227 * the data to encrypt
228 * @param zip
229 * TRUE to also compress the data in GZIP format; remember that
230 * compressed and not-compressed content are different; you need
231 * to know which is which when decoding
232 *
233 * @return the encrypted data, encoded in Base64
234 *
9fb03c36 235 * @throws SSLException
52e0732e
NR
236 * in case of I/O error (i.e., the data is not what you assumed
237 * it was)
238 */
9fb03c36
NR
239 public String encrypt64(byte[] data, boolean zip) throws SSLException {
240 try {
241 return StringUtils.base64(encrypt(data), zip);
242 } catch (IOException e) {
243 // not exactly true, but we consider here that this error is a crypt
244 // error, not a normal I/O error
245 throw new SSLException(e);
246 }
52e0732e
NR
247 }
248
249 /**
250 * Decode the data which is assumed to be encrypted with the same utilities.
251 *
252 * @param data
253 * the encrypted data to decode
254 *
255 * @return the original, decoded data
256 *
9fb03c36 257 * @throws SSLException
52e0732e
NR
258 * in case of I/O error
259 */
9fb03c36 260 public byte[] decrypt(byte[] data) throws SSLException {
52e0732e
NR
261 try {
262 return dcipher.doFinal(data);
263 } catch (IllegalBlockSizeException e) {
9fb03c36 264 throw new SSLException(e);
52e0732e 265 } catch (BadPaddingException e) {
9fb03c36 266 throw new SSLException(e);
52e0732e
NR
267 }
268 }
269
270 /**
271 * Decode the data which is assumed to be encrypted with the same utilities
272 * and is a Base64 encoded value.
273 *
274 * @param data
275 * the encrypted data to decode in Base64 format
276 * @param zip
277 * TRUE to also uncompress the data from a GZIP format
278 * automatically; if set to FALSE, zipped data can be returned
279 *
280 * @return the original, decoded data
281 *
9fb03c36 282 * @throws SSLException
52e0732e
NR
283 * in case of I/O error
284 */
9fb03c36
NR
285 public byte[] decrypt64(String data, boolean zip) throws SSLException {
286 try {
287 return decrypt(StringUtils.unbase64(data, zip));
288 } catch (IOException e) {
289 // not exactly true, but we consider here that this error is a crypt
290 // error, not a normal I/O error
291 throw new SSLException(e);
292 }
52e0732e
NR
293 }
294
295 /**
296 * Decode the data which is assumed to be encrypted with the same utilities
297 * and is a Base64 encoded value, then convert it into a String (this method
298 * assumes the data <b>was</b> indeed a UTF-8 encoded {@link String}).
299 *
300 * @param data
301 * the encrypted data to decode in Base64 format
302 * @param zip
303 * TRUE to also uncompress the data from a GZIP format
304 * automatically; if set to FALSE, zipped data can be returned
305 *
306 * @return the original, decoded data
307 *
9fb03c36 308 * @throws SSLException
52e0732e
NR
309 * in case of I/O error
310 */
9fb03c36
NR
311 public String decrypt64s(String data, boolean zip) throws SSLException {
312 try {
313 return new String(decrypt(StringUtils.unbase64(data, zip)), "UTF-8");
314 } catch (UnsupportedEncodingException e) {
315 // UTF-8 is required in all confirm JVMs
316 e.printStackTrace();
317 return null;
318 } catch (IOException e) {
319 // not exactly true, but we consider here that this error is a crypt
320 // error, not a normal I/O error
321 throw new SSLException(e);
322 }
52e0732e
NR
323 }
324
325 /**
326 * This is probably <b>NOT</b> secure!
327 *
328 * @param input
329 * some {@link String} input
330 *
331 * @return a 128 bits key computed from the given input
332 */
333 static private byte[] key2key(String input) {
b1ed544b 334 return StringUtils.getMd5Hash("" + input).getBytes();
52e0732e
NR
335 }
336}