blob: c08f55d6e57ebb04cb57473ce26644fe9048d21c [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.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;
}
}