diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/EncryptionUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/EncryptionUtil.java index e8773636ec0..c787efe1623 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/security/EncryptionUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/security/EncryptionUtil.java @@ -119,6 +119,11 @@ public class EncryptionUtil { if (cipher == null) { throw new RuntimeException("Cipher '" + algorithm + "' not available"); } + return getUnwrapKey(conf, subject, wrappedKey, cipher); + } + + private static Key getUnwrapKey(Configuration conf, String subject, + EncryptionProtos.WrappedKey wrappedKey, Cipher cipher) throws IOException, KeyException { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] iv = wrappedKey.hasIv() ? wrappedKey.getIv().toByteArray() : null; Encryption.decryptWithSubjectKey(out, wrappedKey.getData().newInput(), @@ -132,4 +137,26 @@ public class EncryptionUtil { return new SecretKeySpec(keyBytes, wrappedKey.getAlgorithm()); } + /** + * Unwrap a wal key by decrypting it with the secret key of the given subject. The configuration + * must be set up correctly for key alias resolution. + * @param conf configuration + * @param subject subject key alias + * @param value the encrypted key bytes + * @return the raw key bytes + * @throws IOException if key is not found for the subject, or if some I/O error occurs + * @throws KeyException if fail to unwrap the key + */ + public static Key unwrapWALKey(Configuration conf, String subject, byte[] value) + throws IOException, KeyException { + EncryptionProtos.WrappedKey wrappedKey = + EncryptionProtos.WrappedKey.PARSER.parseDelimitedFrom(new ByteArrayInputStream(value)); + String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); + Cipher cipher = Encryption.getCipher(conf, algorithm); + if (cipher == null) { + throw new RuntimeException("Cipher '" + algorithm + "' not available"); + } + return getUnwrapKey(conf, subject, wrappedKey, cipher); + } + } diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/security/TestEncryptionUtil.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/security/TestEncryptionUtil.java index f9dd30bfce2..b0e3464f4b2 100644 --- a/hbase-client/src/test/java/org/apache/hadoop/hbase/security/TestEncryptionUtil.java +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/security/TestEncryptionUtil.java @@ -75,4 +75,49 @@ public class TestEncryptionUtil { } } + @Test + public void testWALKeyWrapping() throws Exception { + // set up the key provider for testing to resolve a key for our test subject + Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this + conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, KeyProviderForTesting.class.getName()); + + // generate a test key + byte[] keyBytes = new byte[AES.KEY_LENGTH]; + new SecureRandom().nextBytes(keyBytes); + String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); + Key key = new SecretKeySpec(keyBytes, algorithm); + + // wrap the test key + byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key); + assertNotNull(wrappedKeyBytes); + + // unwrap + Key unwrappedKey = EncryptionUtil.unwrapWALKey(conf, "hbase", wrappedKeyBytes); + assertNotNull(unwrappedKey); + // only secretkeyspec supported for now + assertTrue(unwrappedKey instanceof SecretKeySpec); + // did we get back what we wrapped? + assertTrue("Unwrapped key bytes do not match original", + Bytes.equals(keyBytes, unwrappedKey.getEncoded())); + } + + @Test(expected = KeyException.class) + public void testWALKeyWrappingWithIncorrectKey() throws Exception { + // set up the key provider for testing to resolve a key for our test subject + Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this + conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, KeyProviderForTesting.class.getName()); + + // generate a test key + byte[] keyBytes = new byte[AES.KEY_LENGTH]; + new SecureRandom().nextBytes(keyBytes); + String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); + Key key = new SecretKeySpec(keyBytes, algorithm); + + // wrap the test key + byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key); + assertNotNull(wrappedKeyBytes); + + // unwrap with an incorrect key + EncryptionUtil.unwrapWALKey(conf, "other", wrappedKeyBytes); + } } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/SecureProtobufLogReader.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/SecureProtobufLogReader.java index 041dfe2683a..d175741eac8 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/SecureProtobufLogReader.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/SecureProtobufLogReader.java @@ -78,7 +78,7 @@ public class SecureProtobufLogReader extends ProtobufLogReader { // First try the WAL key, if one is configured if (walKeyName != null) { try { - key = EncryptionUtil.unwrapKey(conf, walKeyName, keyBytes); + key = EncryptionUtil.unwrapWALKey(conf, walKeyName, keyBytes); } catch (KeyException e) { if (LOG.isDebugEnabled()) { LOG.debug("Unable to unwrap key with WAL key '" + walKeyName + "'"); @@ -91,7 +91,7 @@ public class SecureProtobufLogReader extends ProtobufLogReader { User.getCurrent().getShortName()); try { // Then, try the cluster master key - key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes); + key = EncryptionUtil.unwrapWALKey(conf, masterKeyName, keyBytes); } catch (KeyException e) { // If the current master key fails to unwrap, try the alternate, if // one is configured @@ -102,7 +102,7 @@ public class SecureProtobufLogReader extends ProtobufLogReader { conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY); if (alternateKeyName != null) { try { - key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes); + key = EncryptionUtil.unwrapWALKey(conf, alternateKeyName, keyBytes); } catch (KeyException ex) { throw new IOException(ex); }