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