View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.security;
19  
20  import java.io.ByteArrayInputStream;
21  import java.io.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.security.Key;
24  import java.security.KeyException;
25  import java.security.SecureRandom;
26  
27  import javax.crypto.spec.SecretKeySpec;
28  
29  import org.apache.hadoop.hbase.util.ByteStringer;
30  import org.apache.hadoop.hbase.classification.InterfaceAudience;
31  import org.apache.hadoop.hbase.classification.InterfaceStability;
32  import org.apache.hadoop.conf.Configuration;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.io.crypto.Cipher;
35  import org.apache.hadoop.hbase.io.crypto.Encryption;
36  import org.apache.hadoop.hbase.protobuf.generated.EncryptionProtos;
37  import org.apache.hadoop.hbase.util.Bytes;
38  
39  /**
40   * Some static utility methods for encryption uses in hbase-client.
41   */
42  @InterfaceAudience.Private
43  @InterfaceStability.Evolving
44  public class EncryptionUtil {
45  
46    static private final SecureRandom RNG = new SecureRandom();
47  
48    /**
49     * Protect a key by encrypting it with the secret key of the given subject.
50     * The configuration must be set up correctly for key alias resolution.
51     * @param conf configuration
52     * @param key the raw key bytes
53     * @param algorithm the algorithm to use with this key material
54     * @return the encrypted key bytes
55     * @throws IOException
56     */
57    public static byte[] wrapKey(Configuration conf, byte[] key, String algorithm)
58        throws IOException {
59      return wrapKey(conf,
60        conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, User.getCurrent().getShortName()),
61        new SecretKeySpec(key, algorithm));
62    }
63  
64    /**
65     * Protect a key by encrypting it with the secret key of the given subject.
66     * The configuration must be set up correctly for key alias resolution.
67     * @param conf configuration
68     * @param subject subject key alias
69     * @param key the key
70     * @return the encrypted key bytes
71     */
72    public static byte[] wrapKey(Configuration conf, String subject, Key key)
73        throws IOException {
74      // Wrap the key with the configured encryption algorithm.
75      String algorithm =
76          conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
77      Cipher cipher = Encryption.getCipher(conf, algorithm);
78      if (cipher == null) {
79        throw new RuntimeException("Cipher '" + algorithm + "' not available");
80      }
81      EncryptionProtos.WrappedKey.Builder builder = EncryptionProtos.WrappedKey.newBuilder();
82      builder.setAlgorithm(key.getAlgorithm());
83      byte[] iv = null;
84      if (cipher.getIvLength() > 0) {
85        iv = new byte[cipher.getIvLength()];
86        RNG.nextBytes(iv);
87        builder.setIv(ByteStringer.wrap(iv));
88      }
89      byte[] keyBytes = key.getEncoded();
90      builder.setLength(keyBytes.length);
91      builder.setHash(ByteStringer.wrap(Encryption.hash128(keyBytes)));
92      ByteArrayOutputStream out = new ByteArrayOutputStream();
93      Encryption.encryptWithSubjectKey(out, new ByteArrayInputStream(keyBytes), subject,
94        conf, cipher, iv);
95      builder.setData(ByteStringer.wrap(out.toByteArray()));
96      // Build and return the protobuf message
97      out.reset();
98      builder.build().writeDelimitedTo(out);
99      return out.toByteArray();
100   }
101 
102   /**
103    * Unwrap a key by decrypting it with the secret key of the given subject.
104    * The configuration must be set up correctly for key alias resolution.
105    * @param conf configuration
106    * @param subject subject key alias
107    * @param value the encrypted key bytes
108    * @return the raw key bytes
109    * @throws IOException
110    * @throws KeyException
111    */
112   public static Key unwrapKey(Configuration conf, String subject, byte[] value)
113       throws IOException, KeyException {
114     EncryptionProtos.WrappedKey wrappedKey = EncryptionProtos.WrappedKey.PARSER
115         .parseDelimitedFrom(new ByteArrayInputStream(value));
116     String algorithm = conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY,
117       HConstants.CIPHER_AES);
118     Cipher cipher = Encryption.getCipher(conf, algorithm);
119     if (cipher == null) {
120       throw new RuntimeException("Cipher '" + algorithm + "' not available");
121     }
122     return getUnwrapKey(conf, subject, wrappedKey, cipher);
123   }
124 
125   private static Key getUnwrapKey(Configuration conf, String subject,
126       EncryptionProtos.WrappedKey wrappedKey, Cipher cipher) throws IOException, KeyException {
127     ByteArrayOutputStream out = new ByteArrayOutputStream();
128     byte[] iv = wrappedKey.hasIv() ? wrappedKey.getIv().toByteArray() : null;
129     Encryption.decryptWithSubjectKey(out, wrappedKey.getData().newInput(),
130       wrappedKey.getLength(), subject, conf, cipher, iv);
131     byte[] keyBytes = out.toByteArray();
132     if (wrappedKey.hasHash()) {
133       if (!Bytes.equals(wrappedKey.getHash().toByteArray(), Encryption.hash128(keyBytes))) {
134         throw new KeyException("Key was not successfully unwrapped");
135       }
136     }
137     return new SecretKeySpec(keyBytes, wrappedKey.getAlgorithm());
138   }
139 
140   /**
141    * Unwrap a wal key by decrypting it with the secret key of the given subject. The configuration
142    * must be set up correctly for key alias resolution.
143    * @param conf configuration
144    * @param subject subject key alias
145    * @param value the encrypted key bytes
146    * @return the raw key bytes
147    * @throws IOException if key is not found for the subject, or if some I/O error occurs
148    * @throws KeyException if fail to unwrap the key
149    */
150   public static Key unwrapWALKey(Configuration conf, String subject, byte[] value)
151       throws IOException, KeyException {
152     EncryptionProtos.WrappedKey wrappedKey =
153         EncryptionProtos.WrappedKey.PARSER.parseDelimitedFrom(new ByteArrayInputStream(value));
154     String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
155     Cipher cipher = Encryption.getCipher(conf, algorithm);
156     if (cipher == null) {
157       throw new RuntimeException("Cipher '" + algorithm + "' not available");
158     }
159     return getUnwrapKey(conf, subject, wrappedKey, cipher);
160   }
161 
162 }