blob: b9e7cb2fdf9f60ea9061e3ac920a95d6581e5487 [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.crypto.key;
import com.microsoft.azure.keyvault.KeyVaultClient;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AlgorithmParameters;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.KeyStoreSpi;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.SealedObject;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.apache.hadoop.conf.Configuration;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.crypto.key.KeyProvider.Metadata;
import org.apache.hadoop.crypto.key.RangerKeyStoreProvider.KeyMetadata;
import org.apache.log4j.Logger;
import org.apache.ranger.entity.XXRangerKeyStore;
import org.apache.ranger.kms.dao.DaoManager;
import org.apache.ranger.kms.dao.RangerKMSDao;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
/**
* This class provides the Database store implementation.
*/
public class RangerKeyStore extends KeyStoreSpi {
static final Logger logger = Logger.getLogger(RangerKeyStore.class);
private static final String KEY_METADATA = "KeyMetadata";
private static final String KEY_NAME_VALIDATION = "[a-z,A-Z,0-9](?!.*--)(?!.*__)(?!.*-_)(?!.*_-)[\\w\\-\\_]*";
private static final Pattern pattern = Pattern.compile(KEY_NAME_VALIDATION);
private static final String AZURE_KEYVAULT_ENABLED = "ranger.kms.azurekeyvault.enabled";
private boolean azureKeyVaultEnabled = false;
private DaoManager daoManager;
private RangerKeyVaultKeyGenerator kvKeyGen;
// keys
private static class KeyEntry {
Date date = new Date(); // the creation date of this entry
}
// Secret key
private static final class SecretKeyEntry {
Date date = new Date(); // the creation date of this entry
SealedObject sealedKey;
String cipher_field;
int bit_length;
String description;
String attributes;
int version;
}
private static final class SecretKeyByteEntry {
Date date = new Date();
byte[] key;
String cipher_field;
int bit_length;
String description;
String attributes;
int version;
}
private Map<String, Object> keyEntries = new ConcurrentHashMap<>();
private Map<String, Object> deltaEntries = new ConcurrentHashMap<>();
RangerKeyStore() {
}
public RangerKeyStore(DaoManager daoManager) {
this.daoManager = daoManager;
}
public RangerKeyStore(DaoManager daoManager, Configuration conf, KeyVaultClient kvClient) {
this.daoManager = daoManager;
this.kvKeyGen = new RangerKeyVaultKeyGenerator(conf, kvClient);
if(conf != null
&& StringUtils.isNotEmpty(conf
.get(AZURE_KEYVAULT_ENABLED))
&& conf.get(AZURE_KEYVAULT_ENABLED).equalsIgnoreCase(
"true")){
azureKeyVaultEnabled = true;
}
}
String convertAlias(String alias) {
return alias.toLowerCase();
}
@Override
public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException, UnrecoverableKeyException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.engineGetKey()");
}
Key key = null;
Object entry = keyEntries.get(convertAlias(alias));
if (!(entry instanceof SecretKeyEntry)) {
return null;
}
try {
key = unsealKey(((SecretKeyEntry) entry).sealedKey, password);
} catch (Exception e) {
logger.error("==> RangerKeyStore.engineGetKey() error: ", e);
}
if (logger.isDebugEnabled()) {
logger.debug("<== RangerKeyStore.engineGetKey()");
}
return key;
}
public byte[] engineGetDecryptedZoneKeyByte(String alias) throws Exception {
try {
Object entry = keyEntries.get(convertAlias(alias));
if (!(entry instanceof SecretKeyByteEntry)) {
return null;
}
SecretKeyByteEntry key = (SecretKeyByteEntry) entry;
byte[] decryptKeyByte = kvKeyGen.dencryptZoneKey(key.key);
return decryptKeyByte;
} catch (Exception ex) {
throw new Exception("Error while decrpting zone key. Name : "
+ alias + " Error : " + ex);
}
}
public Key engineGetDecryptedZoneKey(String alias) throws Exception {
byte[] decryptKeyByte = engineGetDecryptedZoneKeyByte(alias);
Metadata metadata = engineGetKeyMetadata(alias);
Key k = new KeyByteMetadata(metadata, decryptKeyByte);
return k;
}
public Metadata engineGetKeyMetadata(String alias) {
Object entry = keyEntries.get(convertAlias(alias));
if (!(entry instanceof SecretKeyByteEntry)) {
return null;
}
SecretKeyByteEntry key = (SecretKeyByteEntry) entry;
ObjectMapper mapper = new ObjectMapper();
Map<String, String> attributesMap = null;
try {
attributesMap = mapper.readValue(key.attributes,
new TypeReference<Map<String, String>>() {
});
} catch (JsonParseException e) {
logger.error("Invalid attribute string data: " + e.getMessage());
} catch (JsonMappingException e) {
logger.error("Invalid attribute string data: " + e.getMessage());
} catch (IOException e) {
logger.error("Invalid attribute string data: " + e.getMessage());
}
Metadata meta = new Metadata(key.cipher_field, key.bit_length,
key.description, attributesMap, key.date, key.version);
return meta;
}
public void addSecureKeyByteEntry(String alias, Key key, String cipher,
int bitLength, String description, int version, String attributes)
throws KeyStoreException {
SecretKeyByteEntry entry = new SecretKeyByteEntry();
synchronized (deltaEntries) {
try {
entry.date = new Date();
// encrypt and store the key
entry.key = kvKeyGen.encryptZoneKey(key);
entry.cipher_field = cipher;
entry.bit_length = bitLength;
entry.description = description;
entry.version = version;
entry.attributes = attributes;
deltaEntries.put(convertAlias(alias), entry);
} catch (Exception e) {
logger.error(e.getMessage());
throw new KeyStoreException(e.getMessage());
}
}
synchronized (keyEntries) {
try {
keyEntries.put(convertAlias(alias), entry);
} catch (Exception e) {
logger.error(e.getMessage());
throw new KeyStoreException(e.getMessage());
}
}
}
@Override
public Date engineGetCreationDate(String alias) {
Object entry = keyEntries.get(convertAlias(alias));
Date date = null;
if (entry != null) {
KeyEntry keyEntry = (KeyEntry) entry;
if (keyEntry.date != null) {
date = new Date(keyEntry.date.getTime());
}
}
return date;
}
public void addKeyEntry(String alias, Key key, char[] password, String cipher, int bitLength, String description, int version, String attributes)
throws KeyStoreException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.addKeyEntry()");
logger.debug("Adding entry for alias:" + alias);
}
SecretKeyEntry entry = new SecretKeyEntry();
synchronized (deltaEntries) {
try {
entry.date = new Date();
// seal and store the key
entry.sealedKey = sealKey(key, password);
entry.cipher_field = cipher;
entry.bit_length = bitLength;
entry.description = description;
entry.version = version;
entry.attributes = attributes;
deltaEntries.put(convertAlias(alias), entry);
} catch (Exception e) {
logger.error("==> RangerKeyStore.addKeyEntry() error: ", e);
throw new KeyStoreException(e.getMessage());
}
}
synchronized (keyEntries) {
try {
keyEntries.put(convertAlias(alias), entry);
} catch (Exception e) {
logger.error("==> RangerKeyStore.addKeyEntry() error: ", e);
throw new KeyStoreException(e.getMessage());
}
}
}
private SealedObject sealKey(Key key, char[] password) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.sealKey()");
}
// Create SecretKey
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndTripleDES");
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
pbeKeySpec.clearPassword();
// Generate random bytes + set up the PBEParameterSpec
SecureRandom random = new SecureRandom();
byte[] salt = new byte[8];
random.nextBytes(salt);
PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20);
// Seal the Key
Cipher cipher = Cipher.getInstance("PBEWithMD5AndTripleDES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, pbeSpec);
if (logger.isDebugEnabled()) {
logger.debug("<== RangerKeyStore.sealKey()");
}
return new RangerSealedObject(key, cipher);
}
private Key unsealKey(SealedObject sealedKey, char[] password) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.unsealKey()");
}
// Create SecretKey
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndTripleDES");
PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
pbeKeySpec.clearPassword();
// Get the AlgorithmParameters from RangerSealedObject
AlgorithmParameters algorithmParameters = null;
if (sealedKey instanceof RangerSealedObject) {
algorithmParameters = ((RangerSealedObject) sealedKey).getParameters();
} else {
algorithmParameters = new RangerSealedObject(sealedKey).getParameters();
}
// Unseal the Key
Cipher cipher = Cipher.getInstance("PBEWithMD5AndTripleDES");
cipher.init(Cipher.DECRYPT_MODE, secretKey, algorithmParameters);
if (logger.isDebugEnabled()) {
logger.debug("<== RangerKeyStore.unsealKey()");
}
return (Key) sealedKey.getObject(cipher);
}
@Override
public void engineDeleteEntry(String alias)
throws KeyStoreException {
synchronized (keyEntries) {
dbOperationDelete(convertAlias(alias));
keyEntries.remove(convertAlias(alias));
}
synchronized (deltaEntries) {
deltaEntries.remove(convertAlias(alias));
}
}
private void dbOperationDelete(String alias) {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.dbOperationDelete(" + alias + ")");
}
try {
if (daoManager != null) {
RangerKMSDao rangerKMSDao = new RangerKMSDao(daoManager);
rangerKMSDao.deleteByAlias(alias);
}
} catch (Exception e) {
logger.error("==> RangerKeyStore.dbOperationDelete() error : ", e);
}
}
@Override
public Enumeration<String> engineAliases() {
return Collections.enumeration(keyEntries.keySet());
}
@Override
public boolean engineContainsAlias(String alias) {
return keyEntries.containsKey(convertAlias(alias));
}
@Override
public int engineSize() {
return keyEntries.size();
}
@Override
public void engineStore(OutputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.engineStore()");
}
synchronized (deltaEntries) {
if (azureKeyVaultEnabled) {
for (Entry<String, Object> entry : deltaEntries.entrySet()) {
Long creationDate = ((SecretKeyByteEntry) entry.getValue()).date
.getTime();
SecretKeyByteEntry secretSecureKey = (SecretKeyByteEntry) entry
.getValue();
XXRangerKeyStore xxRangerKeyStore = mapObjectToEntity(
entry.getKey(), creationDate, secretSecureKey.key,
secretSecureKey.cipher_field,
secretSecureKey.bit_length,
secretSecureKey.description,
secretSecureKey.version, secretSecureKey.attributes);
dbOperationStore(xxRangerKeyStore);
}
} else {
// password is mandatory when storing
if (password == null) {
throw new IllegalArgumentException(
"Ranger Master Key can't be null");
}
MessageDigest md = getKeyedMessageDigest(password);
byte digest[] = md.digest();
for (Entry<String, Object> entry : deltaEntries.entrySet()) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(
new DigestOutputStream(baos, md));
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(dos);
oos.writeObject(((SecretKeyEntry) entry.getValue()).sealedKey);
dos.write(digest);
dos.flush();
Long creationDate = ((SecretKeyEntry) entry.getValue()).date
.getTime();
SecretKeyEntry secretKey = (SecretKeyEntry) entry
.getValue();
XXRangerKeyStore xxRangerKeyStore = mapObjectToEntity(
entry.getKey(), creationDate,
baos.toByteArray(), secretKey.cipher_field,
secretKey.bit_length, secretKey.description,
secretKey.version, secretKey.attributes);
dbOperationStore(xxRangerKeyStore);
} finally {
if (oos != null) {
oos.close();
} else {
dos.close();
}
}
}
}
clearDeltaEntires();
}
}
private XXRangerKeyStore mapObjectToEntity(String alias, Long creationDate,
byte[] byteArray, String cipher_field, int bit_length,
String description, int version, String attributes) {
XXRangerKeyStore xxRangerKeyStore = new XXRangerKeyStore();
xxRangerKeyStore.setAlias(alias);
xxRangerKeyStore.setCreatedDate(creationDate);
xxRangerKeyStore.setEncoded(DatatypeConverter.printBase64Binary(byteArray));
xxRangerKeyStore.setCipher(cipher_field);
xxRangerKeyStore.setBitLength(bit_length);
xxRangerKeyStore.setDescription(description);
xxRangerKeyStore.setVersion(version);
xxRangerKeyStore.setAttributes(attributes);
return xxRangerKeyStore;
}
public void dbOperationStore(XXRangerKeyStore rangerKeyStore) {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.dbOperationStore()");
}
try {
if (daoManager != null) {
RangerKMSDao rangerKMSDao = new RangerKMSDao(daoManager);
XXRangerKeyStore xxRangerKeyStore = rangerKMSDao.findByAlias(rangerKeyStore.getAlias());
boolean keyStoreExists = true;
if (xxRangerKeyStore == null) {
xxRangerKeyStore = new XXRangerKeyStore();
keyStoreExists = false;
}
xxRangerKeyStore = mapToEntityBean(rangerKeyStore, xxRangerKeyStore);
if (keyStoreExists) {
xxRangerKeyStore = rangerKMSDao.update(xxRangerKeyStore);
} else {
xxRangerKeyStore = rangerKMSDao.create(xxRangerKeyStore);
}
}
} catch (Exception e) {
logger.error("==> RangerKeyStore.dbOperationStore() error : ", e);
}
}
private XXRangerKeyStore mapToEntityBean(XXRangerKeyStore rangerKMSKeyStore, XXRangerKeyStore xxRangerKeyStore) {
xxRangerKeyStore.setAlias(rangerKMSKeyStore.getAlias());
xxRangerKeyStore.setCreatedDate(rangerKMSKeyStore.getCreatedDate());
xxRangerKeyStore.setEncoded(rangerKMSKeyStore.getEncoded());
xxRangerKeyStore.setCipher(rangerKMSKeyStore.getCipher());
xxRangerKeyStore.setBitLength(rangerKMSKeyStore.getBitLength());
xxRangerKeyStore.setDescription(rangerKMSKeyStore.getDescription());
xxRangerKeyStore.setVersion(rangerKMSKeyStore.getVersion());
xxRangerKeyStore.setAttributes(rangerKMSKeyStore.getAttributes());
return xxRangerKeyStore;
}
@Override
public void engineLoad(InputStream stream, char[] password)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.engineLoad()");
}
synchronized (keyEntries) {
List<XXRangerKeyStore> rangerKeyDetails = dbOperationLoad();
if (rangerKeyDetails == null || rangerKeyDetails.size() < 1) {
if (logger.isDebugEnabled()) {
logger.debug("RangerKeyStore might be null or key is not present in the database.");
}
return;
}
keyEntries.clear();
if (azureKeyVaultEnabled) {
for (XXRangerKeyStore rangerKey : rangerKeyDetails) {
String encodedStr = rangerKey.getEncoded();
byte[] encodedByte = DatatypeConverter
.parseBase64Binary(encodedStr);
String alias;
SecretKeyByteEntry entry = new SecretKeyByteEntry();
alias = rangerKey.getAlias();
entry.date = new Date(rangerKey.getCreatedDate());
entry.cipher_field = rangerKey.getCipher();
entry.bit_length = rangerKey.getBitLength();
entry.description = rangerKey.getDescription();
entry.version = rangerKey.getVersion();
entry.attributes = rangerKey.getAttributes();
entry.key = encodedByte;
keyEntries.put(alias, entry);
}
} else {
DataInputStream dis;
MessageDigest md = null;
if (password != null) {
md = getKeyedMessageDigest(password);
}
byte computed[] = {};
if (md != null) {
computed = md.digest();
}
for (XXRangerKeyStore rangerKey : rangerKeyDetails) {
String encoded = rangerKey.getEncoded();
byte[] data = DatatypeConverter.parseBase64Binary(encoded);
if (data != null && data.length > 0) {
stream = new ByteArrayInputStream(data);
} else {
logger.error("No Key found for alias "
+ rangerKey.getAlias());
}
if (computed != null) {
int counter = 0;
for (int i = computed.length - 1; i >= 0; i--) {
if (computed[i] != data[data.length - (1 + counter)]) {
Throwable t = new UnrecoverableKeyException(
"Password verification failed");
logger.error(
"Keystore was tampered with, or password was incorrect.",
t);
throw (IOException) new IOException(
"Keystore was tampered with, or "
+ "password was incorrect")
.initCause(t);
} else {
counter++;
}
}
}
if (password != null) {
dis = new DataInputStream(new DigestInputStream(stream,
md));
} else {
dis = new DataInputStream(stream);
}
ObjectInputStream ois = null;
try {
String alias;
SecretKeyEntry entry = new SecretKeyEntry();
// read the alias
alias = rangerKey.getAlias();
// read the (entry creation) date
entry.date = new Date(rangerKey.getCreatedDate());
entry.cipher_field = rangerKey.getCipher();
entry.bit_length = rangerKey.getBitLength();
entry.description = rangerKey.getDescription();
entry.version = rangerKey.getVersion();
entry.attributes = rangerKey.getAttributes();
// read the sealed key
try {
ois = new ObjectInputStream(dis);
entry.sealedKey = (SealedObject) ois.readObject();
} catch (ClassNotFoundException cnfe) {
throw new IOException(cnfe.getMessage());
}
// Add the entry to the list
keyEntries.put(alias, entry);
} finally {
if (ois != null) {
ois.close();
} else {
dis.close();
}
}
}
}
}
}
private List<XXRangerKeyStore> dbOperationLoad() throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStore.dbOperationLoad()");
}
try {
if (daoManager != null) {
RangerKMSDao rangerKMSDao = new RangerKMSDao(daoManager);
if (logger.isDebugEnabled()) {
logger.debug("<== RangerKeyStore.dbOperationLoad()");
}
return rangerKMSDao.getAllKeys();
}
} catch (Exception e) {
logger.error("==> RangerKeyStore.dbOperationLoad() error:", e);
}
if (logger.isDebugEnabled()) {
logger.debug("<== RangerKeyStore.dbOperationLoad()");
}
return null;
}
/**
* To guard against tampering with the keystore, we append a keyed
* hash with a bit of whitener.
*/
private final String SECRET_KEY_HASH_WORD = "Apache Ranger";
private MessageDigest getKeyedMessageDigest(char[] aKeyPassword)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
int i, j;
MessageDigest md = MessageDigest.getInstance("SHA");
byte[] keyPasswordBytes = new byte[aKeyPassword.length * 2];
for (i = 0, j = 0; i < aKeyPassword.length; i++) {
keyPasswordBytes[j++] = (byte) (aKeyPassword[i] >> 8);
keyPasswordBytes[j++] = (byte) aKeyPassword[i];
}
md.update(keyPasswordBytes);
for (i = 0; i < keyPasswordBytes.length; i++)
keyPasswordBytes[i] = 0;
md.update(SECRET_KEY_HASH_WORD.getBytes("UTF8"));
return md;
}
@Override
public void engineSetKeyEntry(String arg0, byte[] arg1, Certificate[] arg2)
throws KeyStoreException {
}
@Override
public Certificate engineGetCertificate(String alias) {
return null;
}
@Override
public String engineGetCertificateAlias(Certificate cert) {
return null;
}
@Override
public Certificate[] engineGetCertificateChain(String alias) {
return null;
}
@Override
public boolean engineIsCertificateEntry(String alias) {
return false;
}
@Override
public boolean engineIsKeyEntry(String alias) {
return false;
}
@Override
public void engineSetCertificateEntry(String alias, Certificate cert)
throws KeyStoreException {
}
@Override
public void engineSetKeyEntry(String alias, Key key, char[] password,
Certificate[] chain) throws KeyStoreException {
}
//
// The method is created to support JKS migration (from hadoop-common KMS keystore to RangerKMS keystore)
//
private static final String METADATA_FIELDNAME = "metadata";
private static final int NUMBER_OF_BITS_PER_BYTE = 8;
public void engineLoadKeyStoreFile(InputStream stream, char[] storePass,
char[] keyPass, char[] masterKey, String fileFormat)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStoreProvider.engineLoadKeyStoreFile()");
}
synchronized (deltaEntries) {
KeyStore ks;
if (azureKeyVaultEnabled) {
try {
ks = KeyStore.getInstance(fileFormat);
ks.load(stream, storePass);
deltaEntries.clear();
for (Enumeration<String> name = ks.aliases(); name
.hasMoreElements();) {
SecretKeyByteEntry entry = new SecretKeyByteEntry();
String alias = (String) name.nextElement();
Key k = ks.getKey(alias, keyPass);
SecretKey secretKey = null;
if (k instanceof JavaKeyStoreProvider.KeyMetadata) {
JavaKeyStoreProvider.KeyMetadata keyMetadata = (JavaKeyStoreProvider.KeyMetadata) k;
Field f = JavaKeyStoreProvider.KeyMetadata.class
.getDeclaredField(METADATA_FIELDNAME);
f.setAccessible(true);
Metadata metadata = (Metadata) f.get(keyMetadata);
entry.bit_length = metadata.getBitLength();
entry.cipher_field = metadata.getAlgorithm();
entry.version = metadata.getVersions();
Constructor<RangerKeyStoreProvider.KeyMetadata> constructor = RangerKeyStoreProvider.KeyMetadata.class
.getDeclaredConstructor(Metadata.class);
constructor.setAccessible(true);
RangerKeyStoreProvider.KeyMetadata nk = constructor
.newInstance(metadata);
k = nk;
secretKey = new SecretKeySpec(k.getEncoded(),
getAlgorithm(metadata.getAlgorithm()));
} else if (k instanceof KeyByteMetadata) {
Metadata metadata = ((KeyByteMetadata) k).metadata;
entry.cipher_field = metadata.getCipher();
entry.version = metadata.getVersions();
entry.bit_length = metadata.getBitLength();
if (k.getEncoded() != null && k.getEncoded().length > 0) {
secretKey = new SecretKeySpec(k.getEncoded(),
getAlgorithm(metadata.getAlgorithm()));
} else {
KeyGenerator keyGenerator = KeyGenerator
.getInstance(getAlgorithm(metadata.getCipher()));
keyGenerator.init(metadata.getBitLength());
byte[] keyByte = keyGenerator.generateKey().getEncoded();
secretKey = new SecretKeySpec(keyByte,
getAlgorithm(metadata.getCipher()));
}
} else if (k instanceof KeyMetadata) {
Metadata metadata = ((KeyMetadata) k).metadata;
entry.bit_length = metadata.getBitLength();
entry.cipher_field = metadata.getCipher();
entry.version = metadata.getVersions();
if (k.getEncoded() != null
&& k.getEncoded().length > 0) {
secretKey = new SecretKeySpec(k.getEncoded(),
getAlgorithm(metadata.getAlgorithm()));
} else {
KeyGenerator keyGenerator = KeyGenerator
.getInstance(getAlgorithm(metadata
.getCipher()));
keyGenerator.init(metadata.getBitLength());
byte[] keyByte = keyGenerator.generateKey()
.getEncoded();
secretKey = new SecretKeySpec(keyByte,
getAlgorithm(metadata.getCipher()));
}
}else {
entry.bit_length = (k.getEncoded().length * NUMBER_OF_BITS_PER_BYTE);
entry.cipher_field = k.getAlgorithm();
if (alias.split("@").length == 2) {
entry.version = Integer.parseInt(alias
.split("@")[1]) + 1;
} else {
entry.version = 1;
}
if(k.getEncoded() != null && k.getEncoded().length > 0){
secretKey = new SecretKeySpec(k.getEncoded(),
getAlgorithm(k.getAlgorithm()));
}
}
String keyName = alias.split("@")[0];
validateKeyName(keyName);
entry.attributes = "{\"key.acl.name\":\"" + keyName
+ "\"}";
entry.key = kvKeyGen.encryptZoneKey(secretKey);
entry.date = ks.getCreationDate(alias);
entry.description = k.getFormat() + " - "
+ ks.getType();
deltaEntries.put(alias, entry);
}
} catch (Throwable t) {
logger.error("Unable to load keystore file ", t);
throw new IOException(t);
}
} else {
try {
ks = KeyStore.getInstance(fileFormat);
ks.load(stream, storePass);
deltaEntries.clear();
for (Enumeration<String> name = ks.aliases(); name
.hasMoreElements();) {
SecretKeyEntry entry = new SecretKeyEntry();
String alias = (String) name.nextElement();
Key k = ks.getKey(alias, keyPass);
if (k instanceof JavaKeyStoreProvider.KeyMetadata) {
JavaKeyStoreProvider.KeyMetadata keyMetadata = (JavaKeyStoreProvider.KeyMetadata) k;
Field f = JavaKeyStoreProvider.KeyMetadata.class
.getDeclaredField(METADATA_FIELDNAME);
f.setAccessible(true);
Metadata metadata = (Metadata) f.get(keyMetadata);
entry.bit_length = metadata.getBitLength();
entry.cipher_field = metadata.getAlgorithm();
entry.version = metadata.getVersions();
Constructor<RangerKeyStoreProvider.KeyMetadata> constructor = RangerKeyStoreProvider.KeyMetadata.class
.getDeclaredConstructor(Metadata.class);
constructor.setAccessible(true);
RangerKeyStoreProvider.KeyMetadata nk = constructor
.newInstance(metadata);
k = nk;
} else if (k instanceof KeyMetadata) {
Metadata metadata = ((KeyMetadata) k).metadata;
entry.bit_length = metadata.getBitLength();
entry.cipher_field = metadata.getCipher();
entry.version = metadata.getVersions();
} else {
entry.bit_length = (k.getEncoded().length * NUMBER_OF_BITS_PER_BYTE);
entry.cipher_field = k.getAlgorithm();
entry.version = (alias.split("@").length == 2) ? (Integer
.parseInt(alias.split("@")[1]) + 1) : 1;
}
String keyName = alias.split("@")[0];
validateKeyName(keyName);
entry.attributes = "{\"key.acl.name\":\"" + keyName
+ "\"}";
Class<?> c = null;
Object o = null;
try {
c = Class
.forName("com.sun.crypto.provider.KeyProtector");
Constructor<?> constructor = c
.getDeclaredConstructor(char[].class);
constructor.setAccessible(true);
o = constructor.newInstance(masterKey);
// seal and store the key
Method m = c.getDeclaredMethod("seal", Key.class);
m.setAccessible(true);
entry.sealedKey = (SealedObject) m.invoke(o, k);
} catch (ClassNotFoundException | NoSuchMethodException
| SecurityException | InstantiationException
| IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
logger.error(e.getMessage());
throw new IOException(e.getMessage());
}
entry.date = ks.getCreationDate(alias);
entry.description = k.getFormat() + " - "
+ ks.getType();
deltaEntries.put(alias, entry);
}
} catch (Throwable t) {
logger.error("Unable to load keystore file ", t);
throw new IOException(t);
}
}
}
}
public void engineLoadToKeyStoreFile(OutputStream stream, char[] storePass,
char[] keyPass, char[] masterKey, String fileFormat)
throws IOException, NoSuchAlgorithmException, CertificateException {
if (logger.isDebugEnabled()) {
logger.debug("==> RangerKeyStoreProvider.engineLoadToKeyStoreFile()");
}
synchronized (keyEntries) {
KeyStore ks;
try {
ks = KeyStore.getInstance(fileFormat);
if (ks != null) {
ks.load(null, storePass);
String alias = null;
engineLoad(null, masterKey);
Enumeration<String> e = engineAliases();
Key key;
while (e.hasMoreElements()) {
alias = e.nextElement();
if(azureKeyVaultEnabled){
key = engineGetDecryptedZoneKey(alias);
} else {
key = engineGetKey(alias, masterKey);
if (key instanceof KeyMetadata) {
Metadata meta = ((KeyMetadata) key).metadata;
if (meta != null) {
key = new KeyMetadata(meta);
}
}
}
ks.setKeyEntry(alias, key, keyPass, null);
}
ks.store(stream, storePass);
}
} catch (Throwable t) {
logger.error("Unable to load keystore file ", t);
throw new IOException(t);
}
}
}
private void validateKeyName(String name) {
Matcher matcher = pattern.matcher(name);
if (!matcher.matches()) {
throw new IllegalArgumentException(
"Key Name : "
+ name
+ ", should start with alpha/numeric letters and can have special characters - (hypen) or _ (underscore)");
}
}
public void clearDeltaEntires() {
deltaEntries.clear();
}
private Object getKeyEntry(String alias) {
return keyEntries.get(alias);
}
public XXRangerKeyStore convertKeysBetweenRangerKMSAndAzureKeyVault(
String alias, Key key,
RangerKeyVaultKeyGenerator rangerKVKeyGenerator) {
try {
XXRangerKeyStore xxRangerKeyStore;
SecretKeyEntry secretKey = (SecretKeyEntry) getKeyEntry(alias);
if (key instanceof KeyMetadata) {
Metadata meta = ((KeyMetadata) key).metadata;
KeyGenerator keyGenerator = KeyGenerator
.getInstance(getAlgorithm(meta.getCipher()));
keyGenerator.init(meta.getBitLength());
byte[] keyByte = keyGenerator.generateKey().getEncoded();
Key ezkey = new SecretKeySpec(keyByte,
getAlgorithm(meta.getCipher()));
byte[] encryptedKey = rangerKVKeyGenerator
.encryptZoneKey(ezkey);
Long creationDate = new Date().getTime();
String attributes = secretKey.attributes;
xxRangerKeyStore = mapObjectToEntity(alias, creationDate,
encryptedKey, meta.getCipher(), meta.getBitLength(),
meta.getDescription(), meta.getVersions(),
attributes);
} else {
byte[] encryptedKey = rangerKVKeyGenerator.encryptZoneKey(key);
Long creationDate = secretKey.date.getTime();
int version = secretKey.version;
if ((alias.split("@").length == 2)
&& (((Integer.parseInt(alias.split("@")[1])) + 1) != secretKey.version)) {
version++;
}
xxRangerKeyStore = mapObjectToEntity(alias, creationDate,
encryptedKey, secretKey.cipher_field,
secretKey.bit_length, secretKey.description, version,
secretKey.attributes);
}
return xxRangerKeyStore;
} catch (Throwable t) {
throw new RuntimeException(
"Migration failed between key secure and Ranger DB : ", t);
}
}
public String getAlgorithm(String cipher) {
int slash = cipher.indexOf(47);
if (slash == -1) {
return cipher;
}
return cipher.substring(0, slash);
}
/**
* Encapsulate the encrypted key, so that we can retrieve the AlgorithmParameters object on the decryption side
*/
private static class RangerSealedObject extends SealedObject {
/**
*
*/
private static final long serialVersionUID = -7551578543434362070L;
protected RangerSealedObject(SealedObject so) {
super(so);
}
protected RangerSealedObject(Serializable object, Cipher cipher) throws IllegalBlockSizeException, IOException {
super(object, cipher);
}
public AlgorithmParameters getParameters() throws NoSuchAlgorithmException, IOException {
AlgorithmParameters algorithmParameters = AlgorithmParameters.getInstance("PBEWithMD5AndTripleDES");
algorithmParameters.init(super.encodedParams);
return algorithmParameters;
}
}
public static class KeyByteMetadata implements Key, Serializable {
private Metadata metadata;
private byte[] keyByte;
private final static long serialVersionUID = 8405872419967874451L;
private KeyByteMetadata(Metadata meta, byte[] encoded) {
this.metadata = meta;
this.keyByte = encoded;
}
@Override
public String getAlgorithm() {
return metadata.getCipher();
}
@Override
public String getFormat() {
return KEY_METADATA;
}
@Override
public byte[] getEncoded() {
return this.keyByte;
}
private void writeObject(ObjectOutputStream out) throws IOException {
byte[] serialized = metadata.serialize();
out.writeInt(serialized.length);
out.write(serialized);
out.writeInt(keyByte.length);
out.write(keyByte);
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
byte[] metadataBuf = new byte[in.readInt()];
in.readFully(metadataBuf);
metadata = new Metadata(metadataBuf);
byte[] keybyteBuf = new byte[in.readInt()];
in.readFully(keybyteBuf);
keyByte = keybyteBuf;
}
}
}