diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index fe6445fd7c3..3fafe86220d 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -97,6 +97,9 @@ Release 2.6.0 - UNRELEASED HADOOP-10432. Refactor SSLFactory to expose static method to determine HostnameVerifier. (tucu) + HADOOP-10429. KeyStores should have methods to generate the materials + themselves, KeyShell should use them. (tucu) + OPTIMIZATIONS HADOOP-10838. Byte array native checksumming. (James Thomas via todd) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java index 3bbb5568c2d..6dd8063c9da 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProvider.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URI; +import java.security.NoSuchAlgorithmException; import java.util.Date; import java.util.List; @@ -34,6 +35,8 @@ import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; +import javax.crypto.KeyGenerator; + /** * A provider of secret key material for Hadoop applications. Provides an * abstraction to separate key storage from users of encryption. It @@ -314,6 +317,56 @@ public abstract class KeyProvider { public abstract KeyVersion createKey(String name, byte[] material, Options options) throws IOException; + /** + * Get the algorithm from the cipher. + * + * @return the algorithm name + */ + private String getAlgorithm(String cipher) { + int slash = cipher.indexOf('/'); + if (slash == -1) { + return cipher; + } else { + return cipher.substring(0, slash); + } + } + + /** + * Generates a key material. + * + * @param size length of the key. + * @param algorithm algorithm to use for generating the key. + * @return the generated key. + * @throws NoSuchAlgorithmException + */ + protected byte[] generateKey(int size, String algorithm) + throws NoSuchAlgorithmException { + algorithm = getAlgorithm(algorithm); + KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm); + keyGenerator.init(size); + byte[] key = keyGenerator.generateKey().getEncoded(); + return key; + } + + /** + * Create a new key generating the material for it. + * The given key must not already exist. + *

+ * This implementation generates the key material and calls the + * {@link #createKey(String, byte[], Options)} method. + * + * @param name the base name of the key + * @param options the options for the new key. + * @return the version name of the first version of the key. + * @throws IOException + * @throws NoSuchAlgorithmException + */ + public KeyVersion createKey(String name, Options options) + throws NoSuchAlgorithmException, IOException { + byte[] material = generateKey(options.getBitLength(), options.getCipher()); + return createKey(name, material, options); + } + /** * Delete the given key. * @param name the name of the key to delete @@ -332,6 +385,23 @@ public abstract class KeyProvider { byte[] material ) throws IOException; + /** + * Roll a new version of the given key generating the material for it. + *

+ * This implementation generates the key material and calls the + * {@link #rollNewVersion(String, byte[])} method. + * + * @param name the basename of the key + * @return the name of the new version of the key + * @throws IOException + */ + public KeyVersion rollNewVersion(String name) throws NoSuchAlgorithmException, + IOException { + Metadata meta = getMetadata(name); + byte[] material = generateKey(meta.getBitLength(), meta.getCipher()); + return rollNewVersion(name, material); + } + /** * Ensures that any changes to the keys are written to persistent store. * @throws IOException diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java index 633a8592dea..6093f234b81 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyShell.java @@ -185,16 +185,6 @@ public class KeyShell extends Configured implements Tool { return provider; } - protected byte[] generateKey(int size, String algorithm) - throws NoSuchAlgorithmException { - out.println("Generating key using size: " + size + " and algorithm: " - + algorithm); - KeyGenerator keyGenerator = KeyGenerator.getInstance(algorithm); - keyGenerator.init(size); - byte[] key = keyGenerator.generateKey().getEncoded(); - return key; - } - protected void printProviderWritten() { out.println(provider.getClass().getName() + " has been updated."); } @@ -289,9 +279,7 @@ public class KeyShell extends Configured implements Tool { out.println("Rolling key version from KeyProvider: " + provider.toString() + " for key name: " + keyName); try { - byte[] material = null; - material = generateKey(md.getBitLength(), md.getAlgorithm()); - provider.rollNewVersion(keyName, material); + provider.rollNewVersion(keyName); out.println(keyName + " has been successfully rolled."); provider.flush(); printProviderWritten(); @@ -423,9 +411,7 @@ public class KeyShell extends Configured implements Tool { warnIfTransientProvider(); try { Options options = KeyProvider.options(getConf()); - String alg = getAlgorithm(options.getCipher()); - byte[] material = generateKey(options.getBitLength(), alg); - provider.createKey(keyName, material, options); + provider.createKey(keyName, options); out.println(keyName + " has been successfully created."); provider.flush(); printProviderWritten(); @@ -441,19 +427,6 @@ public class KeyShell extends Configured implements Tool { } } - /** - * Get the algorithm from the cipher. - * @return the algorithm name - */ - public String getAlgorithm(String cipher) { - int slash = cipher.indexOf('/'); - if (slash == - 1) { - return cipher; - } else { - return cipher.substring(0, slash); - } - } - @Override public String getUsage() { return USAGE + ":\n\n" + DESC; diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProvider.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProvider.java index 197355ed0ec..2bd1bfe0302 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProvider.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProvider.java @@ -17,6 +17,7 @@ */ package org.apache.hadoop.crypto.key; +import junit.framework.Assert; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; @@ -24,9 +25,11 @@ import org.junit.Test; import java.io.IOException; import java.net.URI; +import java.security.NoSuchAlgorithmException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -34,6 +37,8 @@ import static org.junit.Assert.assertArrayEquals; public class TestKeyProvider { + private static final String CIPHER = "AES"; + @Test public void testBuildVersionName() throws Exception { assertEquals("/a/b@3", KeyProvider.buildVersionName("/a/b", 3)); @@ -109,4 +114,82 @@ public class TestKeyProvider { assertEquals(new Path("user:///"), KeyProvider.unnestUri(new URI("outer://user/"))); } + + private static class MyKeyProvider extends KeyProvider { + private String algorithm; + private int size; + private byte[] material; + + @Override + public KeyVersion getKeyVersion(String versionName) + throws IOException { + return null; + } + + @Override + public List getKeys() throws IOException { + return null; + } + + @Override + public List getKeyVersions(String name) + throws IOException { + return null; + } + + @Override + public Metadata getMetadata(String name) throws IOException { + return new Metadata(CIPHER, 128, new Date(), 0); + } + + @Override + public KeyVersion createKey(String name, byte[] material, + Options options) throws IOException { + this.material = material; + return null; + } + + @Override + public void deleteKey(String name) throws IOException { + + } + + @Override + public KeyVersion rollNewVersion(String name, byte[] material) + throws IOException { + this.material = material; + return null; + } + + @Override + public void flush() throws IOException { + + } + + @Override + protected byte[] generateKey(int size, String algorithm) + throws NoSuchAlgorithmException { + this.size = size; + this.algorithm = algorithm; + return super.generateKey(size, algorithm); + } + } + + @Test + public void testMaterialGeneration() throws Exception { + MyKeyProvider kp = new MyKeyProvider(); + KeyProvider.Options options = new KeyProvider.Options(new Configuration()); + options.setCipher(CIPHER); + options.setBitLength(128); + kp.createKey("hello", options); + Assert.assertEquals(128, kp.size); + Assert.assertEquals(CIPHER, kp.algorithm); + Assert.assertNotNull(kp.material); + + kp = new MyKeyProvider(); + kp.rollNewVersion("hello"); + Assert.assertEquals(128, kp.size); + Assert.assertEquals(CIPHER, kp.algorithm); + Assert.assertNotNull(kp.material); + } }