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.hbase.classification.InterfaceAudience;
38 import org.apache.hadoop.hbase.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.Unstable
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, int outLen,
470 String subject, Configuration conf, Cipher cipher, byte[] iv) throws IOException {
471 Key key = getSecretKeyForSubject(subject, conf);
472 if (key == null) {
473 throw new IOException("No key found for subject '" + subject + "'");
474 }
475 Decryptor d = cipher.getDecryptor();
476 d.setKey(key);
477 d.setIv(iv);
478 try {
479 decrypt(out, in, outLen, d);
480 } catch (IOException e) {
481
482
483 String alternateAlgorithm = conf.get(HConstants.CRYPTO_ALTERNATE_KEY_ALGORITHM_CONF_KEY);
484 if (alternateAlgorithm != null) {
485 if (LOG.isDebugEnabled()) {
486 LOG.debug("Unable to decrypt data with current cipher algorithm '"
487 + conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES)
488 + "'. Trying with the alternate cipher algorithm '" + alternateAlgorithm
489 + "' configured.");
490 }
491 Cipher alterCipher = Encryption.getCipher(conf, alternateAlgorithm);
492 if (alterCipher == null) {
493 throw new RuntimeException("Cipher '" + alternateAlgorithm + "' not available");
494 }
495 d = alterCipher.getDecryptor();
496 d.setKey(key);
497 d.setIv(iv);
498 decrypt(out, in, outLen, d);
499 } else {
500 throw new IOException(e);
501 }
502 }
503 }
504
505 private static ClassLoader getClassLoaderForClass(Class<?> c) {
506 ClassLoader cl = Thread.currentThread().getContextClassLoader();
507 if (cl == null) {
508 cl = c.getClassLoader();
509 }
510 if (cl == null) {
511 cl = ClassLoader.getSystemClassLoader();
512 }
513 if (cl == null) {
514 throw new RuntimeException("A ClassLoader to load the Cipher could not be determined");
515 }
516 return cl;
517 }
518
519 public static CipherProvider getCipherProvider(Configuration conf) {
520 String providerClassName = conf.get(HConstants.CRYPTO_CIPHERPROVIDER_CONF_KEY,
521 DefaultCipherProvider.class.getName());
522 try {
523 CipherProvider provider = (CipherProvider)
524 ReflectionUtils.newInstance(getClassLoaderForClass(CipherProvider.class)
525 .loadClass(providerClassName),
526 conf);
527 return provider;
528 } catch (Exception e) {
529 throw new RuntimeException(e);
530 }
531 }
532
533 static final Map<Pair<String,String>,KeyProvider> keyProviderCache =
534 new ConcurrentHashMap<Pair<String,String>,KeyProvider>();
535
536 public static KeyProvider getKeyProvider(Configuration conf) {
537 String providerClassName = conf.get(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY,
538 KeyStoreKeyProvider.class.getName());
539 String providerParameters = conf.get(HConstants.CRYPTO_KEYPROVIDER_PARAMETERS_KEY, "");
540 try {
541 Pair<String,String> providerCacheKey = new Pair<String,String>(providerClassName,
542 providerParameters);
543 KeyProvider provider = keyProviderCache.get(providerCacheKey);
544 if (provider != null) {
545 return provider;
546 }
547 provider = (KeyProvider) ReflectionUtils.newInstance(
548 getClassLoaderForClass(KeyProvider.class).loadClass(providerClassName),
549 conf);
550 provider.init(providerParameters);
551 if (LOG.isDebugEnabled()) {
552 LOG.debug("Installed " + providerClassName + " into key provider cache");
553 }
554 keyProviderCache.put(providerCacheKey, provider);
555 return provider;
556 } catch (Exception e) {
557 throw new RuntimeException(e);
558 }
559 }
560
561 public static void incrementIv(byte[] iv) {
562 incrementIv(iv, 1);
563 }
564
565 public static void incrementIv(byte[] iv, int v) {
566 int length = iv.length;
567 boolean carry = true;
568
569 do {
570 for (int i = 0; i < length; i++) {
571 if (carry) {
572 iv[i] = (byte) ((iv[i] + 1) & 0xFF);
573 carry = 0 == iv[i];
574 } else {
575 break;
576 }
577 }
578 v--;
579 } while (v > 0);
580 }
581
582 }