001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.util; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.io.IOException; 024import java.util.Map; 025import java.util.concurrent.ConcurrentHashMap; 026 027import org.apache.hadoop.hbase.HBaseInterfaceAudience; 028import org.apache.yetus.audience.InterfaceAudience; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031import org.apache.hadoop.conf.Configuration; 032import org.apache.hadoop.hbase.HConstants; 033import org.apache.hadoop.hbase.io.crypto.DefaultCipherProvider; 034import org.apache.hadoop.hbase.io.crypto.Encryption; 035import org.apache.hadoop.hbase.io.crypto.KeyStoreKeyProvider; 036import org.apache.hadoop.hbase.security.EncryptionUtil; 037 038@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 039public class EncryptionTest { 040 private static final Logger LOG = LoggerFactory.getLogger(EncryptionTest.class); 041 042 static final Map<String, Boolean> keyProviderResults = new ConcurrentHashMap<>(); 043 static final Map<String, Boolean> cipherProviderResults = new ConcurrentHashMap<>(); 044 static final Map<String, Boolean> cipherResults = new ConcurrentHashMap<>(); 045 046 private EncryptionTest() { 047 } 048 049 /** 050 * Check that the configured key provider can be loaded and initialized, or 051 * throw an exception. 052 * 053 * @param conf 054 * @throws IOException 055 */ 056 public static void testKeyProvider(final Configuration conf) throws IOException { 057 String providerClassName = conf.get(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY, 058 KeyStoreKeyProvider.class.getName()); 059 Boolean result = keyProviderResults.get(providerClassName); 060 if (result == null) { 061 try { 062 Encryption.getKeyProvider(conf); 063 keyProviderResults.put(providerClassName, true); 064 } catch (Exception e) { // most likely a RuntimeException 065 keyProviderResults.put(providerClassName, false); 066 throw new IOException("Key provider " + providerClassName + " failed test: " + 067 e.getMessage(), e); 068 } 069 } else if (result.booleanValue() == false) { 070 throw new IOException("Key provider " + providerClassName + " previously failed test"); 071 } 072 } 073 074 /** 075 * Check that the configured cipher provider can be loaded and initialized, or 076 * throw an exception. 077 * 078 * @param conf 079 * @throws IOException 080 */ 081 public static void testCipherProvider(final Configuration conf) throws IOException { 082 String providerClassName = conf.get(HConstants.CRYPTO_CIPHERPROVIDER_CONF_KEY, 083 DefaultCipherProvider.class.getName()); 084 Boolean result = cipherProviderResults.get(providerClassName); 085 if (result == null) { 086 try { 087 Encryption.getCipherProvider(conf); 088 cipherProviderResults.put(providerClassName, true); 089 } catch (Exception e) { // most likely a RuntimeException 090 cipherProviderResults.put(providerClassName, false); 091 throw new IOException("Cipher provider " + providerClassName + " failed test: " + 092 e.getMessage(), e); 093 } 094 } else if (result.booleanValue() == false) { 095 throw new IOException("Cipher provider " + providerClassName + " previously failed test"); 096 } 097 } 098 099 /** 100 * Check that the specified cipher can be loaded and initialized, or throw 101 * an exception. Verifies key and cipher provider configuration as a 102 * prerequisite for cipher verification. 103 * 104 * @param conf 105 * @param cipher 106 * @param key 107 * @throws IOException 108 */ 109 public static void testEncryption(final Configuration conf, final String cipher, 110 byte[] key) throws IOException { 111 if (cipher == null) { 112 return; 113 } 114 testKeyProvider(conf); 115 testCipherProvider(conf); 116 Boolean result = cipherResults.get(cipher); 117 if (result == null) { 118 try { 119 Encryption.Context context = Encryption.newContext(conf); 120 context.setCipher(Encryption.getCipher(conf, cipher)); 121 if (key == null) { 122 // Make a random key since one was not provided 123 context.setKey(context.getCipher().getRandomKey()); 124 } else { 125 // This will be a wrapped key from schema 126 context.setKey(EncryptionUtil.unwrapKey(conf, 127 conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, "hbase"), 128 key)); 129 } 130 byte[] iv = null; 131 if (context.getCipher().getIvLength() > 0) { 132 iv = new byte[context.getCipher().getIvLength()]; 133 Bytes.random(iv); 134 } 135 byte[] plaintext = new byte[1024]; 136 Bytes.random(plaintext); 137 ByteArrayOutputStream out = new ByteArrayOutputStream(); 138 Encryption.encrypt(out, new ByteArrayInputStream(plaintext), context, iv); 139 byte[] ciphertext = out.toByteArray(); 140 out.reset(); 141 Encryption.decrypt(out, new ByteArrayInputStream(ciphertext), plaintext.length, 142 context, iv); 143 byte[] test = out.toByteArray(); 144 if (!Bytes.equals(plaintext, test)) { 145 throw new IOException("Did not pass encrypt/decrypt test"); 146 } 147 cipherResults.put(cipher, true); 148 } catch (Exception e) { 149 cipherResults.put(cipher, false); 150 throw new IOException("Cipher " + cipher + " failed test: " + e.getMessage(), e); 151 } 152 } else if (result.booleanValue() == false) { 153 throw new IOException("Cipher " + cipher + " previously failed test"); 154 } 155 } 156}