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