Quellcode durchsuchen

HADOOP-10429. KeyStores should have methods to generate the materials themselves, KeyShell should use them. (tucu)

Conflicts:
	hadoop-common-project/hadoop-common/CHANGES.txt

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1619511 13f79535-47bb-0310-9956-ffa450edef68
Alejandro Abdelnur vor 10 Jahren
Ursprung
Commit
64bb8b4449

+ 3 - 0
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)

+ 70 - 0
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.
+   * <p/>
+   * 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.
+   * <p/>
+   * 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

+ 2 - 29
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;

+ 83 - 0
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<String> getKeys() throws IOException {
+      return null;
+    }
+
+    @Override
+    public List<KeyVersion> 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);
+  }
 }