HADOOP-10994. KeyProviderCryptoExtension should use CryptoCodec for generation/decryption of keys. (tucu)

This commit is contained in:
Alejandro Abdelnur 2014-08-29 14:21:58 -07:00
parent b61b78e5c6
commit 5889f4d5f3
10 changed files with 93 additions and 23 deletions

View File

@ -146,6 +146,9 @@ Release 2.6.0 - UNRELEASED
HADOOP-10814. Update Tomcat version used by HttpFS and KMS to latest HADOOP-10814. Update Tomcat version used by HttpFS and KMS to latest
6.x version. (rkanter via tucu) 6.x version. (rkanter via tucu)
HADOOP-10994. KeyProviderCryptoExtension should use CryptoCodec for
generation/decryption of keys. (tucu)
OPTIMIZATIONS OPTIMIZATIONS
HADOOP-10838. Byte array native checksumming. (James Thomas via todd) HADOOP-10838. Byte array native checksumming. (James Thomas via todd)

View File

@ -108,6 +108,7 @@ public class JavaKeyStoreProvider extends KeyProvider {
private final Map<String, Metadata> cache = new HashMap<String, Metadata>(); private final Map<String, Metadata> cache = new HashMap<String, Metadata>();
private JavaKeyStoreProvider(URI uri, Configuration conf) throws IOException { private JavaKeyStoreProvider(URI uri, Configuration conf) throws IOException {
super(conf);
this.uri = uri; this.uri = uri;
path = ProviderUtils.unnestUri(uri); path = ProviderUtils.unnestUri(uri);
fs = path.getFileSystem(conf); fs = path.getFileSystem(conf);

View File

@ -56,6 +56,8 @@ public abstract class KeyProvider {
"hadoop.security.key.default.bitlength"; "hadoop.security.key.default.bitlength";
public static final int DEFAULT_BITLENGTH = 128; public static final int DEFAULT_BITLENGTH = 128;
private final Configuration conf;
/** /**
* The combination of both the key version name and the key material. * The combination of both the key version name and the key material.
*/ */
@ -353,6 +355,24 @@ public abstract class KeyProvider {
} }
} }
/**
* Constructor.
*
* @param conf configuration for the provider
*/
public KeyProvider(Configuration conf) {
this.conf = new Configuration(conf);
}
/**
* Return the provider configuration.
*
* @return the provider configuration
*/
public Configuration getConf() {
return conf;
}
/** /**
* A helper function to create an options object. * A helper function to create an options object.
* @param conf the configuration to use * @param conf the configuration to use

View File

@ -19,6 +19,7 @@
package org.apache.hadoop.crypto.key; package org.apache.hadoop.crypto.key;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -29,6 +30,9 @@ import javax.crypto.spec.SecretKeySpec;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.crypto.CryptoCodec;
import org.apache.hadoop.crypto.Decryptor;
import org.apache.hadoop.crypto.Encryptor;
/** /**
* A KeyProvider with Cryptographic Extensions specifically for generating * A KeyProvider with Cryptographic Extensions specifically for generating
@ -239,18 +243,25 @@ public class KeyProviderCryptoExtension extends
Preconditions.checkNotNull(encryptionKey, Preconditions.checkNotNull(encryptionKey,
"No KeyVersion exists for key '%s' ", encryptionKeyName); "No KeyVersion exists for key '%s' ", encryptionKeyName);
// Generate random bytes for new key and IV // Generate random bytes for new key and IV
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
CryptoCodec cc = CryptoCodec.getInstance(keyProvider.getConf());
final byte[] newKey = new byte[encryptionKey.getMaterial().length]; final byte[] newKey = new byte[encryptionKey.getMaterial().length];
RANDOM.get().nextBytes(newKey); cc.generateSecureRandom(newKey);
final byte[] iv = new byte[cipher.getBlockSize()]; final byte[] iv = new byte[cc.getCipherSuite().getAlgorithmBlockSize()];
RANDOM.get().nextBytes(iv); cc.generateSecureRandom(iv);
// Encryption key IV is derived from new key's IV // Encryption key IV is derived from new key's IV
final byte[] encryptionIV = EncryptedKeyVersion.deriveIV(iv); final byte[] encryptionIV = EncryptedKeyVersion.deriveIV(iv);
// Encrypt the new key Encryptor encryptor = cc.createEncryptor();
cipher.init(Cipher.ENCRYPT_MODE, encryptor.init(encryptionKey.getMaterial(), encryptionIV);
new SecretKeySpec(encryptionKey.getMaterial(), "AES"), int keyLen = newKey.length;
new IvParameterSpec(encryptionIV)); ByteBuffer bbIn = ByteBuffer.allocateDirect(keyLen);
final byte[] encryptedKey = cipher.doFinal(newKey); ByteBuffer bbOut = ByteBuffer.allocateDirect(keyLen);
bbIn.put(newKey);
bbIn.flip();
encryptor.encrypt(bbIn, bbOut);
bbOut.flip();
byte[] encryptedKey = new byte[keyLen];
bbOut.get(encryptedKey);
return new EncryptedKeyVersion(encryptionKeyName, return new EncryptedKeyVersion(encryptionKeyName,
encryptionKey.getVersionName(), iv, encryptionKey.getVersionName(), iv,
new KeyVersion(encryptionKey.getName(), EEK, encryptedKey)); new KeyVersion(encryptionKey.getName(), EEK, encryptedKey));
@ -274,19 +285,25 @@ public class KeyProviderCryptoExtension extends
KeyProviderCryptoExtension.EEK, KeyProviderCryptoExtension.EEK,
encryptedKeyVersion.getEncryptedKeyVersion().getVersionName() encryptedKeyVersion.getEncryptedKeyVersion().getVersionName()
); );
final byte[] encryptionKeyMaterial = encryptionKey.getMaterial();
// Encryption key IV is determined from encrypted key's IV // Encryption key IV is determined from encrypted key's IV
final byte[] encryptionIV = final byte[] encryptionIV =
EncryptedKeyVersion.deriveIV(encryptedKeyVersion.getEncryptedKeyIv()); EncryptedKeyVersion.deriveIV(encryptedKeyVersion.getEncryptedKeyIv());
// Init the cipher with encryption key parameters
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); CryptoCodec cc = CryptoCodec.getInstance(keyProvider.getConf());
cipher.init(Cipher.DECRYPT_MODE, Decryptor decryptor = cc.createDecryptor();
new SecretKeySpec(encryptionKeyMaterial, "AES"), decryptor.init(encryptionKey.getMaterial(), encryptionIV);
new IvParameterSpec(encryptionIV));
// Decrypt the encrypted key
final KeyVersion encryptedKV = final KeyVersion encryptedKV =
encryptedKeyVersion.getEncryptedKeyVersion(); encryptedKeyVersion.getEncryptedKeyVersion();
final byte[] decryptedKey = cipher.doFinal(encryptedKV.getMaterial()); int keyLen = encryptedKV.getMaterial().length;
ByteBuffer bbIn = ByteBuffer.allocateDirect(keyLen);
ByteBuffer bbOut = ByteBuffer.allocateDirect(keyLen);
bbIn.put(encryptedKV.getMaterial());
bbIn.flip();
decryptor.decrypt(bbIn, bbOut);
bbOut.flip();
byte[] decryptedKey = new byte[keyLen];
bbOut.get(decryptedKey);
return new KeyVersion(encryptionKey.getName(), EK, decryptedKey); return new KeyVersion(encryptionKey.getName(), EK, decryptedKey);
} }

View File

@ -40,6 +40,7 @@ public abstract class KeyProviderExtension
private E extension; private E extension;
public KeyProviderExtension(KeyProvider keyProvider, E extensions) { public KeyProviderExtension(KeyProvider keyProvider, E extensions) {
super(keyProvider.getConf());
this.keyProvider = keyProvider; this.keyProvider = keyProvider;
this.extension = extensions; this.extension = extensions;
} }

View File

@ -44,7 +44,8 @@ public class UserProvider extends KeyProvider {
private final Credentials credentials; private final Credentials credentials;
private final Map<String, Metadata> cache = new HashMap<String, Metadata>(); private final Map<String, Metadata> cache = new HashMap<String, Metadata>();
private UserProvider() throws IOException { private UserProvider(Configuration conf) throws IOException {
super(conf);
user = UserGroupInformation.getCurrentUser(); user = UserGroupInformation.getCurrentUser();
credentials = user.getCredentials(); credentials = user.getCredentials();
} }
@ -145,7 +146,7 @@ public class UserProvider extends KeyProvider {
public KeyProvider createProvider(URI providerName, public KeyProvider createProvider(URI providerName,
Configuration conf) throws IOException { Configuration conf) throws IOException {
if (SCHEME_NAME.equals(providerName.getScheme())) { if (SCHEME_NAME.equals(providerName.getScheme())) {
return new UserProvider(); return new UserProvider(conf);
} }
return null; return null;
} }

View File

@ -283,6 +283,7 @@ public class KMSClientProvider extends KeyProvider implements CryptoExtension,
} }
public KMSClientProvider(URI uri, Configuration conf) throws IOException { public KMSClientProvider(URI uri, Configuration conf) throws IOException {
super(conf);
Path path = ProviderUtils.unnestUri(uri); Path path = ProviderUtils.unnestUri(uri);
URL url = path.toUri().toURL(); URL url = path.toUri().toURL();
kmsUrl = createServiceURL(url); kmsUrl = createServiceURL(url);

View File

@ -19,6 +19,7 @@ package org.apache.hadoop.crypto.key;
import java.util.Date; import java.util.Date;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.kms.KMSClientProvider; import org.apache.hadoop.crypto.key.kms.KMSClientProvider;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -32,6 +33,7 @@ public class TestCachingKeyProvider {
KeyProvider mockProv = Mockito.mock(KeyProvider.class); KeyProvider mockProv = Mockito.mock(KeyProvider.class);
Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey); Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey);
Mockito.when(mockProv.getCurrentKey(Mockito.eq("k2"))).thenReturn(null); Mockito.when(mockProv.getCurrentKey(Mockito.eq("k2"))).thenReturn(null);
Mockito.when(mockProv.getConf()).thenReturn(new Configuration());
KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100); KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100);
// asserting caching // asserting caching
@ -58,6 +60,7 @@ public class TestCachingKeyProvider {
Mockito.when(mockProv.getKeyVersion(Mockito.eq("k1@0"))) Mockito.when(mockProv.getKeyVersion(Mockito.eq("k1@0")))
.thenReturn(mockKey); .thenReturn(mockKey);
Mockito.when(mockProv.getKeyVersion(Mockito.eq("k2@0"))).thenReturn(null); Mockito.when(mockProv.getKeyVersion(Mockito.eq("k2@0"))).thenReturn(null);
Mockito.when(mockProv.getConf()).thenReturn(new Configuration());
KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100); KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100);
// asserting caching // asserting caching
@ -88,6 +91,7 @@ public class TestCachingKeyProvider {
KeyProvider mockProv = Mockito.mock(KeyProvider.class); KeyProvider mockProv = Mockito.mock(KeyProvider.class);
Mockito.when(mockProv.getMetadata(Mockito.eq("k1"))).thenReturn(mockMeta); Mockito.when(mockProv.getMetadata(Mockito.eq("k1"))).thenReturn(mockMeta);
Mockito.when(mockProv.getMetadata(Mockito.eq("k2"))).thenReturn(null); Mockito.when(mockProv.getMetadata(Mockito.eq("k2"))).thenReturn(null);
Mockito.when(mockProv.getConf()).thenReturn(new Configuration());
KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100); KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100);
// asserting caching // asserting caching
@ -112,6 +116,7 @@ public class TestCachingKeyProvider {
KeyProvider.KeyVersion mockKey = Mockito.mock(KeyProvider.KeyVersion.class); KeyProvider.KeyVersion mockKey = Mockito.mock(KeyProvider.KeyVersion.class);
KeyProvider mockProv = Mockito.mock(KeyProvider.class); KeyProvider mockProv = Mockito.mock(KeyProvider.class);
Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey); Mockito.when(mockProv.getCurrentKey(Mockito.eq("k1"))).thenReturn(mockKey);
Mockito.when(mockProv.getConf()).thenReturn(new Configuration());
KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100); KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100);
Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); Assert.assertEquals(mockKey, cache.getCurrentKey("k1"));
Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1")); Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1"));
@ -134,6 +139,7 @@ public class TestCachingKeyProvider {
.thenReturn(mockKey); .thenReturn(mockKey);
Mockito.when(mockProv.getMetadata(Mockito.eq("k1"))).thenReturn( Mockito.when(mockProv.getMetadata(Mockito.eq("k1"))).thenReturn(
new KMSClientProvider.KMSMetadata("c", 0, "l", null, new Date(), 1)); new KMSClientProvider.KMSMetadata("c", 0, "l", null, new Date(), 1));
Mockito.when(mockProv.getConf()).thenReturn(new Configuration());
KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100); KeyProvider cache = new CachingKeyProvider(mockProv, 100, 100);
Assert.assertEquals(mockKey, cache.getCurrentKey("k1")); Assert.assertEquals(mockKey, cache.getCurrentKey("k1"));
Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1")); Mockito.verify(mockProv, Mockito.times(1)).getCurrentKey(Mockito.eq("k1"));

View File

@ -159,6 +159,10 @@ public class TestKeyProvider {
private int size; private int size;
private byte[] material; private byte[] material;
public MyKeyProvider(Configuration conf) {
super(conf);
}
@Override @Override
public KeyVersion getKeyVersion(String versionName) public KeyVersion getKeyVersion(String versionName)
throws IOException { throws IOException {
@ -216,7 +220,7 @@ public class TestKeyProvider {
@Test @Test
public void testMaterialGeneration() throws Exception { public void testMaterialGeneration() throws Exception {
MyKeyProvider kp = new MyKeyProvider(); MyKeyProvider kp = new MyKeyProvider(new Configuration());
KeyProvider.Options options = new KeyProvider.Options(new Configuration()); KeyProvider.Options options = new KeyProvider.Options(new Configuration());
options.setCipher(CIPHER); options.setCipher(CIPHER);
options.setBitLength(128); options.setBitLength(128);
@ -225,10 +229,19 @@ public class TestKeyProvider {
Assert.assertEquals(CIPHER, kp.algorithm); Assert.assertEquals(CIPHER, kp.algorithm);
Assert.assertNotNull(kp.material); Assert.assertNotNull(kp.material);
kp = new MyKeyProvider(); kp = new MyKeyProvider(new Configuration());
kp.rollNewVersion("hello"); kp.rollNewVersion("hello");
Assert.assertEquals(128, kp.size); Assert.assertEquals(128, kp.size);
Assert.assertEquals(CIPHER, kp.algorithm); Assert.assertEquals(CIPHER, kp.algorithm);
Assert.assertNotNull(kp.material); Assert.assertNotNull(kp.material);
} }
@Test
public void testConfiguration() throws Exception {
Configuration conf = new Configuration(false);
conf.set("a", "A");
MyKeyProvider kp = new MyKeyProvider(conf);
Assert.assertEquals("A", kp.getConf().get("a"));
}
} }

View File

@ -29,11 +29,16 @@ import org.apache.hadoop.security.Credentials;
import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.Token;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
public class TestKeyProviderDelegationTokenExtension { public class TestKeyProviderDelegationTokenExtension {
public static abstract class MockKeyProvider extends public static abstract class MockKeyProvider extends
KeyProvider implements DelegationTokenExtension { KeyProvider implements DelegationTokenExtension {
public MockKeyProvider() {
super(new Configuration(false));
}
} }
@Test @Test
@ -50,9 +55,11 @@ public class TestKeyProviderDelegationTokenExtension {
Assert.assertNull(kpDTE1.addDelegationTokens("user", credentials)); Assert.assertNull(kpDTE1.addDelegationTokens("user", credentials));
MockKeyProvider mock = mock(MockKeyProvider.class); MockKeyProvider mock = mock(MockKeyProvider.class);
Mockito.when(mock.getConf()).thenReturn(new Configuration());
when(mock.addDelegationTokens("renewer", credentials)).thenReturn( when(mock.addDelegationTokens("renewer", credentials)).thenReturn(
new Token<?>[]{new Token(null, null, new Text("kind"), new Text( new Token<?>[]{new Token(null, null, new Text("kind"), new Text(
"service")) }); "service"))}
);
KeyProviderDelegationTokenExtension kpDTE2 = KeyProviderDelegationTokenExtension kpDTE2 =
KeyProviderDelegationTokenExtension KeyProviderDelegationTokenExtension
.createKeyProviderDelegationTokenExtension(mock); .createKeyProviderDelegationTokenExtension(mock);