Skip to content

Commit

Permalink
HBASE-14901 Remove duplicate code to create/manage encryption keys
Browse files Browse the repository at this point in the history
Signed-off-by: Gary Helmling <garyh@apache.org>
  • Loading branch information
nkedel authored and ghelmling committed Dec 10, 2015
1 parent 6f8d5e8 commit 9511150
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 166 deletions.
Expand Up @@ -26,7 +26,10 @@


import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;


import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.classification.InterfaceAudience; import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.classification.InterfaceStability; import org.apache.hadoop.hbase.classification.InterfaceStability;
Expand All @@ -41,10 +44,17 @@
*/ */
@InterfaceAudience.Private @InterfaceAudience.Private
@InterfaceStability.Evolving @InterfaceStability.Evolving
public class EncryptionUtil { public final class EncryptionUtil {
static private final Log LOG = LogFactory.getLog(EncryptionUtil.class);


static private final SecureRandom RNG = new SecureRandom(); static private final SecureRandom RNG = new SecureRandom();


/**
* Private constructor to keep this class from being instantiated.
*/
private EncryptionUtil() {
}

/** /**
* Protect a key by encrypting it with the secret key of the given subject. * Protect a key by encrypting it with the secret key of the given subject.
* The configuration must be set up correctly for key alias resolution. * The configuration must be set up correctly for key alias resolution.
Expand Down Expand Up @@ -159,4 +169,90 @@ public static Key unwrapWALKey(Configuration conf, String subject, byte[] value)
return getUnwrapKey(conf, subject, wrappedKey, cipher); return getUnwrapKey(conf, subject, wrappedKey, cipher);
} }


/**
* Helper to create an encyption context.
*
* @param conf The current configuration.
* @param family The current column descriptor.
* @return The created encryption context.
* @throws IOException if an encryption key for the column cannot be unwrapped
*/
public static Encryption.Context createEncryptionContext(Configuration conf,
HColumnDescriptor family) throws IOException {
Encryption.Context cryptoContext = Encryption.Context.NONE;
String cipherName = family.getEncryptionType();
if (cipherName != null) {
Cipher cipher;
Key key;
byte[] keyBytes = family.getEncryptionKey();
if (keyBytes != null) {
// Family provides specific key material
key = unwrapKey(conf, keyBytes);
// Use the algorithm the key wants
cipher = Encryption.getCipher(conf, key.getAlgorithm());
if (cipher == null) {
throw new RuntimeException("Cipher '" + key.getAlgorithm() + "' is not available");
}
// Fail if misconfigured
// We use the encryption type specified in the column schema as a sanity check on
// what the wrapped key is telling us
if (!cipher.getName().equalsIgnoreCase(cipherName)) {
throw new RuntimeException("Encryption for family '" + family.getNameAsString()
+ "' configured with type '" + cipherName + "' but key specifies algorithm '"
+ cipher.getName() + "'");
}
} else {
// Family does not provide key material, create a random key
cipher = Encryption.getCipher(conf, cipherName);
if (cipher == null) {
throw new RuntimeException("Cipher '" + cipherName + "' is not available");
}
key = cipher.getRandomKey();
}
cryptoContext = Encryption.newContext(conf);
cryptoContext.setCipher(cipher);
cryptoContext.setKey(key);
}
return cryptoContext;
}

/**
* Helper for {@link #unwrapKey(Configuration, String, byte[])} which automatically uses the
* configured master and alternative keys, rather than having to specify a key type to unwrap
* with.
*
* The configuration must be set up correctly for key alias resolution.
*
* @param conf the current configuration
* @param keyBytes the key encrypted by master (or alternative) to unwrap
* @return the key bytes, decrypted
* @throws IOException if the key cannot be unwrapped
*/
public static Key unwrapKey(Configuration conf, byte[] keyBytes) throws IOException {
Key key;
String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY,
User.getCurrent().getShortName());
try {
// First try the master key
key = unwrapKey(conf, masterKeyName, keyBytes);
} catch (KeyException e) {
// If the current master key fails to unwrap, try the alternate, if
// one is configured
if (LOG.isDebugEnabled()) {
LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
}
String alternateKeyName =
conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
if (alternateKeyName != null) {
try {
key = unwrapKey(conf, alternateKeyName, keyBytes);
} catch (KeyException ex) {
throw new IOException(ex);
}
} else {
throw new IOException(e);
}
}
return key;
}
} }
Expand Up @@ -21,14 +21,17 @@
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;


import java.io.IOException;
import java.security.Key; import java.security.Key;
import java.security.KeyException; import java.security.KeyException;
import java.security.SecureRandom; import java.security.SecureRandom;


import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;


import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.io.crypto.Encryption;
import org.apache.hadoop.hbase.io.crypto.KeyProviderForTesting; import org.apache.hadoop.hbase.io.crypto.KeyProviderForTesting;
import org.apache.hadoop.hbase.io.crypto.aes.AES; import org.apache.hadoop.hbase.io.crypto.aes.AES;
import org.apache.hadoop.hbase.testclassification.ClientTests; import org.apache.hadoop.hbase.testclassification.ClientTests;
Expand All @@ -39,6 +42,9 @@


@Category({ClientTests.class, SmallTests.class}) @Category({ClientTests.class, SmallTests.class})
public class TestEncryptionUtil { public class TestEncryptionUtil {
// There does not seem to be a ready way to test either getKeyFromBytesOrMasterKey
// or createEncryptionContext, and the existing code under MobUtils appeared to be
// untested. Not ideal!


@Test @Test
public void testKeyWrapping() throws Exception { public void testKeyWrapping() throws Exception {
Expand Down
Expand Up @@ -21,26 +21,25 @@
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.security.Key; import java.security.Key;
import java.security.KeyException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;


import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.conf.Configurable; import org.apache.hadoop.conf.Configurable;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.ByteBufferedKeyOnlyKeyValue;
import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellComparator; import org.apache.hadoop.hbase.CellComparator;
import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.ByteBufferedKeyOnlyKeyValue; import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.OffheapKeyValue; import org.apache.hadoop.hbase.OffheapKeyValue;
import org.apache.hadoop.hbase.ShareableMemory; import org.apache.hadoop.hbase.ShareableMemory;
import org.apache.hadoop.hbase.SizeCachedKeyValue; import org.apache.hadoop.hbase.SizeCachedKeyValue;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.SizeCachedNoTagsKeyValue; import org.apache.hadoop.hbase.SizeCachedNoTagsKeyValue;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.fs.HFileSystem; import org.apache.hadoop.hbase.fs.HFileSystem;
import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper; import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
import org.apache.hadoop.hbase.io.compress.Compression; import org.apache.hadoop.hbase.io.compress.Compression;
Expand All @@ -52,7 +51,6 @@
import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo; import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
import org.apache.hadoop.hbase.nio.ByteBuff; import org.apache.hadoop.hbase.nio.ByteBuff;
import org.apache.hadoop.hbase.security.EncryptionUtil; import org.apache.hadoop.hbase.security.EncryptionUtil;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.ByteBufferUtils; import org.apache.hadoop.hbase.util.ByteBufferUtils;
import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.IdLock; import org.apache.hadoop.hbase.util.IdLock;
Expand Down Expand Up @@ -1817,29 +1815,7 @@ protected HFileContext createHFileContext(FSDataInputStreamWrapper fsdis, long f
if (keyBytes != null) { if (keyBytes != null) {
Encryption.Context cryptoContext = Encryption.newContext(conf); Encryption.Context cryptoContext = Encryption.newContext(conf);
Key key; Key key;
String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, key = EncryptionUtil.unwrapKey(conf, keyBytes);
User.getCurrent().getShortName());
try {
// First try the master key
key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
} catch (KeyException e) {
// If the current master key fails to unwrap, try the alternate, if
// one is configured
if (LOG.isDebugEnabled()) {
LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
}
String alternateKeyName =
conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
if (alternateKeyName != null) {
try {
key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes);
} catch (KeyException ex) {
throw new IOException(ex);
}
} else {
throw new IOException(e);
}
}
// Use the algorithm the key wants // Use the algorithm the key wants
Cipher cipher = Encryption.getCipher(conf, key.getAlgorithm()); Cipher cipher = Encryption.getCipher(conf, key.getAlgorithm());
if (cipher == null) { if (cipher == null) {
Expand Down
Expand Up @@ -20,8 +20,6 @@


import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.security.Key;
import java.security.KeyException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
Expand Down Expand Up @@ -58,7 +56,6 @@
import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.io.HFileLink; import org.apache.hadoop.hbase.io.HFileLink;
import org.apache.hadoop.hbase.io.compress.Compression; import org.apache.hadoop.hbase.io.compress.Compression;
import org.apache.hadoop.hbase.io.crypto.Cipher;
import org.apache.hadoop.hbase.io.crypto.Encryption; import org.apache.hadoop.hbase.io.crypto.Encryption;
import org.apache.hadoop.hbase.io.hfile.CacheConfig; import org.apache.hadoop.hbase.io.hfile.CacheConfig;
import org.apache.hadoop.hbase.io.hfile.HFileContext; import org.apache.hadoop.hbase.io.hfile.HFileContext;
Expand All @@ -70,8 +67,6 @@
import org.apache.hadoop.hbase.regionserver.BloomType; import org.apache.hadoop.hbase.regionserver.BloomType;
import org.apache.hadoop.hbase.regionserver.HStore; import org.apache.hadoop.hbase.regionserver.HStore;
import org.apache.hadoop.hbase.regionserver.StoreFile; import org.apache.hadoop.hbase.regionserver.StoreFile;
import org.apache.hadoop.hbase.security.EncryptionUtil;
import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.hadoop.hbase.util.FSUtils; import org.apache.hadoop.hbase.util.FSUtils;
Expand All @@ -82,7 +77,7 @@
* The mob utilities * The mob utilities
*/ */
@InterfaceAudience.Private @InterfaceAudience.Private
public class MobUtils { public final class MobUtils {


private static final Log LOG = LogFactory.getLog(MobUtils.class); private static final Log LOG = LogFactory.getLog(MobUtils.class);


Expand All @@ -94,6 +89,13 @@ protected SimpleDateFormat initialValue() {
} }
}; };



/**
* Private constructor to keep this class from being instantiated.
*/
private MobUtils() {
}

/** /**
* Formats a date to a string. * Formats a date to a string.
* @param date The date. * @param date The date.
Expand Down Expand Up @@ -774,74 +776,6 @@ public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
return pool; return pool;
} }


/**
* Creates the encyption context.
* @param conf The current configuration.
* @param family The current column descriptor.
* @return The encryption context.
* @throws IOException
*/
public static Encryption.Context createEncryptionContext(Configuration conf,
HColumnDescriptor family) throws IOException {
// TODO the code is repeated, and needs to be unified.
Encryption.Context cryptoContext = Encryption.Context.NONE;
String cipherName = family.getEncryptionType();
if (cipherName != null) {
Cipher cipher;
Key key;
byte[] keyBytes = family.getEncryptionKey();
if (keyBytes != null) {
// Family provides specific key material
String masterKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, User
.getCurrent().getShortName());
try {
// First try the master key
key = EncryptionUtil.unwrapKey(conf, masterKeyName, keyBytes);
} catch (KeyException e) {
// If the current master key fails to unwrap, try the alternate, if
// one is configured
if (LOG.isDebugEnabled()) {
LOG.debug("Unable to unwrap key with current master key '" + masterKeyName + "'");
}
String alternateKeyName = conf.get(HConstants.CRYPTO_MASTERKEY_ALTERNATE_NAME_CONF_KEY);
if (alternateKeyName != null) {
try {
key = EncryptionUtil.unwrapKey(conf, alternateKeyName, keyBytes);
} catch (KeyException ex) {
throw new IOException(ex);
}
} else {
throw new IOException(e);
}
}
// Use the algorithm the key wants
cipher = Encryption.getCipher(conf, key.getAlgorithm());
if (cipher == null) {
throw new RuntimeException("Cipher '" + key.getAlgorithm() + "' is not available");
}
// Fail if misconfigured
// We use the encryption type specified in the column schema as a sanity check on
// what the wrapped key is telling us
if (!cipher.getName().equalsIgnoreCase(cipherName)) {
throw new RuntimeException("Encryption for family '" + family.getNameAsString()
+ "' configured with type '" + cipherName + "' but key specifies algorithm '"
+ cipher.getName() + "'");
}
} else {
// Family does not provide key material, create a random key
cipher = Encryption.getCipher(conf, cipherName);
if (cipher == null) {
throw new RuntimeException("Cipher '" + cipherName + "' is not available");
}
key = cipher.getRandomKey();
}
cryptoContext = Encryption.newContext(conf);
cryptoContext.setCipher(cipher);
cryptoContext.setKey(key);
}
return cryptoContext;
}

/** /**
* Checks whether this table has mob-enabled columns. * Checks whether this table has mob-enabled columns.
* @param htd The current table descriptor. * @param htd The current table descriptor.
Expand Down
Expand Up @@ -72,6 +72,7 @@
import org.apache.hadoop.hbase.regionserver.StoreFileInfo; import org.apache.hadoop.hbase.regionserver.StoreFileInfo;
import org.apache.hadoop.hbase.regionserver.StoreFileScanner; import org.apache.hadoop.hbase.regionserver.StoreFileScanner;
import org.apache.hadoop.hbase.regionserver.StoreScanner; import org.apache.hadoop.hbase.regionserver.StoreScanner;
import org.apache.hadoop.hbase.security.EncryptionUtil;
import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair; import org.apache.hadoop.hbase.util.Pair;


Expand Down Expand Up @@ -113,7 +114,7 @@ public PartitionedMobCompactor(Configuration conf, FileSystem fs, TableName tabl
copyOfConf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0f); copyOfConf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0f);
compactionCacheConfig = new CacheConfig(copyOfConf); compactionCacheConfig = new CacheConfig(copyOfConf);
tableNameTag = new Tag(TagType.MOB_TABLE_NAME_TAG_TYPE, tableName.getName()); tableNameTag = new Tag(TagType.MOB_TABLE_NAME_TAG_TYPE, tableName.getName());
cryptoContext = MobUtils.createEncryptionContext(copyOfConf, column); cryptoContext = EncryptionUtil.createEncryptionContext(copyOfConf, column);
} }


@Override @Override
Expand Down
Expand Up @@ -45,6 +45,7 @@
import org.apache.hadoop.hbase.regionserver.MemStore; import org.apache.hadoop.hbase.regionserver.MemStore;
import org.apache.hadoop.hbase.regionserver.MemStoreSnapshot; import org.apache.hadoop.hbase.regionserver.MemStoreSnapshot;
import org.apache.hadoop.hbase.regionserver.StoreFile; import org.apache.hadoop.hbase.regionserver.StoreFile;
import org.apache.hadoop.hbase.security.EncryptionUtil;
import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.mapreduce.Reducer.Context; import org.apache.hadoop.mapreduce.Reducer.Context;


Expand Down Expand Up @@ -90,7 +91,7 @@ public MemStoreWrapper(Context context, FileSystem fs, BufferedMutator table,
flushSize = this.conf.getLong(MobConstants.MOB_SWEEP_TOOL_COMPACTION_MEMSTORE_FLUSH_SIZE, flushSize = this.conf.getLong(MobConstants.MOB_SWEEP_TOOL_COMPACTION_MEMSTORE_FLUSH_SIZE,
MobConstants.DEFAULT_MOB_SWEEP_TOOL_COMPACTION_MEMSTORE_FLUSH_SIZE); MobConstants.DEFAULT_MOB_SWEEP_TOOL_COMPACTION_MEMSTORE_FLUSH_SIZE);
mobFamilyDir = MobUtils.getMobFamilyPath(conf, table.getName(), hcd.getNameAsString()); mobFamilyDir = MobUtils.getMobFamilyPath(conf, table.getName(), hcd.getNameAsString());
cryptoContext = MobUtils.createEncryptionContext(conf, hcd); cryptoContext = EncryptionUtil.createEncryptionContext(conf, hcd);
} }


public void setPartitionId(CompactionPartitionId partitionId) { public void setPartitionId(CompactionPartitionId partitionId) {
Expand Down

0 comments on commit 9511150

Please sign in to comment.