This is an automated email from the ASF dual-hosted git repository.
dhavalshah9131 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new d50d8e654 RANGER-5313: Support FIPS compliant crypto algorithm to
encrypt/decry… (#723)
d50d8e654 is described below
commit d50d8e654320e3817fb842fb918a7a0d78ff3fc7
Author: Vikas Kumar <[email protected]>
AuthorDate: Fri Nov 21 19:51:50 2025 +0530
RANGER-5313: Support FIPS compliant crypto algorithm to encrypt/decry…
(#723)
* RANGER-5313: Support FIPS compliant crypto algorithm to encrypt/decrypt
for service password
* RANGER-5313: Support FIPS compliant crypto algorithm to encrypt/decrypt
for service password-Review comments fixes
---
.../apache/ranger/plugin/util/PasswordUtils.java | 111 +++++++++++++++----
.../plugin/util/RangerSupportedCryptoAlgo.java | 120 +++++++++++++++++++++
.../java/org/apache/ranger/biz/ServiceDBStore.java | 2 +-
3 files changed, 214 insertions(+), 19 deletions(-)
diff --git
a/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java
b/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java
index 3b7d08a3b..b173925cb 100644
---
a/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java
+++
b/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java
@@ -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;
@@ -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;
@@ -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 {
@@ -158,7 +158,7 @@ public static String getDecryptPassword(String password) {
}
public String getCryptAlgo() {
- return cryptAlgo;
+ return cryptAlgo.getAlgoName();
}
public String getPassword() {
@@ -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());
@@ -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);
@@ -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;
+ }
+ }
}
diff --git
a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSupportedCryptoAlgo.java
b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSupportedCryptoAlgo.java
new file mode 100644
index 000000000..940d4c0ed
--- /dev/null
+++
b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSupportedCryptoAlgo.java
@@ -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());
+ }
+}
diff --git
a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
index 0f0601036..cf6c536c9 100644
--- a/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
+++ b/security-admin/src/main/java/org/apache/ranger/biz/ServiceDBStore.java
@@ -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);