You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by al...@apache.org on 2015/12/05 02:10:25 UTC

nifi git commit: NIFI-1242:

Repository: nifi
Updated Branches:
  refs/heads/master ee14d8f9d -> f83e6d33c


NIFI-1242:

Added logic and test resources to debug JCE unlimited strength cryptography policy issues and incorporated into processor property validation.
Excluded test resources from RAT check
Added KeyDerivationFunction enum.
Added kdf property in EncryptContent processor and provided to PasswordBasedEncryptor.
Added logic in PasswordBasedEncryptor to handle variable KDF.
Added unit tests for EncryptContent processor.
Added test resources and excluded from RAT check.

plain.txt: This is a plaintext message.

0s @ 12:20:32 $ openssl enc -aes-256-cbc -e -in plain.txt -out salted_raw.enc -k thisIsABadPassword -p
salt=31DC301A6C7B8A0B
key=CB878A6E167A5B530B8F2BD175E6359E3092AFF7C83274A22A5B421D79E599AC
iv =0C614A72FC06B454B84E035B3FA8F877
0s @ 12:20:44 $ xxd salted_raw.enc
0000000: 5361 6c74 6564 5f5f 31dc 301a 6c7b 8a0b  Salted__1.0.l{..
0000010: 616b c65d f767 504d c085 ba7a c517 d0cb  ak.].gPM...z....
0000020: 7832 211e f573 b6f1 ded2 8f59 88e8 088f  x2!..s.....Y....

0s @ 20:14:00 $ openssl enc -aes-256-cbc -e -in plain.txt -out unsalted_raw.enc -k thisIsABadPassword -p -nosalt
key=711E85689CE7AFF6F410AEA43ABC5446842F685B84879B2E00F977C22B9E9A7D
iv =0C90ABF8ECE84B92BAA2CD448EC760F0
0s @ 20:14:17 $ xxd unsalted_raw.enc
0000000: 70cd 2984 fdbb 0e7c c01b 7206 88b1 6b50  p.)....|..r...kP
0000010: 5eeb e4f3 4036 773b 00ce dd8e 85d8 f90a  ^...@6w;........

This closes #140

Signed-off-by: Aldrin Piri <al...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/f83e6d33
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/f83e6d33
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/f83e6d33

Branch: refs/heads/master
Commit: f83e6d33c592e3a061b0bf6c41884c42f4971379
Parents: ee14d8f
Author: Andy LoPresto <al...@gmail.com>
Authored: Thu Dec 3 12:23:45 2015 -0800
Committer: Aldrin Piri <al...@apache.org>
Committed: Fri Dec 4 19:45:10 2015 -0500

----------------------------------------------------------------------
 .../nifi/security/util/EncryptionMethod.java    |  18 +-
 .../security/util/KeyDerivationFunction.java    |  55 ++++++
 .../nifi-standard-processors/pom.xml            |   3 +
 .../processors/standard/EncryptContent.java     | 192 +++++++++++--------
 .../standard/util/PasswordBasedEncryptor.java   | 133 +++++++++++--
 .../processors/standard/TestEncryptContent.java | 142 +++++++++++++-
 .../test/resources/TestEncryptContent/plain.txt |   1 +
 .../resources/TestEncryptContent/salted_raw.enc |   1 +
 .../TestEncryptContent/unsalted_raw.enc         | Bin 0 -> 32 bytes
 9 files changed, 440 insertions(+), 105 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/EncryptionMethod.java
----------------------------------------------------------------------
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 55f5986..ce0c50e 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
@@ -27,25 +27,25 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 public enum EncryptionMethod {
 
     MD5_128AES("PBEWITHMD5AND128BITAES-CBC-OPENSSL", "BC", false),
-    MD5_256AES("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC", false),
-    SHA1_RC2("PBEWITHSHA1ANDRC2", "BC", false),
-    SHA1_DES("PBEWITHSHA1ANDDES", "BC", false),
     MD5_192AES("PBEWITHMD5AND192BITAES-CBC-OPENSSL", "BC", false),
+    MD5_256AES("PBEWITHMD5AND256BITAES-CBC-OPENSSL", "BC", false),
     MD5_DES("PBEWITHMD5ANDDES", "BC", false),
     MD5_RC2("PBEWITHMD5ANDRC2", "BC", false),
+    SHA1_RC2("PBEWITHSHA1ANDRC2", "BC", false),
+    SHA1_DES("PBEWITHSHA1ANDDES", "BC", false),
+    SHA_128AES("PBEWITHSHAAND128BITAES-CBC-BC", "BC", true),
     SHA_192AES("PBEWITHSHAAND192BITAES-CBC-BC", "BC", true),
+    SHA_256AES("PBEWITHSHAAND256BITAES-CBC-BC", "BC", true),
+    SHA_40RC2("PBEWITHSHAAND40BITRC2-CBC", "BC", true),
+    SHA_128RC2("PBEWITHSHAAND128BITRC2-CBC", "BC", true),
     SHA_40RC4("PBEWITHSHAAND40BITRC4", "BC", true),
+    SHA_128RC4("PBEWITHSHAAND128BITRC4", "BC", true),
     SHA256_128AES("PBEWITHSHA256AND128BITAES-CBC-BC", "BC", true),
-    SHA_128RC2("PBEWITHSHAAND128BITRC2-CBC", "BC", true),
-    SHA_128AES("PBEWITHSHAAND128BITAES-CBC-BC", "BC", true),
     SHA256_192AES("PBEWITHSHA256AND192BITAES-CBC-BC", "BC", true),
-    SHA_2KEYTRIPLEDES("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "BC", true),
     SHA256_256AES("PBEWITHSHA256AND256BITAES-CBC-BC", "BC", true),
-    SHA_40RC2("PBEWITHSHAAND40BITRC2-CBC", "BC", true),
-    SHA_256AES("PBEWITHSHAAND256BITAES-CBC-BC", "BC", true),
+    SHA_2KEYTRIPLEDES("PBEWITHSHAAND2-KEYTRIPLEDES-CBC", "BC", true),
     SHA_3KEYTRIPLEDES("PBEWITHSHAAND3-KEYTRIPLEDES-CBC", "BC", true),
     SHA_TWOFISH("PBEWITHSHAANDTWOFISH-CBC", "BC", true),
-    SHA_128RC4("PBEWITHSHAAND128BITRC4", "BC", true),
     PGP("PGP", "BC", false),
     PGP_ASCII_ARMOR("PGP-ASCII-ARMOR", "BC", false);
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyDerivationFunction.java
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyDerivationFunction.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyDerivationFunction.java
new file mode 100644
index 0000000..f8b4731
--- /dev/null
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyDerivationFunction.java
@@ -0,0 +1,55 @@
+/*
+* 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.nifi.security.util;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * Enumeration capturing essential information about the various key derivation functions that might be supported.
+ */
+public enum KeyDerivationFunction {
+
+    NIFI_LEGACY("NiFi legacy KDF", "MD5 @ 1000 iterations"),
+    OPENSSL_EVP_BYTES_TO_KEY("OpenSSL EVP_BytesToKey", "Single iteration MD5 compatible with PKCS#5 v1.5");
+    // TODO: Implement bcrypt, scrypt, and PBKDF2
+
+    private final String name;
+    private final String description;
+
+    KeyDerivationFunction(String name, String description) {
+        this.name = name;
+        this.description = description;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    @Override
+    public String toString() {
+        final ToStringBuilder builder = new ToStringBuilder(this);
+        ToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE);
+        builder.append("KDF Name", name);
+        builder.append("Description", description);
+        return builder.toString();
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
index 0427927..64c2597 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
@@ -310,6 +310,9 @@ language governing permissions and limitations under the License. -->
                         <exclude>src/test/resources/TestIdentifyMimeType/flowfilev1.tar</exclude>
                         <exclude>src/test/resources/TestUnpackContent/data.tar</exclude>
                         <exclude>src/test/resources/TestUnpackContent/data.zip</exclude>
+                        <exclude>src/test/resources/TestEncryptContent/plain.txt</exclude>
+                        <exclude>src/test/resources/TestEncryptContent/salted_raw.enc</exclude>
+                        <exclude>src/test/resources/TestEncryptContent/unsalted_raw.enc</exclude>
                     </excludes>
                 </configuration>
             </plugin>

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/EncryptContent.java
----------------------------------------------------------------------
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 c63edc9..50397c8 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
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.annotation.behavior.EventDriven;
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
@@ -51,6 +52,7 @@ import org.apache.nifi.processors.standard.util.OpenPGPKeyBasedEncryptor;
 import org.apache.nifi.processors.standard.util.OpenPGPPasswordBasedEncryptor;
 import org.apache.nifi.processors.standard.util.PasswordBasedEncryptor;
 import org.apache.nifi.security.util.EncryptionMethod;
+import org.apache.nifi.security.util.KeyDerivationFunction;
 import org.apache.nifi.util.StopWatch;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
@@ -66,60 +68,68 @@ public class EncryptContent extends AbstractProcessor {
     public static final String DECRYPT_MODE = "Decrypt";
 
     public static final PropertyDescriptor MODE = new PropertyDescriptor.Builder()
-            .name("Mode")
-            .description("Specifies whether the content should be encrypted or decrypted")
-            .required(true)
-            .allowableValues(ENCRYPT_MODE, DECRYPT_MODE)
-            .defaultValue(ENCRYPT_MODE)
-            .build();
+        .name("Mode")
+        .description("Specifies whether the content should be encrypted or decrypted")
+        .required(true)
+        .allowableValues(ENCRYPT_MODE, DECRYPT_MODE)
+        .defaultValue(ENCRYPT_MODE)
+        .build();
+    public static final PropertyDescriptor KEY_DERIVATION_FUNCTION = new PropertyDescriptor.Builder()
+        .name("key-derivation-function")
+        .displayName("Key Derivation Function")
+        .description("Specifies the key derivation function to generate the key from the password (and salt)")
+        .required(true)
+        .allowableValues(KeyDerivationFunction.values())
+        .defaultValue(KeyDerivationFunction.NIFI_LEGACY.name())
+        .build();
     public static final PropertyDescriptor ENCRYPTION_ALGORITHM = new PropertyDescriptor.Builder()
-            .name("Encryption Algorithm")
-            .description("The Encryption Algorithm to use")
-            .required(true)
-            .allowableValues(EncryptionMethod.values())
-            .defaultValue(EncryptionMethod.MD5_256AES.name())
-            .build();
+        .name("Encryption Algorithm")
+        .description("The Encryption Algorithm to use")
+        .required(true)
+        .allowableValues(EncryptionMethod.values())
+        .defaultValue(EncryptionMethod.MD5_128AES.name())
+        .build();
     public static final PropertyDescriptor PASSWORD = new PropertyDescriptor.Builder()
-            .name("Password")
-            .description("The Password to use for encrypting or decrypting the data")
-            .required(false)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .sensitive(true)
-            .build();
+        .name("Password")
+        .description("The Password to use for encrypting or decrypting the data")
+        .required(false)
+        .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+        .sensitive(true)
+        .build();
     public static final PropertyDescriptor PUBLIC_KEYRING = new PropertyDescriptor.Builder()
-            .name("public-keyring-file")
-            .displayName("Public Keyring File")
-            .description("In a PGP encrypt mode, this keyring contains the public key of the recipient")
-            .required(false)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .build();
+        .name("public-keyring-file")
+        .displayName("Public Keyring File")
+        .description("In a PGP encrypt mode, this keyring contains the public key of the recipient")
+        .required(false)
+        .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+        .build();
     public static final PropertyDescriptor PUBLIC_KEY_USERID = new PropertyDescriptor.Builder()
-            .name("public-key-user-id")
-            .displayName("Public Key User Id")
-            .description("In a PGP encrypt mode, this user id of the recipient")
-            .required(false)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .build();
+        .name("public-key-user-id")
+        .displayName("Public Key User Id")
+        .description("In a PGP encrypt mode, this user id of the recipient")
+        .required(false)
+        .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+        .build();
     public static final PropertyDescriptor PRIVATE_KEYRING = new PropertyDescriptor.Builder()
-            .name("private-keyring-file")
-            .displayName("Private Keyring File")
-            .description("In a PGP decrypt mode, this keyring contains the private key of the recipient")
-            .required(false)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .build();
+        .name("private-keyring-file")
+        .displayName("Private Keyring File")
+        .description("In a PGP decrypt mode, this keyring contains the private key of the recipient")
+        .required(false)
+        .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+        .build();
     public static final PropertyDescriptor PRIVATE_KEYRING_PASSPHRASE = new PropertyDescriptor.Builder()
-            .name("private-keyring-passphrase")
-            .displayName("Private Keyring Passphrase")
-            .description("In a PGP decrypt mode, this is the private keyring passphrase")
-            .required(false)
-            .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
-            .sensitive(true)
-            .build();
+        .name("private-keyring-passphrase")
+        .displayName("Private Keyring Passphrase")
+        .description("In a PGP decrypt mode, this is the private keyring passphrase")
+        .required(false)
+        .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
+        .sensitive(true)
+        .build();
 
     public static final Relationship REL_SUCCESS = new Relationship.Builder().name("success")
-            .description("Any FlowFile that is successfully encrypted or decrypted will be routed to success").build();
+        .description("Any FlowFile that is successfully encrypted or decrypted will be routed to success").build();
     public static final Relationship REL_FAILURE = new Relationship.Builder().name("failure")
-            .description("Any FlowFile that cannot be encrypted or decrypted will be routed to failure").build();
+        .description("Any FlowFile that cannot be encrypted or decrypted will be routed to failure").build();
 
     private List<PropertyDescriptor> properties;
     private Set<Relationship> relationships;
@@ -133,6 +143,7 @@ public class EncryptContent extends AbstractProcessor {
     protected void init(final ProcessorInitializationContext context) {
         final List<PropertyDescriptor> properties = new ArrayList<>();
         properties.add(MODE);
+        properties.add(KEY_DERIVATION_FUNCTION);
         properties.add(ENCRYPTION_ALGORITHM);
         properties.add(PASSWORD);
         properties.add(PUBLIC_KEYRING);
@@ -168,9 +179,11 @@ public class EncryptContent extends AbstractProcessor {
     @Override
     protected Collection<ValidationResult> customValidate(final ValidationContext context) {
         final List<ValidationResult> validationResults = new ArrayList<>(super.customValidate(context));
-        final String method = context.getProperty(ENCRYPTION_ALGORITHM).getValue();
-        final String algorithm = EncryptionMethod.valueOf(method).getAlgorithm();
+        final String methodValue = context.getProperty(ENCRYPTION_ALGORITHM).getValue();
+        final EncryptionMethod method = EncryptionMethod.valueOf(methodValue);
+        final String algorithm = method.getAlgorithm();
         final String password = context.getProperty(PASSWORD).getValue();
+        final String kdf = context.getProperty(KEY_DERIVATION_FUNCTION).getValue();
         if (isPGPAlgorithm(algorithm)) {
             if (password == null) {
                 final boolean encrypt = context.getProperty(MODE).getValue().equalsIgnoreCase(ENCRYPT_MODE);
@@ -180,23 +193,23 @@ public class EncryptContent extends AbstractProcessor {
                     final String publicUserId = context.getProperty(PUBLIC_KEY_USERID).getValue();
                     if (publicKeyring == null || publicUserId == null) {
                         validationResults.add(new ValidationResult.Builder().subject(PUBLIC_KEYRING.getDisplayName())
-                                .explanation(algorithm + " encryption without a " + PASSWORD.getDisplayName() + " requires both "
-                                        + PUBLIC_KEYRING.getDisplayName() + " and " + PUBLIC_KEY_USERID.getDisplayName())
-                                .build());
+                            .explanation(algorithm + " encryption without a " + PASSWORD.getDisplayName() + " requires both "
+                                + PUBLIC_KEYRING.getDisplayName() + " and " + PUBLIC_KEY_USERID.getDisplayName())
+                            .build());
                     } else {
                         // verify the public keyring contains the user id
                         try {
                             if (OpenPGPKeyBasedEncryptor.getPublicKey(publicUserId, publicKeyring) == null) {
                                 validationResults.add(new ValidationResult.Builder().subject(PUBLIC_KEYRING.getDisplayName())
-                                        .explanation(PUBLIC_KEYRING.getDisplayName() + " " + publicKeyring
-                                                + " does not contain user id " + publicUserId)
-                                        .build());
+                                    .explanation(PUBLIC_KEYRING.getDisplayName() + " " + publicKeyring
+                                        + " does not contain user id " + publicUserId)
+                                    .build());
                             }
                         } catch (final Exception e) {
                             validationResults.add(new ValidationResult.Builder().subject(PUBLIC_KEYRING.getDisplayName())
-                                    .explanation("Invalid " + PUBLIC_KEYRING.getDisplayName() + " " + publicKeyring
-                                            + " because " + e.toString())
-                                    .build());
+                                .explanation("Invalid " + PUBLIC_KEYRING.getDisplayName() + " " + publicKeyring
+                                    + " because " + e.toString())
+                                .build());
                         }
                     }
                 } else {
@@ -205,31 +218,55 @@ public class EncryptContent extends AbstractProcessor {
                     final String keyringPassphrase = context.getProperty(PRIVATE_KEYRING_PASSPHRASE).getValue();
                     if (privateKeyring == null || keyringPassphrase == null) {
                         validationResults.add(new ValidationResult.Builder().subject(PRIVATE_KEYRING.getName())
-                                .explanation(algorithm + " decryption without a " + PASSWORD.getDisplayName() + " requires both "
-                                        + PRIVATE_KEYRING.getDisplayName() + " and " + PRIVATE_KEYRING_PASSPHRASE.getDisplayName())
-                                .build());
+                            .explanation(algorithm + " decryption without a " + PASSWORD.getDisplayName() + " requires both "
+                                + PRIVATE_KEYRING.getDisplayName() + " and " + PRIVATE_KEYRING_PASSPHRASE.getDisplayName())
+                            .build());
                     } else {
-                        final String providerName = EncryptionMethod.valueOf(method).getProvider();
+                        final String providerName = EncryptionMethod.valueOf(methodValue).getProvider();
                         // verify the passphrase works on the private keyring
                         try {
                             if (!OpenPGPKeyBasedEncryptor.validateKeyring(providerName, privateKeyring, keyringPassphrase.toCharArray())) {
                                 validationResults.add(new ValidationResult.Builder().subject(PRIVATE_KEYRING.getDisplayName())
-                                        .explanation(PRIVATE_KEYRING.getDisplayName() + " " + privateKeyring
-                                                + " could not be opened with the provided " + PRIVATE_KEYRING_PASSPHRASE.getDisplayName())
-                                        .build());
+                                    .explanation(PRIVATE_KEYRING.getDisplayName() + " " + privateKeyring
+                                        + " could not be opened with the provided " + PRIVATE_KEYRING_PASSPHRASE.getDisplayName())
+                                    .build());
                             }
                         } catch (final Exception e) {
                             validationResults.add(new ValidationResult.Builder().subject(PRIVATE_KEYRING.getDisplayName())
-                                    .explanation("Invalid " + PRIVATE_KEYRING.getDisplayName() + " " + privateKeyring
-                                            + " because " + e.toString())
-                                    .build());
+                                .explanation("Invalid " + PRIVATE_KEYRING.getDisplayName() + " " + privateKeyring
+                                    + " because " + e.toString())
+                                .build());
                         }
                     }
                 }
             }
-        } else if (password == null) {
-            validationResults.add(new ValidationResult.Builder().subject(PASSWORD.getName())
+        } else { // PBE
+            if (!PasswordBasedEncryptor.supportsUnlimitedStrength()) {
+                if (method.isUnlimitedStrength()) {
+                    validationResults.add(new ValidationResult.Builder().subject(ENCRYPTION_ALGORITHM.getName())
+                        .explanation(methodValue + " (" + algorithm + ") is not supported by this JVM due to lacking JCE Unlimited " +
+                            "Strength Jurisdiction Policy files.").build());
+                }
+            }
+            int allowedKeyLength = PasswordBasedEncryptor.getMaxAllowedKeyLength(ENCRYPTION_ALGORITHM.getName());
+
+            if (StringUtils.isEmpty(password)) {
+                validationResults.add(new ValidationResult.Builder().subject(PASSWORD.getName())
                     .explanation(PASSWORD.getDisplayName() + " is required when using algorithm " + algorithm).build());
+            } else {
+                if (password.getBytes().length * 8 > allowedKeyLength) {
+                    validationResults.add(new ValidationResult.Builder().subject(PASSWORD.getName())
+                        .explanation("Password length greater than " + allowedKeyLength + " bits is not supported by this JVM" +
+                            " due to lacking JCE Unlimited Strength Jurisdiction Policy files.").build());
+                }
+            }
+
+            // Perform some analysis on the selected encryption algorithm to ensure the JVM can support it and the associated key
+
+            if (StringUtils.isEmpty(kdf)) {
+                validationResults.add(new ValidationResult.Builder().subject(KEY_DERIVATION_FUNCTION.getName())
+                    .explanation(KEY_DERIVATION_FUNCTION.getDisplayName() + " is required when using algorithm " + algorithm).build());
+            }
         }
         return validationResults;
     }
@@ -247,6 +284,7 @@ public class EncryptContent extends AbstractProcessor {
         final String providerName = encryptionMethod.getProvider();
         final String algorithm = encryptionMethod.getAlgorithm();
         final String password = context.getProperty(PASSWORD).getValue();
+        final KeyDerivationFunction kdf = KeyDerivationFunction.valueOf(context.getProperty(KEY_DERIVATION_FUNCTION).getValue());
         final boolean encrypt = context.getProperty(MODE).getValue().equalsIgnoreCase(ENCRYPT_MODE);
 
         Encryptor encryptor;
@@ -262,14 +300,14 @@ public class EncryptContent extends AbstractProcessor {
                 } else if (!encrypt && privateKeyring != null) {
                     final char[] keyringPassphrase = context.getProperty(PRIVATE_KEYRING_PASSPHRASE).getValue().toCharArray();
                     encryptor = new OpenPGPKeyBasedEncryptor(algorithm, providerName, privateKeyring, null, keyringPassphrase,
-                            filename);
+                        filename);
                 } else {
                     final char[] passphrase = Normalizer.normalize(password, Normalizer.Form.NFC).toCharArray();
                     encryptor = new OpenPGPPasswordBasedEncryptor(algorithm, providerName, passphrase, filename);
                 }
-            } else {
+            } else { // PBE
                 final char[] passphrase = Normalizer.normalize(password, Normalizer.Form.NFC).toCharArray();
-                encryptor = new PasswordBasedEncryptor(algorithm, providerName, passphrase);
+                encryptor = new PasswordBasedEncryptor(algorithm, providerName, passphrase, kdf);
             }
 
             if (encrypt) {
@@ -279,7 +317,7 @@ public class EncryptContent extends AbstractProcessor {
             }
 
         } catch (final Exception e) {
-            logger.error("Failed to initialize {}cryption algorithm because - ", new Object[] { encrypt ? "en" : "de", e });
+            logger.error("Failed to initialize {}cryption algorithm because - ", new Object[]{encrypt ? "en" : "de", e});
             session.rollback();
             context.yield();
             return;
@@ -288,20 +326,20 @@ public class EncryptContent extends AbstractProcessor {
         try {
             final StopWatch stopWatch = new StopWatch(true);
             flowFile = session.write(flowFile, callback);
-            logger.info("successfully {}crypted {}", new Object[] { encrypt ? "en" : "de", flowFile });
+            logger.info("successfully {}crypted {}", new Object[]{encrypt ? "en" : "de", flowFile});
             session.getProvenanceReporter().modifyContent(flowFile, stopWatch.getElapsed(TimeUnit.MILLISECONDS));
             session.transfer(flowFile, REL_SUCCESS);
         } catch (final ProcessException e) {
-            logger.error("Cannot {}crypt {} - ", new Object[] { encrypt ? "en" : "de", flowFile, e });
+            logger.error("Cannot {}crypt {} - ", new Object[]{encrypt ? "en" : "de", flowFile, e});
             session.transfer(flowFile, REL_FAILURE);
             return;
         }
     }
 
-    public static interface Encryptor {
-        public StreamCallback getEncryptionCallback() throws Exception;
+    public interface Encryptor {
+        StreamCallback getEncryptionCallback() throws Exception;
 
-        public StreamCallback getDecryptionCallback() throws Exception;
+        StreamCallback getDecryptionCallback() throws Exception;
     }
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PasswordBasedEncryptor.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PasswordBasedEncryptor.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PasswordBasedEncryptor.java
index 1f45d6f..d3d50b8 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PasswordBasedEncryptor.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/main/java/org/apache/nifi/processors/standard/util/PasswordBasedEncryptor.java
@@ -20,7 +20,11 @@ import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.List;
 
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -30,9 +34,11 @@ import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.PBEParameterSpec;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.io.StreamCallback;
 import org.apache.nifi.processors.standard.EncryptContent.Encryptor;
+import org.apache.nifi.security.util.KeyDerivationFunction;
 import org.apache.nifi.stream.io.StreamUtils;
 
 public class PasswordBasedEncryptor implements Encryptor {
@@ -40,28 +46,99 @@ public class PasswordBasedEncryptor implements Encryptor {
     private Cipher cipher;
     private int saltSize;
     private SecretKey secretKey;
+    private KeyDerivationFunction kdf;
+    private int iterationsCount = LEGACY_KDF_ITERATIONS;
 
     @Deprecated
-    public static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
-    public static final int DEFAULT_SALT_SIZE = 8;
+    private static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
+    private static final int DEFAULT_SALT_SIZE = 8;
+    // TODO: Eventually KDF-specific values should be refactored into injectable interface impls
+    private static final int LEGACY_KDF_ITERATIONS = 1000;
+    private static final int OPENSSL_EVP_HEADER_SIZE = 8;
+    private static final int OPENSSL_EVP_SALT_SIZE = 8;
+    private static final String OPENSSL_EVP_HEADER_MARKER = "Salted__";
+    private static final int OPENSSL_EVP_KDF_ITERATIONS = 0;
+    private static final int DEFAULT_MAX_ALLOWED_KEY_LENGTH = 128;
 
-    public PasswordBasedEncryptor(final String algorithm, final String providerName, final char[] password) {
+    private static boolean isUnlimitedStrengthCryptographyEnabled;
+
+    // Evaluate an unlimited strength algorithm to determine if we support the capability we have on the system
+    static {
+        try {
+            isUnlimitedStrengthCryptographyEnabled = (Cipher.getMaxAllowedKeyLength("AES") > DEFAULT_MAX_ALLOWED_KEY_LENGTH);
+        } catch (NoSuchAlgorithmException e) {
+            // if there are issues with this, we default back to the value established
+            isUnlimitedStrengthCryptographyEnabled = false;
+        }
+    }
+
+    public PasswordBasedEncryptor(final String algorithm, final String providerName, final char[] password, KeyDerivationFunction kdf) {
         super();
         try {
             // initialize cipher
             this.cipher = Cipher.getInstance(algorithm, providerName);
-            int algorithmBlockSize = cipher.getBlockSize();
-            this.saltSize = (algorithmBlockSize > 0) ? algorithmBlockSize : DEFAULT_SALT_SIZE;
+            this.kdf = kdf;
+
+            if (isOpenSSLKDF()) {
+                this.saltSize = OPENSSL_EVP_SALT_SIZE;
+                this.iterationsCount = OPENSSL_EVP_KDF_ITERATIONS;
+            } else {
+                int algorithmBlockSize = cipher.getBlockSize();
+                this.saltSize = (algorithmBlockSize > 0) ? algorithmBlockSize : DEFAULT_SALT_SIZE;
+            }
 
             // initialize SecretKey from password
-            PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
-            SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm, providerName);
+            final PBEKeySpec pbeKeySpec = new PBEKeySpec(password);
+            final SecretKeyFactory factory = SecretKeyFactory.getInstance(algorithm, providerName);
             this.secretKey = factory.generateSecret(pbeKeySpec);
         } catch (Exception e) {
             throw new ProcessException(e);
         }
     }
 
+    public static int getMaxAllowedKeyLength(final String algorithm) {
+        if (StringUtils.isEmpty(algorithm)) {
+            return DEFAULT_MAX_ALLOWED_KEY_LENGTH;
+        }
+        String parsedCipher = parseCipherFromAlgorithm(algorithm);
+        try {
+            return Cipher.getMaxAllowedKeyLength(parsedCipher);
+        } catch (NoSuchAlgorithmException e) {
+            // Default algorithm max key length on unmodified JRE
+            return DEFAULT_MAX_ALLOWED_KEY_LENGTH;
+        }
+    }
+
+    private static String parseCipherFromAlgorithm(final String algorithm) {
+        // This is not optimal but the algorithms do not have a standard format
+        final String AES = "AES";
+        final String TDES = "TRIPLEDES";
+        final String DES = "DES";
+        final String RC4 = "RC4";
+        final String RC2 = "RC2";
+        final String TWOFISH = "TWOFISH";
+        final List<String> SYMMETRIC_CIPHERS = Arrays.asList(AES, TDES, DES, RC4, RC2, TWOFISH);
+
+        // The algorithms contain "TRIPLEDES" but the cipher name is "DESede"
+        final String ACTUAL_TDES_CIPHER = "DESede";
+
+        for (String cipher : SYMMETRIC_CIPHERS) {
+            if (algorithm.contains(cipher)) {
+                if (cipher.equals(TDES)) {
+                    return ACTUAL_TDES_CIPHER;
+                } else {
+                    return cipher;
+                }
+            }
+        }
+
+        return algorithm;
+    }
+
+    public static boolean supportsUnlimitedStrength() {
+        return isUnlimitedStrengthCryptographyEnabled;
+    }
+
     @Override
     public StreamCallback getEncryptionCallback() throws ProcessException {
         try {
@@ -79,21 +156,50 @@ public class PasswordBasedEncryptor implements Encryptor {
         return new DecryptCallback();
     }
 
+    private int getIterationsCount() {
+        return iterationsCount;
+    }
+
+    private boolean isOpenSSLKDF() {
+        return KeyDerivationFunction.OPENSSL_EVP_BYTES_TO_KEY.equals(kdf);
+    }
+
     private class DecryptCallback implements StreamCallback {
 
         public DecryptCallback() {
         }
-
         @Override
         public void process(final InputStream in, final OutputStream out) throws IOException {
-            final byte[] salt = new byte[saltSize];
+            byte[] salt = new byte[saltSize];
+
             try {
+                // If the KDF is OpenSSL, try to read the salt from the input stream
+                if (isOpenSSLKDF()) {
+                    // The header and salt format is "Salted__salt x8b" in ASCII
+
+                    // Try to read the header and salt from the input
+                    byte[] header = new byte[PasswordBasedEncryptor.OPENSSL_EVP_HEADER_SIZE];
+
+                    // Mark the stream in case there is no salt
+                    in.mark(OPENSSL_EVP_HEADER_SIZE + 1);
+                    StreamUtils.fillBuffer(in, header);
+
+                    final byte[] headerMarkerBytes = OPENSSL_EVP_HEADER_MARKER.getBytes(StandardCharsets.US_ASCII);
+
+                    if (!Arrays.equals(headerMarkerBytes, header)) {
+                        // No salt present
+                        salt = new byte[0];
+                        // Reset the stream because we skipped 8 bytes of cipher text
+                        in.reset();
+                    }
+                }
+
                 StreamUtils.fillBuffer(in, salt);
             } catch (final EOFException e) {
                 throw new ProcessException("Cannot decrypt because file size is smaller than salt size", e);
             }
 
-            final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 1000);
+            final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, getIterationsCount());
             try {
                 cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
             } catch (final Exception e) {
@@ -127,13 +233,18 @@ public class PasswordBasedEncryptor implements Encryptor {
 
         @Override
         public void process(final InputStream in, final OutputStream out) throws IOException {
-            final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, 1000);
+            final PBEParameterSpec parameterSpec = new PBEParameterSpec(salt, getIterationsCount());
             try {
                 cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
             } catch (final Exception e) {
                 throw new ProcessException(e);
             }
 
+            // If this is OpenSSL EVP, the salt must be preceded by the header
+            if (isOpenSSLKDF()) {
+                out.write(OPENSSL_EVP_HEADER_MARKER.getBytes(StandardCharsets.US_ASCII));
+            }
+
             out.write(salt);
 
             final byte[] buffer = new byte[65536];

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
index 8e7bc05..ee21a50 100644
--- a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestEncryptContent.java
@@ -19,20 +19,35 @@ package org.apache.nifi.processors.standard;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Paths;
+import java.security.Security;
 import java.util.Collection;
-import java.util.HashSet;
 
+import org.apache.commons.codec.binary.Hex;
 import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.processors.standard.util.PasswordBasedEncryptor;
 import org.apache.nifi.security.util.EncryptionMethod;
+import org.apache.nifi.security.util.KeyDerivationFunction;
 import org.apache.nifi.util.MockFlowFile;
 import org.apache.nifi.util.MockProcessContext;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Before;
 import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class TestEncryptContent {
 
+    private static final Logger logger = LoggerFactory.getLogger(TestEncryptContent.class);
+
+    @Before
+    public void setUp() {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
     @Test
     public void testRoundTrip() throws IOException {
         final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent());
@@ -60,12 +75,110 @@ public class TestEncryptContent {
             testRunner.run();
             testRunner.assertAllFlowFilesTransferred(EncryptContent.REL_SUCCESS, 1);
 
+            logger.info("Successfully decrypted {}", method.name());
+
             flowFile = testRunner.getFlowFilesForRelationship(EncryptContent.REL_SUCCESS).get(0);
             flowFile.assertContentEquals(new File("src/test/resources/hello.txt"));
         }
     }
 
     @Test
+    public void testShouldDetermineMaxKeySizeForAlgorithms() throws IOException {
+        // Arrange
+        final String AES_ALGORITHM = EncryptionMethod.MD5_256AES.getAlgorithm();
+        final String DES_ALGORITHM = EncryptionMethod.MD5_DES.getAlgorithm();
+
+        final int AES_MAX_LENGTH = PasswordBasedEncryptor.supportsUnlimitedStrength() ? Integer.MAX_VALUE : 128;
+        final int DES_MAX_LENGTH = PasswordBasedEncryptor.supportsUnlimitedStrength() ? Integer.MAX_VALUE : 64;
+
+        // Act
+        int determinedAESMaxLength = PasswordBasedEncryptor.getMaxAllowedKeyLength(AES_ALGORITHM);
+        int determinedTDESMaxLength = PasswordBasedEncryptor.getMaxAllowedKeyLength(DES_ALGORITHM);
+
+        // Assert
+        assert determinedAESMaxLength == AES_MAX_LENGTH;
+        assert determinedTDESMaxLength == DES_MAX_LENGTH;
+    }
+
+    @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 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"));
+    }
+
+    @Test
+    public void testDecryptShouldDefaultToLegacyKDF() throws IOException {
+        // Arrange
+        final TestRunner testRunner = TestRunners.newTestRunner(new EncryptContent());
+
+        // Assert
+        Assert.assertEquals("Decrypt should default to Legacy KDF", testRunner.getProcessor().getPropertyDescriptor(EncryptContent.KEY_DERIVATION_FUNCTION
+                .getName()).getDefaultValue(), KeyDerivationFunction.NIFI_LEGACY.name());
+    }
+
+    @Test
     public void testDecryptSmallerThanSaltSize() {
         final TestRunner runner = TestRunners.newTestRunner(EncryptContent.class);
         runner.setProperty(EncryptContent.PASSWORD, "Hello, World!");
@@ -96,7 +209,6 @@ public class TestEncryptContent {
         Collection<ValidationResult> results;
         MockProcessContext pc;
 
-        results = new HashSet<>();
         runner.enqueue(new byte[0]);
         pc = (MockProcessContext) runner.getProcessContext();
         results = pc.validate();
@@ -106,7 +218,24 @@ public class TestEncryptContent {
                     .contains(EncryptContent.PASSWORD.getDisplayName() + " is required when using algorithm"));
         }
 
-        results = new HashSet<>();
+        runner.enqueue(new byte[0]);
+        runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.MD5_256AES.name());
+        runner.setProperty(EncryptContent.PASSWORD, "ThisIsAPasswordThatIsLongerThanSixteenCharacters");
+        pc = (MockProcessContext) runner.getProcessContext();
+        results = pc.validate();
+        if (!PasswordBasedEncryptor.supportsUnlimitedStrength()) {
+            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 " + PasswordBasedEncryptor.getMaxAllowedKeyLength(EncryptionMethod.MD5_256AES.getAlgorithm())
+                                + " bits is not supported by this JVM due to lacking JCE Unlimited Strength Jurisdiction Policy files."));
+            }
+        } else {
+            Assert.assertEquals(0, results.size());
+        }
+        runner.removeProperty(EncryptContent.PASSWORD);
+
         runner.setProperty(EncryptContent.ENCRYPTION_ALGORITHM, EncryptionMethod.PGP.name());
         runner.setProperty(EncryptContent.PUBLIC_KEYRING, "src/test/resources/TestEncryptContent/text.txt");
         runner.enqueue(new byte[0]);
@@ -120,7 +249,6 @@ public class TestEncryptContent {
                             + EncryptContent.PUBLIC_KEY_USERID.getDisplayName()));
         }
 
-        results = new HashSet<>();
         runner.setProperty(EncryptContent.PUBLIC_KEY_USERID, "USERID");
         runner.enqueue(new byte[0]);
         pc = (MockProcessContext) runner.getProcessContext();
@@ -133,7 +261,6 @@ public class TestEncryptContent {
         runner.removeProperty(EncryptContent.PUBLIC_KEYRING);
         runner.removeProperty(EncryptContent.PUBLIC_KEY_USERID);
 
-        results = new HashSet<>();
         runner.setProperty(EncryptContent.MODE, EncryptContent.DECRYPT_MODE);
         runner.setProperty(EncryptContent.PRIVATE_KEYRING, "src/test/resources/TestEncryptContent/text.txt");
         runner.enqueue(new byte[0]);
@@ -143,12 +270,11 @@ public class TestEncryptContent {
         for (final ValidationResult vr : results) {
             Assert.assertTrue(vr.toString().contains(
                     " decryption without a " + EncryptContent.PASSWORD.getDisplayName() + " requires both "
-                    + EncryptContent.PRIVATE_KEYRING.getDisplayName() + " and "
-                    + EncryptContent.PRIVATE_KEYRING_PASSPHRASE.getDisplayName()));
+                            + EncryptContent.PRIVATE_KEYRING.getDisplayName() + " and "
+                            + EncryptContent.PRIVATE_KEYRING_PASSPHRASE.getDisplayName()));
 
         }
 
-        results = new HashSet<>();
         runner.setProperty(EncryptContent.PRIVATE_KEYRING_PASSPHRASE, "PASSWORD");
         runner.enqueue(new byte[0]);
         pc = (MockProcessContext) runner.getProcessContext();

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/plain.txt
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/plain.txt b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/plain.txt
new file mode 100644
index 0000000..aae2266
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/plain.txt
@@ -0,0 +1 @@
+This is a plaintext message.
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/salted_raw.enc
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/salted_raw.enc b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/salted_raw.enc
new file mode 100644
index 0000000..8dad319
--- /dev/null
+++ b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/salted_raw.enc
@@ -0,0 +1 @@
+Salted__1�0l{�ak�]�gPM���z���x2!�s���ҏY���
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/f83e6d33/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/unsalted_raw.enc
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/unsalted_raw.enc b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/unsalted_raw.enc
new file mode 100644
index 0000000..9b0c75a
Binary files /dev/null and b/nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestEncryptContent/unsalted_raw.enc differ