| /* |
| * 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.ranger.plugin.util; |
| |
| import java.io.IOException; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.SecureRandom; |
| import java.util.Map; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.SecretKey; |
| import javax.crypto.SecretKeyFactory; |
| import javax.crypto.spec.IvParameterSpec; |
| import javax.crypto.spec.PBEKeySpec; |
| import javax.crypto.spec.PBEParameterSpec; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import com.google.common.base.Splitter; |
| import com.google.common.base.Strings; |
| import com.google.common.collect.Lists; |
| import com.sun.jersey.core.util.Base64; |
| public class PasswordUtils { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class); |
| |
| private final String cryptAlgo; |
| private String password; |
| private final int iterationCount; |
| private final char[] encryptKey; |
| private final byte[] salt; |
| private final byte[] iv; |
| private static final String LEN_SEPARATOR_STR = ":"; |
| |
| public static final String PBE_SHA512_AES_128 = "PBEWITHHMACSHA512ANDAES_128"; |
| |
| public static final String DEFAULT_CRYPT_ALGO = "PBEWithMD5AndDES"; |
| public static final String DEFAULT_ENCRYPT_KEY = "tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV"; |
| public static final String DEFAULT_SALT = "f77aLYLo"; |
| public static final int DEFAULT_ITERATION_COUNT = 17; |
| public static final byte[] DEFAULT_INITIAL_VECTOR = new byte[16]; |
| |
| public static String encryptPassword(String aPassword) throws IOException { |
| return build(aPassword).encrypt(); |
| } |
| |
| public static PasswordUtils build(String aPassword) { |
| return new PasswordUtils(aPassword); |
| } |
| |
| private String encrypt() throws IOException { |
| String ret = null; |
| String strToEncrypt = null; |
| if (password == null) { |
| strToEncrypt = ""; |
| } else { |
| strToEncrypt = password.length() + LEN_SEPARATOR_STR + password; |
| } |
| try { |
| Cipher engine = Cipher.getInstance(cryptAlgo); |
| PBEKeySpec keySpec = new PBEKeySpec(encryptKey); |
| SecretKeyFactory skf = SecretKeyFactory.getInstance(cryptAlgo); |
| SecretKey key = skf.generateSecret(keySpec); |
| engine.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, iterationCount, new IvParameterSpec(iv))); |
| byte[] encryptedStr = engine.doFinal(strToEncrypt.getBytes()); |
| ret = new String(Base64.encode(encryptedStr)); |
| } |
| catch(Throwable t) { |
| LOG.error("Unable to encrypt password due to error", t); |
| throw new IOException("Unable to encrypt password due to error", t); |
| } |
| return ret; |
| } |
| |
| PasswordUtils(String aPassword) { |
| String[] crypt_algo_array = null; |
| byte[] SALT; |
| char[] ENCRYPT_KEY; |
| if (aPassword != null && aPassword.contains(",")) { |
| crypt_algo_array = Lists.newArrayList(Splitter.on(",").split(aPassword)).toArray(new String[0]); |
| } |
| if (crypt_algo_array != null && crypt_algo_array.length > 4) { |
| int index = 0; |
| cryptAlgo = crypt_algo_array[index++]; // 0 |
| ENCRYPT_KEY = crypt_algo_array[index++].toCharArray(); // 1 |
| SALT = crypt_algo_array[index++].getBytes(); // 2 |
| iterationCount = Integer.parseInt(crypt_algo_array[index++]);// 3 |
| if (needsIv(cryptAlgo)) { |
| iv = Base64.decode(crypt_algo_array[index++]); |
| } else { |
| iv = DEFAULT_INITIAL_VECTOR; |
| } |
| password = crypt_algo_array[index++]; |
| if (crypt_algo_array.length > index) { |
| for (int i = index; i < crypt_algo_array.length; i++) { |
| password = password + "," + crypt_algo_array[i]; |
| } |
| } |
| } else { |
| cryptAlgo = DEFAULT_CRYPT_ALGO; |
| ENCRYPT_KEY = DEFAULT_ENCRYPT_KEY.toCharArray(); |
| SALT = DEFAULT_SALT.getBytes(); |
| iterationCount = DEFAULT_ITERATION_COUNT; |
| iv = DEFAULT_INITIAL_VECTOR; |
| password = aPassword; |
| } |
| Map<String, String> env = System.getenv(); |
| String encryptKeyStr = env.get("ENCRYPT_KEY"); |
| if (encryptKeyStr == null) { |
| encryptKey=ENCRYPT_KEY; |
| }else{ |
| encryptKey=encryptKeyStr.toCharArray(); |
| } |
| String saltStr = env.get("ENCRYPT_SALT"); |
| if (saltStr == null) { |
| salt = SALT; |
| }else{ |
| salt=saltStr.getBytes(); |
| } |
| } |
| |
| public static String decryptPassword(String aPassword) throws IOException { |
| return build(aPassword).decrypt(); |
| } |
| |
| private String decrypt() throws IOException { |
| String ret = null; |
| try { |
| byte[] decodedPassword = Base64.decode(password); |
| Cipher engine = Cipher.getInstance(cryptAlgo); |
| PBEKeySpec keySpec = new PBEKeySpec(encryptKey); |
| SecretKeyFactory skf = SecretKeyFactory.getInstance(cryptAlgo); |
| SecretKey key = skf.generateSecret(keySpec); |
| engine.init(Cipher.DECRYPT_MODE, key,new PBEParameterSpec(salt, iterationCount, new IvParameterSpec(iv))); |
| String decrypted = new String(engine.doFinal(decodedPassword)); |
| int foundAt = decrypted.indexOf(LEN_SEPARATOR_STR); |
| if (foundAt > -1) { |
| if (decrypted.length() > foundAt) { |
| ret = decrypted.substring(foundAt+1); |
| } |
| else { |
| ret = ""; |
| } |
| } |
| else { |
| ret = null; |
| } |
| } |
| catch(Throwable t) { |
| LOG.error("Unable to decrypt password due to error", t); |
| throw new IOException("Unable to decrypt password due to error", t); |
| } |
| return ret; |
| } |
| |
| public static boolean needsIv(String cryptoAlgo) { |
| if (Strings.isNullOrEmpty(cryptoAlgo)) |
| return false; |
| |
| return PBE_SHA512_AES_128.toLowerCase().equals(cryptoAlgo.toLowerCase()) |
| || cryptoAlgo.toLowerCase().contains("aes_128") || cryptoAlgo.toLowerCase().contains("aes_256"); |
| } |
| |
| public static String generateIvIfNeeded(String cryptAlgo) throws NoSuchAlgorithmException { |
| if (!needsIv(cryptAlgo)) |
| return null; |
| return generateBase64EncodedIV(); |
| } |
| |
| private static String generateBase64EncodedIV() throws NoSuchAlgorithmException { |
| byte[] iv = new byte[16]; |
| SecureRandom.getInstanceStrong().nextBytes(iv); |
| return new String(Base64.encode(iv)); |
| } |
| |
| public String getCryptAlgo() { |
| return cryptAlgo; |
| } |
| |
| public String getPassword() { |
| return password; |
| } |
| |
| public int getIterationCount() { |
| return iterationCount; |
| } |
| |
| public char[] getEncryptKey() { |
| return encryptKey; |
| } |
| |
| public byte[] getSalt() { |
| return salt; |
| } |
| |
| public byte[] getIv() { |
| return iv; |
| } |
| |
| public String getIvAsString() { |
| return new String(Base64.encode(getIv())); |
| } |
| public static String getDecryptPassword(String password) { |
| String decryptedPwd = null; |
| try { |
| decryptedPwd = decryptPassword(password); |
| } catch (Exception ex) { |
| LOG.warn("Password decryption failed, trying original password string."); |
| decryptedPwd = null; |
| } finally { |
| if (decryptedPwd == null) { |
| decryptedPwd = password; |
| } |
| } |
| return decryptedPwd; |
| } |
| } |