| /* |
| * 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; |
| } |
| } |