HADOOP-10994. KeyProviderCryptoExtension should use CryptoCodec for generation/decryption of keys. (tucu)
This commit is contained in:
parent
b61b78e5c6
commit
5889f4d5f3
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue