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.security;
019
020import static org.junit.Assert.assertNotNull;
021import static org.junit.Assert.assertTrue;
022import static org.junit.Assert.fail;
023
024import java.security.Key;
025import java.security.KeyException;
026import java.security.SecureRandom;
027import javax.crypto.spec.SecretKeySpec;
028import org.apache.hadoop.conf.Configuration;
029import org.apache.hadoop.hbase.HBaseClassTestRule;
030import org.apache.hadoop.hbase.HConstants;
031import org.apache.hadoop.hbase.io.crypto.KeyProviderForTesting;
032import org.apache.hadoop.hbase.io.crypto.aes.AES;
033import org.apache.hadoop.hbase.testclassification.ClientTests;
034import org.apache.hadoop.hbase.testclassification.SmallTests;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.junit.ClassRule;
037import org.junit.Test;
038import org.junit.experimental.categories.Category;
039
040@Category({ClientTests.class, SmallTests.class})
041public class TestEncryptionUtil {
042
043  @ClassRule
044  public static final HBaseClassTestRule CLASS_RULE =
045      HBaseClassTestRule.forClass(TestEncryptionUtil.class);
046
047  // There does not seem to be a ready way to test either getKeyFromBytesOrMasterKey
048  // or createEncryptionContext, and the existing code under MobUtils appeared to be
049  // untested.  Not ideal!
050
051  @Test
052  public void testKeyWrapping() throws Exception {
053    // set up the key provider for testing to resolve a key for our test subject
054    Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this
055    conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, KeyProviderForTesting.class.getName());
056
057    // generate a test key
058    byte[] keyBytes = new byte[AES.KEY_LENGTH];
059    new SecureRandom().nextBytes(keyBytes);
060    String algorithm =
061        conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
062    Key key = new SecretKeySpec(keyBytes, algorithm);
063
064    // wrap the test key
065    byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key);
066    assertNotNull(wrappedKeyBytes);
067
068    // unwrap
069    Key unwrappedKey = EncryptionUtil.unwrapKey(conf, "hbase", wrappedKeyBytes);
070    assertNotNull(unwrappedKey);
071    // only secretkeyspec supported for now
072    assertTrue(unwrappedKey instanceof SecretKeySpec);
073    // did we get back what we wrapped?
074    assertTrue("Unwrapped key bytes do not match original",
075      Bytes.equals(keyBytes, unwrappedKey.getEncoded()));
076
077    // unwrap with an incorrect key
078    try {
079      EncryptionUtil.unwrapKey(conf, "other", wrappedKeyBytes);
080      fail("Unwrap with incorrect key did not throw KeyException");
081    } catch (KeyException e) {
082      // expected
083    }
084  }
085
086  @Test
087  public void testWALKeyWrapping() throws Exception {
088    // set up the key provider for testing to resolve a key for our test subject
089    Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this
090    conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, KeyProviderForTesting.class.getName());
091
092    // generate a test key
093    byte[] keyBytes = new byte[AES.KEY_LENGTH];
094    new SecureRandom().nextBytes(keyBytes);
095    String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
096    Key key = new SecretKeySpec(keyBytes, algorithm);
097
098    // wrap the test key
099    byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key);
100    assertNotNull(wrappedKeyBytes);
101
102    // unwrap
103    Key unwrappedKey = EncryptionUtil.unwrapWALKey(conf, "hbase", wrappedKeyBytes);
104    assertNotNull(unwrappedKey);
105    // only secretkeyspec supported for now
106    assertTrue(unwrappedKey instanceof SecretKeySpec);
107    // did we get back what we wrapped?
108    assertTrue("Unwrapped key bytes do not match original",
109      Bytes.equals(keyBytes, unwrappedKey.getEncoded()));
110  }
111
112  @Test(expected = KeyException.class)
113  public void testWALKeyWrappingWithIncorrectKey() throws Exception {
114    // set up the key provider for testing to resolve a key for our test subject
115    Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this
116    conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, KeyProviderForTesting.class.getName());
117
118    // generate a test key
119    byte[] keyBytes = new byte[AES.KEY_LENGTH];
120    new SecureRandom().nextBytes(keyBytes);
121    String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES);
122    Key key = new SecretKeySpec(keyBytes, algorithm);
123
124    // wrap the test key
125    byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key);
126    assertNotNull(wrappedKeyBytes);
127
128    // unwrap with an incorrect key
129    EncryptionUtil.unwrapWALKey(conf, "other", wrappedKeyBytes);
130  }
131}