private static AllowableValue[] buildEncryptionMethodAllowableValues() { final EncryptionMethod[] encryptionMethods = EncryptionMethod.values(); List<AllowableValue> allowableValues = new ArrayList<>(encryptionMethods.length); for (EncryptionMethod em : encryptionMethods) { allowableValues.add(new AllowableValue(em.name(), em.name(), em.toString())); } return allowableValues.toArray(new AllowableValue[0]); }
throw new IllegalArgumentException(encryptionMethod.name() + " requires a PBECipherProvider");
protected Cipher getInitializedCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, byte[] iv, int keyLength, boolean encryptMode) throws Exception { if (encryptionMethod == null) { throw new IllegalArgumentException("The encryption method must be specified"); } if (!encryptionMethod.isCompatibleWithStrongKDFs()) { throw new IllegalArgumentException(encryptionMethod.name() + " is not compatible with Bcrypt"); } if (StringUtils.isEmpty(password)) { throw new IllegalArgumentException("Encryption with an empty password is not supported"); } String algorithm = encryptionMethod.getAlgorithm(); String provider = encryptionMethod.getProvider(); final String cipherName = CipherUtility.parseCipherFromAlgorithm(algorithm); if (!CipherUtility.isValidKeyLength(keyLength, cipherName)) { throw new IllegalArgumentException(String.valueOf(keyLength) + " is not a valid key length for " + cipherName); } String bcryptSalt = formatSaltForBcrypt(salt); String hash = BCrypt.hashpw(password, bcryptSalt); /* The SHA-512 hash is required in order to derive a key longer than 184 bits (the resulting size of the Bcrypt hash) and ensuring the avalanche effect causes higher key entropy (if all derived keys follow a consistent pattern, it weakens the strength of the encryption) */ MessageDigest digest = MessageDigest.getInstance("SHA-512", provider); byte[] dk = digest.digest(hash.getBytes(StandardCharsets.UTF_8)); dk = Arrays.copyOf(dk, keyLength / 8); SecretKey tempKey = new SecretKeySpec(dk, algorithm); KeyedCipherProvider keyedCipherProvider = new AESKeyedCipherProvider(); return keyedCipherProvider.getCipher(encryptionMethod, tempKey, iv, encryptMode); }
public KeyedEncryptor(final EncryptionMethod encryptionMethod, final byte[] keyBytes, final byte[] iv) { super(); try { if (encryptionMethod == null) { throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with null encryption method"); } if (!encryptionMethod.isKeyedCipher()) { throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with encryption method " + encryptionMethod.name()); } this.encryptionMethod = encryptionMethod; if (keyBytes == null || keyBytes.length == 0) { throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with empty key"); } if (!CipherUtility.isValidKeyLengthForAlgorithm(keyBytes.length * 8, encryptionMethod.getAlgorithm())) { throw new IllegalArgumentException("Cannot instantiate a keyed encryptor with key of length " + keyBytes.length); } String cipherName = CipherUtility.parseCipherFromAlgorithm(encryptionMethod.getAlgorithm()); this.key = new SecretKeySpec(keyBytes, cipherName); this.iv = iv; } catch (Exception e) { throw new ProcessException(e); } }
throw new IllegalArgumentException(encryptionMethod.name() + " is not compatible with Scrypt");
protected Cipher getInitializedCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, byte[] iv, int keyLength, boolean encryptMode) throws Exception { if (encryptionMethod == null) { throw new IllegalArgumentException("The encryption method must be specified"); } if (!encryptionMethod.isCompatibleWithStrongKDFs()) { throw new IllegalArgumentException(encryptionMethod.name() + " is not compatible with PBKDF2"); } String algorithm = encryptionMethod.getAlgorithm(); final String cipherName = CipherUtility.parseCipherFromAlgorithm(algorithm); if (!CipherUtility.isValidKeyLength(keyLength, cipherName)) { throw new IllegalArgumentException(String.valueOf(keyLength) + " is not a valid key length for " + cipherName); } if (StringUtils.isEmpty(password)) { throw new IllegalArgumentException("Encryption with an empty password is not supported"); } if (salt == null || salt.length < DEFAULT_SALT_LENGTH) { throw new IllegalArgumentException("The salt must be at least " + DEFAULT_SALT_LENGTH + " bytes. To generate a salt, use PBKDF2CipherProvider#generateSalt()"); } PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(this.prf); gen.init(password.getBytes(StandardCharsets.UTF_8), salt, getIterationCount()); byte[] dk = ((KeyParameter) gen.generateDerivedParameters(keyLength)).getKey(); SecretKey tempKey = new SecretKeySpec(dk, algorithm); KeyedCipherProvider keyedCipherProvider = new AESKeyedCipherProvider(); return keyedCipherProvider.getCipher(encryptionMethod, tempKey, iv, encryptMode); }
@Test public void testPGPDecrypt() throws IOException { final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); runner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP_ASCII_ARMOR.name()); runner.setProperty(EncryptContent.PASSWORD, "Hello, World!"); runner.enqueue(Paths.get("src/test/resources/TestEncryptContent/text.txt.asc")); runner.run(); runner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1); final MockFlowFile flowFile = runner.getFlowFilesForRelationship(EncryptContent.REL_SUCCESS).get(0); flowFile.assertContentEquals(Paths.get("src/test/resources/TestEncryptContent/text.txt")); }
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());
.explanation(encryptionMethod.name() + " (" + encryptionMethod.getAlgorithm() + ") is not supported by this JVM due to lacking JCE Unlimited " + "Strength Jurisdiction Policy files. See Admin Guide.").build());
@Test public void testShouldDecryptOpenSSLRawUnsalted() throws IOException { // Arrange Assume.assumeTrue("Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.", PasswordBasedEncryptor.supportsUnlimitedStrength()); final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent()); final String password = "thisIsABadPassword"; final EncryptionMethod method = EncryptionMethod.MD5_256AES; final KeyDerivationFunction kdf = KeyDerivationFunction.OPENSSL_EVP_BYTES_TO_KEY; testRunner.setProperty(EncryptContent.PASSWORD, password); testRunner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, kdf.name()); testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, method.name()); testRunner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE); // Act testRunner.enqueue(Paths.get("src/test/resources/TestEncryptContent/unsalted_raw.enc")); testRunner.clearTransferState(); testRunner.run(); // Assert testRunner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1); testRunner.assertQueueEmpty(); MockFlowFile flowFile = testRunner.getFlowFilesForRelationship(EncryptContent.REL_SUCCESS).get(0); logger.info("Decrypted contents (hex): {}", Hex.encodeHexString(flowFile.toByteArray())); logger.info("Decrypted contents: {}", new String(flowFile.toByteArray(), "UTF-8")); // Assert flowFile.assertContentEquals(new File("src/test/resources/TestEncryptContent/plain.txt")); }
logger.info("Attempting {}", encryptionMethod.name()); testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name()); testRunner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); testRunner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1); logger.info("Successfully decrypted {}", encryptionMethod.name());
@Test public void testShouldDecryptOpenSSLRawSalted() throws IOException { // Arrange Assume.assumeTrue("Test is being skipped due to this JVM lacking JCE Unlimited Strength Jurisdiction Policy file.", PasswordBasedEncryptor.supportsUnlimitedStrength()); final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent()); final String password = "thisIsABadPassword"; final EncryptionMethod method = EncryptionMethod.MD5_256AES; final KeyDerivationFunction kdf = KeyDerivationFunction.OPENSSL_EVP_BYTES_TO_KEY; testRunner.setProperty(EncryptContent.PASSWORD, password); testRunner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, kdf.name()); testRunner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, method.name()); testRunner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE); // Act testRunner.enqueue(Paths.get("src/test/resources/TestEncryptContent/salted_raw.enc")); testRunner.clearTransferState(); testRunner.run(); // Assert testRunner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1); testRunner.assertQueueEmpty(); MockFlowFile flowFile = testRunner.getFlowFilesForRelationship(EncryptContent.REL_SUCCESS).get(0); logger.info("Decrypted contents (hex): {}", Hex.encodeHexString(flowFile.toByteArray())); logger.info("Decrypted contents: {}", new String(flowFile.toByteArray(), "UTF-8")); // Assert flowFile.assertContentEquals(new File("src/test/resources/TestEncryptContent/plain.txt")); }
@Test public void testShouldValidatePGPPublicKeyringContainsUserId() { // Arrange final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); Collection<ValidationResult> results; MockProcessContext pc; runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name()); runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/pubring.gpg"); runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID"); runner.enqueue(new byte[0]); pc = (MockProcessContext) runner.getProcessContext(); // Act results = pc.validate(); // Assert Assert.assertEquals(1, results.size()); ValidationResult vr = (ValidationResult) results.toArray()[0]; String expectedResult = "PGPException: Could not find a public key with the given userId"; String message = "'" + vr.toString() + "' contains '" + expectedResult + "'"; Assert.assertTrue(message, vr.toString().contains(expectedResult)); }
@Test public void testShouldValidatePGPPublicKeyringExists() { // Arrange final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); Collection<ValidationResult> results; MockProcessContext pc; runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name()); runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/pubring.gpg.missing"); runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID"); runner.enqueue(new byte[0]); pc = (MockProcessContext) runner.getProcessContext(); // Act results = pc.validate(); // Assert Assert.assertEquals(1, results.size()); ValidationResult vr = (ValidationResult) results.toArray()[0]; String expectedResult = "java.io.FileNotFoundException"; String message = "'" + vr.toString() + "' contains '" + expectedResult + "'"; Assert.assertTrue(message, vr.toString().contains(expectedResult)); }
@Test public void testShouldValidatePGPPublicKeyringIsProperFormat() { // Arrange final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); Collection<ValidationResult> results; MockProcessContext pc; runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name()); runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/text.txt"); runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID"); runner.enqueue(new byte[0]); pc = (MockProcessContext) runner.getProcessContext(); // Act results = pc.validate(); // Assert Assert.assertEquals(1, results.size()); ValidationResult vr = (ValidationResult) results.toArray()[0]; String expectedResult = " java.io.IOException: invalid header encountered"; String message = "'" + vr.toString() + "' contains '" + expectedResult + "'"; Assert.assertTrue(message, vr.toString().contains(expectedResult)); }
@Test public void testShouldExtractPGPPublicKeyFromKeyring() { // Arrange final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); Collection<ValidationResult> results; MockProcessContext pc; runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name()); runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/pubring.gpg"); runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "NiFi PGP Test Key (Short test key for NiFi PGP unit tests) <alopresto.apache+test@gmail.com>"); runner.enqueue(new byte[0]); pc = (MockProcessContext) runner.getProcessContext(); // Act results = pc.validate(); // Assert Assert.assertEquals(0, results.size()); }
@Test public void testShouldValidatePGPPublicKeyringRequiresUserId() { // Arrange final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class); Collection<ValidationResult> results; MockProcessContext pc; runner.setProperty(EncryptContent.MODE, EncryptContent.ENCRYPT_MODE); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name()); runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/pubring.gpg"); runner.enqueue(new byte[0]); pc = (MockProcessContext) runner.getProcessContext(); // Act results = pc.validate(); // Assert Assert.assertEquals(1, results.size()); ValidationResult vr = (ValidationResult) results.toArray()[0]; String expectedResult = " encryption without a " + EncryptContent.PASSWORD.getDisplayName() + " requires both " + EncryptContent.PUBLIC_KEYRING.getDisplayName() + " and " + EncryptContent.PUBLIC_KEY_USERID.getDisplayName(); String message = "'" + vr.toString() + "' contains '" + expectedResult + "'"; Assert.assertTrue(message, vr.toString().contains(expectedResult)); }
runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, encryptionMethod.name()); runner.setProperty(EncryptContent.KEY_DERIVATION_FUNCTION, KeyDerivationFunction.NIFI_LEGACY.name()); runner.setProperty(EncryptContent.PASSWORD, "ThisIsAPasswordThatIsLongerThanSixteenCharacters"); runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name()); runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/text.txt"); runner.enqueue(new byte[0]);
private static AllowableValue[] buildEncryptionMethodAllowableValues() { final EncryptionMethod[] encryptionMethods = EncryptionMethod.values(); List<AllowableValue> allowableValues = new ArrayList<>(encryptionMethods.length); for (EncryptionMethod em : encryptionMethods) { allowableValues.add(new AllowableValue(em.name(), em.name(), em.toString())); } return allowableValues.toArray(new AllowableValue[0]); }
protected Cipher getInitializedCipher(EncryptionMethod encryptionMethod, String password, byte[] salt, byte[] iv, int keyLength, boolean encryptMode) throws Exception { if (encryptionMethod == null) { throw new IllegalArgumentException("The encryption method must be specified"); } if (!encryptionMethod.isCompatibleWithStrongKDFs()) { throw new IllegalArgumentException(encryptionMethod.name() + " is not compatible with PBKDF2"); } String algorithm = encryptionMethod.getAlgorithm(); final String cipherName = CipherUtility.parseCipherFromAlgorithm(algorithm); if (!CipherUtility.isValidKeyLength(keyLength, cipherName)) { throw new IllegalArgumentException(String.valueOf(keyLength) + " is not a valid key length for " + cipherName); } if (StringUtils.isEmpty(password)) { throw new IllegalArgumentException("Encryption with an empty password is not supported"); } if (salt == null || salt.length < DEFAULT_SALT_LENGTH) { throw new IllegalArgumentException("The salt must be at least " + DEFAULT_SALT_LENGTH + " bytes. To generate a salt, use PBKDF2CipherProvider#generateSalt()"); } PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator(this.prf); gen.init(password.getBytes(StandardCharsets.UTF_8), salt, getIterationCount()); byte[] dk = ((KeyParameter) gen.generateDerivedParameters(keyLength)).getKey(); SecretKey tempKey = new SecretKeySpec(dk, algorithm); KeyedCipherProvider keyedCipherProvider = new AESKeyedCipherProvider(); return keyedCipherProvider.getCipher(encryptionMethod, tempKey, iv, encryptMode); }