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);

Reply via email to