diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/CryptoCipherProvider.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/CryptoCipherProvider.java new file mode 100644 index 0000000..3f5cd2d --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/CryptoCipherProvider.java @@ -0,0 +1,76 @@ +/* + * 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.hbase.io.crypto; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; +import org.apache.hadoop.hbase.io.crypto.aes.CommonsCryptoAES; + +/** + * The default cipher provider. Supports AES via the Commons Crypto. + */ +@InterfaceAudience.Public +@InterfaceStability.Evolving +public final class CryptoCipherProvider implements CipherProvider { + + private static CryptoCipherProvider instance; + + public static CryptoCipherProvider getInstance() { + if (instance != null) { + return instance; + } + instance = new CryptoCipherProvider(); + return instance; + } + + private Configuration conf = HBaseConfiguration.create(); + + // Prevent instantiation + private CryptoCipherProvider() { } + + @Override + public Configuration getConf() { + return conf; + } + + @Override + public void setConf(Configuration conf) { + this.conf = conf; + } + + @Override + public String getName() { + return "commons"; + } + + @Override + public Cipher getCipher(String name) { + if (name.equalsIgnoreCase("AES")) { + return new CommonsCryptoAES(this); + } + throw new RuntimeException("Cipher '" + name + "' is not supported by provider '" + + getName() + "'"); + } + + @Override + public String[] getSupportedCiphers() { + return new String[] { "AES" }; + } + +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAES.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAES.java new file mode 100644 index 0000000..abb26ef --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAES.java @@ -0,0 +1,160 @@ +/* + * 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.hbase.io.crypto.aes; + +import com.google.common.base.Preconditions; +import org.apache.commons.crypto.cipher.CryptoCipherFactory; +import org.apache.commons.crypto.random.CryptoRandomFactory; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; +import org.apache.hadoop.hbase.io.crypto.*; + +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.SecureRandom; +import java.util.Properties; +import java.util.Random; + +@InterfaceAudience.Private +@InterfaceStability.Evolving +public class CommonsCryptoAES extends Cipher { + + private static final Log LOG = LogFactory.getLog(CommonsCryptoAES.class); + + public static final int KEY_LENGTH = 16; + public static final int BLOCK_SIZE = 16; + public static final int IV_LENGTH = 16; + + public static final String CIPHER_MODE_KEY = "hbase.crypto.commons.mode"; + public static final String CIPHER_CLASSES_KEY = "hbase.crypto.commons.cipher.classes"; + public static final String CIPHER_JCE_PROVIDER_KEY = "hbase.crypto.commons.cipher.jce.provider"; + + public static final String RNG_CLASSES_KEY = "hbase.crypto.commons.secure.random.classes"; + public static final String RNG_JAVA_ALGORITHM_KEY = "hbase.crypto.commons.secure.random.java.algorithm"; + public static final String RNG_DEVICE_FILE_PATH_KEY = "hbase.crypto.commons.secure.random.device.file.path"; + + private final String cipherMode; + private Random rng; + private Properties props; + + public CommonsCryptoAES(CipherProvider provider) { + super(provider); + // The mode for Commons Crypto Ciphers + cipherMode = provider.getConf().get(CIPHER_MODE_KEY, "AES/CTR/NoPadding"); + // Reads Commons Crypto properties from HBase conf + props = readCryptoProps(provider.getConf()); + // RNG provider, null if default + try { + rng = (Random) CryptoRandomFactory.getCryptoRandom(); + } catch (GeneralSecurityException e) { + LOG.warn("Could not instantiate specified RNG, falling back to default", e); + rng = new SecureRandom(); + } + } + + private static Properties readCryptoProps(Configuration conf) { + Properties props = new Properties(); + + props.setProperty(CryptoCipherFactory.CLASSES_KEY, conf.get(CIPHER_CLASSES_KEY)); + props.setProperty(CryptoCipherFactory.JCE_PROVIDER_KEY, conf.get(CIPHER_JCE_PROVIDER_KEY)); + + props.setProperty(CryptoRandomFactory.CLASSES_KEY, conf.get(RNG_CLASSES_KEY)); + props.setProperty(CryptoRandomFactory.JAVA_ALGORITHM_KEY, conf.get(RNG_JAVA_ALGORITHM_KEY)); + props.setProperty(CryptoRandomFactory.DEVICE_FILE_PATH_KEY, conf.get(RNG_DEVICE_FILE_PATH_KEY)); + + return props; + } + + @Override + public String getName() { + return "AES"; + } + + @Override + public int getKeyLength() { + return KEY_LENGTH; + } + + @Override + public int getIvLength() { + return IV_LENGTH; + } + + @Override + public Key getRandomKey() { + byte[] keyBytes = new byte[getKeyLength()]; + rng.nextBytes(keyBytes); + return new SecretKeySpec(keyBytes, getName()); + } + + @Override + public Encryptor getEncryptor() { + return new CommonsCryptoAESEncryptor(cipherMode, props); + } + + @Override + public Decryptor getDecryptor() { + return new CommonsCryptoAESDecryptor(cipherMode, props); + } + + @Override + public OutputStream createEncryptionStream(OutputStream out, Context context, + byte[] iv) throws IOException { + Preconditions.checkNotNull(context); + Preconditions.checkState(context.getKey() != null, "Context does not have a key"); + Preconditions.checkNotNull(iv); + Encryptor e = getEncryptor(); + e.setKey(context.getKey()); + e.setIv(iv); + return e.createEncryptionStream(out); + } + + @Override + public OutputStream createEncryptionStream(OutputStream out, + Encryptor encryptor) throws + IOException { + return encryptor.createEncryptionStream(out); + } + + @Override + public InputStream createDecryptionStream(InputStream in, Context context, + byte[] iv) throws IOException { + Preconditions.checkNotNull(context); + Preconditions.checkState(context.getKey() != null, "Context does not have a key"); + Preconditions.checkNotNull(iv); + Decryptor d = getDecryptor(); + d.setKey(context.getKey()); + d.setIv(iv); + return d.createDecryptionStream(in); + } + + @Override + public InputStream createDecryptionStream(InputStream in, + Decryptor decryptor) throws + IOException { + Preconditions.checkNotNull(decryptor); + return decryptor.createDecryptionStream(in); + } +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAESDecryptor.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAESDecryptor.java new file mode 100644 index 0000000..e33e366 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAESDecryptor.java @@ -0,0 +1,84 @@ +/* + * 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.hbase.io.crypto.aes; + +import com.google.common.base.Preconditions; +import org.apache.commons.crypto.stream.CryptoInputStream; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; +import org.apache.hadoop.hbase.io.crypto.Decryptor; + +import javax.crypto.spec.IvParameterSpec; +import java.io.IOException; +import java.io.InputStream; +import java.security.Key; +import java.util.Properties; + +@InterfaceAudience.Private +@InterfaceStability.Evolving +public class CommonsCryptoAESDecryptor implements Decryptor { + + private String cipherMode; + private Properties properties; + private Key key; + private byte[] iv; + + public CommonsCryptoAESDecryptor(String cipherMode, Properties properties) { + this.cipherMode = cipherMode; + this.properties = properties; + } + + @Override + public void setKey(Key key) { + Preconditions.checkNotNull(key, "Key cannot be null"); + this.key = key; + } + + @Override + public int getIvLength() { + return CommonsCryptoAES.IV_LENGTH; + } + + @Override + public int getBlockSize() { + return CommonsCryptoAES.BLOCK_SIZE; + } + + @Override + public void setIv(byte[] iv) { + Preconditions.checkNotNull(iv, "IV cannot be null"); + Preconditions.checkArgument(iv.length == CommonsCryptoAES.IV_LENGTH, "Invalid IV length"); + this.iv = iv; + } + + @Override + public InputStream createDecryptionStream(InputStream in) { + try { + return new CryptoInputStream(cipherMode, properties, in, key, new + IvParameterSpec(iv)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void reset() { + ; + } + +} diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAESEncryptor.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAESEncryptor.java new file mode 100644 index 0000000..89ecc29 --- /dev/null +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/io/crypto/aes/CommonsCryptoAESEncryptor.java @@ -0,0 +1,87 @@ +/* + * 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.hbase.io.crypto.aes; + +import com.google.common.base.Preconditions; +import org.apache.commons.crypto.stream.CryptoOutputStream; +import org.apache.hadoop.hbase.classification.InterfaceAudience; +import org.apache.hadoop.hbase.classification.InterfaceStability; +import org.apache.hadoop.hbase.io.crypto.Encryptor; + +import javax.crypto.spec.IvParameterSpec; +import java.io.IOException; +import java.io.OutputStream; +import java.security.Key; +import java.util.Properties; + +@InterfaceAudience.Private +@InterfaceStability.Evolving +public class CommonsCryptoAESEncryptor implements Encryptor { + + private String cipherMode; + private Properties properties; + private Key key; + private byte[] iv; + + public CommonsCryptoAESEncryptor(String cipherMode, Properties properties) { + this.cipherMode = cipherMode; + this.properties = properties; + } + + @Override + public void setKey(Key key) { + this.key = key; + } + + @Override + public int getIvLength() { + return CommonsCryptoAES.IV_LENGTH; + } + + @Override + public int getBlockSize() { + return CommonsCryptoAES.BLOCK_SIZE; + } + + @Override + public byte[] getIv() { + return iv; + } + + @Override + public void setIv(byte[] iv) { + Preconditions.checkNotNull(iv, "IV cannot be null"); + Preconditions.checkArgument(iv.length == CommonsCryptoAES.IV_LENGTH, "Invalid IV length"); + this.iv = iv; + } + + @Override + public OutputStream createEncryptionStream(OutputStream out) { + try { + return new CryptoOutputStream(cipherMode, properties, out, key, new + IvParameterSpec(iv)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void reset() { + ; + } +} diff --git a/pom.xml b/pom.xml index b26c015..ad5d7b2 100644 --- a/pom.xml +++ b/pom.xml @@ -1179,6 +1179,7 @@ 1.2 2.2 3.1 + 1.0.0 3.3.0 3.2.2 @@ -1794,6 +1795,17 @@ log4j log4j + + org.apache.commons + commons-crypto + ${commons-crypto.version} + + + net.java.dev.jna + jna + + + junit