Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,29 @@
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 java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.apache.ranger.plugin.util.RangerSupportedCryptoAlgo.PBEWITHHMACSHA512ANDAES_128;

public class PasswordUtils {
private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class);

public static final String PBE_SHA512_AES_128 = "PBEWITHHMACSHA512ANDAES_128";
public static final String DEFAULT_CRYPT_ALGO = "PBEWithMD5AndDES";
public static final RangerSupportedCryptoAlgo DEFAULT_CRYPT_ALGO = RangerSupportedCryptoAlgo.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];
private static final String LEN_SEPARATOR_STR = ":";

private final String cryptAlgo;
private final RangerSupportedCryptoAlgo cryptAlgo;
private final int iterationCount;
private final char[] encryptKey;
private final byte[] salt;
Expand All @@ -67,12 +67,12 @@ public class PasswordUtils {
if (cryptAlgoArray != null && cryptAlgoArray.length > 4) {
int index = 0;

cryptAlgo = cryptAlgoArray[index++]; // 0
cryptAlgo = RangerSupportedCryptoAlgo.getValueOf(cryptAlgoArray[index++]); // 0
lEncryptKey = cryptAlgoArray[index++].toCharArray(); // 1
lSalt = cryptAlgoArray[index++].getBytes(); // 2
iterationCount = Integer.parseInt(cryptAlgoArray[index++]); // 3

if (needsIv(cryptAlgo)) {
if (needsIv(cryptAlgo.getAlgoName())) {
iv = Base64.decode(cryptAlgoArray[index++]);
} else {
iv = DEFAULT_INITIAL_VECTOR;
Expand Down Expand Up @@ -129,8 +129,8 @@ public static boolean needsIv(String cryptoAlgo) {
return false;
}

return PBE_SHA512_AES_128.equalsIgnoreCase(cryptoAlgo)
|| cryptoAlgo.toLowerCase().contains("aes_128") || cryptoAlgo.toLowerCase().contains("aes_256");
return PBEWITHHMACSHA512ANDAES_128.getAlgoName().equalsIgnoreCase(cryptoAlgo)
|| cryptoAlgo.toLowerCase().contains("aes_128") || cryptoAlgo.toLowerCase().contains("aes_256") || RangerSupportedCryptoAlgo.PBKDF2WITHHMACSHA256.getAlgoName().equalsIgnoreCase(cryptoAlgo);
}

public static String generateIvIfNeeded(String cryptAlgo) throws NoSuchAlgorithmException {
Expand Down Expand Up @@ -158,7 +158,7 @@ public static String getDecryptPassword(String password) {
}

public String getCryptAlgo() {
return cryptAlgo;
return cryptAlgo.getAlgoName();
}

public String getPassword() {
Expand Down Expand Up @@ -196,12 +196,12 @@ private String encrypt() throws IOException {
}

try {
Cipher engine = Cipher.getInstance(cryptAlgo);
PBEKeySpec keySpec = new PBEKeySpec(encryptKey);
SecretKeyFactory skf = SecretKeyFactory.getInstance(cryptAlgo);
Cipher engine = Cipher.getInstance(this.cryptAlgo.getCipherTransformation());
PBEKeySpec keySpec = getPBEParameterSpec(encryptKey, cryptAlgo);
SecretKeyFactory skf = SecretKeyFactory.getInstance(cryptAlgo.getAlgoName());
SecretKey key = skf.generateSecret(keySpec);

engine.init(Cipher.ENCRYPT_MODE, key, new PBEParameterSpec(salt, iterationCount, new IvParameterSpec(iv)));
engine.init(Cipher.ENCRYPT_MODE, key, cryptAlgo.getAlgoParamSpec(new PBEParams(keySpec.getSalt() != null ? keySpec.getSalt() : this.salt, this.iterationCount, iv)));

byte[] encryptedStr = engine.doFinal(strToEncrypt.getBytes());

Expand All @@ -220,12 +220,12 @@ private String decrypt() throws IOException {

try {
byte[] decodedPassword = Base64.decode(password);
Cipher engine = Cipher.getInstance(cryptAlgo);
PBEKeySpec keySpec = new PBEKeySpec(encryptKey);
SecretKeyFactory skf = SecretKeyFactory.getInstance(cryptAlgo);
Cipher engine = Cipher.getInstance(cryptAlgo.getCipherTransformation());
PBEKeySpec keySpec = getPBEParameterSpec(encryptKey, cryptAlgo);
SecretKeyFactory skf = SecretKeyFactory.getInstance(cryptAlgo.getAlgoName());
SecretKey key = skf.generateSecret(keySpec);

engine.init(Cipher.DECRYPT_MODE, key, new PBEParameterSpec(salt, iterationCount, new IvParameterSpec(iv)));
engine.init(Cipher.DECRYPT_MODE, key, cryptAlgo.getAlgoParamSpec(new PBEParams(keySpec.getSalt() != null ? keySpec.getSalt() : this.salt, this.iterationCount, iv)));

String decrypted = new String(engine.doFinal(decodedPassword));
int foundAt = decrypted.indexOf(LEN_SEPARATOR_STR);
Expand Down Expand Up @@ -374,4 +374,79 @@ public PasswordGenerator build() {
}
}
}

private PBEKeySpec getPBEParameterSpec(char[] password, RangerSupportedCryptoAlgo encrAlgo) throws Throwable {
PBEKeySpec pbeKeySpec;
if (RangerSupportedCryptoAlgo.isFIPSCompliantAlgorithm(encrAlgo)) {
pbeKeySpec = new PBEKeySpec(getCompliantPassword(String.copyValueOf(password), encrAlgo).toCharArray(), generateSalt(calculateCompliantSaltSize(salt.length, encrAlgo)), iterationCount, encrAlgo.getKeyLength());
} else {
pbeKeySpec = new PBEKeySpec(password);
}
return pbeKeySpec;
}

// For FIPS, salt size must be at least 128 bits, that is, at least 16 in length.
private static int calculateCompliantSaltSize(int saltSize, RangerSupportedCryptoAlgo encrAlgo) {
int compliantSaltSize = saltSize;
if (encrAlgo.getMinSaltSize().isPresent()) {
int minSaltSize = encrAlgo.getMinSaltSize().get();
while (compliantSaltSize < minSaltSize) {
compliantSaltSize = compliantSaltSize * 2;
}
}

return compliantSaltSize;
}

/*
For FIPS, salt size must be at least 128 bits, that is, at least 16 in length.
*/
private byte[] generateSalt(int saltSize) throws Throwable {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] saltGen = md.digest(salt);
byte[] salt = new byte[saltSize];
System.arraycopy(saltGen, 0, salt, 0, this.salt.length);
return salt;
}

/*
For FIPS Algo, InApprovedOnlyMode requires password to be at least 112 bits, that is minimum length should be 14
If provided password is less than 14, this method appends the same password till it reaches the minimum length of 14.
And it is for FIPS only.
*/
private String getCompliantPassword(String password, RangerSupportedCryptoAlgo encrAlgo) {
String newPwd = password;

if (encrAlgo.getMinPwdLength().isPresent()) {
int requiredPwdLength = encrAlgo.getMinPwdLength().get();
while (newPwd.length() < requiredPwdLength) {
newPwd = newPwd.concat(password);
}
}
return newPwd;
}

public static class PBEParams {
private byte[] salt;
private int iterationCount;
private byte[] iv;

public PBEParams(byte[] salt, int iterationCount, byte[] iv) {
this.salt = salt;
this.iterationCount = iterationCount;
this.iv = iv;
}

public byte[] getSalt() {
return salt;
}

public int getIterationCount() {
return iterationCount;
}

public byte[] getIv() {
return iv;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
* 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 javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEParameterSpec;

import java.security.spec.AlgorithmParameterSpec;
import java.util.Optional;
import java.util.function.Function;

public enum RangerSupportedCryptoAlgo {
PBEWITHHMACSHA512ANDAES_128("PBEWithHmacSHA512AndAES_128",
"PBEWithHmacSHA512AndAES_128",
0,
Optional.empty(),
Optional.empty(),
pbeParams -> new PBEParameterSpec(pbeParams.getSalt(), pbeParams.getIterationCount(), new IvParameterSpec(pbeParams.getIv()))),

@Deprecated
PBEWITHMD5ANDDES("PBEWithMD5AndDES",
"PBEWithMD5AndDES",
0,
Optional.empty(),
Optional.empty(),
pbeParams -> new PBEParameterSpec(pbeParams.getSalt(), pbeParams.getIterationCount(), new IvParameterSpec(pbeParams.getIv()))),

@Deprecated
PBEWITHMD5ANDTRIPLEDES("PBEWithMD5AndTripleDES",
"PBEWithMD5AndTripleDES",
0,
Optional.empty(),
Optional.empty(),
pbeParams -> new PBEParameterSpec(pbeParams.getSalt(), pbeParams.getIterationCount(), new IvParameterSpec(pbeParams.getIv()))),

PBEWITHSHA1ANDDESEDE("PBEWithSHA1AndDESede",
"PBEWithSHA1AndDESede",
0,
Optional.empty(),
Optional.empty(),
pbeParams -> new PBEParameterSpec(pbeParams.getSalt(), pbeParams.getIterationCount(), new IvParameterSpec(pbeParams.getIv()))),
PBKDF2WITHHMACSHA256("PBKDF2WithHmacSHA256",
"AES/CBC/PKCS7Padding",
64 * 4,
Optional.of(16),
Optional.of(14),
pbeParams -> new IvParameterSpec(pbeParams.getIv()));

private final String encrAlgoName;

private final String cipherTransformation;
private final int keyLength;
private final Optional<Integer> minSaltSize;
private final Optional<Integer> minPwdLength;
private final Function<PasswordUtils.PBEParams, AlgorithmParameterSpec> algoParamSpecFunc;

RangerSupportedCryptoAlgo(String encrAlgoName, String cipherTransformation, int keyLength, Optional<Integer> minSaltSize, Optional<Integer> minPwdLength, Function<PasswordUtils.PBEParams, AlgorithmParameterSpec> algoParamSpecFunc) {
this.encrAlgoName = encrAlgoName;
this.cipherTransformation = cipherTransformation;
this.keyLength = keyLength;
this.minSaltSize = minSaltSize;
this.minPwdLength = minPwdLength;
this.algoParamSpecFunc = algoParamSpecFunc;
}

public int getKeyLength() {
return this.keyLength;
}

public String getAlgoName() {
return this.encrAlgoName;
}

public String getCipherTransformation() {
return this.cipherTransformation;
}

public Optional<Integer> getMinSaltSize() {
return this.minSaltSize;
}

public Optional<Integer> getMinPwdLength() {
return this.minPwdLength;
}

public AlgorithmParameterSpec getAlgoParamSpec(byte[] salt, int iterationCount, byte[] iv) {
return new PBEParameterSpec(salt, iterationCount, new IvParameterSpec(iv));
}

public AlgorithmParameterSpec getAlgoParamSpec(PasswordUtils.PBEParams pbeParams) {
return this.algoParamSpecFunc.apply(pbeParams);
}

public static RangerSupportedCryptoAlgo getFIPSCompliantAlgorithm() {
return RangerSupportedCryptoAlgo.PBKDF2WITHHMACSHA256;
}

public static boolean isFIPSCompliantAlgorithm(RangerSupportedCryptoAlgo encrAlgo) {
return RangerSupportedCryptoAlgo.PBKDF2WITHHMACSHA256.equals(encrAlgo);
}

public static RangerSupportedCryptoAlgo getValueOf(String val) {
return RangerSupportedCryptoAlgo.valueOf(val.toUpperCase());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public class ServiceDBStore extends AbstractServiceStore {
public static final String SERVICE_ADMIN_USERS = "service.admin.users";
public static final String SERVICE_ADMIN_GROUPS = "service.admin.groups";
public static final String GDS_SERVICE_NAME = "_gds";
public static final String CRYPT_ALGO = PropertiesUtil.getProperty("ranger.password.encryption.algorithm", PasswordUtils.DEFAULT_CRYPT_ALGO);
public static final String CRYPT_ALGO = PropertiesUtil.getProperty("ranger.password.encryption.algorithm", PasswordUtils.DEFAULT_CRYPT_ALGO.getAlgoName());
public static final String ENCRYPT_KEY = PropertiesUtil.getProperty("ranger.password.encryption.key", PasswordUtils.DEFAULT_ENCRYPT_KEY);
public static final String SALT = PropertiesUtil.getProperty("ranger.password.salt", PasswordUtils.DEFAULT_SALT);
public static final Integer ITERATION_COUNT = PropertiesUtil.getIntProperty("ranger.password.iteration.count", PasswordUtils.DEFAULT_ITERATION_COUNT);
Expand Down