blob: f45ecffa80d9643a93c6132a0229670da3bc942d [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hbase.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.classification.InterfaceAudience;
import org.apache.hadoop.hbase.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.io.crypto.DefaultCipherProvider;
import org.apache.hadoop.hbase.io.crypto.Encryption;
import org.apache.hadoop.hbase.io.crypto.KeyStoreKeyProvider;
import org.apache.hadoop.hbase.security.EncryptionUtil;
@InterfaceAudience.Public
@InterfaceStability.Evolving
public class EncryptionTest {
private static final Log LOG = LogFactory.getLog(EncryptionTest.class);
static final Map<String, Boolean> keyProviderResults = new ConcurrentHashMap<String, Boolean>();
static final Map<String, Boolean> cipherProviderResults =
new ConcurrentHashMap<String, Boolean>();
static final Map<String, Boolean> cipherResults = new ConcurrentHashMap<String, Boolean>();
private EncryptionTest() {
}
/**
* Check that the configured key provider can be loaded and initialized, or
* throw an exception.
*
* @param conf
* @throws IOException
*/
public static void testKeyProvider(final Configuration conf) throws IOException {
String providerClassName = conf.get(HConstants.CRYPTO_KEYPROVIDER_CONF_KEY,
KeyStoreKeyProvider.class.getName());
Boolean result = keyProviderResults.get(providerClassName);
if (result == null) {
try {
Encryption.getKeyProvider(conf);
keyProviderResults.put(providerClassName, true);
} catch (Exception e) { // most likely a RuntimeException
keyProviderResults.put(providerClassName, false);
throw new IOException("Key provider " + providerClassName + " failed test: " +
e.getMessage(), e);
}
} else if (result.booleanValue() == false) {
throw new IOException("Key provider " + providerClassName + " previously failed test");
}
}
/**
* Check that the configured cipher provider can be loaded and initialized, or
* throw an exception.
*
* @param conf
* @throws IOException
*/
public static void testCipherProvider(final Configuration conf) throws IOException {
String providerClassName = conf.get(HConstants.CRYPTO_CIPHERPROVIDER_CONF_KEY,
DefaultCipherProvider.class.getName());
Boolean result = cipherProviderResults.get(providerClassName);
if (result == null) {
try {
Encryption.getCipherProvider(conf);
cipherProviderResults.put(providerClassName, true);
} catch (Exception e) { // most likely a RuntimeException
cipherProviderResults.put(providerClassName, false);
throw new IOException("Cipher provider " + providerClassName + " failed test: " +
e.getMessage(), e);
}
} else if (result.booleanValue() == false) {
throw new IOException("Cipher provider " + providerClassName + " previously failed test");
}
}
/**
* Check that the specified cipher can be loaded and initialized, or throw
* an exception. Verifies key and cipher provider configuration as a
* prerequisite for cipher verification.
*
* @param conf
* @param cipher
* @param key
* @throws IOException
*/
public static void testEncryption(final Configuration conf, final String cipher,
byte[] key) throws IOException {
if (cipher == null) {
return;
}
testKeyProvider(conf);
testCipherProvider(conf);
Boolean result = cipherResults.get(cipher);
if (result == null) {
try {
Encryption.Context context = Encryption.newContext(conf);
context.setCipher(Encryption.getCipher(conf, cipher));
if (key == null) {
// Make a random key since one was not provided
context.setKey(context.getCipher().getRandomKey());
} else {
// This will be a wrapped key from schema
context.setKey(EncryptionUtil.unwrapKey(conf,
conf.get(HConstants.CRYPTO_MASTERKEY_NAME_CONF_KEY, "hbase"),
key));
}
byte[] iv = null;
if (context.getCipher().getIvLength() > 0) {
iv = new byte[context.getCipher().getIvLength()];
Bytes.random(iv);
}
byte[] plaintext = new byte[1024];
Bytes.random(plaintext);
ByteArrayOutputStream out = new ByteArrayOutputStream();
Encryption.encrypt(out, new ByteArrayInputStream(plaintext), context, iv);
byte[] ciphertext = out.toByteArray();
out.reset();
Encryption.decrypt(out, new ByteArrayInputStream(ciphertext), plaintext.length,
context, iv);
byte[] test = out.toByteArray();
if (!Bytes.equals(plaintext, test)) {
throw new IOException("Did not pass encrypt/decrypt test");
}
cipherResults.put(cipher, true);
} catch (Exception e) {
cipherResults.put(cipher, false);
throw new IOException("Cipher " + cipher + " failed test: " + e.getMessage(), e);
}
} else if (result.booleanValue() == false) {
throw new IOException("Cipher " + cipher + " previously failed test");
}
}
}