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