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