HADOOP-13443. KMS should check the type of underlying keyprovider of KeyProviderExtension before falling back to default. Contributed by Anthony Young-Garner.

This commit is contained in:
Xiao Chen 2016-08-04 15:54:52 +08:00
parent 8f1c374bec
commit 05db64913d
2 changed files with 231 additions and 8 deletions

View File

@ -389,24 +389,39 @@ public KeyVersion decryptEncryptedKey(EncryptedKeyVersion encryptedKey)
} }
/** /**
* Creates a <code>KeyProviderCryptoExtension</code> using a given * Creates a <code>KeyProviderCryptoExtension</code> using a given
* {@link KeyProvider}. * {@link KeyProvider}.
* <p/> * <p/>
* If the given <code>KeyProvider</code> implements the * If the given <code>KeyProvider</code> implements the
* {@link CryptoExtension} interface the <code>KeyProvider</code> itself * {@link CryptoExtension} interface the <code>KeyProvider</code> itself
* will provide the extension functionality, otherwise a default extension * will provide the extension functionality.
* If the given <code>KeyProvider</code> implements the
* {@link KeyProviderExtension} interface and the KeyProvider being
* extended by the <code>KeyProvider</code> implements the
* {@link CryptoExtension} interface, the KeyProvider being extended will
* provide the extension functionality. Otherwise, a default extension
* implementation will be used. * implementation will be used.
* *
* @param keyProvider <code>KeyProvider</code> to use to create the * @param keyProvider <code>KeyProvider</code> to use to create the
* <code>KeyProviderCryptoExtension</code> extension. * <code>KeyProviderCryptoExtension</code> extension.
* @return a <code>KeyProviderCryptoExtension</code> instance using the * @return a <code>KeyProviderCryptoExtension</code> instance using the
* given <code>KeyProvider</code>. * given <code>KeyProvider</code>.
*/ */
public static KeyProviderCryptoExtension createKeyProviderCryptoExtension( public static KeyProviderCryptoExtension createKeyProviderCryptoExtension(
KeyProvider keyProvider) { KeyProvider keyProvider) {
CryptoExtension cryptoExtension = (keyProvider instanceof CryptoExtension) CryptoExtension cryptoExtension = null;
? (CryptoExtension) keyProvider if (keyProvider instanceof CryptoExtension) {
: new DefaultCryptoExtension(keyProvider); cryptoExtension = (CryptoExtension) keyProvider;
} else if (keyProvider instanceof KeyProviderExtension &&
((KeyProviderExtension)keyProvider).getKeyProvider() instanceof
KeyProviderCryptoExtension.CryptoExtension) {
KeyProviderExtension keyProviderExtension =
(KeyProviderExtension)keyProvider;
cryptoExtension =
(CryptoExtension)keyProviderExtension.getKeyProvider();
} else {
cryptoExtension = new DefaultCryptoExtension(keyProvider);
}
return new KeyProviderCryptoExtension(keyProvider, cryptoExtension); return new KeyProviderCryptoExtension(keyProvider, cryptoExtension);
} }

View File

@ -17,9 +17,13 @@
*/ */
package org.apache.hadoop.crypto.key; package org.apache.hadoop.crypto.key;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
@ -27,6 +31,7 @@
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion;
import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -132,4 +137,207 @@ public void testEncryptDecrypt() throws Exception {
assertArrayEquals("Wrong key material from decryptEncryptedKey", assertArrayEquals("Wrong key material from decryptEncryptedKey",
manualMaterial, apiMaterial); manualMaterial, apiMaterial);
} }
@Test
public void testNonDefaultCryptoExtensionSelectionWithCachingKeyProvider()
throws Exception {
Configuration config = new Configuration();
KeyProvider localKp = new DummyCryptoExtensionKeyProvider(config);
localKp = new CachingKeyProvider(localKp, 30000, 30000);
EncryptedKeyVersion localEkv = getEncryptedKeyVersion(config, localKp);
Assert.assertEquals("dummyFakeKey@1",
localEkv.getEncryptionKeyVersionName());
}
@Test
public void testDefaultCryptoExtensionSelectionWithCachingKeyProvider()
throws Exception {
Configuration config = new Configuration();
KeyProvider localKp =
new UserProvider.Factory().
createProvider(new URI("user:///"), config);
localKp = new CachingKeyProvider(localKp, 30000, 30000);
EncryptedKeyVersion localEkv = getEncryptedKeyVersion(config, localKp);
Assert.assertEquals(ENCRYPTION_KEY_NAME+"@0",
localEkv.getEncryptionKeyVersionName());
}
@Test
public void testNonDefaultCryptoExtensionSelectionOnKeyProviderExtension()
throws Exception {
Configuration config = new Configuration();
KeyProvider localKp = new UserProvider.Factory().
createProvider(new URI("user:///"), config);
localKp = new DummyCachingCryptoExtensionKeyProvider(localKp, 30000, 30000);
EncryptedKeyVersion localEkv = getEncryptedKeyVersion(config, localKp);
Assert.assertEquals("dummyCachingFakeKey@1",
localEkv.getEncryptionKeyVersionName());
}
private EncryptedKeyVersion getEncryptedKeyVersion(Configuration config,
KeyProvider localKp)
throws IOException, GeneralSecurityException {
KeyProvider.Options localOptions = new KeyProvider.Options(config);
localOptions.setCipher(CIPHER);
localOptions.setBitLength(128);
KeyVersion localEncryptionKey =
localKp.createKey(ENCRYPTION_KEY_NAME,
SecureRandom.getSeed(16), localOptions);
KeyProviderCryptoExtension localKpExt =
KeyProviderCryptoExtension.
createKeyProviderCryptoExtension(localKp);
return localKpExt.generateEncryptedKey(localEncryptionKey.getName());
}
/**
* Dummy class to test that this key provider is chosen to
* provide CryptoExtension services over the DefaultCryptoExtension.
*/
public class DummyCryptoExtensionKeyProvider extends KeyProvider
implements KeyProviderCryptoExtension.CryptoExtension {
private KeyProvider kp;
private KeyVersion kv;
private EncryptedKeyVersion ekv;
public DummyCryptoExtensionKeyProvider(Configuration conf) {
super(conf);
conf = new Configuration();
try {
this.kp = new UserProvider.Factory().createProvider(
new URI("user:///"), conf);
this.kv = new KeyVersion(ENCRYPTION_KEY_NAME,
"dummyFakeKey@1", new byte[16]);
this.ekv = new EncryptedKeyVersion(ENCRYPTION_KEY_NAME,
"dummyFakeKey@1", new byte[16], kv);
} catch (URISyntaxException e) {
fail(e.getMessage());
} catch (IOException e) {
fail(e.getMessage());
}
}
@Override
public void warmUpEncryptedKeys(String... keyNames) throws IOException {
}
@Override
public void drain(String keyName) {
}
@Override
public EncryptedKeyVersion generateEncryptedKey(String encryptionKeyName)
throws IOException, GeneralSecurityException {
return this.ekv;
}
@Override
public KeyVersion decryptEncryptedKey(
EncryptedKeyVersion encryptedKeyVersion)
throws IOException, GeneralSecurityException {
return kv;
}
@Override
public KeyVersion getKeyVersion(String versionName)
throws IOException {
return this.kp.getKeyVersion(versionName);
}
@Override
public List<String> getKeys() throws IOException {
return this.kp.getKeys();
}
@Override
public List<KeyVersion> getKeyVersions(String name)
throws IOException {
return this.kp.getKeyVersions(name);
}
@Override
public Metadata getMetadata(String name)
throws IOException {
return this.kp.getMetadata(name);
}
@Override
public KeyVersion createKey(String name, byte[] material,
Options localOptions) throws IOException {
return this.kp.createKey(name, material, localOptions);
}
@Override
public void deleteKey(String name) throws IOException {
this.kp.deleteKey(name);
}
@Override
public KeyVersion rollNewVersion(String name,
byte[] material) throws IOException {
return this.kp.rollNewVersion(name, material);
}
@Override
public void flush() throws IOException {
this.kp.flush();
}
}
/**
* Dummy class to verify that CachingKeyProvider is used to
* provide CryptoExtension services if the CachingKeyProvider itself
* implements CryptoExtension.
*/
public class DummyCachingCryptoExtensionKeyProvider
extends CachingKeyProvider
implements KeyProviderCryptoExtension.CryptoExtension {
private KeyProvider kp;
private KeyVersion kv;
private EncryptedKeyVersion ekv;
public DummyCachingCryptoExtensionKeyProvider(KeyProvider keyProvider,
long keyTimeoutMillis,
long currKeyTimeoutMillis) {
super(keyProvider, keyTimeoutMillis, currKeyTimeoutMillis);
conf = new Configuration();
try {
this.kp = new UserProvider.Factory().createProvider(
new URI("user:///"), conf);
this.kv = new KeyVersion(ENCRYPTION_KEY_NAME,
"dummyCachingFakeKey@1", new byte[16]);
this.ekv = new EncryptedKeyVersion(ENCRYPTION_KEY_NAME,
"dummyCachingFakeKey@1", new byte[16], kv);
} catch (URISyntaxException e) {
fail(e.getMessage());
} catch (IOException e) {
fail(e.getMessage());
}
}
@Override
public void warmUpEncryptedKeys(String... keyNames) throws IOException {
}
@Override
public void drain(String keyName) {
}
@Override
public EncryptedKeyVersion generateEncryptedKey(String encryptionKeyName)
throws IOException, GeneralSecurityException {
return this.ekv;
}
@Override
public KeyVersion decryptEncryptedKey(
EncryptedKeyVersion encryptedKeyVersion)
throws IOException, GeneralSecurityException {
return kv;
}
}
} }