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.regionserver; 019 020import static org.junit.jupiter.api.Assertions.assertNotNull; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022 023import java.security.Key; 024import java.util.ArrayList; 025import java.util.List; 026import org.apache.hadoop.conf.Configuration; 027import org.apache.hadoop.fs.Path; 028import org.apache.hadoop.hbase.HBaseTestingUtil; 029import org.apache.hadoop.hbase.HConstants; 030import org.apache.hadoop.hbase.TableName; 031import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 032import org.apache.hadoop.hbase.client.Put; 033import org.apache.hadoop.hbase.client.Table; 034import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 035import org.apache.hadoop.hbase.io.crypto.Encryption; 036import org.apache.hadoop.hbase.io.crypto.MockAesKeyProvider; 037import org.apache.hadoop.hbase.io.hfile.CacheConfig; 038import org.apache.hadoop.hbase.io.hfile.HFile; 039import org.apache.hadoop.hbase.testclassification.MediumTests; 040import org.apache.hadoop.hbase.testclassification.RegionServerTests; 041import org.apache.hadoop.hbase.util.Bytes; 042import org.junit.jupiter.api.AfterAll; 043import org.junit.jupiter.api.BeforeAll; 044import org.junit.jupiter.api.Tag; 045import org.junit.jupiter.api.Test; 046 047@Tag(RegionServerTests.TAG) 048@Tag(MediumTests.TAG) 049public class TestEncryptionRandomKeying { 050 051 private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil(); 052 private static Configuration conf = TEST_UTIL.getConfiguration(); 053 private static TableDescriptorBuilder tdb; 054 055 private static List<Path> findStorefilePaths(TableName tableName) throws Exception { 056 List<Path> paths = new ArrayList<>(); 057 for (Region region : TEST_UTIL.getRSForFirstRegionInTable(tableName) 058 .getRegions(tdb.build().getTableName())) { 059 for (HStore store : ((HRegion) region).getStores()) { 060 for (HStoreFile storefile : store.getStorefiles()) { 061 paths.add(storefile.getPath()); 062 } 063 } 064 } 065 return paths; 066 } 067 068 private static byte[] extractHFileKey(Path path) throws Exception { 069 HFile.Reader reader = 070 HFile.createReader(TEST_UTIL.getTestFileSystem(), path, new CacheConfig(conf), true, conf); 071 try { 072 Encryption.Context cryptoContext = reader.getFileContext().getEncryptionContext(); 073 assertNotNull(cryptoContext, "Reader has a null crypto context"); 074 Key key = cryptoContext.getKey(); 075 if (key == null) { 076 return null; 077 } 078 return key.getEncoded(); 079 } finally { 080 reader.close(); 081 } 082 } 083 084 @BeforeAll 085 public static void setUp() throws Exception { 086 conf.setInt("hfile.format.version", 3); 087 conf.set(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, MockAesKeyProvider.class.getName()); 088 conf.set(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, "hbase"); 089 090 // Create the table schema 091 // Specify an encryption algorithm without a key 092 tdb = 093 TableDescriptorBuilder.newBuilder(TableName.valueOf("default", "TestEncryptionRandomKeying")); 094 ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = 095 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("cf")); 096 String algorithm = conf.get(HConstants.CRYPTO_KEY_ALGORITHM_CONF_KEY, HConstants.CIPHER_AES); 097 columnFamilyDescriptorBuilder.setEncryptionType(algorithm); 098 tdb.setColumnFamily(columnFamilyDescriptorBuilder.build()); 099 100 // Start the minicluster 101 TEST_UTIL.startMiniCluster(1); 102 103 // Create the test table 104 TEST_UTIL.getAdmin().createTable(tdb.build()); 105 TEST_UTIL.waitTableAvailable(tdb.build().getTableName(), 5000); 106 107 // Create a store file 108 Table table = TEST_UTIL.getConnection().getTable(tdb.build().getTableName()); 109 try { 110 table.put( 111 new Put(Bytes.toBytes("testrow")).addColumn(columnFamilyDescriptorBuilder.build().getName(), 112 Bytes.toBytes("q"), Bytes.toBytes("value"))); 113 } finally { 114 table.close(); 115 } 116 TEST_UTIL.getAdmin().flush(tdb.build().getTableName()); 117 } 118 119 @AfterAll 120 public static void tearDown() throws Exception { 121 TEST_UTIL.shutdownMiniCluster(); 122 } 123 124 @Test 125 public void testRandomKeying() throws Exception { 126 // Verify we have store file(s) with a random key 127 final List<Path> initialPaths = findStorefilePaths(tdb.build().getTableName()); 128 assertTrue(initialPaths.size() > 0); 129 for (Path path : initialPaths) { 130 assertNotNull(extractHFileKey(path), "Store file " + path + " is not encrypted"); 131 } 132 } 133 134}