diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/EncryptionMethod.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/EncryptionMethod.java index 62d72a2c35..0ba3d59a03 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/EncryptionMethod.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/EncryptionMethod.java @@ -49,7 +49,8 @@ public enum EncryptionMethod { SHA_TWOFISH("PBEWITHSHAANDTWOFISH-CBC", "BC", false, false), PGP("PGP", "BC", false, false), PGP_ASCII_ARMOR("PGP-ASCII-ARMOR", "BC", false, false), - // New encryption methods which used keyed encryption + // AES/CBC/NoPadding supported for decryption + AES_CBC_NO_PADDING("AES/CBC/NoPadding", "BC", false, true), AES_CBC("AES/CBC/PKCS7Padding", "BC", false, true), AES_CTR("AES/CTR/NoPadding", "BC", false, true), AES_GCM("AES/GCM/NoPadding", "BC", false, true); diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/AESKeyedCipherProviderGroovyTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/AESKeyedCipherProviderGroovyTest.groovy index 80821496ba..35d1464a8e 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/AESKeyedCipherProviderGroovyTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/AESKeyedCipherProviderGroovyTest.groovy @@ -43,6 +43,8 @@ class AESKeyedCipherProviderGroovyTest { private static final String KEY_HEX = "0123456789ABCDEFFEDCBA9876543210" + private static final String PLAINTEXT = "ExactBlockSizeRequiredForProcess" + private static final List keyedEncryptionMethods = EncryptionMethod.values().findAll { it.keyedCipher } private static final SecretKey key = new SecretKeySpec(Hex.decodeHex(KEY_HEX as char[]), "AES") @@ -73,8 +75,6 @@ class AESKeyedCipherProviderGroovyTest { // Arrange KeyedCipherProvider cipherProvider = new AESKeyedCipherProvider() - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : keyedEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -84,7 +84,7 @@ class AESKeyedCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, key, iv, false) @@ -93,7 +93,7 @@ class AESKeyedCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -102,8 +102,6 @@ class AESKeyedCipherProviderGroovyTest { // Arrange KeyedCipherProvider cipherProvider = new AESKeyedCipherProvider() - final String plaintext = "This is a plaintext message." - // Act keyedEncryptionMethods.each { EncryptionMethod em -> logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -113,7 +111,7 @@ class AESKeyedCipherProviderGroovyTest { // Initialize a cipher for encryption Cipher cipher = cipherProvider.getCipher(em, key, iv, true) - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, key, iv, false) @@ -122,7 +120,7 @@ class AESKeyedCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -134,8 +132,6 @@ class AESKeyedCipherProviderGroovyTest { KeyedCipherProvider cipherProvider = new AESKeyedCipherProvider() final List LONG_KEY_LENGTHS = [192, 256] - final String plaintext = "This is a plaintext message." - SecureRandom secureRandom = new SecureRandom() // Act @@ -156,7 +152,7 @@ class AESKeyedCipherProviderGroovyTest { // Initialize a cipher for encryption Cipher cipher = cipherProvider.getCipher(em, localKey, iv, true) - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, localKey, iv, false) @@ -165,7 +161,7 @@ class AESKeyedCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } } @@ -240,7 +236,7 @@ class AESKeyedCipherProviderGroovyTest { // Arrange KeyedCipherProvider cipherProvider = new AESKeyedCipherProvider() - final String PLAINTEXT = "This is a plaintext message." + final String plaintext = "This is a plaintext message." // These values can be generated by running `$ ./openssl_aes.rb` in the terminal final byte[] IV = Hex.decodeHex("e0bc8cc7fbc0bdfdc184dc22ce2fcb5b" as char[]) @@ -261,7 +257,7 @@ class AESKeyedCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert PLAINTEXT.equals(recovered) + assert plaintext.equals(recovered) } @Test @@ -269,8 +265,6 @@ class AESKeyedCipherProviderGroovyTest { // Arrange KeyedCipherProvider cipherProvider = new AESKeyedCipherProvider() - final String plaintext = "This is a plaintext message." - // Act keyedEncryptionMethods.each { EncryptionMethod em -> logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -280,7 +274,7 @@ class AESKeyedCipherProviderGroovyTest { // Initialize a cipher for encryption Cipher cipher = cipherProvider.getCipher(em, key, iv, true) - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") def msg = shouldFail(IllegalArgumentException) { diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/Argon2CipherProviderGroovyTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/Argon2CipherProviderGroovyTest.groovy index dde7fcb14c..79c92d42ca 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/Argon2CipherProviderGroovyTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/Argon2CipherProviderGroovyTest.groovy @@ -42,6 +42,8 @@ import static groovy.test.GroovyAssert.shouldFail class Argon2CipherProviderGroovyTest extends GroovyTestCase { private static final Logger logger = LoggerFactory.getLogger(Argon2CipherProviderGroovyTest.class) + private static final String PLAINTEXT = "ExactBlockSizeRequiredForProcess" + private static List strongKDFEncryptionMethods private static final int DEFAULT_KEY_LENGTH = 128 @@ -85,8 +87,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { final String PASSWORD = "shortPassword" final byte[] SALT = cipherProvider.generateSalt() - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -96,7 +96,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, DEFAULT_KEY_LENGTH, false) @@ -105,7 +105,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -210,8 +210,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("01" * 16 as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -220,7 +218,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, false) @@ -229,7 +227,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -244,8 +242,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { final int LONG_KEY_LENGTH = 256 - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -255,7 +251,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, LONG_KEY_LENGTH, false) @@ -264,7 +260,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -369,8 +365,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("00" * 16 as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -379,7 +373,7 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") def msg = shouldFail(IllegalArgumentException) { @@ -399,8 +393,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("01" * 16 as char[]) - final String PLAINTEXT = "This is a plaintext message." - final def VALID_KEY_LENGTHS = AES_KEY_LENGTHS EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC @@ -432,8 +424,6 @@ class Argon2CipherProviderGroovyTest extends GroovyTestCase { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("00" * 16 as char[]) - final String PLAINTEXT = "This is a plaintext message." - final def INVALID_KEY_LENGTHS = [-1, 40, 64, 112, 512] EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/BcryptCipherProviderGroovyTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/BcryptCipherProviderGroovyTest.groovy index b60c58cfdf..4968d3bafe 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/BcryptCipherProviderGroovyTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/BcryptCipherProviderGroovyTest.groovy @@ -46,6 +46,8 @@ import static org.junit.Assert.assertTrue class BcryptCipherProviderGroovyTest { private static final Logger logger = LoggerFactory.getLogger(BcryptCipherProviderGroovyTest.class) + private static final String PLAINTEXT = "ExactBlockSizeRequiredForProcess" + private static List strongKDFEncryptionMethods private static final int DEFAULT_KEY_LENGTH = 128 @@ -86,8 +88,6 @@ class BcryptCipherProviderGroovyTest { final String PASSWORD = "shortPassword" final byte[] SALT = cipherProvider.generateSalt() - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -97,7 +97,7 @@ class BcryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, DEFAULT_KEY_LENGTH, false) @@ -106,7 +106,7 @@ class BcryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -119,8 +119,6 @@ class BcryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("01" * 16 as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -129,7 +127,7 @@ class BcryptCipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, false) @@ -138,7 +136,7 @@ class BcryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -155,8 +153,6 @@ class BcryptCipherProviderGroovyTest { final int LONG_KEY_LENGTH = 256 - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -166,7 +162,7 @@ class BcryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, LONG_KEY_LENGTH, false) @@ -175,7 +171,7 @@ class BcryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -389,8 +385,6 @@ class BcryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("00" * 16 as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -399,7 +393,7 @@ class BcryptCipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") def msg = shouldFail(IllegalArgumentException) { @@ -420,8 +414,6 @@ class BcryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("01" * 16 as char[]) - final String PLAINTEXT = "This is a plaintext message." - // Currently only AES ciphers are compatible with Bcrypt, so redundant to test all algorithms final def VALID_KEY_LENGTHS = AES_KEY_LENGTHS EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC @@ -456,8 +448,6 @@ class BcryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("00" * 16 as char[]) - final String PLAINTEXT = "This is a plaintext message." - // Currently only AES ciphers are compatible with Bcrypt, so redundant to test all algorithms final def INVALID_KEY_LENGTHS = [-1, 40, 64, 112, 512] EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC @@ -520,8 +510,6 @@ class BcryptCipherProviderGroovyTest { byte[] keyDigestBytes = sha512.digest(hashOutputBytes[-31..-1] as byte[]) logger.info("Key digest (${keyDigestBytes.length}): ${Hex.encodeHexString(keyDigestBytes)}") - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -531,7 +519,7 @@ class BcryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, DEFAULT_KEY_LENGTH, false) @@ -551,8 +539,8 @@ class BcryptCipherProviderGroovyTest { logger.info("Verified: ${verificationRecovered}") // Assert - assert plaintext == recovered - assert plaintext == verificationRecovered + assert PLAINTEXT == recovered + assert PLAINTEXT == verificationRecovered } } @@ -564,8 +552,6 @@ class BcryptCipherProviderGroovyTest { final String PASSWORD = "shortPassword" final byte[] SALT = cipherProvider.generateSalt() - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -575,7 +561,7 @@ class BcryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getLegacyDecryptCipher(em, PASSWORD, SALT, iv, DEFAULT_KEY_LENGTH) @@ -584,7 +570,7 @@ class BcryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext == recovered + assert PLAINTEXT == recovered } } @@ -597,8 +583,6 @@ class BcryptCipherProviderGroovyTest { final byte[] SALT = null final EncryptionMethod em = EncryptionMethod.AES_CBC - final String plaintext = "This is a plaintext message." - // Act logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -610,7 +594,7 @@ class BcryptCipherProviderGroovyTest { } logger.expected("Encrypt error: ${encryptMsg}") - byte[] cipherBytes = plaintext.reverse().getBytes(StandardCharsets.UTF_8) + byte[] cipherBytes = PLAINTEXT.reverse().getBytes(StandardCharsets.UTF_8) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") def decryptMsg = shouldFail(IllegalArgumentException) { diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/CipherUtilityGroovyTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/CipherUtilityGroovyTest.groovy index 424b810b1a..bb42a90932 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/CipherUtilityGroovyTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/CipherUtilityGroovyTest.groovy @@ -40,7 +40,7 @@ class CipherUtilityGroovyTest extends GroovyTestCase { private static final List SYMMETRIC_ALGORITHMS = EncryptionMethod.values().findAll { it.algorithm.startsWith("PBE") || it.algorithm.startsWith("AES") }*.algorithm private static final Map> ALGORITHMS_MAPPED_BY_CIPHER = SYMMETRIC_ALGORITHMS.groupBy { String algorithm -> CIPHERS.find { algorithm.contains(it) } } - // Manually mapped as of 01/19/16 0.5.0 + // Manually mapped as of 03/21/21 1.13.0 private static final Map> ALGORITHMS_MAPPED_BY_KEY_LENGTH = [ (40) : ["PBEWITHSHAAND40BITRC2-CBC", "PBEWITHSHAAND40BITRC4"], @@ -56,18 +56,21 @@ class CipherUtilityGroovyTest extends GroovyTestCase { "PBEWITHSHAAND128BITRC2-CBC", "PBEWITHSHAAND128BITRC4", "PBEWITHSHAANDTWOFISH-CBC", + "AES/CBC/NoPadding", "AES/CBC/PKCS7Padding", "AES/CTR/NoPadding", "AES/GCM/NoPadding"], (192): ["PBEWITHMD5AND192BITAES-CBC-OPENSSL", "PBEWITHSHA256AND192BITAES-CBC-BC", "PBEWITHSHAAND192BITAES-CBC-BC", + "AES/CBC/NoPadding", "AES/CBC/PKCS7Padding", "AES/CTR/NoPadding", "AES/GCM/NoPadding"], (256): ["PBEWITHMD5AND256BITAES-CBC-OPENSSL", "PBEWITHSHA256AND256BITAES-CBC-BC", "PBEWITHSHAAND256BITAES-CBC-BC", + "AES/CBC/NoPadding", "AES/CBC/PKCS7Padding", "AES/CTR/NoPadding", "AES/GCM/NoPadding"] diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/PBKDF2CipherProviderGroovyTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/PBKDF2CipherProviderGroovyTest.groovy index 795f3dc271..c03795b673 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/PBKDF2CipherProviderGroovyTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/PBKDF2CipherProviderGroovyTest.groovy @@ -35,6 +35,8 @@ import static org.junit.Assert.assertTrue class PBKDF2CipherProviderGroovyTest { private static final Logger logger = LoggerFactory.getLogger(PBKDF2CipherProviderGroovyTest.class) + private static final String PLAINTEXT = "ExactBlockSizeRequiredForProcess" + private static List strongKDFEncryptionMethods public static final String MICROBENCHMARK = "microbenchmark" @@ -79,8 +81,6 @@ class PBKDF2CipherProviderGroovyTest { final String PASSWORD = "shortPassword" final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -90,7 +90,7 @@ class PBKDF2CipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, DEFAULT_KEY_LENGTH, false) @@ -99,7 +99,7 @@ class PBKDF2CipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -140,8 +140,6 @@ class PBKDF2CipherProviderGroovyTest { final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) final byte[] IV = Hex.decodeHex(IV_HEX as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -150,7 +148,7 @@ class PBKDF2CipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, false) @@ -159,7 +157,7 @@ class PBKDF2CipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -176,8 +174,6 @@ class PBKDF2CipherProviderGroovyTest { final int LONG_KEY_LENGTH = 256 - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -187,7 +183,7 @@ class PBKDF2CipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, LONG_KEY_LENGTH, false) @@ -196,7 +192,7 @@ class PBKDF2CipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -209,7 +205,6 @@ class PBKDF2CipherProviderGroovyTest { final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) final byte[] IV = Hex.decodeHex(IV_HEX as char[]) - final String plaintext = "This is a plaintext message." final EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC String prf = "" @@ -232,7 +227,6 @@ class PBKDF2CipherProviderGroovyTest { final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) final byte[] IV = Hex.decodeHex(IV_HEX as char[]) - final String plaintext = "This is a plaintext message." final EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC final PBKDF2CipherProvider SHA512_PROVIDER = new PBKDF2CipherProvider(DEFAULT_PRF, TEST_ITERATION_COUNT) @@ -249,7 +243,7 @@ class PBKDF2CipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(encryptionMethod, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = SHA512_PROVIDER.getCipher(encryptionMethod, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, false) @@ -258,7 +252,7 @@ class PBKDF2CipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } @Test @@ -271,7 +265,6 @@ class PBKDF2CipherProviderGroovyTest { final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) final byte[] IV = Hex.decodeHex(IV_HEX as char[]) - final String plaintext = "This is a plaintext message." final EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC // Act @@ -286,7 +279,7 @@ class PBKDF2CipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(encryptionMethod, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(encryptionMethod, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, false) @@ -295,7 +288,7 @@ class PBKDF2CipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -334,7 +327,6 @@ class PBKDF2CipherProviderGroovyTest { RandomIVPBECipherProvider sha256CP = new PBKDF2CipherProvider("SHA-256", TEST_ITERATION_COUNT) RandomIVPBECipherProvider sha512CP = new PBKDF2CipherProvider("SHA-512", TEST_ITERATION_COUNT) - final String PLAINTEXT = "This is a plaintext message." final String PASSWORD = "thisIsABadPassword" final byte[] SALT = [0x11] * 16 final byte[] IV = [0x22] * 16 @@ -370,8 +362,6 @@ class PBKDF2CipherProviderGroovyTest { final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) final byte[] IV = Hex.decodeHex(IV_HEX as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -380,7 +370,7 @@ class PBKDF2CipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") def msg = shouldFail(IllegalArgumentException) { @@ -426,8 +416,6 @@ class PBKDF2CipherProviderGroovyTest { final byte[] SALT = Hex.decodeHex(SALT_HEX as char[]) final byte[] IV = Hex.decodeHex(IV_HEX as char[]) - final String PLAINTEXT = "This is a plaintext message." - // Currently only AES ciphers are compatible with PBKDF2, so redundant to test all algorithms final def VALID_KEY_LENGTHS = AES_KEY_LENGTHS EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/ScryptCipherProviderGroovyTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/ScryptCipherProviderGroovyTest.groovy index d6dd5b358c..2a92b1f65a 100644 --- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/ScryptCipherProviderGroovyTest.groovy +++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/crypto/ScryptCipherProviderGroovyTest.groovy @@ -46,6 +46,8 @@ import static org.junit.Assert.assertTrue class ScryptCipherProviderGroovyTest { private static final Logger logger = LoggerFactory.getLogger(ScryptCipherProviderGroovyTest.class) + private static final String PLAINTEXT = "ExactBlockSizeRequiredForProcess" + private static List strongKDFEncryptionMethods private static final int DEFAULT_KEY_LENGTH = 128 @@ -88,8 +90,6 @@ class ScryptCipherProviderGroovyTest { final String PASSWORD = "shortPassword" final byte[] SALT = cipherProvider.generateSalt() - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -99,7 +99,7 @@ class ScryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, DEFAULT_KEY_LENGTH, false) @@ -108,7 +108,7 @@ class ScryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -119,8 +119,6 @@ class ScryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("01" * 16 as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -129,7 +127,7 @@ class ScryptCipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, false) @@ -138,7 +136,7 @@ class ScryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -153,8 +151,6 @@ class ScryptCipherProviderGroovyTest { final int LONG_KEY_LENGTH = 256 - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -164,7 +160,7 @@ class ScryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") cipher = cipherProvider.getCipher(em, PASSWORD, SALT, iv, LONG_KEY_LENGTH, false) @@ -173,7 +169,7 @@ class ScryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } } @@ -265,7 +261,6 @@ class ScryptCipherProviderGroovyTest { final String EXPECTED_FORMATTED_SALT = cipherProvider.formatSaltForScrypt(SALT) logger.info("Expected salt: ${EXPECTED_FORMATTED_SALT}") - final String plaintext = "This is a plaintext message." EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC logger.info("Using algorithm: ${encryptionMethod.getAlgorithm()}") @@ -276,7 +271,7 @@ class ScryptCipherProviderGroovyTest { byte[] iv = cipher.getIV() logger.info("IV: ${Hex.encodeHexString(iv)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") // Manually initialize a cipher for decrypt with the expected salt @@ -294,7 +289,7 @@ class ScryptCipherProviderGroovyTest { logger.info("Recovered: ${recovered}") // Assert - assert plaintext.equals(recovered) + assert PLAINTEXT.equals(recovered) } @Test @@ -368,8 +363,6 @@ class ScryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("00" * 16 as char[]) - final String plaintext = "This is a plaintext message." - // Act for (EncryptionMethod em : strongKDFEncryptionMethods) { logger.info("Using algorithm: ${em.getAlgorithm()}") @@ -378,7 +371,7 @@ class ScryptCipherProviderGroovyTest { Cipher cipher = cipherProvider.getCipher(em, PASSWORD, SALT, IV, DEFAULT_KEY_LENGTH, true) logger.info("IV: ${Hex.encodeHexString(IV)}") - byte[] cipherBytes = cipher.doFinal(plaintext.getBytes("UTF-8")) + byte[] cipherBytes = cipher.doFinal(PLAINTEXT.getBytes("UTF-8")) logger.info("Cipher text: ${Hex.encodeHexString(cipherBytes)} ${cipherBytes.length}") def msg = shouldFail(IllegalArgumentException) { @@ -398,8 +391,6 @@ class ScryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("01" * 16 as char[]) - final String PLAINTEXT = "This is a plaintext message." - final def VALID_KEY_LENGTHS = AES_KEY_LENGTHS EncryptionMethod encryptionMethod = EncryptionMethod.AES_CBC @@ -431,8 +422,6 @@ class ScryptCipherProviderGroovyTest { final byte[] SALT = cipherProvider.generateSalt() final byte[] IV = Hex.decodeHex("00" * 16 as char[]) - final String PLAINTEXT = "This is a plaintext message." - // Even though Scrypt can derive keys of arbitrary length, it will fail to validate if the underlying cipher does not support it final def INVALID_KEY_LENGTHS = [-1, 40, 64, 112, 512] // Currently only AES ciphers are compatible with Scrypt, so redundant to test all algorithms diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java index f08337977b..522980c690 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java @@ -322,8 +322,8 @@ public class EncryptContent extends AbstractProcessor { final String password = context.getProperty(PASSWORD).getValue(); final KeyDerivationFunction kdf = KeyDerivationFunction.valueOf(context.getProperty(KEY_DERIVATION_FUNCTION).getValue()); final String keyHex = context.getProperty(RAW_KEY_HEX).getValue(); + final boolean encrypt = context.getProperty(MODE).getValue().equalsIgnoreCase(ENCRYPT_MODE); if (isPGPAlgorithm(algorithm)) { - final boolean encrypt = context.getProperty(MODE).getValue().equalsIgnoreCase(ENCRYPT_MODE); final String publicKeyring = context.getProperty(PUBLIC_KEYRING).getValue(); final String publicUserId = context.getProperty(PUBLIC_KEY_USERID).getValue(); final String privateKeyring = context.getProperty(PRIVATE_KEYRING).getValue(); @@ -334,7 +334,7 @@ public class EncryptContent extends AbstractProcessor { } else { // Not PGP boolean allowWeakCrypto = context.getProperty(ALLOW_WEAK_CRYPTO).getValue().equalsIgnoreCase(WEAK_CRYPTO_ALLOWED_NAME); if (encryptionMethod.isKeyedCipher()) { // Raw key or derived key from password - validationResults.addAll(validateKeyed(encryptionMethod, kdf, keyHex, password, allowWeakCrypto)); + validationResults.addAll(validateKeyed(encryptionMethod, kdf, keyHex, password, allowWeakCrypto, encrypt)); } else { // PBE validationResults.addAll(validatePBE(encryptionMethod, kdf, password, allowWeakCrypto)); } @@ -479,7 +479,7 @@ public class EncryptContent extends AbstractProcessor { } - private List validateKeyed(EncryptionMethod encryptionMethod, KeyDerivationFunction kdf, String keyHex, String password, boolean allowWeakCrypto) { + private List validateKeyed(EncryptionMethod encryptionMethod, KeyDerivationFunction kdf, String keyHex, String password, boolean allowWeakCrypto, boolean encrypt) { List validationResults = new ArrayList<>(); boolean limitedStrengthCrypto = !CipherUtility.isUnlimitedStrengthCryptoSupported(); @@ -526,6 +526,17 @@ public class EncryptContent extends AbstractProcessor { .explanation(KEY_DERIVATION_FUNCTION.getDisplayName() + " is required to be " + StringUtils.join(kdfsForKeyedCipher, ", ") + " when using algorithm " + encryptionMethod.getAlgorithm()).build()); } + + if (encrypt && EncryptionMethod.AES_CBC_NO_PADDING == encryptionMethod) { + validationResults.add(new ValidationResult.Builder() + .subject(ENCRYPTION_ALGORITHM.getDisplayName()) + .input(encryptionMethod.name()) + .explanation(String.format("Encryption not supported for [%s]", encryptionMethod.getAlgorithm())) + .valid(false) + .build() + ); + } + return validationResults; } diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/TestEncryptContentGroovy.groovy b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/TestEncryptContentGroovy.groovy index 1e7d6f99e3..15b3f6d03f 100644 --- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/TestEncryptContentGroovy.groovy +++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/groovy/org/apache/nifi/processors/standard/TestEncryptContentGroovy.groovy @@ -25,6 +25,7 @@ import org.apache.nifi.security.util.KeyDerivationFunction import org.apache.nifi.security.util.crypto.Argon2CipherProvider import org.apache.nifi.security.util.crypto.Argon2SecureHasher import org.apache.nifi.security.util.crypto.CipherUtility +import org.apache.nifi.security.util.crypto.KeyedEncryptor import org.apache.nifi.security.util.crypto.PasswordBasedEncryptor import org.apache.nifi.security.util.crypto.RandomIVPBECipherProvider import org.apache.nifi.util.MockFlowFile @@ -58,6 +59,8 @@ class TestEncryptContentGroovy { private static final String WEAK_CRYPTO_ALLOWED = EncryptContent.WEAK_CRYPTO_ALLOWED_NAME private static final String WEAK_CRYPTO_NOT_ALLOWED = EncryptContent.WEAK_CRYPTO_NOT_ALLOWED_NAME + private static final List SUPPORTED_KEYED_ENCRYPTION_METHODS = EncryptionMethod.values().findAll { it.isKeyedCipher() && it != EncryptionMethod.AES_CBC_NO_PADDING } + @BeforeClass static void setUpOnce() throws Exception { Security.addProvider(new BouncyCastleProvider()) @@ -198,15 +201,13 @@ class TestEncryptContentGroovy { Collection results MockProcessContext pc - def encryptionMethods = EncryptionMethod.values().findAll { it.isKeyedCipher() } - final int VALID_KEY_LENGTH = 128 final String VALID_KEY_HEX = "ab" * (VALID_KEY_LENGTH / 8) logger.info("Using key ${VALID_KEY_HEX} (${VALID_KEY_HEX.length() * 4} bits)") runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE) - encryptionMethods.each { EncryptionMethod encryptionMethod -> + SUPPORTED_KEYED_ENCRYPTION_METHODS.each { EncryptionMethod encryptionMethod -> logger.info("Trying encryption method ${encryptionMethod.name()}") runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name()) @@ -315,8 +316,7 @@ class TestEncryptContentGroovy { Collection results MockProcessContext pc - def keyedEncryptionMethods = EncryptionMethod.values().findAll { it.isKeyedCipher() } - logger.info("Testing keyed encryption methods: ${keyedEncryptionMethods*.name()}") + logger.info("Testing keyed encryption methods: ${SUPPORTED_KEYED_ENCRYPTION_METHODS*.name()}") final int VALID_KEY_LENGTH = 128 final String VALID_KEY_HEX = "ab" * (VALID_KEY_LENGTH / 8) @@ -330,7 +330,7 @@ class TestEncryptContentGroovy { final def VALID_KDFS = KeyDerivationFunction.values().findAll { it.isStrongKDF() } // Scenario 1 - RKH w/ KDF NONE & em in [CBC, CTR, GCM] (no password) - keyedEncryptionMethods.each { EncryptionMethod kem -> + SUPPORTED_KEYED_ENCRYPTION_METHODS.each { EncryptionMethod kem -> logger.info("Trying encryption method ${kem.name()} with KDF ${none.name()}") runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, kem.name()) runner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, none.name()) @@ -440,9 +440,7 @@ class TestEncryptContentGroovy { testRunner.setProperty(EncryptContent.RAW_KEY_HEX, RAW_KEY_HEX) testRunner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, KeyDerivationFunction.NONE.name()) - def keyedCipherEMs = EncryptionMethod.values().findAll { it.isKeyedCipher() } - - keyedCipherEMs.each { EncryptionMethod encryptionMethod -> + SUPPORTED_KEYED_ENCRYPTION_METHODS.each { EncryptionMethod encryptionMethod -> logger.info("Attempting {}", encryptionMethod.name()) testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name()) testRunner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE) @@ -469,6 +467,33 @@ class TestEncryptContentGroovy { } } + @Test + void testDecryptAesCbcNoPadding() { + final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent()) + final String RAW_KEY_HEX = "ab" * 16 + testRunner.setProperty(EncryptContent.RAW_KEY_HEX, RAW_KEY_HEX) + testRunner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, KeyDerivationFunction.NONE.name()) + testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.AES_CBC_NO_PADDING.name()) + testRunner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE) + + final String content = "ExactBlockSizeRequiredForProcess" + final byte[] bytes = content.getBytes(StandardCharsets.UTF_8) + final ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes) + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream() + + final KeyedEncryptor encryptor = new KeyedEncryptor(EncryptionMethod.AES_CBC_NO_PADDING, Hex.decodeHex(RAW_KEY_HEX)) + encryptor.encryptionCallback.process(inputStream, outputStream) + outputStream.close() + + final byte[] encrypted = outputStream.toByteArray() + testRunner.enqueue(encrypted) + testRunner.run() + + testRunner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1) + MockFlowFile flowFile = testRunner.getFlowFilesForRelationship(EncryptContent.REL_SUCCESS).get(0) + flowFile.assertContentEquals(content) + } + // TODO: Implement @Test void testArgon2EncryptionShouldWriteAttributesWithEncryptionMetadata() throws IOException {