Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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,11 +26,10 @@
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;
Expand All @@ -41,14 +40,14 @@ public class PasswordUtils {
private static final Logger LOG = LoggerFactory.getLogger(PasswordUtils.class);

public static final String PBE_SHA512_AES_128 = "PBEWITHHMACSHA512ANDAES_128";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this? Aren't we now going to use PBEWithHmacSHA512AndAES_128 from RangerSupportedCryptoAlgo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@spolavarpau1 , thanks for your review. I have fixed this. Pls review it one more time.

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 +66,12 @@ public class PasswordUtils {
if (cryptAlgoArray != null && cryptAlgoArray.length > 4) {
int index = 0;

cryptAlgo = cryptAlgoArray[index++]; // 0
cryptAlgo = RangerSupportedCryptoAlgo.valueOf(cryptAlgoArray[index++]); // 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to handle the case for crypto algorithm that is not in the list of enums supported in RangerSupportedCryptoAlgo. Can you please check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially idea was to keep all supported algorithm at one place. Although I have put many used PBE based algorithm in the enum, But I agree that someone might might have used a totally different crypto algorithm.

I am working to update the code to support this behaviour as well. Can we merge this PR and follow this as a separate JIRA ?

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 @@ -130,7 +129,7 @@ public static boolean needsIv(String cryptoAlgo) {
}

return PBE_SHA512_AES_128.equalsIgnoreCase(cryptoAlgo)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above. Do we need to replace this check with the one from RangerSupportedCryptoAlgo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

|| cryptoAlgo.toLowerCase().contains("aes_128") || cryptoAlgo.toLowerCase().contains("aes_256");
|| 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 +157,7 @@ public static String getDecryptPassword(String password) {
}

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

public String getPassword() {
Expand Down Expand Up @@ -196,12 +195,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 +219,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 +373,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,116 @@
/*
* 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public void testEncryptWithSHA1AndDESede() throws IOException {

@Test
public void testEncryptWithSHA512AndAES128() throws IOException, NoSuchAlgorithmException {
String freeTextPasswordMetaData = join("PBEWITHHMACSHA512ANDAES_128", "ENCRYPT_KEY", "SALTSALT", "4", PasswordUtils.generateIvIfNeeded("PBEWITHHMACSHA512ANDAES_128"));
String freeTextPasswordMetaData = join("PBEWithHmacSHA512AndAES_128", "ENCRYPT_KEY", "SALTSALT", "4", PasswordUtils.generateIvIfNeeded("PBEWithHmacSHA512AndAES_128"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of upgrades, would this case difference in the algorithm name has any affect?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, "secretPasswordNoOneWillEverKnow"));

assertNotNull(encryptedPassword);
Expand All @@ -84,7 +84,7 @@ public void testEncryptWithSHA512AndAES128() throws IOException, NoSuchAlgorithm

@Test
public void testEncryptWithSHA512AndAES128WithMultipleComasInPass() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = "asd,qwe,123";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -97,7 +97,7 @@ public void testEncryptWithSHA512AndAES128WithMultipleComasInPass() throws IOExc

@Test
public void testEncryptWithSHA512AndAES128WithSingleComaInPass() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = "asd,123";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -110,7 +110,7 @@ public void testEncryptWithSHA512AndAES128WithSingleComaInPass() throws IOExcept

@Test
public void testEncryptWithSHA512AndAES128EndingWithSingleComa() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = "asd,";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -123,7 +123,7 @@ public void testEncryptWithSHA512AndAES128EndingWithSingleComa() throws IOExcept

@Test
public void testEncryptWithSHA512AndAES128StartingWithSingleComa() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = ",asd";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -136,7 +136,7 @@ public void testEncryptWithSHA512AndAES128StartingWithSingleComa() throws IOExce

@Test
public void testEncryptWithSHA512AndAES128MultipleComasInTheEnd() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = "asd,,";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -149,7 +149,7 @@ public void testEncryptWithSHA512AndAES128MultipleComasInTheEnd() throws IOExcep

@Test
public void testEncryptWithSHA512AndAES128MultipleComasSurroundingText() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = ",,a,,";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -162,7 +162,7 @@ public void testEncryptWithSHA512AndAES128MultipleComasSurroundingText() throws

@Test
public void testEncryptWithSHA512AndAES128MultipleComasBeforeText() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = ",,,a";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -175,7 +175,7 @@ public void testEncryptWithSHA512AndAES128MultipleComasBeforeText() throws IOExc

@Test
public void testEncryptWithSHA512AndAES128MultipleComasOnlyPassword() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = ",,,";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand All @@ -188,7 +188,7 @@ public void testEncryptWithSHA512AndAES128MultipleComasOnlyPassword() throws IOE

@Test
public void testEncryptWithSHA512AndAES128SingleComaOnlyPassword() throws IOException {
String freeTextPasswordMetaData = "PBEWITHHMACSHA512ANDAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPasswordMetaData = "PBEWithHmacSHA512AndAES_128,tzL1AKl5uc4NKYaoQ4P3WLGIBFPXWPWdu1fRm9004jtQiV,f77aLYLo,1000,9f3vNL0ijeHF4RWN/yUo0A==";
String freeTextPassword = ",";
String encryptedPassword = PasswordUtils.encryptPassword(join(freeTextPasswordMetaData, freeTextPassword));

Expand Down
Loading
Loading