Commit | Line | Data |
---|---|---|
52e0732e NR |
1 | package be.nikiroo.utils; |
2 | ||
3 | import java.io.IOException; | |
d20c8d77 NR |
4 | import java.io.InputStream; |
5 | import java.io.OutputStream; | |
9fb03c36 | 6 | import java.io.UnsupportedEncodingException; |
52e0732e NR |
7 | import java.security.InvalidKeyException; |
8 | import java.security.NoSuchAlgorithmException; | |
9 | ||
10 | import javax.crypto.BadPaddingException; | |
11 | import javax.crypto.Cipher; | |
d20c8d77 NR |
12 | import javax.crypto.CipherInputStream; |
13 | import javax.crypto.CipherOutputStream; | |
52e0732e NR |
14 | import javax.crypto.IllegalBlockSizeException; |
15 | import javax.crypto.NoSuchPaddingException; | |
16 | import javax.crypto.SecretKey; | |
17 | import javax.crypto.spec.SecretKeySpec; | |
9fb03c36 | 18 | import 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 | */ | |
27 | public 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 | } |