blob: 5614c1640b74393c646c4e0445b4c47136b37aa9 [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 java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.log4j.Logger;
import org.apache.ranger.kms.dao.DaoManager;
import org.apache.ranger.kms.dao.RangerMasterKeyDao;
import org.apache.ranger.entity.XXRangerMasterKey;
import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException;
import com.sun.org.apache.xml.internal.security.utils.Base64;
public class RangerMasterKey implements RangerKMSMKI{
private static final Logger logger = Logger.getLogger(RangerMasterKey.class);
private static final String MK_CIPHER = "AES";
private static final int MK_KeySize = 256;
private static final int SALT_SIZE = 8;
private static final String PBE_ALGO = "PBEWithMD5AndTripleDES";
private static final String MD_ALGO = "MD5";
private DaoManager daoManager;
public RangerMasterKey() {
}
public RangerMasterKey(DaoManager daoManager) {
this.daoManager = daoManager;
}
/**
* To get Master Key
* @param password password to be used for decryption
* @return Decrypted Master Key
* @throws Throwable
*/
@Override
public String getMasterKey(String password) throws Throwable{
logger.info("Getting Master Key");
byte masterKeyByte[] = getEncryptedMK();
if(masterKeyByte != null && masterKeyByte.length > 0){
return decryptMasterKey(masterKeyByte, password);
}else{
throw new Exception("No Master Key Found");
}
}
public SecretKey getMasterSecretKey(String password) throws Throwable{
logger.info("Getting Master Key");
byte masterKeyByte[] = getEncryptedMK();
if(masterKeyByte != null && masterKeyByte.length > 0){
return decryptMasterKeySK(masterKeyByte, password);
}else{
throw new Exception("No Master Key Found");
}
}
/**
* Generate the master key, encrypt it and save it in the database
* @param password password to be used for encryption
* @return true if the master key was successfully created
* false if master key generation was unsuccessful or the master key already exists
* @throws Throwable
*/
@Override
public boolean generateMasterKey(String password) throws Throwable{
logger.info("Generating Master Key");
String encryptedMasterKey = encryptMasterKey(password);
String savedKey = saveEncryptedMK(encryptedMasterKey, daoManager);
if(savedKey != null && !savedKey.trim().equals("")){
logger.debug("Master Key Created with id = "+savedKey);
return true;
}
return false;
}
public boolean generateMKFromHSMMK(String password, byte[] key) throws Throwable{
logger.info("Generating Master Key");
String encryptedMasterKey = encryptMasterKey(password, key);
String savedKey = saveEncryptedMK(encryptedMasterKey, daoManager);
if(savedKey != null && !savedKey.trim().equals("")){
logger.debug("Master Key Created with id = "+savedKey);
return true;
}
return false;
}
private String decryptMasterKey(byte masterKey[], String password) throws Throwable {
logger.debug("Decrypting Master Key");
PBEKeySpec pbeKeyspec = getPBEParameterSpec(password);
byte[] masterKeyFromDBDecrypted = decryptKey(masterKey, pbeKeyspec);
SecretKey masterKeyFromDB = getMasterKeyFromBytes(masterKeyFromDBDecrypted);
return Base64.encode(masterKeyFromDB.getEncoded());
}
private SecretKey decryptMasterKeySK(byte masterKey[], String password) throws Throwable {
logger.debug("Decrypting Master Key");
PBEKeySpec pbeKeyspec = getPBEParameterSpec(password);
byte[] masterKeyFromDBDecrypted = decryptKey(masterKey, pbeKeyspec);
return getMasterKeyFromBytes(masterKeyFromDBDecrypted);
}
private byte[] getEncryptedMK() throws Base64DecodingException {
logger.debug("Retrieving Encrypted Master Key from database");
try{
if(daoManager != null){
RangerMasterKeyDao rangerKMSDao = new RangerMasterKeyDao(daoManager);
List<XXRangerMasterKey> lstRangerMasterKey = rangerKMSDao.getAll();
if(lstRangerMasterKey.size() < 1){
throw new Exception("No Master Key exists");
}else if(lstRangerMasterKey.size() > 1){
throw new Exception("More than one Master Key exists");
}else {
XXRangerMasterKey rangerMasterKey = rangerKMSDao.getById(lstRangerMasterKey.get(0).getId());
String masterKeyStr = rangerMasterKey.getMasterKey();
return Base64.decode(masterKeyStr);
}
}
}catch(Exception e){
e.printStackTrace();
}
return null;
}
private String saveEncryptedMK(String encryptedMasterKey, DaoManager daoManager) {
logger.debug("Saving Encrypted Master Key to database");
XXRangerMasterKey xxRangerMasterKey = new XXRangerMasterKey();
xxRangerMasterKey.setCipher(MK_CIPHER);
xxRangerMasterKey.setBitLength(MK_KeySize);
xxRangerMasterKey.setMasterKey(encryptedMasterKey);
try{
if(daoManager != null){
RangerMasterKeyDao rangerKMSDao = new RangerMasterKeyDao(daoManager);
Long l = rangerKMSDao.getAllCount();
if(l < 1){
XXRangerMasterKey rangerMasterKey = rangerKMSDao.create(xxRangerMasterKey);
return rangerMasterKey.getId().toString();
}
}
}catch(Exception e){
e.printStackTrace();
}
return null;
}
private String encryptMasterKey(String password) throws Throwable {
logger.debug("Encrypting Master Key");
Key secretKey = generateMasterKey();
PBEKeySpec pbeKeySpec = getPBEParameterSpec(password);
byte[] masterKeyToDB = encryptKey(secretKey.getEncoded(), pbeKeySpec);
return Base64.encode(masterKeyToDB);
}
private String encryptMasterKey(String password, byte[] secretKey) throws Throwable {
logger.debug("Encrypting Master Key");
PBEKeySpec pbeKeySpec = getPBEParameterSpec(password);
byte[] masterKeyToDB = encryptKey(secretKey, pbeKeySpec);
return Base64.encode(masterKeyToDB);
}
private Key generateMasterKey() throws NoSuchAlgorithmException{
KeyGenerator kg = KeyGenerator.getInstance(MK_CIPHER);
kg.init(MK_KeySize);
return kg.generateKey();
}
private PBEKeySpec getPBEParameterSpec(String password) throws Throwable {
MessageDigest md = MessageDigest.getInstance(MD_ALGO);
byte[] saltGen = md.digest(password.getBytes());
byte[] salt = new byte[SALT_SIZE];
System.arraycopy(saltGen, 0, salt, 0, SALT_SIZE);
int iteration = password.toCharArray().length + 1;
return new PBEKeySpec(password.toCharArray(), salt, iteration);
}
private byte[] encryptKey(byte[] data, PBEKeySpec keyspec) throws Throwable {
SecretKey key = getPasswordKey(keyspec);
if(keyspec.getSalt() != null) {
PBEParameterSpec paramSpec = new PBEParameterSpec(keyspec.getSalt(), keyspec.getIterationCount());
Cipher c = Cipher.getInstance(key.getAlgorithm());
c.init(Cipher.ENCRYPT_MODE, key,paramSpec);
return c.doFinal(data);
}
return null;
}
private SecretKey getPasswordKey(PBEKeySpec keyspec) throws Throwable {
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBE_ALGO);
return factory.generateSecret(keyspec);
}
private byte[] decryptKey(byte[] encrypted, PBEKeySpec keyspec) throws Throwable {
SecretKey key = getPasswordKey(keyspec);
if(keyspec.getSalt() != null) {
PBEParameterSpec paramSpec = new PBEParameterSpec(keyspec.getSalt(), keyspec.getIterationCount());
Cipher c = Cipher.getInstance(key.getAlgorithm());
c.init(Cipher.DECRYPT_MODE, key, paramSpec);
return c.doFinal(encrypted);
}
return null;
}
private SecretKey getMasterKeyFromBytes(byte[] keyData) throws Throwable {
return new SecretKeySpec(keyData, MK_CIPHER);
}
public Map<String, String> getPropertiesWithPrefix(Properties props, String prefix) {
Map<String, String> prefixedProperties = new HashMap<String, String>();
if(props != null && prefix != null) {
for(String key : props.stringPropertyNames()) {
if(key == null) {
continue;
}
String val = props.getProperty(key);
if(key.startsWith(prefix)) {
key = key.substring(prefix.length());
if(key != null) {
prefixedProperties.put(key, val);
}
}
}
}
return prefixedProperties;
}
}