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 javax.crypto.spec.SecretKeySpec; 027import org.apache.hadoop.conf.Configuration; 028import org.apache.hadoop.hbase.HBaseClassTestRule; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.io.crypto.Encryption; 031import org.apache.hadoop.hbase.io.crypto.MockAesKeyProvider; 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 private static final String INVALID_HASH_ALG = "this-hash-algorithm-not-exists hopefully... :)"; 044 private static final String DEFAULT_HASH_ALGORITHM = "use-default"; 045 046 @ClassRule 047 public static final HBaseClassTestRule CLASS_RULE = 048 HBaseClassTestRule.forClass(TestEncryptionUtil.class); 049 050 // There does not seem to be a ready way to test either getKeyFromBytesOrMasterKey 051 // or createEncryptionContext, and the existing code under MobUtils appeared to be 052 // untested. Not ideal! 053 054 @Test 055 public void testKeyWrappingUsingHashAlgDefault() throws Exception { 056 testKeyWrapping(DEFAULT_HASH_ALGORITHM); 057 } 058 059 @Test 060 public void testKeyWrappingUsingHashAlgMD5() throws Exception { 061 testKeyWrapping("MD5"); 062 } 063 064 @Test 065 public void testKeyWrappingUsingHashAlgSHA256() throws Exception { 066 testKeyWrapping("SHA-256"); 067 } 068 069 @Test 070 public void testKeyWrappingUsingHashAlgSHA384() throws Exception { 071 testKeyWrapping("SHA-384"); 072 } 073 074 @Test(expected = RuntimeException.class) 075 public void testKeyWrappingWithInvalidHashAlg() throws Exception { 076 testKeyWrapping(INVALID_HASH_ALG); 077 } 078 079 @Test 080 public void testWALKeyWrappingUsingHashAlgDefault() throws Exception { 081 testWALKeyWrapping(DEFAULT_HASH_ALGORITHM); 082 } 083 084 @Test 085 public void testWALKeyWrappingUsingHashAlgMD5() throws Exception { 086 testWALKeyWrapping("MD5"); 087 } 088 089 @Test 090 public void testWALKeyWrappingUsingHashAlgSHA256() throws Exception { 091 testWALKeyWrapping("SHA-256"); 092 } 093 094 @Test 095 public void testWALKeyWrappingUsingHashAlgSHA384() throws Exception { 096 testWALKeyWrapping("SHA-384"); 097 } 098 099 @Test(expected = RuntimeException.class) 100 public void testWALKeyWrappingWithInvalidHashAlg() throws Exception { 101 testWALKeyWrapping(INVALID_HASH_ALG); 102 } 103 104 @Test(expected = KeyException.class) 105 public void testWALKeyWrappingWithIncorrectKey() throws Exception { 106 // set up the key provider for testing to resolve a key for our test subject 107 Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this 108 conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, MockAesKeyProvider.class.getName()); 109 110 // generate a test key 111 byte[] keyBytes = new byte[AES.KEY_LENGTH]; 112 Bytes.secureRandom(keyBytes); 113 String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 114 Key key = new SecretKeySpec(keyBytes, algorithm); 115 116 // wrap the test key 117 byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key); 118 assertNotNull(wrappedKeyBytes); 119 120 // unwrap with an incorrect key 121 EncryptionUtil.unwrapWALKey(conf, "other", wrappedKeyBytes); 122 } 123 124 @Test(expected = KeyException.class) 125 public void testHashAlgorithmMismatchWhenFailExpected() throws Exception { 126 Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this 127 conf.setBoolean(Encryption.CRYPTO_KEY_FAIL_ON_ALGORITHM_MISMATCH_CONF_KEY, true); 128 testKeyWrappingWithMismatchingAlgorithms(conf); 129 } 130 131 @Test 132 public void testHashAlgorithmMismatchWhenFailNotExpected() throws Exception { 133 Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this 134 conf.setBoolean(Encryption.CRYPTO_KEY_FAIL_ON_ALGORITHM_MISMATCH_CONF_KEY, false); 135 testKeyWrappingWithMismatchingAlgorithms(conf); 136 } 137 138 @Test 139 public void testHashAlgorithmMismatchShouldNotFailWithDefaultConfig() throws Exception { 140 Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this 141 testKeyWrappingWithMismatchingAlgorithms(conf); 142 } 143 144 private void testKeyWrapping(String hashAlgorithm) throws Exception { 145 // set up the key provider for testing to resolve a key for our test subject 146 Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this 147 conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, MockAesKeyProvider.class.getName()); 148 if (!hashAlgorithm.equals(DEFAULT_HASH_ALGORITHM)) { 149 conf.set(Encryption.CRYPTO_KEY_HASH_ALGORITHM_CONF_KEY, hashAlgorithm); 150 } 151 152 // generate a test key 153 byte[] keyBytes = new byte[AES.KEY_LENGTH]; 154 Bytes.secureRandom(keyBytes); 155 String algorithm = conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 156 Key key = new SecretKeySpec(keyBytes, algorithm); 157 158 // wrap the test key 159 byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key); 160 assertNotNull(wrappedKeyBytes); 161 162 // unwrap 163 Key unwrappedKey = EncryptionUtil.unwrapKey(conf, "hbase", wrappedKeyBytes); 164 assertNotNull(unwrappedKey); 165 // only secretkeyspec supported for now 166 assertTrue(unwrappedKey instanceof SecretKeySpec); 167 // did we get back what we wrapped? 168 assertTrue("Unwrapped key bytes do not match original", 169 Bytes.equals(keyBytes, unwrappedKey.getEncoded())); 170 171 // unwrap with an incorrect key 172 try { 173 EncryptionUtil.unwrapKey(conf, "other", wrappedKeyBytes); 174 fail("Unwrap with incorrect key did not throw KeyException"); 175 } catch (KeyException e) { 176 // expected 177 } 178 } 179 180 private void testWALKeyWrapping(String hashAlgorithm) throws Exception { 181 // set up the key provider for testing to resolve a key for our test subject 182 Configuration conf = new Configuration(); // we don't need HBaseConfiguration for this 183 conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, MockAesKeyProvider.class.getName()); 184 if (!hashAlgorithm.equals(DEFAULT_HASH_ALGORITHM)) { 185 conf.set(Encryption.CRYPTO_KEY_HASH_ALGORITHM_CONF_KEY, hashAlgorithm); 186 } 187 188 // generate a test key 189 byte[] keyBytes = new byte[AES.KEY_LENGTH]; 190 Bytes.secureRandom(keyBytes); 191 String algorithm = conf.get(HConstants.CRYPTO_WAL_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 192 Key key = new SecretKeySpec(keyBytes, algorithm); 193 194 // wrap the test key 195 byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key); 196 assertNotNull(wrappedKeyBytes); 197 198 // unwrap 199 Key unwrappedKey = EncryptionUtil.unwrapWALKey(conf, "hbase", wrappedKeyBytes); 200 assertNotNull(unwrappedKey); 201 // only secretkeyspec supported for now 202 assertTrue(unwrappedKey instanceof SecretKeySpec); 203 // did we get back what we wrapped? 204 assertTrue("Unwrapped key bytes do not match original", 205 Bytes.equals(keyBytes, unwrappedKey.getEncoded())); 206 } 207 208 private void testKeyWrappingWithMismatchingAlgorithms(Configuration conf) throws Exception { 209 // we use MD5 to hash the encryption key during wrapping 210 conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, MockAesKeyProvider.class.getName()); 211 conf.set(Encryption.CRYPTO_KEY_HASH_ALGORITHM_CONF_KEY, "MD5"); 212 213 // generate a test key 214 byte[] keyBytes = new byte[AES.KEY_LENGTH]; 215 Bytes.secureRandom(keyBytes); 216 String algorithm = conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 217 Key key = new SecretKeySpec(keyBytes, algorithm); 218 219 // wrap the test key 220 byte[] wrappedKeyBytes = EncryptionUtil.wrapKey(conf, "hbase", key); 221 assertNotNull(wrappedKeyBytes); 222 223 // we set the default hash algorithm to SHA-384 during unwrapping 224 conf.set(Encryption.CRYPTO_KEY_HASH_ALGORITHM_CONF_KEY, "SHA-384"); 225 226 // unwrap 227 // we expect to fail, if CRYPTO_KEY_FAIL_ON_ALGORITHM_MISMATCH_CONF_KEY == true 228 // otherwise we will use the algorithm written during wrapping 229 Key unwrappedKey = EncryptionUtil.unwrapKey(conf, "hbase", wrappedKeyBytes); 230 assertNotNull(unwrappedKey); 231 232 // did we get back what we wrapped? 233 assertTrue("Unwrapped key bytes do not match original", 234 Bytes.equals(keyBytes, unwrappedKey.getEncoded())); 235 } 236 237}