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 com.google.protobuf.HBaseZeroCopyByteString;
30  import org.apache.hadoop.classification.InterfaceAudience;
31  import org.apache.hadoop.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. Keys
67     * are always wrapped using AES.
68     * @param conf configuration
69     * @param subject subject key alias
70     * @param key the key
71     * @return the encrypted key bytes
72     */
73    public static byte[] wrapKey(Configuration conf, String subject, Key key)
74        throws IOException {
75      // Wrap the key with AES
76      Cipher cipher = Encryption.getCipher(conf, "AES");
77      if (cipher == null) {
78        throw new RuntimeException("Cipher 'AES' not available");
79      }
80      EncryptionProtos.WrappedKey.Builder builder = EncryptionProtos.WrappedKey.newBuilder();
81      builder.setAlgorithm(key.getAlgorithm());
82      byte[] iv = null;
83      if (cipher.getIvLength() > 0) {
84        iv = new byte[cipher.getIvLength()];
85        RNG.nextBytes(iv);
86        builder.setIv(HBaseZeroCopyByteString.wrap(iv));
87      }
88      byte[] keyBytes = key.getEncoded();
89      builder.setLength(keyBytes.length);
90      builder.setHash(HBaseZeroCopyByteString.wrap(Encryption.hash128(keyBytes)));
91      ByteArrayOutputStream out = new ByteArrayOutputStream();
92      Encryption.encryptWithSubjectKey(out, new ByteArrayInputStream(keyBytes), subject,
93        conf, cipher, iv);
94      builder.setData(HBaseZeroCopyByteString.wrap(out.toByteArray()));
95      // Build and return the protobuf message
96      out.reset();
97      builder.build().writeDelimitedTo(out);
98      return out.toByteArray();
99    }
100 
101   /**
102    * Unwrap a key by decrypting it with the secret key of the given subject.
103    * The configuration must be set up correctly for key alias resolution. Keys
104    * are always unwrapped using AES.
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     Cipher cipher = Encryption.getCipher(conf, "AES");
117     if (cipher == null) {
118       throw new RuntimeException("Algorithm 'AES' not available");
119     }
120     ByteArrayOutputStream out = new ByteArrayOutputStream();
121     byte[] iv = wrappedKey.hasIv() ? wrappedKey.getIv().toByteArray() : null;
122     Encryption.decryptWithSubjectKey(out, wrappedKey.getData().newInput(),
123       wrappedKey.getLength(), subject, conf, cipher, iv);
124     byte[] keyBytes = out.toByteArray();
125     if (wrappedKey.hasHash()) {
126       if (!Bytes.equals(wrappedKey.getHash().toByteArray(), Encryption.hash128(keyBytes))) {
127         throw new KeyException("Key was not successfully unwrapped");
128       }
129     }
130     return new SecretKeySpec(keyBytes, wrappedKey.getAlgorithm());
131   }
132 
133 }