001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.io.crypto.aes; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.OutputStream; 023import java.security.GeneralSecurityException; 024import java.security.Key; 025import java.security.SecureRandom; 026import java.util.Properties; 027 028import javax.crypto.spec.SecretKeySpec; 029 030import org.apache.commons.crypto.cipher.CryptoCipherFactory; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.hbase.io.crypto.Cipher; 033import org.apache.hadoop.hbase.io.crypto.CipherProvider; 034import org.apache.hadoop.hbase.io.crypto.Context; 035import org.apache.hadoop.hbase.io.crypto.Decryptor; 036import org.apache.hadoop.hbase.io.crypto.Encryptor; 037import org.apache.yetus.audience.InterfaceAudience; 038import org.apache.yetus.audience.InterfaceStability; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041 042import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 043import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 044 045@InterfaceAudience.Private 046@InterfaceStability.Evolving 047public class CommonsCryptoAES extends Cipher { 048 049 private static final Logger LOG = LoggerFactory.getLogger(CommonsCryptoAES.class); 050 051 public static final String CIPHER_MODE_KEY = "hbase.crypto.commons.mode"; 052 public static final String CIPHER_CLASSES_KEY = "hbase.crypto.commons.cipher.classes"; 053 public static final String CIPHER_JCE_PROVIDER_KEY = "hbase.crypto.commons.cipher.jce.provider"; 054 055 private final String cipherMode; 056 private Properties props; 057 private final String rngAlgorithm; 058 private SecureRandom rng; 059 060 public CommonsCryptoAES(CipherProvider provider) { 061 super(provider); 062 // The mode for Commons Crypto Ciphers 063 cipherMode = provider.getConf().get(CIPHER_MODE_KEY, "AES/CTR/NoPadding"); 064 // Reads Commons Crypto properties from HBase conf 065 props = readCryptoProps(provider.getConf()); 066 // RNG algorithm 067 rngAlgorithm = provider.getConf().get(RNG_ALGORITHM_KEY, "SHA1PRNG"); 068 // RNG provider, null if default 069 String rngProvider = provider.getConf().get(RNG_PROVIDER_KEY); 070 try { 071 if (rngProvider != null) { 072 rng = SecureRandom.getInstance(rngAlgorithm, rngProvider); 073 } else { 074 rng = SecureRandom.getInstance(rngAlgorithm); 075 } 076 } catch (GeneralSecurityException e) { 077 LOG.warn("Could not instantiate specified RNG, falling back to default", e); 078 rng = new SecureRandom(); 079 } 080 } 081 082 private static Properties readCryptoProps(Configuration conf) { 083 Properties props = new Properties(); 084 085 props.setProperty(CryptoCipherFactory.CLASSES_KEY, conf.get(CIPHER_CLASSES_KEY, "")); 086 props.setProperty(CryptoCipherFactory.JCE_PROVIDER_KEY, conf.get(CIPHER_JCE_PROVIDER_KEY, "")); 087 088 return props; 089 } 090 091 @Override 092 public String getName() { 093 return "AES"; 094 } 095 096 @Override 097 public int getKeyLength() { 098 return KEY_LENGTH; 099 } 100 101 @Override 102 public int getIvLength() { 103 return IV_LENGTH; 104 } 105 106 @Override 107 public Key getRandomKey() { 108 byte[] keyBytes = new byte[getKeyLength()]; 109 rng.nextBytes(keyBytes); 110 return new SecretKeySpec(keyBytes, getName()); 111 } 112 113 @Override 114 public Encryptor getEncryptor() { 115 return new CommonsCryptoAESEncryptor(cipherMode, props, rng); 116 } 117 118 @Override 119 public Decryptor getDecryptor() { 120 return new CommonsCryptoAESDecryptor(cipherMode, props); 121 } 122 123 @Override 124 public OutputStream createEncryptionStream(OutputStream out, Context context, 125 byte[] iv) throws IOException { 126 Preconditions.checkNotNull(context); 127 Preconditions.checkState(context.getKey() != null, "Context does not have a key"); 128 Preconditions.checkNotNull(iv); 129 Encryptor e = getEncryptor(); 130 e.setKey(context.getKey()); 131 e.setIv(iv); 132 return e.createEncryptionStream(out); 133 } 134 135 @Override 136 public OutputStream createEncryptionStream(OutputStream out, 137 Encryptor encryptor) throws 138 IOException { 139 return encryptor.createEncryptionStream(out); 140 } 141 142 @Override 143 public InputStream createDecryptionStream(InputStream in, Context context, 144 byte[] iv) throws IOException { 145 Preconditions.checkNotNull(context); 146 Preconditions.checkState(context.getKey() != null, "Context does not have a key"); 147 Preconditions.checkNotNull(iv); 148 Decryptor d = getDecryptor(); 149 d.setKey(context.getKey()); 150 d.setIv(iv); 151 return d.createDecryptionStream(in); 152 } 153 154 @Override 155 public InputStream createDecryptionStream(InputStream in, 156 Decryptor decryptor) throws 157 IOException { 158 Preconditions.checkNotNull(decryptor); 159 return decryptor.createDecryptionStream(in); 160 } 161 162 @VisibleForTesting 163 SecureRandom getRNG() { 164 return rng; 165 } 166}