1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.hadoop.hbase.io.crypto;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.OutputStream;
22 import java.security.DigestException;
23 import java.security.Key;
24 import java.security.MessageDigest;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.spec.InvalidKeySpecException;
27 import java.util.Map;
28 import java.util.concurrent.ConcurrentHashMap;
29
30 import javax.crypto.SecretKeyFactory;
31 import javax.crypto.spec.PBEKeySpec;
32 import javax.crypto.spec.SecretKeySpec;
33
34 import org.apache.commons.io.IOUtils;
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.classification.InterfaceAudience;
38 import org.apache.hadoop.classification.InterfaceStability;
39 import org.apache.hadoop.conf.Configuration;
40 import org.apache.hadoop.hbase.HBaseConfiguration;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.hbase.util.Pair;
44 import org.apache.hadoop.util.ReflectionUtils;
45
46
47
48
49 @InterfaceAudience.Public
50 @InterfaceStability.Evolving
51 public final class Encryption {
52
53 private static final Log LOG = LogFactory.getLog(Encryption.class);
54
55
56
57
58 public static class Context extends org.apache.hadoop.hbase.io.crypto.Context {
59
60
61 public static final Context NONE = new Context();
62
63 private Context() {
64 super();
65 }
66
67 private Context(Configuration conf) {
68 super(conf);
69 }
70
71 @Override
72 public Context setCipher(Cipher cipher) {
73 super.setCipher(cipher);
74 return this;
75 }
76
77 @Override
78 public Context setKey(Key key) {
79 super.setKey(key);
80 return this;
81 }
82
83 public Context setKey(byte[] key) {
84 super.setKey(new SecretKeySpec(key, getCipher().getName()));
85 return this;
86 }
87 }
88
89 public static Context newContext() {
90 return new Context();
91 }
92
93 public static Context newContext(Configuration conf) {
94 return new Context(conf);
95 }
96
97
98 private Encryption() {
99 super();
100 }
101
102
103
104
105
106
107 public static Cipher getCipher(Configuration conf, String name) {
108 return getCipherProvider(conf).getCipher(name);
109 }
110
111
112
113
114
115
116 public static String[] getSupportedCiphers() {
117 return getSupportedCiphers(HBaseConfiguration.create());
118 }
119
120
121
122
123
124
125 public static String[] getSupportedCiphers(Configuration conf) {
126 return getCipherProvider(conf).getSupportedCiphers();
127 }
128
129
130
131
132 public static byte[] hash128(String... args) {
133 byte[] result = new byte[16];
134 try {
135 MessageDigest md = MessageDigest.getInstance("MD5");
136 for (String arg: args) {
137 md.update(Bytes.toBytes(arg));
138 }
139 md.digest(result, 0, result.length);
140 return result;
141 } catch (NoSuchAlgorithmException e) {
142 throw new RuntimeException(e);
143 } catch (DigestException e) {
144 throw new RuntimeException(e);
145 }
146 }
147
148
149
150
151 public static byte[] hash128(byte[]... args) {
152 byte[] result = new byte[16];
153 try {
154 MessageDigest md = MessageDigest.getInstance("MD5");
155 for (byte[] arg: args) {
156 md.update(arg);
157 }
158 md.digest(result, 0, result.length);
159 return result;
160 } catch (NoSuchAlgorithmException e) {
161 throw new RuntimeException(e);
162 } catch (DigestException e) {
163 throw new RuntimeException(e);
164 }
165 }
166
167
168
169
170 public static byte[] hash256(String... args) {
171 byte[] result = new byte[32];
172 try {
173 MessageDigest md = MessageDigest.getInstance("SHA-256");
174 for (String arg: args) {
175 md.update(Bytes.toBytes(arg));
176 }
177 md.digest(result, 0, result.length);
178 return result;
179 } catch (NoSuchAlgorithmException e) {
180 throw new RuntimeException(e);
181 } catch (DigestException e) {
182 throw new RuntimeException(e);
183 }
184 }
185
186
187
188
189 public static byte[] hash256(byte[]... args) {
190 byte[] result = new byte[32];
191 try {
192 MessageDigest md = MessageDigest.getInstance("SHA-256");
193 for (byte[] arg: args) {
194 md.update(arg);
195 }
196 md.digest(result, 0, result.length);
197 return result;
198 } catch (NoSuchAlgorithmException e) {
199 throw new RuntimeException(e);
200 } catch (DigestException e) {
201 throw new RuntimeException(e);
202 }
203 }
204
205
206
207
208
209
210 public static byte[] pbkdf128(String... args) {
211 byte[] salt = new byte[128];
212 Bytes.random(salt);
213 StringBuilder sb = new StringBuilder();
214 for (String s: args) {
215 sb.append(s);
216 }
217 PBEKeySpec spec = new PBEKeySpec(sb.toString().toCharArray(), salt, 10000, 128);
218 try {
219 return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
220 .generateSecret(spec).getEncoded();
221 } catch (NoSuchAlgorithmException e) {
222 throw new RuntimeException(e);
223 } catch (InvalidKeySpecException e) {
224 throw new RuntimeException(e);
225 }
226 }
227
228
229
230
231
232
233 public static byte[] pbkdf128(byte[]... args) {
234 byte[] salt = new byte[128];
235 Bytes.random(salt);
236 StringBuilder sb = new StringBuilder();
237 for (byte[] b: args) {
238 sb.append(b);
239 }
240 PBEKeySpec spec = new PBEKeySpec(sb.toString().toCharArray(), salt, 10000, 128);
241 try {
242 return SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1")
243 .generateSecret(spec).getEncoded();
244 } catch (NoSuchAlgorithmException e) {
245 throw new RuntimeException(e);
246 } catch (InvalidKeySpecException e) {
247 throw new RuntimeException(e);
248 }
249 }
250
251
252
253
254
255
256
257
258
259
260
261
262
263 public static void encrypt(OutputStream out, byte[] src, int offset,
264 int length, Encryptor e) throws IOException {
265 OutputStream cout = e.createEncryptionStream(out);
266 try {
267 cout.write(src, offset, length);
268 } finally {
269 cout.close();
270 }
271 }
272
273
274
275
276
277
278
279
280
281
282
283 public static void encrypt(OutputStream out, byte[] src, int offset,
284 int length, Context context, byte[] iv) throws IOException {
285 Encryptor e = context.getCipher().getEncryptor();
286 e.setKey(context.getKey());
287 e.setIv(iv);
288 e.reset();
289 encrypt(out, src, offset, length, e);
290 }
291
292
293
294
295
296
297
298
299
300
301
302 public static void encrypt(OutputStream out, InputStream in, Encryptor e)
303 throws IOException {
304 OutputStream cout = e.createEncryptionStream(out);
305 try {
306 IOUtils.copy(in, cout);
307 } finally {
308 cout.close();
309 }
310 }
311
312
313
314
315
316
317
318
319
320 public static void encrypt(OutputStream out, InputStream in, Context context,
321 byte[] iv) throws IOException {
322 Encryptor e = context.getCipher().getEncryptor();
323 e.setKey(context.getKey());
324 e.setIv(iv);
325 e.reset();
326 encrypt(out, in, e);
327 }
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342 public static void decrypt(byte[] dest, int destOffset, InputStream in,
343 int destSize, Decryptor d) throws IOException {
344 InputStream cin = d.createDecryptionStream(in);
345 try {
346 IOUtils.readFully(cin, dest, destOffset, destSize);
347 } finally {
348 cin.close();
349 }
350 }
351
352
353
354
355
356
357
358
359
360
361
362 public static void decrypt(byte[] dest, int destOffset, InputStream in,
363 int destSize, Context context, byte[] iv) throws IOException {
364 Decryptor d = context.getCipher().getDecryptor();
365 d.setKey(context.getKey());
366 d.setIv(iv);
367 decrypt(dest, destOffset, in, destSize, d);
368 }
369
370
371
372
373
374
375
376
377
378 public static void decrypt(OutputStream out, InputStream in, int outLen,
379 Decryptor d) throws IOException {
380 InputStream cin = d.createDecryptionStream(in);
381 byte buf[] = new byte[8*1024];
382 long remaining = outLen;
383 try {
384 while (remaining > 0) {
385 int toRead = (int)(remaining < buf.length ? remaining : buf.length);
386 int read = cin.read(buf, 0, toRead);
387 if (read < 0) {
388 break;
389 }
390 out.write(buf, 0, read);
391 remaining -= read;
392 }
393 } finally {
394 cin.close();
395 }
396 }
397
398
399
400
401
402
403
404
405
406
407 public static void decrypt(OutputStream out, InputStream in, int outLen,
408 Context context, byte[] iv) throws IOException {
409 Decryptor d = context.getCipher().getDecryptor();
410 d.setKey(context.getKey());
411 d.setIv(iv);
412 decrypt(out, in, outLen, d);
413 }
414
415
416
417
418
419
420
421
422 public static Key getSecretKeyForSubject(String subject, Configuration conf)
423 throws IOException {
424 KeyProvider provider = (KeyProvider)getKeyProvider(conf);
425 if (provider != null) try {
426 Key[] keys = provider.getKeys(new String[] { subject });
427 if (keys != null && keys.length > 0) {
428 return keys[0];
429 }
430 } catch (Exception e) {
431 throw new IOException(e);
432 }
433 throw new IOException("No key found for subject '" + subject + "'");
434 }
435
436
437
438
439
440
441
442
443
444
445 public static void encryptWithSubjectKey(OutputStream out, InputStream in,
446 String subject, Configuration conf, Cipher cipher, byte[] iv)
447 throws IOException {
448 Key key = getSecretKeyForSubject(subject, conf);
449 if (key == null) {
450 throw new IOException("No key found for subject '" + subject + "'");
451 }
452 Encryptor e = cipher.getEncryptor();
453 e.setKey(key);
454 e.setIv(iv);
455 encrypt(out, in, e);
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469 public static void decryptWithSubjectKey(OutputStream out, InputStream in,
470 int outLen, String subject, Configuration conf, Cipher cipher,
471 byte[] iv) throws IOException {
472 Key key = getSecretKeyForSubject(subject, conf);
473 if (key == null) {
474 throw new IOException("No key found for subject '" + subject + "'");
475 }
476 Decryptor d = cipher.getDecryptor();
477 d.setKey(key);
478 d.setIv(iv);
479 decrypt(out, in, outLen, d);
480 }
481
482 private static ClassLoader getClassLoaderForClass(Class<?> c) {
483 ClassLoader cl = Thread.currentThread().getContextClassLoader();
484 if (cl == null) {
485 cl = c.getClassLoader();
486 }
487 if (cl == null) {
488 cl = ClassLoader.getSystemClassLoader();
489 }
490 if (cl == null) {
491 throw new RuntimeException("A ClassLoader to load the Cipher could not be determined");
492 }
493 return cl;
494 }
495
496 public static CipherProvider getCipherProvider(Configuration conf) {
497 String providerClassName = conf.get(HConstants.CRYPTO_CIPHERPROVIDER_CONF_KEY,
498 DefaultCipherProvider.class.getName());
499 try {
500 CipherProvider provider = (CipherProvider)
501 ReflectionUtils.newInstance(getClassLoaderForClass(CipherProvider.class)
502 .loadClass(providerClassName),
503 conf);
504 return provider;
505 } catch (Exception e) {
506 throw new RuntimeException(e);
507 }
508 }
509
510 static final Map<Pair<String,String>,KeyProvider> keyProviderCache =
511 new ConcurrentHashMap<Pair<String,String>,KeyProvider>();
512
513 public static KeyProvider getKeyProvider(Configuration conf) {
514 String providerClassName = conf.get(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY,
515 KeyStoreKeyProvider.class.getName());
516 String providerParameters = conf.get(HConstants.CRYPTO_KEYPROVIDER_PARAMETERS_KEY, "");
517 try {
518 Pair<String,String> providerCacheKey = new Pair<String,String>(providerClassName,
519 providerParameters);
520 KeyProvider provider = keyProviderCache.get(providerCacheKey);
521 if (provider != null) {
522 return provider;
523 }
524 provider = (KeyProvider) ReflectionUtils.newInstance(
525 getClassLoaderForClass(KeyProvider.class).loadClass(providerClassName),
526 conf);
527 provider.init(providerParameters);
528 if (LOG.isDebugEnabled()) {
529 LOG.debug("Installed " + providerClassName + " into key provider cache");
530 }
531 keyProviderCache.put(providerCacheKey, provider);
532 return provider;
533 } catch (Exception e) {
534 throw new RuntimeException(e);
535 }
536 }
537
538 public static void incrementIv(byte[] iv) {
539 incrementIv(iv, 1);
540 }
541
542 public static void incrementIv(byte[] iv, int v) {
543 int length = iv.length;
544 boolean carry = true;
545
546 do {
547 for (int i = 0; i < length; i++) {
548 if (carry) {
549 iv[i] = (byte) ((iv[i] + 1) & 0xFF);
550 carry = 0 == iv[i];
551 } else {
552 break;
553 }
554 }
555 v--;
556 } while (v > 0);
557 }
558
559 }