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