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 bf2ca0f19 RANGER-3174: Support for strong cryptography 
algorithm-PBKDF2WithHmacSHA256 (#593)
bf2ca0f19 is described below

commit bf2ca0f192c8cf382e5aab56cadf86777c43e4d5
Author: Vikas Kumar <[email protected]>
AuthorDate: Fri Jun 27 22:49:03 2025 +0530

    RANGER-3174: Support for strong cryptography algorithm-PBKDF2WithHmacSHA256 
(#593)
---
 .../org/apache/hadoop/crypto/key/RangerKMSMKI.java |   6 +-
 .../apache/hadoop/crypto/key/RangerKeyStore.java   | 145 +++++++++++----
 .../hadoop/crypto/key/RangerKeyStoreProvider.java  |  37 +++-
 .../apache/hadoop/crypto/key/RangerMasterKey.java  | 205 ++++++++++++++++-----
 .../hadoop/crypto/key/SupportedPBECryptoAlgo.java  |  74 ++++++++
 5 files changed, 381 insertions(+), 86 deletions(-)

diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKMSMKI.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKMSMKI.java
index 198890e36..83789c2e1 100644
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKMSMKI.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKMSMKI.java
@@ -32,5 +32,9 @@ default byte[] encryptZoneKey(Key zoneKey) throws Exception {
         return null;
     }
 
-    default void onInitialization() throws Exception                     {}
+    default void onInitialization() throws Exception {}
+
+    default boolean reencryptMKWithFipsAlgo(String mkPassword) throws 
Exception {
+        return  false;
+    }
 }
diff --git a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java
index 3b397fb4f..5763ad304 100644
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStore.java
@@ -27,6 +27,7 @@
 import org.apache.ranger.entity.XXRangerKeyStore;
 import org.apache.ranger.kms.dao.DaoManager;
 import org.apache.ranger.kms.dao.RangerKMSDao;
+import org.apache.ranger.plugin.util.JsonUtilsV2;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -37,7 +38,6 @@
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.PBEParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 import javax.xml.bind.DatatypeConverter;
 
@@ -78,6 +78,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -95,11 +96,13 @@ public class RangerKeyStore extends KeyStoreSpi {
     private static final String  AZURE_KEYVAULT_ENABLED  = 
"ranger.kms.azurekeyvault.enabled";
     private static final String  METADATA_FIELDNAME      = "metadata";
     private static final int     NUMBER_OF_BITS_PER_BYTE = 8;
-    private static final String  SECRET_KEY_HASH_WORD    = "Apache Ranger";
+    private static final String SECRET_KEY_HASH_WORD     = "Apache Ranger";
+    public  static final String KEY_CRYPTO_ALGO_NAME     = "keyCryptoAlgoName";
 
     private final    RangerKMSDao        kmsDao;
     private final    RangerKMSMKI        masterKeyProvider;
     private final    boolean             keyVaultEnabled;
+    private          boolean             isFIPSEnabled;
     private final    Map<String, Object> deltaEntries = new 
ConcurrentHashMap<>();
     private volatile Map<String, Object> keyEntries   = new 
ConcurrentHashMap<>();
 
@@ -107,6 +110,11 @@ public RangerKeyStore(DaoManager daoManager) {
         this(daoManager, false, null);
     }
 
+    public RangerKeyStore(boolean isFIPSEnabled, DaoManager daoManager) {
+        this(daoManager);
+        this.isFIPSEnabled = isFIPSEnabled;
+    }
+
     public RangerKeyStore(DaoManager daoManager, Configuration conf, 
KeyVaultClient kvClient) {
         this.kmsDao            = daoManager != null ? 
daoManager.getRangerKMSDao() : null;
         this.masterKeyProvider = new RangerAzureKeyVaultKeyGenerator(conf, 
kvClient);
@@ -131,7 +139,7 @@ public Key engineGetKey(String alias, char[] password) 
throws NoSuchAlgorithmExc
 
         if (entry instanceof SecretKeyEntry) {
             try {
-                ret = unsealKey(((SecretKeyEntry) entry).sealedKey, password);
+                ret = unsealKey((SecretKeyEntry) entry, password);
             } catch (Exception e) {
                 logger.error("engineGetKey({}) error", alias, e);
             }
@@ -470,13 +478,20 @@ public void addSecureKeyByteEntry(String alias, Key key, 
String cipher, int bitL
         logger.debug("<== addSecureKeyByteEntry({})", alias);
     }
 
+    private String addEncrAlgoNameInKeyAttrib(String jsonAttrib) throws 
Exception {
+        Map<String, String> attribMap = JsonUtilsV2.jsonToMap(jsonAttrib);
+        attribMap.put(KEY_CRYPTO_ALGO_NAME, 
SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256.getAlgoName());
+        return JsonUtilsV2.mapToJson(attribMap);
+    }
+
     public void addKeyEntry(String alias, Key key, char[] password, String 
cipher, int bitLength, String description, int version, String attributes) 
throws KeyStoreException {
         logger.debug("==> addKeyEntry({})", alias);
 
         SecretKeyEntry entry;
 
         try {
-            entry = new SecretKeyEntry(sealKey(key, password), cipher, 
bitLength, description, version, attributes);
+            String updatedAttribute = isFIPSEnabled ? 
addEncrAlgoNameInKeyAttrib(attributes) : attributes;
+            entry = new SecretKeyEntry(sealKey(key, password), cipher, 
bitLength, description, version, updatedAttribute);
         } catch (Exception e) {
             logger.error("addKeyEntry({}) error", alias, e);
 
@@ -916,62 +931,101 @@ private String convertAlias(String alias) {
     }
 
     private SealedObject sealKey(Key key, char[] password) throws Exception {
-        logger.debug("==> sealKey()");
+        logger.debug("==> RangerKeyStore.sealKey()");
 
         // Create SecretKey
-        SecretKeyFactory secretKeyFactory = 
SecretKeyFactory.getInstance("PBEWithMD5AndTripleDES");
-        PBEKeySpec       pbeKeySpec       = new PBEKeySpec(password);
-        SecretKey        secretKey        = 
secretKeyFactory.generateSecret(pbeKeySpec);
+        SupportedPBECryptoAlgo encrAlgo         = isFIPSEnabled ? 
SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256 : 
SupportedPBECryptoAlgo.PBEWithMD5AndTripleDES;
+        SecretKeyFactory       secretKeyFactory = 
SecretKeyFactory.getInstance(encrAlgo.getAlgoName());
 
-        pbeKeySpec.clearPassword();
-
-        // Generate random bytes, set up the PBEParameterSpec, seal the key
+        PBEKeySpec             pbeKeySpec = null;
+        PBEKeySpec             pbeCipherSpec = null;
+        byte[] salt = null;
         SecureRandom random = new SecureRandom();
-        byte[]       salt   = new byte[8];
+        if (SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256.equals(encrAlgo)) {
+            salt = new byte[8 * 2];
+            random.nextBytes(salt);
+            pbeKeySpec = new PBEKeySpec(password, salt, 20, 
encrAlgo.getKeyLength());
+            pbeCipherSpec = pbeKeySpec;
+        } else {
+            salt = new byte[8];
+            random.nextBytes(salt);
+            pbeKeySpec = new PBEKeySpec(password);
+            pbeCipherSpec = new PBEKeySpec(password, salt, 20);
+        }
 
-        random.nextBytes(salt);
+        SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
+        pbeKeySpec.clearPassword();
 
-        PBEParameterSpec pbeSpec = new PBEParameterSpec(salt, 20);
-        Cipher           cipher  = 
Cipher.getInstance("PBEWithMD5AndTripleDES");
+        // Seal the Key
+        Cipher cipher = Cipher.getInstance(encrAlgo.getCipherTransformation());
+        cipher.init(Cipher.ENCRYPT_MODE, secretKey, 
encrAlgo.getAlgoParamSpec(pbeCipherSpec));
 
-        cipher.init(Cipher.ENCRYPT_MODE, secretKey, pbeSpec);
+        logger.debug("<== RangerKeyStore.sealKey()");
 
-        RangerSealedObject ret = new RangerSealedObject(key, cipher);
+        return new RangerSealedObject(key, cipher, salt);
+    }
 
-        logger.debug("<== sealKey(): ret={}", ret);
+    private Key unsealKey(SecretKeyEntry secretKeyEntry, char[] password) 
throws Exception {
+        logger.debug("==> RangerKeyStore.unsealKey()");
 
-        return ret;
-    }
+        // fetch encryption algo name
+        String encrAlgoName = 
JsonUtilsV2.jsonToMap(secretKeyEntry.attributes).get(KEY_CRYPTO_ALGO_NAME);
+        SupportedPBECryptoAlgo encrAlgo = StringUtils.isNotEmpty(encrAlgoName) 
? SupportedPBECryptoAlgo.valueOf(encrAlgoName) : 
SupportedPBECryptoAlgo.PBEWithMD5AndTripleDES;
 
-    private Key unsealKey(SealedObject sealedKey, char[] password) throws 
Exception {
-        logger.debug("==> unsealKey()");
+        SealedObject sealedKey = secretKeyEntry.sealedKey;
+        // Get the AlgorithmParameters from RangerSealedObject
+        AlgorithmParameters algorithmParameters = null;
+        byte[] salt = null;
+        PBEKeySpec pbeKeySpec = null;
+
+        if (SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256.equals(encrAlgo)) {
+            salt = ((RangerSealedObject) sealedKey).getSalt();
+            pbeKeySpec = new PBEKeySpec(password, salt, 20, 
encrAlgo.getKeyLength());
+        } else { // not yet re-encrypted, older algo
+            algorithmParameters = sealedKey instanceof RangerSealedObject ? 
((RangerSealedObject) sealedKey).getParameters(encrAlgo.getAlgoName()) : new 
RangerSealedObject(sealedKey).getParameters(encrAlgo.getAlgoName());
+            pbeKeySpec = new PBEKeySpec(password);
+        }
 
         // Create SecretKey
-        SecretKeyFactory secretKeyFactory = 
SecretKeyFactory.getInstance("PBEWithMD5AndTripleDES");
-        PBEKeySpec       pbeKeySpec       = new PBEKeySpec(password);
-        SecretKey        secretKey        = 
secretKeyFactory.generateSecret(pbeKeySpec);
-
+        SecretKeyFactory secretKeyFactory = 
SecretKeyFactory.getInstance(encrAlgo.getAlgoName());
+        SecretKey secretKey = secretKeyFactory.generateSecret(pbeKeySpec);
         pbeKeySpec.clearPassword();
 
-        // Get the AlgorithmParameters from RangerSealedObject
-        AlgorithmParameters algorithmParameters;
+        // Unseal the Key
+        Cipher cipher = Cipher.getInstance(encrAlgo.getCipherTransformation());
 
-        if (sealedKey instanceof RangerSealedObject) {
-            algorithmParameters = ((RangerSealedObject) 
sealedKey).getParameters();
+        if (SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256.equals(encrAlgo)) {
+            cipher.init(Cipher.DECRYPT_MODE, secretKey, 
encrAlgo.getAlgoParamSpec(pbeKeySpec));
         } else {
-            algorithmParameters = new 
RangerSealedObject(sealedKey).getParameters();
+            cipher.init(Cipher.DECRYPT_MODE, secretKey, algorithmParameters);
         }
 
-        // Unseal the Key
-        Cipher cipher = Cipher.getInstance("PBEWithMD5AndTripleDES");
-
-        cipher.init(Cipher.DECRYPT_MODE, secretKey, algorithmParameters);
+        logger.debug("<== RangerKeyStore.unsealKey()");
 
-        Key ret = (Key) sealedKey.getObject(cipher);
+        return (Key) sealedKey.getObject(cipher);
+    }
 
-        logger.debug("<== unsealKey(): ret={}", ret);
+    public void reencryptZoneKeysWithNewAlgo(InputStream stream, char[] 
password) throws Exception {
+        logger.debug("==> RangerKeyStore.reencryptZoneKeysWithNewAlgo");
 
-        return ret;
+        try {
+            this.engineLoad(stream, password);
+            Set<String> keyAliases = new HashSet<>(keyEntries.keySet());
+            logger.info("Count of key aliases to be re-encrypted = {}", 
keyAliases.size());
+            for (String keyAlias : keyAliases) {
+                Key key = this.engineGetKey(keyAlias, password);
+                SecretKeyEntry entry = (SecretKeyEntry) 
keyEntries.remove(keyAlias);
+                this.addKeyEntry(keyAlias, key, password, entry.cipherField, 
entry.bitLength, entry.description, entry.version, entry.attributes);
+            }
+            this.engineStore(null, password);
+            logger.info("All zone keys got re-encrypted");
+        } catch (IOException | NoSuchAlgorithmException | CertificateException 
| UnrecoverableKeyException | KeyStoreException e) {
+            logger.error("Error occurred while re-encrypting the zone keys", 
e);
+            throw e;
+        }
+        finally {
+            logger.debug("<== RangerKeyStore.reencryptZoneKeysWithNewAlgo");
+        }
     }
 
     // keys
@@ -1034,6 +1088,8 @@ private static final class SecretKeyByteEntry {
     private static class RangerSealedObject extends SealedObject {
         private static final long serialVersionUID = -7551578543434362070L;
 
+        private byte[] salt;
+
         /**
          *
          */
@@ -1045,13 +1101,22 @@ protected RangerSealedObject(Serializable object, 
Cipher cipher) throws IllegalB
             super(object, cipher);
         }
 
-        public AlgorithmParameters getParameters() throws 
NoSuchAlgorithmException, IOException {
-            AlgorithmParameters algorithmParameters = 
AlgorithmParameters.getInstance("PBEWithMD5AndTripleDES");
+        protected RangerSealedObject(Serializable object, Cipher cipher, 
byte[] salt) throws IllegalBlockSizeException, IOException {
+            this(object, cipher);
+            this.salt =  salt;
+        }
+
+        public AlgorithmParameters getParameters(String algoName) throws 
NoSuchAlgorithmException, IOException {
+            AlgorithmParameters algorithmParameters = 
AlgorithmParameters.getInstance(algoName);
 
             algorithmParameters.init(super.encodedParams);
 
             return algorithmParameters;
         }
+
+        public byte[] getSalt() {
+            return this.salt;
+        }
     }
 
     public static class KeyByteMetadata implements Key, Serializable {
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java
index 5ba567ea3..fe852f691 100755
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerKeyStoreProvider.java
@@ -17,6 +17,7 @@
 
 package org.apache.hadoop.crypto.key;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.classification.InterfaceAudience;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.Path;
@@ -93,6 +94,10 @@ public class RangerKeyStoreProvider extends KeyProvider {
     private final boolean               keyVaultEnabled;
     private       boolean               changed;
 
+    private        boolean isFIPSEnabled;
+    private        boolean isMKReencrypted;
+    private        boolean isNewMKGenerated;
+
     public RangerKeyStoreProvider(Configuration conf) throws Throwable {
         super(conf);
 
@@ -123,6 +128,10 @@ public RangerKeyStoreProvider(Configuration conf) throws 
Throwable {
         final DaoManager   daoManager  = rangerKMSDB.getDaoManager();
         final RangerKMSMKI masterKeyProvider;
 
+        String storeType = conf.get("ranger.keystore.file.type", 
KeyStore.getDefaultType());
+        this.isFIPSEnabled = StringUtils.equalsIgnoreCase("bcfks", storeType) 
? true : false;
+        logger.info("isFIPSEnabled={}",  isFIPSEnabled);
+
         if (isHSMEnabled) {
             logger.info("Ranger KMS HSM is enabled for storing master key.");
 
@@ -216,8 +225,30 @@ public RangerKeyStoreProvider(Configuration conf) throws 
Throwable {
             logger.info("Ranger KMS Database is enabled for storing master 
key.");
 
             masterKeyProvider = new RangerMasterKey(daoManager);
-            dbStore           = new RangerKeyStore(daoManager);
-            masterKey         = 
this.generateAndGetMasterKey(masterKeyProvider, password);
+
+            dbStore   = new RangerKeyStore(isFIPSEnabled, daoManager);
+            char[] tempMK;
+            tempMK = this.generateAndGetMasterKey(masterKeyProvider, password);
+
+            if (isFIPSEnabled && !isNewMKGenerated) {
+                logger.info("MasterKey already exists and FIPS is enabled, may 
require re-encryption with compliant algorithm");
+                this.isMKReencrypted = 
masterKeyProvider.reencryptMKWithFipsAlgo(password);
+                logger.info("MasterKey re-encryption status {}", 
this.isMKReencrypted);
+
+                // initialize with new MK
+                try {
+                    tempMK = 
masterKeyProvider.getMasterKey(password).toCharArray();
+                } catch (Throwable e) {
+                    throw new RuntimeException("Error while getting Ranger 
Master key, Error - " + e.getMessage());
+                }
+            }
+
+            this.masterKey = tempMK;
+        }
+
+        // If MK required re-encryption, means Zone keys were also encrypted 
using older algo and needs to be re-encrypted.
+        if (isFIPSEnabled  && isMKReencrypted) {
+            this.dbStore.reencryptZoneKeysWithNewAlgo(null, this.masterKey);
         }
 
         reloadKeys();
@@ -595,7 +626,7 @@ private char[] generateAndGetMasterKey(final RangerKMSMKI 
masterKeyProvider, fin
         char[] ret;
 
         try {
-            masterKeyProvider.generateMasterKey(password);
+            this.isNewMKGenerated = 
masterKeyProvider.generateMasterKey(password);
         } catch (Throwable cause) {
             throw new RuntimeException("Error while generating Ranger Master 
key, Error - ", cause);
         }
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerMasterKey.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerMasterKey.java
index d07a8adbd..30b0ee032 100755
--- a/kms/src/main/java/org/apache/hadoop/crypto/key/RangerMasterKey.java
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/RangerMasterKey.java
@@ -34,13 +34,13 @@
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.PBEParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.spec.AlgorithmParameterSpec;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
@@ -48,15 +48,18 @@
 public class RangerMasterKey implements RangerKMSMKI {
     private static final Logger logger = 
LoggerFactory.getLogger(RangerMasterKey.class);
 
+    public static final int PADDING_STRING_ELEM_COUNT = 7;
     public  static final String     DBKS_SITE_XML           = "dbks-site.xml";
     private static final String     DEFAULT_MK_CIPHER       = "AES";
     private static final int        DEFAULT_MK_KeySize      = 256;
     private static final int        DEFAULT_SALT_SIZE       = 8;
     private static final String     DEFAULT_SALT            = 
"abcdefghijklmnopqrstuvwxyz01234567890";
-    private static final String     DEFAULT_CRYPT_ALGO      = 
"PBEWithMD5AndTripleDES";
     private static final int        DEFAULT_ITERATION_COUNT = 1000;
     private static final Properties serverConfigProperties  = new Properties();
 
+    private static SupportedPBECryptoAlgo defaultCryptAlgo = 
SupportedPBECryptoAlgo.PBEWithMD5AndTripleDES;
+    private static SupportedPBECryptoAlgo encrCryptoAlgo   = defaultCryptAlgo;
+
     public  static String  mkCipher;
     public  static Integer mkKeySize = 0;
     public  static Integer saltSize  = 0;
@@ -67,6 +70,7 @@ public class RangerMasterKey implements RangerKMSMKI {
     public  static String  paddingString;
     private static String password;
     private static String defaultMdAlgo;
+    private static boolean isFipsEnabled;
 
     private final RangerMasterKeyDao masterKeyDao;
 
@@ -101,7 +105,7 @@ public static void getPasswordParam(String 
paddedEncryptedPwd) {
             mkCipher  = DEFAULT_MK_CIPHER;
             mkKeySize = DEFAULT_MK_KeySize;
             saltSize  = DEFAULT_SALT_SIZE;
-            pbeAlgo   = DEFAULT_CRYPT_ALGO;
+            pbeAlgo   = isFipsEnabled ? 
SupportedPBECryptoAlgo.PBEWithMD5AndTripleDES.getAlgoName() : 
defaultCryptAlgo.getAlgoName();
             mdAlgo    = defaultMdAlgo;
             password  = paddedEncryptedPwd;
             salt      = password;
@@ -171,16 +175,22 @@ public void init() {
 
         XMLUtils.loadConfig(DBKS_SITE_XML, serverConfigProperties);
 
-        defaultMdAlgo  = getConfig("ranger.keystore.file.type", 
KeyStore.getDefaultType()).equalsIgnoreCase("bcfks") ? "SHA-512" : "MD5";
-        mkCipher       = 
getConfig("ranger.kms.service.masterkey.password.cipher", DEFAULT_MK_CIPHER);
-        mkKeySize      = 
getIntConfig("ranger.kms.service.masterkey.password.size", DEFAULT_MK_KeySize);
-        saltSize       = 
getIntConfig("ranger.kms.service.masterkey.password.salt.size", 
DEFAULT_SALT_SIZE);
-        salt           = 
getConfig("ranger.kms.service.masterkey.password.salt", DEFAULT_SALT);
-        pbeAlgo        = 
getConfig("ranger.kms.service.masterkey.password.encryption.algorithm", 
DEFAULT_CRYPT_ALGO);
-        mdAlgo         = 
getConfig("ranger.kms.service.masterkey.password.md.algorithm", defaultMdAlgo);
-        iterationCount = 
getIntConfig("ranger.kms.service.masterkey.password.iteration.count", 
DEFAULT_ITERATION_COUNT);
-        paddingString  = Joiner.on(",").skipNulls().join(mkCipher, mkKeySize, 
saltSize, pbeAlgo, mdAlgo, iterationCount, salt);
-
+        isFipsEnabled       = getConfig("ranger.keystore.file.type", 
KeyStore.getDefaultType()).equalsIgnoreCase("bcfks");
+        defaultMdAlgo       = isFipsEnabled ? "SHA-512" : "MD5";
+        defaultCryptAlgo    = isFipsEnabled ? 
SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256 : defaultCryptAlgo;
+        mkCipher            = 
getConfig("ranger.kms.service.masterkey.password.cipher", DEFAULT_MK_CIPHER);
+        mkKeySize           = 
getIntConfig("ranger.kms.service.masterkey.password.size", DEFAULT_MK_KeySize);
+        saltSize            = 
getIntConfig("ranger.kms.service.masterkey.password.salt.size", 
DEFAULT_SALT_SIZE);
+        salt                = 
getConfig("ranger.kms.service.masterkey.password.salt", DEFAULT_SALT);
+        pbeAlgo             = 
getConfig("ranger.kms.service.masterkey.password.encryption.algorithm", 
defaultCryptAlgo.getAlgoName());
+        encrCryptoAlgo      = SupportedPBECryptoAlgo.valueOf(pbeAlgo);
+        mdAlgo              = 
getConfig("ranger.kms.service.masterkey.password.md.algorithm", defaultMdAlgo);
+        iterationCount      = 
getIntConfig("ranger.kms.service.masterkey.password.iteration.count", 
DEFAULT_ITERATION_COUNT);
+        paddingString       = Joiner.on(",").skipNulls().join(mkCipher, 
mkKeySize, saltSize, pbeAlgo, mdAlgo, iterationCount, salt);
+
+        logger.info("Selected DEFAULT_CRYPT_ALGO={}", defaultCryptAlgo);
+        logger.info("Selected MD_ALGO={}", mdAlgo);
+        logger.info("Selected ENCR_CRYPTO_ALGO={}", encrCryptoAlgo);
         logger.debug("<== RangerMasterKey.init()");
     }
 
@@ -248,6 +258,72 @@ public String getMasterKey(String password) throws 
Throwable {
         }
     }
 
+    private String fetchEncrAlgo(String  encryptedPassString) {
+        String encrAlgo = 
SupportedPBECryptoAlgo.PBEWithMD5AndTripleDES.getAlgoName();
+
+        String[] mkSplits = null;
+        if (encryptedPassString != null && encryptedPassString.contains(",")) {
+            mkSplits = 
Lists.newArrayList(Splitter.on(",").split(encryptedPassString)).toArray(new 
String[0]);
+        }
+
+        if (mkSplits != null && mkSplits.length >= PADDING_STRING_ELEM_COUNT) {
+            encrAlgo = mkSplits[3];
+        }
+
+        return encrAlgo;
+    }
+
+    /**
+     * Generate the master key, encrypt it and save it in the database
+     *
+     * @return true if the master key was successfully created false if master
+     * key generation was unsuccessful or the master key already exists
+     */
+    @Override
+    public boolean reencryptMKWithFipsAlgo(String mkPassword) {
+        logger.debug("==> RangerMasterKey.reencryptMKWithFipsAlgorithm");
+
+        boolean isMKReencrypted = false;
+        // Fetch MK and check the last CryptoAlgo used for encryption
+        List result = getEncryptedMK();
+        String encryptedPassString = null;
+        byte[] masterKeyByte = null;
+        if (CollectionUtils.isNotEmpty(result) && result.size() == 2) {
+            masterKeyByte = (byte[]) result.get(0);
+            encryptedPassString = (String) result.get(1);
+        } else if (CollectionUtils.isNotEmpty(result)) {
+            masterKeyByte = (byte[]) result.get(0);
+        }
+
+        String currentPbeAlgo = fetchEncrAlgo(encryptedPassString);
+        if 
(!SupportedPBECryptoAlgo.isFIPSCompliantAlgorithm(SupportedPBECryptoAlgo.valueOf(currentPbeAlgo))
 && 
!RangerMasterKey.encrCryptoAlgo.getAlgoName().equalsIgnoreCase(currentPbeAlgo)) 
{
+            logger.info("MasterKey key material was encrypted using {} , going 
to re-encrypt using {}",  currentPbeAlgo, RangerMasterKey.encrCryptoAlgo);
+            byte[] oldKeyMaterial = null;
+            try {
+                // get the old MK key material
+                PBEKeySpec pbeKeyspec = getPBEParameterSpec(mkPassword, 
SupportedPBECryptoAlgo.valueOf(currentPbeAlgo));
+                oldKeyMaterial = decryptKey(masterKeyByte, pbeKeyspec);
+
+                // re-encrypt it with new encryption algo
+                init();
+                PBEKeySpec newPbeKeySpec = getPBEParameterSpec(mkPassword, 
encrCryptoAlgo);
+                byte[] masterKeyToDB = encryptKey(oldKeyMaterial, 
newPbeKeySpec);
+
+                String encodeMKToDB = Base64.encode(masterKeyToDB);
+                updateEncryptedMK(paddingString + "," + encodeMKToDB);
+                isMKReencrypted = true;
+                logger.info("MasterKey key material got re-encrypted and saved 
to the DB");
+            } catch (Throwable e) {
+                logger.error(" Error while re-encrypting the  MasterKey", e);
+                throw new RuntimeException(e);
+            }
+        }
+
+        logger.debug("<== RangerMasterKey.reencryptMKWithFipsAlgo");
+
+        return isMKReencrypted;
+    }
+
     public void generateMKFromHSMMK(String password, byte[] key) throws 
Throwable {
         logger.debug("==> RangerMasterKey.generateMKFromHSMMK()");
 
@@ -295,9 +371,9 @@ private String decryptMasterKey(byte[] masterKey, String 
password, String encryp
             getPasswordParam(password);
         }
 
-        PBEKeySpec pbeKeyspec               = getPBEParameterSpec(password);
-        byte[]     masterKeyFromDBDecrypted = decryptKey(masterKey, 
pbeKeyspec);
-        SecretKey  masterKeyFromDB          = 
getMasterKeyFromBytes(masterKeyFromDBDecrypted);
+        PBEKeySpec pbeKeyspec = getPBEParameterSpec(password, 
SupportedPBECryptoAlgo.valueOf(pbeAlgo));
+        byte[] masterKeyFromDBDecrypted = decryptKey(masterKey, pbeKeyspec);
+        SecretKey masterKeyFromDB = 
getMasterKeyFromBytes(masterKeyFromDBDecrypted);
 
         logger.debug("<== RangerMasterKey.decryptMasterKey()");
 
@@ -311,7 +387,7 @@ private SecretKey decryptMasterKeySK(byte[] masterKey, 
String password, String e
             getPasswordParam(password);
         }
 
-        PBEKeySpec pbeKeyspec               = getPBEParameterSpec(password);
+        PBEKeySpec pbeKeyspec               = getPBEParameterSpec(password, 
SupportedPBECryptoAlgo.valueOf(pbeAlgo));
         byte[]     masterKeyFromDBDecrypted = decryptKey(masterKey, 
pbeKeyspec);
 
         logger.debug("<== RangerMasterKey.decryptMasterKeySK()");
@@ -384,6 +460,27 @@ private String saveEncryptedMK(String encryptedMasterKey) {
         return null;
     }
 
+    private void updateEncryptedMK(String encryptedMasterKey) throws Exception 
{
+        logger.debug("==> RangerMasterKey.updateEncryptedMK()");
+        try {
+            if (masterKeyDao != null) {
+                XXRangerMasterKey rangerMasterKey = 
masterKeyDao.getAll().get(0);
+                if (rangerMasterKey != null) {
+                    rangerMasterKey.setMasterKey(encryptedMasterKey);
+                    masterKeyDao.update(rangerMasterKey);
+                }
+
+                logger.debug("<== RangerMasterKey.updateEncryptedMK()");
+            }
+        } catch (Exception e) {
+            String errorMsg = "Error while updating master key in Database!!! 
";
+            logger.error(errorMsg, e);
+            throw new Exception("Error while updating master key in 
Database!!! ", e);
+        }
+
+        logger.debug("<== RangerMasterKey.updateEncryptedMK()");
+    }
+
     /*
         Returns:
         true: if Master Key exists
@@ -402,9 +499,9 @@ private boolean checkMKExistence(RangerMasterKeyDao 
rangerMKDao) {
     private String encryptMasterKey(String password) throws Throwable {
         logger.debug("==> RangerMasterKey.encryptMasterKey()");
 
-        Key        secretKey     = generateMasterKey();
-        PBEKeySpec pbeKeySpec    = getPBEParameterSpec(password);
-        byte[]     masterKeyToDB = encryptKey(secretKey.getEncoded(), 
pbeKeySpec);
+        Key secretKey = generateMasterKey();
+        PBEKeySpec pbeKeySpec = getPBEParameterSpec(password, encrCryptoAlgo);
+        byte[] masterKeyToDB = encryptKey(secretKey.getEncoded(), pbeKeySpec);
 
         logger.debug("<== RangerMasterKey.encryptMasterKey()");
 
@@ -414,8 +511,8 @@ private String encryptMasterKey(String password) throws 
Throwable {
     private String encryptMasterKey(String password, byte[] secretKey) throws 
Throwable {
         logger.debug("==> RangerMasterKey.encryptMasterKey()");
 
-        PBEKeySpec pbeKeySpec    = getPBEParameterSpec(password);
-        byte[]     masterKeyToDB = encryptKey(secretKey, pbeKeySpec);
+        PBEKeySpec pbeKeySpec = getPBEParameterSpec(password, encrCryptoAlgo);
+        byte[] masterKeyToDB = encryptKey(secretKey, pbeKeySpec);
 
         logger.debug("<== RangerMasterKey.encryptMasterKey()");
 
@@ -432,28 +529,55 @@ private Key generateMasterKey() throws 
NoSuchAlgorithmException {
         return kg.generateKey();
     }
 
-    private PBEKeySpec getPBEParameterSpec(String password) throws Throwable {
+    private PBEKeySpec getPBEParameterSpec(String password, 
SupportedPBECryptoAlgo encrAlgo) throws Throwable {
         logger.debug("==> RangerMasterKey.getPBEParameterSpec()");
 
-        MessageDigest md      = MessageDigest.getInstance(mdAlgo);
-        byte[]        saltGen = md.digest(salt.getBytes());
-        byte[]        salt    = new byte[saltSize];
+        PBEKeySpec pbeKeySpec;
+        if (SupportedPBECryptoAlgo.isFIPSCompliantAlgorithm(encrAlgo)) {
+            // For FIPS, salt size must be at least 128 bits, that is, at 
least 16 in length.
+            int saltSize = RangerMasterKey.saltSize;
+            while (saltSize < 16) {
+                saltSize = saltSize * 2;
+            }
+            pbeKeySpec = new 
PBEKeySpec(getFIPSCompliantPassword(password).toCharArray(), 
generateSalt(saltSize), iterationCount, encrAlgo.getKeyLength());
+        } else {
+            pbeKeySpec = new PBEKeySpec(password.toCharArray(), 
generateSalt(RangerMasterKey.saltSize), iterationCount);
+        }
+        return pbeKeySpec;
+    }
 
-        System.arraycopy(saltGen, 0, salt, 0, saltSize);
+    /*
+        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(mdAlgo);
+        byte[] saltGen = md.digest(salt.getBytes());
+        byte[] salt = new byte[saltSize];
+        System.arraycopy(saltGen, 0, salt, 0, RangerMasterKey.saltSize);
+        return salt;
+    }
 
-        return new PBEKeySpec(password.toCharArray(), salt, iterationCount);
+    /*
+        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 getFIPSCompliantPassword(String password) {
+        String newPwd = password;
+        while (newPwd.length() < 14) {
+            newPwd = newPwd.concat(password);
+        }
+        return newPwd;
     }
 
     private byte[] encryptKey(byte[] data, PBEKeySpec keyspec) throws 
Throwable {
         logger.debug("==> RangerMasterKey.encryptKey()");
 
-        SecretKey key = getPasswordKey(keyspec);
+        SecretKey key = getPasswordKey(keyspec, encrCryptoAlgo.getAlgoName());
 
         if (keyspec.getSalt() != null) {
-            PBEParameterSpec paramSpec = new 
PBEParameterSpec(keyspec.getSalt(), keyspec.getIterationCount());
-            Cipher           c         = 
Cipher.getInstance(key.getAlgorithm());
-
-            c.init(Cipher.ENCRYPT_MODE, key, paramSpec);
+            Cipher c = 
Cipher.getInstance(encrCryptoAlgo.getCipherTransformation());
+            c.init(Cipher.ENCRYPT_MODE, key, 
encrCryptoAlgo.getAlgoParamSpec(keyspec));
 
             logger.debug("<== RangerMasterKey.encryptKey()");
 
@@ -465,10 +589,10 @@ private byte[] encryptKey(byte[] data, PBEKeySpec 
keyspec) throws Throwable {
         return null;
     }
 
-    private SecretKey getPasswordKey(PBEKeySpec keyspec) throws Throwable {
+    private SecretKey getPasswordKey(PBEKeySpec keyspec, String cryptoAlgo) 
throws Throwable {
         logger.debug("==> RangerMasterKey.getPasswordKey()");
 
-        SecretKeyFactory factory = SecretKeyFactory.getInstance(pbeAlgo);
+        SecretKeyFactory factory = SecretKeyFactory.getInstance(cryptoAlgo);
 
         logger.debug("<== RangerMasterKey.getPasswordKey()");
 
@@ -476,14 +600,11 @@ private SecretKey getPasswordKey(PBEKeySpec keyspec) 
throws Throwable {
     }
 
     private byte[] decryptKey(byte[] encrypted, PBEKeySpec keySpec) throws 
Throwable {
-        SecretKey key = getPasswordKey(keySpec);
-
+        SecretKey key = getPasswordKey(keySpec, pbeAlgo);
         if (keySpec.getSalt() != null) {
-            PBEParameterSpec paramSpec = new 
PBEParameterSpec(keySpec.getSalt(), keySpec.getIterationCount());
-            Cipher           c         = 
Cipher.getInstance(key.getAlgorithm());
-
-            c.init(Cipher.DECRYPT_MODE, key, paramSpec);
-
+            AlgorithmParameterSpec algoParamSpec =  
SupportedPBECryptoAlgo.valueOf(pbeAlgo).getAlgoParamSpec(keySpec);
+            Cipher c = 
Cipher.getInstance(SupportedPBECryptoAlgo.valueOf(pbeAlgo).getCipherTransformation());
+            c.init(Cipher.DECRYPT_MODE, key, algoParamSpec);
             return c.doFinal(encrypted);
         }
 
diff --git 
a/kms/src/main/java/org/apache/hadoop/crypto/key/SupportedPBECryptoAlgo.java 
b/kms/src/main/java/org/apache/hadoop/crypto/key/SupportedPBECryptoAlgo.java
new file mode 100644
index 000000000..d2ad0e926
--- /dev/null
+++ b/kms/src/main/java/org/apache/hadoop/crypto/key/SupportedPBECryptoAlgo.java
@@ -0,0 +1,74 @@
+/*
+ * 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.hadoop.crypto.key;
+
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.function.Function;
+
+public enum SupportedPBECryptoAlgo {
+    @Deprecated
+    PBEWithMD5AndTripleDES("PBEWithMD5AndTripleDES",
+      "PBEWithMD5AndTripleDES",
+      0, keySpec -> new PBEParameterSpec(keySpec.getSalt(), 
keySpec.getIterationCount())),
+    @Deprecated
+    PBEWithMD5AndDES("PBEWithMD5AndDES",
+      "PBEWithMD5AndDES",
+      0, keySpec -> new PBEParameterSpec(keySpec.getSalt(), 
keySpec.getIterationCount())),
+    PBKDF2WithHmacSHA256("PBKDF2WithHmacSHA256",
+      "AES/CBC/PKCS7Padding",
+      64 * 4, keySpec -> new IvParameterSpec(keySpec.getSalt()));
+    private final String encrAlgoName;
+    private final String  cipherTransformation;
+    private final int     keyLength;
+    private final Function<PBEKeySpec, AlgorithmParameterSpec> 
algoParamSpecFunc;
+
+    SupportedPBECryptoAlgo(String encrAlgoName, String cipherTransformation, 
int keyLength, Function<PBEKeySpec, AlgorithmParameterSpec> algoParamSpecFunc) {
+        this.encrAlgoName         = encrAlgoName;
+        this.cipherTransformation = cipherTransformation;
+        this.keyLength            = keyLength;
+        this.algoParamSpecFunc    = algoParamSpecFunc;
+    }
+
+    public int getKeyLength() {
+        return this.keyLength;
+    }
+
+    public String getAlgoName() {
+        return this.encrAlgoName;
+    }
+
+    public String getCipherTransformation() {
+        return this.cipherTransformation;
+    }
+
+    public AlgorithmParameterSpec getAlgoParamSpec(PBEKeySpec keySpec) {
+        return this.algoParamSpecFunc.apply(keySpec);
+    }
+
+    public static SupportedPBECryptoAlgo getFIPSCompliantAlgorithm() {
+        return SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256;
+    }
+
+    public static boolean isFIPSCompliantAlgorithm(SupportedPBECryptoAlgo 
encrAlgo) {
+        return SupportedPBECryptoAlgo.PBKDF2WithHmacSHA256.equals(encrAlgo);
+    }
+}


Reply via email to