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; 026 027import javax.crypto.spec.SecretKeySpec; 028 029import org.apache.hadoop.hbase.io.crypto.Cipher; 030import org.apache.hadoop.hbase.io.crypto.CipherProvider; 031import org.apache.hadoop.hbase.io.crypto.Context; 032import org.apache.hadoop.hbase.io.crypto.Decryptor; 033import org.apache.hadoop.hbase.io.crypto.Encryptor; 034import org.apache.yetus.audience.InterfaceAudience; 035import org.apache.yetus.audience.InterfaceStability; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 040import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 041 042/** 043 * AES-128, provided by the JCE 044 * <p> 045 * Algorithm instances are pooled for reuse, so the cipher provider and mode 046 * are configurable but fixed at instantiation. 047 */ 048@InterfaceAudience.Private 049@InterfaceStability.Evolving 050public class AES extends Cipher { 051 052 private static final Logger LOG = LoggerFactory.getLogger(AES.class); 053 054 public static final String CIPHER_MODE_KEY = "hbase.crypto.algorithm.aes.mode"; 055 public static final String CIPHER_PROVIDER_KEY = "hbase.crypto.algorithm.aes.provider"; 056 057 private final String rngAlgorithm; 058 private final String cipherMode; 059 private final String cipherProvider; 060 private SecureRandom rng; 061 062 public AES(CipherProvider provider) { 063 super(provider); 064 // The JCE mode for Ciphers 065 cipherMode = provider.getConf().get(CIPHER_MODE_KEY, "AES/CTR/NoPadding"); 066 // The JCE provider, null if default 067 cipherProvider = provider.getConf().get(CIPHER_PROVIDER_KEY); 068 // RNG algorithm 069 rngAlgorithm = provider.getConf().get(RNG_ALGORITHM_KEY, "SHA1PRNG"); 070 // RNG provider, null if default 071 String rngProvider = provider.getConf().get(RNG_PROVIDER_KEY); 072 try { 073 if (rngProvider != null) { 074 rng = SecureRandom.getInstance(rngAlgorithm, rngProvider); 075 } else { 076 rng = SecureRandom.getInstance(rngAlgorithm); 077 } 078 } catch (GeneralSecurityException e) { 079 LOG.warn("Could not instantiate specified RNG, falling back to default", e); 080 rng = new SecureRandom(); 081 } 082 } 083 084 @Override 085 public String getName() { 086 return "AES"; 087 } 088 089 @Override 090 public int getKeyLength() { 091 return KEY_LENGTH; 092 } 093 094 @Override 095 public int getIvLength() { 096 return IV_LENGTH; 097 } 098 099 @Override 100 public Key getRandomKey() { 101 byte[] keyBytes = new byte[getKeyLength()]; 102 rng.nextBytes(keyBytes); 103 return new SecretKeySpec(keyBytes, getName()); 104 } 105 106 @Override 107 public Encryptor getEncryptor() { 108 return new AESEncryptor(getJCECipherInstance(), rng); 109 } 110 111 @Override 112 public Decryptor getDecryptor() { 113 return new AESDecryptor(getJCECipherInstance()); 114 } 115 116 @Override 117 public OutputStream createEncryptionStream(OutputStream out, Context context, byte[] iv) 118 throws IOException { 119 Preconditions.checkNotNull(context); 120 Preconditions.checkState(context.getKey() != null, "Context does not have a key"); 121 Preconditions.checkNotNull(iv); 122 Encryptor e = getEncryptor(); 123 e.setKey(context.getKey()); 124 e.setIv(iv); 125 return e.createEncryptionStream(out); 126 } 127 128 @Override 129 public OutputStream createEncryptionStream(OutputStream out, Encryptor e) throws IOException { 130 Preconditions.checkNotNull(e); 131 return e.createEncryptionStream(out); 132 } 133 134 @Override 135 public InputStream createDecryptionStream(InputStream in, Context context, byte[] iv) 136 throws IOException { 137 Preconditions.checkNotNull(context); 138 Preconditions.checkState(context.getKey() != null, "Context does not have a key"); 139 Preconditions.checkNotNull(iv); 140 Decryptor d = getDecryptor(); 141 d.setKey(context.getKey()); 142 d.setIv(iv); 143 return d.createDecryptionStream(in); 144 } 145 146 @Override 147 public InputStream createDecryptionStream(InputStream in, Decryptor d) throws IOException { 148 Preconditions.checkNotNull(d); 149 return d.createDecryptionStream(in); 150 } 151 152 @VisibleForTesting 153 SecureRandom getRNG() { 154 return rng; 155 } 156 157 private javax.crypto.Cipher getJCECipherInstance() { 158 try { 159 if (cipherProvider != null) { 160 return javax.crypto.Cipher.getInstance(cipherMode, cipherProvider); 161 } 162 return javax.crypto.Cipher.getInstance(cipherMode); 163 } catch (GeneralSecurityException e) { 164 throw new RuntimeException(e); 165 } 166 } 167 168}