NIFI-10085 Removed CryptoUtility.isUnlimitedStrengthCryptoSupported() and references

This closes #6098

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Emilio Setiadarma 2022-06-03 00:38:13 -07:00 committed by exceptionfactory
parent 4aa0d31d9f
commit 58f93d1f8b
No known key found for this signature in database
GPG Key ID: 29B6A52D2AAE8DBA
10 changed files with 11 additions and 238 deletions

View File

@ -31,7 +31,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -327,14 +326,6 @@ public class CipherUtility {
return MAX_PASSWORD_LENGTH_BY_ALGORITHM.getOrDefault(encryptionMethod.getAlgorithm(), -1);
}
public static boolean isUnlimitedStrengthCryptoSupported() {
try {
return (Cipher.getMaxAllowedKeyLength("AES") > DEFAULT_MAX_ALLOWED_KEY_LENGTH);
} catch (NoSuchAlgorithmException e) {
return false;
}
}
/**
* Returns the salt length for various PBE algorithms. These values were determined empirically from configured/chosen legacy values from the earlier version of the project.
*

View File

@ -59,11 +59,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase {
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
if (CipherUtility.isUnlimitedStrengthCryptoSupported()) {
AES_KEY_LENGTHS = [128, 192, 256]
} else {
AES_KEY_LENGTHS = [128]
}
AES_KEY_LENGTHS = [128, 192, 256]
}
@BeforeEach
@ -225,9 +221,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase {
@Test
void testGetCipherWithUnlimitedStrengthShouldBeInternallyConsistent() throws Exception {
// Arrange
Assumptions.assumeTrue(CipherUtility.isUnlimitedStrengthCryptoSupported(),
"Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.")
final String PASSWORD = "shortPassword"
final byte[] SALT = cipherProvider.generateSalt()

View File

@ -59,11 +59,7 @@ class BcryptCipherProviderGroovyTest {
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
if (CipherUtility.isUnlimitedStrengthCryptoSupported()) {
AES_KEY_LENGTHS = [128, 192, 256]
} else {
AES_KEY_LENGTHS = [128]
}
AES_KEY_LENGTHS = [128, 192, 256]
}
@Test
@ -129,9 +125,6 @@ class BcryptCipherProviderGroovyTest {
@Test
void testGetCipherWithUnlimitedStrengthShouldBeInternallyConsistent() throws Exception {
// Arrange
assumeTrue(CipherUtility.isUnlimitedStrengthCryptoSupported(),
"Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.")
RandomIVPBECipherProvider cipherProvider = new BcryptCipherProvider(4)
final String PASSWORD = "shortPassword"

View File

@ -233,47 +233,4 @@ class NiFiLegacyCipherProviderGroovyTest {
assert plaintext.equals(recovered)
}
}
/**
* This test determines for each PBE encryption algorithm if it actually requires the JCE unlimited strength jurisdiction policies to be installed.
* Even some algorithms that use 128-bit keys (which should be allowed on all systems) throw exceptions because BouncyCastle derives the key
* from the password using a long digest result at the time of key length checking.
* @throws IOException
*/
@EnabledIfSystemProperty(named = "legacyCipherTest", matches = "true", disabledReason = "Only needed once to determine max supported password lengths")
@Test
void testShouldDetermineDependenceOnUnlimitedStrengthCrypto() throws IOException {
def encryptionMethods = EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") }
boolean unlimitedCryptoSupported = CipherUtility.isUnlimitedStrengthCryptoSupported()
logger.info("This JVM supports unlimited strength crypto: ${unlimitedCryptoSupported}")
def longestSupportedPasswordByEM = [:]
encryptionMethods.each { EncryptionMethod encryptionMethod ->
logger.info("Attempting ${encryptionMethod.name()} (${encryptionMethod.algorithm}) which claims unlimited strength required: ${encryptionMethod.unlimitedStrength}")
(1..20).find { int length ->
String password = "x" * length
try {
NiFiLegacyCipherProvider cipherProvider = new NiFiLegacyCipherProvider()
Cipher cipher = cipherProvider.getCipher(encryptionMethod, password, true)
return false
} catch (Exception e) {
logger.error("Unable to create the cipher with ${encryptionMethod.algorithm} and password ${password} (${password.length()}) due to ${e.getMessage()}")
if (!longestSupportedPasswordByEM.containsKey(encryptionMethod)) {
longestSupportedPasswordByEM.put(encryptionMethod, password.length() - 1)
}
return true
}
}
logger.info("\n")
}
logger.info("Longest supported password by encryption method:")
longestSupportedPasswordByEM.each { EncryptionMethod encryptionMethod, int length ->
logger.info("\t${encryptionMethod.algorithm}\t${length}")
}
}
}

View File

@ -33,7 +33,6 @@ import java.security.Security
import static groovy.test.GroovyAssert.shouldFail
import static org.junit.Assert.fail
import static org.junit.jupiter.api.Assumptions.assumeTrue
class OpenSSLPKCS5CipherProviderGroovyTest {
private static final Logger logger = LoggerFactory.getLogger(OpenSSLPKCS5CipherProviderGroovyTest.class)
@ -107,9 +106,6 @@ class OpenSSLPKCS5CipherProviderGroovyTest {
@Test
void testGetCipherWithUnlimitedStrengthShouldBeInternallyConsistent() throws Exception {
// Arrange
assumeTrue(CipherUtility.isUnlimitedStrengthCryptoSupported(),
"Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.")
OpenSSLPKCS5CipherProvider cipherProvider = new OpenSSLPKCS5CipherProvider()
final String PASSWORD = "shortPassword"

View File

@ -20,7 +20,6 @@ import org.apache.commons.codec.binary.Hex
import org.apache.nifi.security.util.EncryptionMethod
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.EnabledIfSystemProperty
import org.slf4j.Logger
@ -31,7 +30,6 @@ import java.security.Security
import static groovy.test.GroovyAssert.shouldFail
import static org.junit.Assert.assertTrue
import static org.junit.jupiter.api.Assumptions.assumeTrue
class PBKDF2CipherProviderGroovyTest {
private static final Logger logger = LoggerFactory.getLogger(PBKDF2CipherProviderGroovyTest.class)
@ -58,11 +56,7 @@ class PBKDF2CipherProviderGroovyTest {
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
if (CipherUtility.isUnlimitedStrengthCryptoSupported()) {
AES_KEY_LENGTHS = [128, 192, 256]
} else {
AES_KEY_LENGTHS = [128]
}
AES_KEY_LENGTHS = [128, 192, 256]
}
@Test

View File

@ -23,7 +23,6 @@ import org.apache.nifi.security.util.crypto.scrypt.Scrypt
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.condition.EnabledIfSystemProperty
import org.slf4j.Logger
@ -38,7 +37,6 @@ import java.security.Security
import static groovy.test.GroovyAssert.shouldFail
import static org.junit.Assert.assertTrue
import static org.junit.jupiter.api.Assumptions.assumeTrue
class ScryptCipherProviderGroovyTest {
private static final Logger logger = LoggerFactory.getLogger(ScryptCipherProviderGroovyTest.class)
@ -63,11 +61,7 @@ class ScryptCipherProviderGroovyTest {
logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
}
if (CipherUtility.isUnlimitedStrengthCryptoSupported()) {
AES_KEY_LENGTHS = [128, 192, 256]
} else {
AES_KEY_LENGTHS = [128]
}
AES_KEY_LENGTHS = [128, 192, 256]
}
@BeforeEach
@ -135,9 +129,6 @@ class ScryptCipherProviderGroovyTest {
@Test
void testGetCipherWithUnlimitedStrengthShouldBeInternallyConsistent() throws Exception {
// Arrange
assumeTrue(CipherUtility.isUnlimitedStrengthCryptoSupported(),
"Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.")
final String PASSWORD = "shortPassword"
final byte[] SALT = cipherProvider.generateSalt()

View File

@ -437,8 +437,6 @@ public class EncryptContent extends AbstractProcessor {
private List<ValidationResult> validatePassword(EncryptionMethod encryptionMethod, KeyDerivationFunction kdf, String password, boolean allowWeakCrypto) {
List<ValidationResult> validationResults = new ArrayList<>();
boolean limitedStrengthCrypto = !CipherUtility.isUnlimitedStrengthCryptoSupported();
// Password required (short circuits validation because other conditions depend on password presence)
if (StringUtils.isEmpty(password)) {
validationResults.add(new ValidationResult.Builder().subject(PASSWORD.getName())
@ -456,40 +454,13 @@ public class EncryptContent extends AbstractProcessor {
}
}
// Multiple checks on machine with limited strength crypto
if (limitedStrengthCrypto) {
// Cannot use unlimited strength ciphers on machine that lacks policies
if (encryptionMethod.isUnlimitedStrength()) {
validationResults.add(new ValidationResult.Builder().subject(ENCRYPTION_ALGORITHM.getName())
.explanation(encryptionMethod.name() + " (" + encryptionMethod.getAlgorithm() + ") is not supported by this JVM due to lacking JCE Unlimited " +
"Strength Jurisdiction Policy files. See Admin Guide.").build());
}
// Check if the password exceeds the limit
final boolean passwordLongerThanLimit = !CipherUtility.passwordLengthIsValidForAlgorithmOnLimitedStrengthCrypto(passwordBytesLength, encryptionMethod);
if (passwordLongerThanLimit) {
int maxPasswordLength = CipherUtility.getMaximumPasswordLengthForAlgorithmOnLimitedStrengthCrypto(encryptionMethod);
validationResults.add(new ValidationResult.Builder().subject(PASSWORD.getName())
.explanation("Password length greater than " + maxPasswordLength + " characters is not supported by this JVM" +
" due to lacking JCE Unlimited Strength Jurisdiction Policy files. See Admin Guide.").build());
}
}
return validationResults;
}
private List<ValidationResult> validateKeyed(EncryptionMethod encryptionMethod, KeyDerivationFunction kdf, String keyHex, String password, boolean allowWeakCrypto, boolean encrypt) {
List<ValidationResult> validationResults = new ArrayList<>();
boolean limitedStrengthCrypto = !CipherUtility.isUnlimitedStrengthCryptoSupported();
if (limitedStrengthCrypto) {
if (encryptionMethod.isUnlimitedStrength()) {
validationResults.add(new ValidationResult.Builder().subject(ENCRYPTION_ALGORITHM.getName())
.explanation(encryptionMethod.name() + " (" + encryptionMethod.getAlgorithm() + ") is not supported by this JVM due to lacking JCE Unlimited " +
"Strength Jurisdiction Policy files. See Admin Guide.").build());
}
}
int allowedKeyLength = PasswordBasedEncryptor.getMaxAllowedKeyLength(ENCRYPTION_ALGORITHM.getName());
// Scenario 1: RKH is present & KDF == NONE

View File

@ -35,7 +35,6 @@ import org.apache.nifi.util.TestRunners
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.After
import org.junit.Assert
import org.junit.Assume
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
@ -81,9 +80,6 @@ class TestEncryptContentGroovy {
@Test
void testShouldValidateMaxKeySizeForAlgorithmsOnUnlimitedStrengthJVM() throws IOException {
// Arrange
Assume.assumeTrue("Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.",
CipherUtility.isUnlimitedStrengthCryptoSupported())
final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class)
Collection<ValidationResult> results
MockProcessContext pc
@ -116,50 +112,6 @@ class TestEncryptContentGroovy {
Assert.assertTrue(message, vr.toString().contains(expectedResult))
}
@Test
void testShouldValidateMaxKeySizeForAlgorithmsOnLimitedStrengthJVM() throws IOException {
// Arrange
Assume.assumeTrue("Test is being skipped because this JVM supports unlimited strength crypto.",
!CipherUtility.isUnlimitedStrengthCryptoSupported())
final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class)
Collection<ValidationResult> results
MockProcessContext pc
EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC
final int MAX_KEY_LENGTH = 128
final String TOO_LONG_KEY_HEX = "ab" * (MAX_KEY_LENGTH / 8 + 1)
logger.info("Using key ${TOO_LONG_KEY_HEX} (${TOO_LONG_KEY_HEX.length() * 4} bits)")
runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE)
runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name())
runner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, KeyDerivationFunction.NONE.name())
runner.setProperty(EncryptContent.RAW_KEY_HEX, TOO_LONG_KEY_HEX)
runner.enqueue(new byte[0])
pc = (MockProcessContext) runner.getProcessContext()
// Act
results = pc.validate()
// Assert
// Two validation problems -- max key size and key length is invalid
Assert.assertEquals(2, results.size())
logger.expected(results)
ValidationResult maxKeyLengthVR = results.first()
String expectedResult = "'raw-key-hex' is invalid because Key length greater than ${MAX_KEY_LENGTH} bits is not supported"
String message = "'" + maxKeyLengthVR.toString() + "' contains '" + expectedResult + "'"
Assert.assertTrue(message, maxKeyLengthVR.toString().contains(expectedResult))
expectedResult = "'raw-key-hex' is invalid because Key must be valid length [128, 192, 256]"
ValidationResult keyLengthInvalidVR = results.last()
message = "'" + keyLengthInvalidVR.toString() + "' contains '" + expectedResult + "'"
Assert.assertTrue(message, keyLengthInvalidVR.toString().contains(expectedResult))
}
@Test
void testShouldValidateKeyFormatAndSizeForAlgorithms() throws IOException {
// Arrange
@ -379,10 +331,6 @@ class TestEncryptContentGroovy {
final String PASSWORD = "short"
def encryptionMethods = EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") }
if (!CipherUtility.isUnlimitedStrengthCryptoSupported()) {
// Remove all unlimited strength algorithms
encryptionMethods.removeAll { it.unlimitedStrength }
}
runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE)
runner.setProperty(EncryptContent.PASSWORD, PASSWORD)
@ -825,53 +773,6 @@ class TestEncryptContentGroovy {
assert [fiveSecondDiff, tenSecondDiff, parsedTenSecondDiff].every { it.days == 0 }
}
@Test
void testShouldCheckMaximumLengthOfPasswordOnLimitedStrengthCryptoJVM() throws IOException {
// Arrange
Assume.assumeTrue("Only run on systems with limited strength crypto", !CipherUtility.isUnlimitedStrengthCryptoSupported())
final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent())
testRunner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, KeyDerivationFunction.NIFI_LEGACY.name())
testRunner.setProperty(EncryptContent.ALLOW_WEAK_CRYPTO, WEAK_CRYPTO_ALLOWED)
Collection<ValidationResult> results
MockProcessContext pc
def encryptionMethods = EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") }
// Use .find instead of .each to allow "breaks" using return false
encryptionMethods.find { EncryptionMethod encryptionMethod ->
def invalidPasswordLength = CipherUtility.getMaximumPasswordLengthForAlgorithmOnLimitedStrengthCrypto(encryptionMethod) + 1
String tooLongPassword = "x" * invalidPasswordLength
if (encryptionMethod.isUnlimitedStrength() || encryptionMethod.isKeyedCipher()) {
return false
// cannot test unlimited strength in unit tests because it's not enabled by the JVM by default.
}
testRunner.setProperty(EncryptContent.PASSWORD, tooLongPassword)
logger.info("Attempting ${encryptionMethod.algorithm} with password of length ${invalidPasswordLength}")
testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name())
testRunner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE)
testRunner.clearTransferState()
testRunner.enqueue(new byte[0])
pc = (MockProcessContext) testRunner.getProcessContext()
// Act
results = pc.validate()
// Assert
logger.expected(results)
Assert.assertEquals(1, results.size())
ValidationResult passwordLengthVR = results.first()
String expectedResult = "'Password' is invalid because Password length greater than ${invalidPasswordLength - 1} characters is not supported by" +
" this JVM due to lacking JCE Unlimited Strength Jurisdiction Policy files."
String message = "'" + passwordLengthVR.toString() + "' contains '" + expectedResult + "'"
Assert.assertTrue(message, passwordLengthVR.toString().contains(expectedResult))
}
}
@Test
void testShouldCheckLengthOfPasswordWhenNotAllowed() throws IOException {
// Arrange
@ -883,7 +784,7 @@ class TestEncryptContentGroovy {
def encryptionMethods = EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") }
boolean limitedStrengthCrypto = !CipherUtility.isUnlimitedStrengthCryptoSupported()
boolean limitedStrengthCrypto = false
boolean allowWeakCrypto = false
testRunner.setProperty(EncryptContent.ALLOW_WEAK_CRYPTO, WEAK_CRYPTO_NOT_ALLOWED)
@ -933,7 +834,7 @@ class TestEncryptContentGroovy {
def encryptionMethods = EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") }
boolean limitedStrengthCrypto = !CipherUtility.isUnlimitedStrengthCryptoSupported()
boolean limitedStrengthCrypto = false
boolean allowWeakCrypto = true
testRunner.setProperty(EncryptContent.ALLOW_WEAK_CRYPTO, WEAK_CRYPTO_ALLOWED)

View File

@ -39,7 +39,6 @@ import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.security.util.EncryptionMethod;
import org.apache.nifi.security.util.KeyDerivationFunction;
import org.apache.nifi.security.util.crypto.CipherUtility;
import org.apache.nifi.security.util.crypto.PasswordBasedEncryptor;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.MockProcessContext;
@ -209,8 +208,8 @@ public class TestEncryptContent {
final String AES_ALGORITHM = EncryptionMethod.MD5_256AES.getAlgorithm();
final String DES_ALGORITHM = EncryptionMethod.MD5_DES.getAlgorithm();
final int AES_MAX_LENGTH = CipherUtility.isUnlimitedStrengthCryptoSupported() ? Integer.MAX_VALUE : 128;
final int DES_MAX_LENGTH = CipherUtility.isUnlimitedStrengthCryptoSupported() ? Integer.MAX_VALUE : 64;
final int AES_MAX_LENGTH = Integer.MAX_VALUE;
final int DES_MAX_LENGTH = Integer.MAX_VALUE;
// Act
int determinedAESMaxLength = PasswordBasedEncryptor.getMaxAllowedKeyLength(AES_ALGORITHM);
@ -224,8 +223,6 @@ public class TestEncryptContent {
@Test
public void testShouldDecryptOpenSSLRawSalted() throws IOException {
// Arrange
Assume.assumeTrue("Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.",
CipherUtility.isUnlimitedStrengthCryptoSupported());
final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent());
@ -258,8 +255,6 @@ public class TestEncryptContent {
@Test
public void testShouldDecryptOpenSSLRawUnsalted() throws IOException {
// Arrange
Assume.assumeTrue("Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.",
CipherUtility.isUnlimitedStrengthCryptoSupported());
final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent());
@ -484,18 +479,9 @@ public class TestEncryptContent {
runner.setProperty(EncryptContent.PASSWORD, "ThisIsAPasswordThatIsLongerThanSixteenCharacters");
pc = (MockProcessContext) runner.getProcessContext();
results = pc.validate();
if (!CipherUtility.isUnlimitedStrengthCryptoSupported()) {
logger.info(results.toString());
Assert.assertEquals(1, results.size());
for (final ValidationResult vr : results) {
Assert.assertTrue(
"Did not successfully catch validation error of a long password in a non-JCE Unlimited Strength environment",
vr.toString().contains("Password length greater than " + CipherUtility.getMaximumPasswordLengthForAlgorithmOnLimitedStrengthCrypto(encryptionMethod)
+ " characters is not supported by this JVM due to lacking JCE Unlimited Strength Jurisdiction Policy files."));
}
} else {
Assert.assertEquals(results.toString(), 0, results.size());
}
Assert.assertEquals(results.toString(), 0, results.size());
runner.removeProperty(EncryptContent.PASSWORD);
runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name());