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