You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by tu...@apache.org on 2014/07/04 21:41:01 UTC
svn commit: r1607918 - in
/hadoop/common/trunk/hadoop-common-project/hadoop-common: ./
src/main/java/org/apache/hadoop/crypto/key/
src/test/java/org/apache/hadoop/crypto/key/
Author: tucu
Date: Fri Jul 4 19:41:00 2014
New Revision: 1607918
URL: http://svn.apache.org/r1607918
Log:
HADOOP-10719. Add generateEncryptedKey and decryptEncryptedKey methods to KeyProvider. (asuresh via tucu)
Added:
hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderCryptoExtension.java
hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderExtension.java
hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderCryptoExtension.java
Modified:
hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt
Modified: hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt?rev=1607918&r1=1607917&r2=1607918&view=diff
==============================================================================
--- hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt (original)
+++ hadoop/common/trunk/hadoop-common-project/hadoop-common/CHANGES.txt Fri Jul 4 19:41:00 2014
@@ -23,6 +23,9 @@ Trunk (Unreleased)
Mike Liddell, Chuan Liu, Lengning Liu, Ivan Mitic, Michael Rys,
Alexander Stojanovich, Brian Swan, and Min Wei via cnauroth)
+ HADOOP-10719. Add generateEncryptedKey and decryptEncryptedKey
+ methods to KeyProvider. (asuresh via tucu)
+
IMPROVEMENTS
HADOOP-8017. Configure hadoop-main pom to get rid of M2E plugin execution
Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderCryptoExtension.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderCryptoExtension.java?rev=1607918&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderCryptoExtension.java (added)
+++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderCryptoExtension.java Fri Jul 4 19:41:00 2014
@@ -0,0 +1,246 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.crypto.key;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * A KeyProvider with Cytographic Extensions specifically for generating
+ * Encrypted Keys as well as decrypting them
+ *
+ */
+public class KeyProviderCryptoExtension extends
+ KeyProviderExtension<KeyProviderCryptoExtension.CryptoExtension> {
+
+ protected static final String EEK = "EEK";
+ protected static final String EK = "EK";
+
+ /**
+ * This is a holder class whose instance contains the keyVersionName, iv
+ * used to generate the encrypted Key and the encrypted KeyVersion
+ */
+ public static class EncryptedKeyVersion {
+ private String keyVersionName;
+ private byte[] iv;
+ private KeyVersion encryptedKey;
+
+ protected EncryptedKeyVersion(String keyVersionName, byte[] iv,
+ KeyVersion encryptedKey) {
+ this.keyVersionName = keyVersionName;
+ this.iv = iv;
+ this.encryptedKey = encryptedKey;
+ }
+
+ public String getKeyVersionName() {
+ return keyVersionName;
+ }
+
+ public byte[] getIv() {
+ return iv;
+ }
+
+ public KeyVersion getEncryptedKey() {
+ return encryptedKey;
+ }
+
+ }
+
+ /**
+ * CryptoExtension is a type of Extension that exposes methods to generate
+ * EncryptedKeys and to decrypt the same.
+ */
+ public interface CryptoExtension extends KeyProviderExtension.Extension {
+
+ /**
+ * Generates a key material and encrypts it using the given key version name
+ * and initialization vector. The generated key material is of the same
+ * length as the <code>KeyVersion</code> material and is encrypted using the
+ * same cipher.
+ * <p/>
+ * NOTE: The generated key is not stored by the <code>KeyProvider</code>
+ *
+ * @param encryptionKeyVersion
+ * a KeyVersion object containing the keyVersion name and material
+ * to encrypt.
+ * @return EncryptedKeyVersion with the generated key material, the version
+ * name is 'EEK' (for Encrypted Encryption Key)
+ * @throws IOException
+ * thrown if the key material could not be generated
+ * @throws GeneralSecurityException
+ * thrown if the key material could not be encrypted because of a
+ * cryptographic issue.
+ */
+ public EncryptedKeyVersion generateEncryptedKey(
+ KeyVersion encryptionKeyVersion) throws IOException,
+ GeneralSecurityException;
+
+ /**
+ * Decrypts an encrypted byte[] key material using the given a key version
+ * name and initialization vector.
+ *
+ * @param encryptedKeyVersion
+ * contains keyVersionName and IV to decrypt the encrypted key
+ * material
+ * @return a KeyVersion with the decrypted key material, the version name is
+ * 'EK' (For Encryption Key)
+ * @throws IOException
+ * thrown if the key material could not be decrypted
+ * @throws GeneralSecurityException
+ * thrown if the key material could not be decrypted because of a
+ * cryptographic issue.
+ */
+ public KeyVersion decryptEncryptedKey(
+ EncryptedKeyVersion encryptedKeyVersion) throws IOException,
+ GeneralSecurityException;
+ }
+
+ private static class DefaultCryptoExtension implements CryptoExtension {
+
+ private final KeyProvider keyProvider;
+
+ private DefaultCryptoExtension(KeyProvider keyProvider) {
+ this.keyProvider = keyProvider;
+ }
+
+ // the IV used to encrypt a EK typically will be the same IV used to
+ // encrypt data with the EK. To avoid any chance of weakening the
+ // encryption because the same IV is used, we simply XOR the IV thus we
+ // are not using the same IV for 2 different encryptions (even if they
+ // are done using different keys)
+ private byte[] flipIV(byte[] iv) {
+ byte[] rIv = new byte[iv.length];
+ for (int i = 0; i < iv.length; i++) {
+ rIv[i] = (byte) (iv[i] ^ 0xff);
+ }
+ return rIv;
+ }
+
+ @Override
+ public EncryptedKeyVersion generateEncryptedKey(KeyVersion keyVersion)
+ throws IOException, GeneralSecurityException {
+ KeyVersion keyVer =
+ keyProvider.getKeyVersion(keyVersion.getVersionName());
+ Preconditions.checkNotNull(keyVer, "KeyVersion name '%s' does not exist",
+ keyVersion.getVersionName());
+ byte[] newKey = new byte[keyVer.getMaterial().length];
+ SecureRandom.getInstance("SHA1PRNG").nextBytes(newKey);
+ Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
+ byte[] iv = SecureRandom.getSeed(cipher.getBlockSize());
+ cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(keyVer.getMaterial(),
+ "AES"), new IvParameterSpec(flipIV(iv)));
+ byte[] ek = cipher.doFinal(newKey);
+ return new EncryptedKeyVersion(keyVersion.getVersionName(), iv,
+ new KeyVersion(keyVer.getName(), EEK, ek));
+ }
+
+ @Override
+ public KeyVersion decryptEncryptedKey(
+ EncryptedKeyVersion encryptedKeyVersion) throws IOException,
+ GeneralSecurityException {
+ KeyVersion keyVer =
+ keyProvider.getKeyVersion(encryptedKeyVersion.getKeyVersionName());
+ Preconditions.checkNotNull(keyVer, "KeyVersion name '%s' does not exist",
+ encryptedKeyVersion.getKeyVersionName());
+ KeyVersion keyVersion = encryptedKeyVersion.getEncryptedKey();
+ Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
+ cipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec(keyVersion.getMaterial(), "AES"),
+ new IvParameterSpec(flipIV(encryptedKeyVersion.getIv())));
+ byte[] ek =
+ cipher.doFinal(encryptedKeyVersion.getEncryptedKey().getMaterial());
+ return new KeyVersion(keyVer.getName(), EK, ek);
+ }
+
+ }
+
+ private KeyProviderCryptoExtension(KeyProvider keyProvider,
+ CryptoExtension extension) {
+ super(keyProvider, extension);
+ }
+
+ /**
+ * Generates a key material and encrypts it using the given key version name
+ * and initialization vector. The generated key material is of the same
+ * length as the <code>KeyVersion</code> material and is encrypted using the
+ * same cipher.
+ * <p/>
+ * NOTE: The generated key is not stored by the <code>KeyProvider</code>
+ *
+ * @param encryptionKey a KeyVersion object containing the keyVersion name and
+ * material to encrypt.
+ * @return EncryptedKeyVersion with the generated key material, the version
+ * name is 'EEK' (for Encrypted Encryption Key)
+ * @throws IOException thrown if the key material could not be generated
+ * @throws GeneralSecurityException thrown if the key material could not be
+ * encrypted because of a cryptographic issue.
+ */
+ public EncryptedKeyVersion generateEncryptedKey(KeyVersion encryptionKey)
+ throws IOException,
+ GeneralSecurityException {
+ return getExtension().generateEncryptedKey(encryptionKey);
+ }
+
+ /**
+ * Decrypts an encrypted byte[] key material using the given a key version
+ * name and initialization vector.
+ *
+ * @param encryptedKey contains keyVersionName and IV to decrypt the encrypted
+ * key material
+ * @return a KeyVersion with the decrypted key material, the version name is
+ * 'EK' (For Encryption Key)
+ * @throws IOException thrown if the key material could not be decrypted
+ * @throws GeneralSecurityException thrown if the key material could not be
+ * decrypted because of a cryptographic issue.
+ */
+ public KeyVersion decryptEncryptedKey(EncryptedKeyVersion encryptedKey)
+ throws IOException, GeneralSecurityException {
+ return getExtension().decryptEncryptedKey(encryptedKey);
+ }
+
+ /**
+ * Creates a <code>KeyProviderCryptoExtension</code> using a given
+ * {@link KeyProvider}.
+ * <p/>
+ * If the given <code>KeyProvider</code> implements the
+ * {@link CryptoExtension} interface the <code>KeyProvider</code> itself
+ * will provide the extension functionality, otherwise a default extension
+ * implementation will be used.
+ *
+ * @param keyProvider <code>KeyProvider</code> to use to create the
+ * <code>KeyProviderCryptoExtension</code> extension.
+ * @return a <code>KeyProviderCryptoExtension</code> instance using the
+ * given <code>KeyProvider</code>.
+ */
+ public static KeyProviderCryptoExtension createKeyProviderCryptoExtension(
+ KeyProvider keyProvider) {
+ CryptoExtension cryptoExtension = (keyProvider instanceof CryptoExtension)
+ ? (CryptoExtension) keyProvider
+ : new DefaultCryptoExtension(keyProvider);
+ return new KeyProviderCryptoExtension(keyProvider, cryptoExtension);
+ }
+
+}
Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderExtension.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderExtension.java?rev=1607918&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderExtension.java (added)
+++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/KeyProviderExtension.java Fri Jul 4 19:41:00 2014
@@ -0,0 +1,123 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.crypto.key;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+
+/**
+ * This is a utility class used to extend the functionality of KeyProvider, that
+ * takes a KeyProvider and an Extension. It implements all the required methods
+ * of the KeyProvider by delegating it to the provided KeyProvider.
+ */
+public abstract class KeyProviderExtension
+<E extends KeyProviderExtension.Extension> extends KeyProvider {
+
+ /**
+ * A marker interface for the KeyProviderExtension subclass implement.
+ */
+ public static interface Extension {
+ }
+
+ private KeyProvider keyProvider;
+ private E extension;
+
+ public KeyProviderExtension(KeyProvider keyProvider, E extensions) {
+ this.keyProvider = keyProvider;
+ this.extension = extensions;
+ }
+
+ protected E getExtension() {
+ return extension;
+ }
+
+ protected KeyProvider getKeyProvider() {
+ return keyProvider;
+ }
+
+ @Override
+ public boolean isTransient() {
+ return keyProvider.isTransient();
+ }
+
+ @Override
+ public Metadata[] getKeysMetadata(String... names) throws IOException {
+ return keyProvider.getKeysMetadata(names);
+ }
+
+ @Override
+ public KeyVersion getCurrentKey(String name) throws IOException {
+ return keyProvider.getCurrentKey(name);
+ }
+
+ @Override
+ public KeyVersion createKey(String name, Options options)
+ throws NoSuchAlgorithmException, IOException {
+ return keyProvider.createKey(name, options);
+ }
+
+ @Override
+ public KeyVersion rollNewVersion(String name)
+ throws NoSuchAlgorithmException, IOException {
+ return keyProvider.rollNewVersion(name);
+ }
+
+ @Override
+ public KeyVersion getKeyVersion(String versionName) throws IOException {
+ return keyProvider.getKeyVersion(versionName);
+ }
+
+ @Override
+ public List<String> getKeys() throws IOException {
+ return keyProvider.getKeys();
+ }
+
+ @Override
+ public List<KeyVersion> getKeyVersions(String name) throws IOException {
+ return keyProvider.getKeyVersions(name);
+ }
+
+ @Override
+ public Metadata getMetadata(String name) throws IOException {
+ return keyProvider.getMetadata(name);
+ }
+
+ @Override
+ public KeyVersion createKey(String name, byte[] material, Options options)
+ throws IOException {
+ return keyProvider.createKey(name, material, options);
+ }
+
+ @Override
+ public void deleteKey(String name) throws IOException {
+ keyProvider.deleteKey(name);
+ }
+
+ @Override
+ public KeyVersion rollNewVersion(String name, byte[] material)
+ throws IOException {
+ return keyProvider.rollNewVersion(name, material);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ keyProvider.flush();
+ }
+}
Added: hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderCryptoExtension.java
URL: http://svn.apache.org/viewvc/hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderCryptoExtension.java?rev=1607918&view=auto
==============================================================================
--- hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderCryptoExtension.java (added)
+++ hadoop/common/trunk/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/crypto/key/TestKeyProviderCryptoExtension.java Fri Jul 4 19:41:00 2014
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.crypto.key;
+
+import org.apache.hadoop.conf.Configuration;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.URI;
+import java.security.SecureRandom;
+
+public class TestKeyProviderCryptoExtension {
+
+ private static final String CIPHER = "AES";
+
+ @Test
+ public void testGenerateEncryptedKey() throws Exception {
+ Configuration conf = new Configuration();
+ KeyProvider kp =
+ new UserProvider.Factory().createProvider(new URI("user:///"), conf);
+ KeyProvider.Options options = new KeyProvider.Options(conf);
+ options.setCipher(CIPHER);
+ options.setBitLength(128);
+ KeyProvider.KeyVersion kv = kp.createKey("foo", SecureRandom.getSeed(16),
+ options);
+ KeyProviderCryptoExtension kpExt =
+ KeyProviderCryptoExtension.createKeyProviderCryptoExtension(kp);
+
+ KeyProviderCryptoExtension.EncryptedKeyVersion ek1 =
+ kpExt.generateEncryptedKey(kv);
+ Assert.assertEquals(KeyProviderCryptoExtension.EEK,
+ ek1.getEncryptedKey().getVersionName());
+ Assert.assertNotNull(ek1.getEncryptedKey().getMaterial());
+ Assert.assertEquals(kv.getMaterial().length,
+ ek1.getEncryptedKey().getMaterial().length);
+ KeyProvider.KeyVersion k1 = kpExt.decryptEncryptedKey(ek1);
+ Assert.assertEquals(KeyProviderCryptoExtension.EK, k1.getVersionName());
+ KeyProvider.KeyVersion k1a = kpExt.decryptEncryptedKey(ek1);
+ Assert.assertArrayEquals(k1.getMaterial(), k1a.getMaterial());
+ Assert.assertEquals(kv.getMaterial().length, k1.getMaterial().length);
+
+ KeyProviderCryptoExtension.EncryptedKeyVersion ek2 =
+ kpExt.generateEncryptedKey(kv);
+ KeyProvider.KeyVersion k2 = kpExt.decryptEncryptedKey(ek2);
+ boolean eq = true;
+ for (int i = 0; eq && i < ek2.getEncryptedKey().getMaterial().length; i++) {
+ eq = k2.getMaterial()[i] == k1.getMaterial()[i];
+ }
+ Assert.assertFalse(eq);
+ }
+}