Repository: nifi
Updated Branches:
  refs/heads/master 5fd4a5579 -> 89eb2ce28


NIFI-2652 Provided key migration capability for encrypted config tool.

Added test resources with 128-bit encryption for environments without unlimited 
strength cryptographic jurisdiction policies installed. All tests pass in both 
128- and 256-bit environments. (+8 squashed commits)
Squashed commits:
[55f127c] NIFI-2652 Updated Admin Guide with instructions for encrypted config 
key migration.
[05abf0e] NIFI-2652 Added unit tests for negative cases for migration argument 
parsing.
Cleaned up TODOs and comments.
[9b73b22] NIFI-2652 Removed SCrypt mock from one unit test that didn't need it. 
Test pollution is removed and all tests pass.
[d17ea77] NIFI-2652 Removed SCrypt mock from one redundant unit test. One 
offender remains ignored.
[0924ce0] NIFI-2652 Removed SCrypt mock from one unit test that did not need 
it. Two offenders remain ignored.
[cb5f850] NIFI-2652 Expanded unit test for combinations into individual tests 
due to System.exit() only be capturable once per test.
Three tests which mock Scrypt for speed are temporarily ignored to perform test 
pollution identification.
[c9cc5dc] NIFI-2652 Added logic and unit test for all combinations of original 
key/password and new key/password.
[19713ec] NIFI-2652 Implemented first pass of key migration logic and provided 
single comprehensive unit test.

This closes #1186.


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

Branch: refs/heads/master
Commit: 89eb2ce28c835a56452162002c46101288e61203
Parents: 5fd4a55
Author: Andy LoPresto <alopre...@apache.org>
Authored: Tue Nov 1 20:49:15 2016 -0700
Committer: Andy LoPresto <alopre...@apache.org>
Committed: Mon Nov 7 15:33:10 2016 -0800

----------------------------------------------------------------------
 .../src/main/asciidoc/administration-guide.adoc |  16 +-
 .../nifi/properties/ConfigEncryptionTool.groovy |  89 +++++-
 .../properties/ConfigEncryptionToolTest.groovy  | 282 +++++++++++++++----
 .../bootstrap_with_master_key_password.conf     |  74 +++++
 .../bootstrap_with_master_key_password_128.conf |  74 +++++
 ...properties_protected_aes_password.properties | 128 +++++++++
 ...erties_protected_aes_password_128.properties | 129 +++++++++
 7 files changed, 725 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-docs/src/main/asciidoc/administration-guide.adoc
----------------------------------------------------------------------
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc 
b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index d997bfc..2ce6564 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -988,13 +988,16 @@ The default encryption algorithm utilized is AES/GCM 
128/256-bit. 128-bit is use
 You can use the following command line options with the `encrypt-config` tool:
 
 * `-b,--bootstrapConf <arg>`          The bootstrap.conf file to persist 
master key
+* `-e,--oldKey <arg>`                 The old raw hexadecimal key to use 
during key migration
 * `-h,--help`                         Prints this usage message
-* `-k,--key <arg>`                    The raw hexadecimal key to use to 
encrypt the sensitive properties (the key can be entered with spaces or '-' 
delimiters to assist manual entry -- these will be ignored)
-* `-n,--niFiProperties <arg>`         The 'nifi.properties' file containing 
unprotected config values (will be overwritten by default unless `-o` is 
provided)
-* `-o,--outputNiFiProperties <arg>`   The destination 'nifi.properties' file 
containing protected config values (will not modify input 'nifi.properties')
+* `-k,--key <arg>`                    The raw hexadecimal key to use to 
encrypt the sensitive properties
+* `-m,--migrate`                      If provided, the sensitive properties 
will be re-encrypted with a new key
+* `-n,--niFiProperties <arg>`         The nifi.properties file containing 
unprotected config values (will be overwritten)
+* `-o,--outputNiFiProperties <arg>`   The destination nifi.properties file 
containing protected config values (will not modify input nifi.properties)
 * `-p,--password <arg>`               The password from which to derive the 
key to use to encrypt the sensitive properties
 * `-r,--useRawKey`                    If provided, the secure console will 
prompt for the raw key value in hexadecimal form
 * `-v,--verbose`                      Sets verbose mode (default false)
+* `-w,--oldPassword <arg>`            The old password from which to derive 
the key during migration
 
 As an example of how the tool works, assume that you have installed the tool 
on a machine supporting 256-bit encryption and with the following existing 
values in the 'nifi.properties' file:
 
@@ -1055,6 +1058,13 @@ Sensitive configuration values are encrypted by the tool 
by default, however you
 
 If the 'nifi.properties' file already has valid protected values, those 
property values are not modified by the tool.
 
+In order to change the key used to encrypt the sensitive values, indicate 
*migration mode* using the `-m` or `--migrate` flag, provide the new key or 
password using the `-k` or `-p` flags as usual, and provide the existing key or 
password using `-e` or `-w` respectively. This will allow the toolkit to 
decrypt the existing values and re-encrypt them, and update `bootstrap.conf` 
with the new key. Only one of the key or password needs to be specified for 
each phase (old vs. new), and any combination is sufficient:
+
+* old key -> new key
+* old key -> new password
+* old password -> new key
+* old password -> new password
+
 [[encrypt-config_password]]
 Password Key Derivation
 ~~~~~~~~~~~~~~~~~~~~~~~

http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
----------------------------------------------------------------------
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
index ace1d50..30a682c 100644
--- 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
@@ -48,10 +48,14 @@ class ConfigEncryptionTool {
     public String loginIdentityProvidersPath
 
     private String keyHex
+    private String migrationKeyHex
     private String password
+    private String migrationPassword
     private NiFiProperties niFiProperties
 
     private boolean usingPassword = true
+    private boolean usingPasswordMigration = true
+    private boolean migration = false
     private boolean isVerbose = false
 
     private static final String HELP_ARG = "help"
@@ -61,7 +65,10 @@ class ConfigEncryptionTool {
     private static final String OUTPUT_NIFI_PROPERTIES_ARG = 
"outputNiFiProperties"
     private static final String KEY_ARG = "key"
     private static final String PASSWORD_ARG = "password"
+    private static final String KEY_MIGRATION_ARG = "oldKey"
+    private static final String PASSWORD_MIGRATION_ARG = "oldPassword"
     private static final String USE_KEY_ARG = "useRawKey"
+    private static final String MIGRATION_ARG = "migrate"
 
     private static final int MIN_PASSWORD_LENGTH = 12
 
@@ -107,8 +114,11 @@ class ConfigEncryptionTool {
         options.addOption("b", BOOTSTRAP_CONF_ARG, true, "The bootstrap.conf 
file to persist master key")
         options.addOption("o", OUTPUT_NIFI_PROPERTIES_ARG, true, "The 
destination nifi.properties file containing protected config values (will not 
modify input nifi.properties)")
         options.addOption("k", KEY_ARG, true, "The raw hexadecimal key to use 
to encrypt the sensitive properties")
+        options.addOption("e", KEY_MIGRATION_ARG, true, "The old raw 
hexadecimal key to use during key migration")
         options.addOption("p", PASSWORD_ARG, true, "The password from which to 
derive the key to use to encrypt the sensitive properties")
+        options.addOption("w", PASSWORD_MIGRATION_ARG, true, "The old password 
from which to derive the key during migration")
         options.addOption("r", USE_KEY_ARG, false, "If provided, the secure 
console will prompt for the raw key value in hexadecimal form")
+        options.addOption("m", MIGRATION_ARG, false, "If provided, the 
sensitive properties will be re-encrypted with a new key")
     }
 
     /**
@@ -151,6 +161,28 @@ class ConfigEncryptionTool {
                 logger.warn("The source nifi.properties and destination 
nifi.properties are identical [${outputNiFiPropertiesPath}] so the original 
will be overwritten")
             }
 
+            if (commandLine.hasOption(MIGRATION_ARG)) {
+                migration = true
+                if (isVerbose) {
+                    logger.info("Key migration mode activated")
+                }
+                if (commandLine.hasOption(PASSWORD_MIGRATION_ARG)) {
+                    usingPasswordMigration = true
+                    if (commandLine.hasOption(KEY_MIGRATION_ARG)) {
+                        printUsageAndThrow("Only one of 
${PASSWORD_MIGRATION_ARG} and ${KEY_MIGRATION_ARG} can be used", 
ExitCode.INVALID_ARGS)
+                    } else {
+                        migrationPassword = 
commandLine.getOptionValue(PASSWORD_MIGRATION_ARG)
+                    }
+                } else {
+                    migrationKeyHex = 
commandLine.getOptionValue(KEY_MIGRATION_ARG)
+                    usingPasswordMigration = !migrationKeyHex
+                }
+            } else {
+                if (commandLine.hasOption(PASSWORD_MIGRATION_ARG) || 
commandLine.hasOption(KEY_MIGRATION_ARG)) {
+                    printUsageAndThrow("${PASSWORD_MIGRATION_ARG} and 
${KEY_MIGRATION_ARG} are ignored unless ${MIGRATION_ARG} is enabled", 
ExitCode.INVALID_ARGS)
+                }
+            }
+
             if (commandLine.hasOption(PASSWORD_ARG)) {
                 usingPassword = true
                 if (commandLine.hasOption(KEY_ARG)) {
@@ -179,25 +211,45 @@ class ConfigEncryptionTool {
         return commandLine
     }
 
-    private String getKey(TextDevice device = TextDevices.defaultTextDevice()) 
{
+    /**
+     * The method returns the provided, derived, or securely-entered key in 
hex format. The reason the parameters must be provided instead of read from the 
fields is because this is used for the regular key/password and the migration 
key/password.
+     *
+     * @param device
+     * @param keyHex
+     * @param password
+     * @param usingPassword
+     * @return
+     */
+    private String getKeyInternal(TextDevice device = 
TextDevices.defaultTextDevice(), String keyHex, String password, boolean 
usingPassword) {
         if (usingPassword) {
             if (!password) {
+                if (isVerbose) {
+                    logger.info("Reading password from secure console")
+                }
                 password = readPasswordFromConsole(device)
             }
             keyHex = deriveKeyFromPassword(password)
             password = null
-            usingPassword = false
-
             return keyHex
         } else {
             if (!keyHex) {
+                if (isVerbose) {
+                    logger.info("Reading hex key from secure console")
+                }
                 keyHex = readKeyFromConsole(device)
             }
-
             return keyHex
         }
     }
 
+    private String getKey(TextDevice textDevice = 
TextDevices.defaultTextDevice()) {
+        getKeyInternal(textDevice, keyHex, password, usingPassword)
+    }
+
+    private String getMigrationKey() {
+        getKeyInternal(TextDevices.defaultTextDevice(), migrationKeyHex, 
migrationPassword, usingPasswordMigration)
+    }
+
     private static String readKeyFromConsole(TextDevice textDevice) {
         textDevice.printf("Enter the master key in hexadecimal format (spaces 
acceptable): ")
         new String(textDevice.readPassword())
@@ -239,12 +291,12 @@ class ConfigEncryptionTool {
      * @return the NiFiProperties instance
      * @throw IOException if the nifi.properties file cannot be read
      */
-    private NiFiProperties loadNiFiProperties() throws IOException {
+    private NiFiProperties loadNiFiProperties(String existingKeyHex = keyHex) 
throws IOException {
         File niFiPropertiesFile
         if (niFiPropertiesPath && (niFiPropertiesFile = new 
File(niFiPropertiesPath)).exists()) {
             NiFiProperties properties
             try {
-                properties = 
NiFiPropertiesLoader.withKey(keyHex).load(niFiPropertiesFile)
+                properties = 
NiFiPropertiesLoader.withKey(existingKeyHex).load(niFiPropertiesFile)
                 logger.info("Loaded NiFiProperties instance with 
${properties.size()} properties")
                 return properties
             } catch (RuntimeException e) {
@@ -529,7 +581,30 @@ class ConfigEncryptionTool {
                     tool.printUsageAndThrow(e.getMessage(), 
ExitCode.INVALID_ARGS)
                 }
 
-                tool.niFiProperties = tool.loadNiFiProperties()
+                if (tool.migration) {
+                    String migrationKeyHex = tool.getMigrationKey()
+
+                    if (!migrationKeyHex) {
+                        tool.printUsageAndThrow("Original hex key must be 
provided for migration", ExitCode.INVALID_ARGS)
+                    }
+
+                    try {
+                        // Validate the length and format
+                        tool.migrationKeyHex = parseKey(migrationKeyHex)
+                    } catch (KeyException e) {
+                        if (tool.isVerbose) {
+                            logger.error("Encountered an error", e)
+                        }
+                        tool.printUsageAndThrow(e.getMessage(), 
ExitCode.INVALID_ARGS)
+                    }
+                }
+                String existingKeyHex = tool.migrationKeyHex ?: tool.keyHex
+
+                try {
+                    tool.niFiProperties = 
tool.loadNiFiProperties(existingKeyHex)
+                } catch (Exception e) {
+                    tool.printUsageAndThrow("Cannot migrate key if no previous 
encryption occurred", ExitCode.ERROR_READING_NIFI_PROPERTIES)
+                }
                 tool.niFiProperties = 
tool.encryptSensitiveProperties(tool.niFiProperties)
             } catch (CommandLineParseException e) {
                 if (e.exitCode == ExitCode.HELP) {

http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
----------------------------------------------------------------------
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
index f471e5f..c1521d7 100644
--- 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
@@ -18,13 +18,11 @@ package org.apache.nifi.properties
 
 import ch.qos.logback.classic.spi.LoggingEvent
 import ch.qos.logback.core.AppenderBase
-import org.apache.commons.codec.binary.Hex
 import org.apache.commons.lang3.SystemUtils
 import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException
 import org.apache.nifi.util.NiFiProperties
 import org.apache.nifi.util.console.TextDevice
 import org.apache.nifi.util.console.TextDevices
-import org.bouncycastle.crypto.generators.SCrypt
 import org.bouncycastle.jce.provider.BouncyCastleProvider
 import org.junit.After
 import org.junit.Assume
@@ -61,6 +59,11 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     private static final String KEY_HEX_256 = KEY_HEX_128 * 2
     public static final String KEY_HEX = isUnlimitedStrengthCryptoAvailable() 
? KEY_HEX_256 : KEY_HEX_128
     private static final String PASSWORD = "thisIsABadPassword"
+    // From ConfigEncryptionTool.deriveKeyFromPassword("thisIsABadPassword")
+    private static final String PASSWORD_KEY_HEX_256 = 
"2C576A9585DB862F5ECBEE5B4FFFCCA14B18D8365968D7081651006507AD2BDE"
+    private static final String PASSWORD_KEY_HEX_128 = 
"2C576A9585DB862F5ECBEE5B4FFFCCA1"
+
+    private static final String PASSWORD_KEY_HEX = 
isUnlimitedStrengthCryptoAvailable() ? PASSWORD_KEY_HEX_256 : 
PASSWORD_KEY_HEX_128
 
     @BeforeClass
     public static void setUpOnce() throws Exception {
@@ -73,7 +76,6 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
 
     @Before
     public void setUp() throws Exception {
-
     }
 
     @After
@@ -281,6 +283,41 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     }
 
     @Test
+    void testParseShouldFailIfMigrationPasswordAndKeyBothProvided() {
+        // Arrange
+        ConfigEncryptionTool tool = new ConfigEncryptionTool()
+
+        // Act
+        def msg = shouldFail {
+            tool.parse("-m -e oldKey -w oldPassword".split(" ") as String[])
+        }
+        logger.expected(msg)
+
+        // Assert
+        assert msg =~ "Only one of oldPassword and oldKey can be used"
+    }
+
+    @Test
+    void testParseShouldIgnoreMigrationKeyAndPasswordIfMigrationNotEnabled() {
+        // Arrange
+        ConfigEncryptionTool tool = new ConfigEncryptionTool()
+
+        def argStrings = ["-e oldKey",
+                          "-w oldPassword"]
+
+        // Act
+        argStrings.each { String argString ->
+            def msg = shouldFail {
+                tool.parse(argString.split(" ") as String[])
+            }
+            logger.expected(msg)
+
+            // Assert
+            assert msg == "oldPassword and oldKey are ignored unless migrate 
is enabled"
+        }
+    }
+
+    @Test
     void testShouldLoadNiFiProperties() {
         // Arrange
         String niFiPropertiesPath = 
"src/test/resources/nifi_with_sensitive_properties_unprotected.properties"
@@ -363,24 +400,12 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
 
         TextDevice mockConsoleDevice = TextDevices.streamDevice(new 
ByteArrayInputStream(PASSWORD.bytes), new ByteArrayOutputStream())
 
-        // Mocked for deterministic output and performance in test -- SCrypt 
is not under test here
-        SCrypt.metaClass.'static'.generate = { byte[] pw, byte[] s, int N, int 
r, int p, int dkLen ->
-            logger.mock("Mock SCrypt.generate(${Hex.encodeHexString(pw)}, 
${Hex.encodeHexString(s)}, ${N}, ${r}, ${p}, ${dkLen})")
-            logger.mock("Returning ${KEY_HEX[0..<dkLen * 2]}")
-            Hex.decodeHex(KEY_HEX[0..<dkLen * 2] as char[])
-        }
-
         // Act
         String readKey = tool.getKey(mockConsoleDevice)
         logger.info("Read key: [${readKey}]")
 
         // Assert
-        assert readKey == KEY_HEX
-        assert tool.keyHex == readKey
-        assert !tool.password
-        assert !tool.usingPassword
-
-        SCrypt.metaClass.'static' = null
+        assert readKey == PASSWORD_KEY_HEX
     }
 
     @Test
@@ -405,9 +430,6 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
 
         // Assert
         assert readKey == KEY_HEX
-        assert tool.keyHex == readKey
-        assert !tool.password
-        assert !tool.usingPassword
     }
 
     @Test
@@ -515,29 +537,6 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     }
 
     @Test
-    void testShouldDeriveKeyFromPassword() {
-        // Arrange
-
-        // Mocked for deterministic output and performance in test -- SCrypt 
is not under test here
-        SCrypt.metaClass.'static'.generate = { byte[] pw, byte[] s, int N, int 
r, int p, int dkLen ->
-            logger.mock("Mock SCrypt.generate(${Hex.encodeHexString(pw)}, 
${Hex.encodeHexString(s)}, ${N}, ${r}, ${p}, ${dkLen})")
-            logger.mock("Returning ${KEY_HEX[0..<dkLen * 2]}")
-            Hex.decodeHex(KEY_HEX[0..<dkLen * 2] as char[])
-        }
-
-        logger.info("Using password: [${PASSWORD}]")
-
-        // Act
-        String derivedKey = 
ConfigEncryptionTool.deriveKeyFromPassword(PASSWORD)
-        logger.info("Derived key:  [${derivedKey}]")
-
-        // Assert
-        assert derivedKey == KEY_HEX
-
-        SCrypt.metaClass.'static' = null
-    }
-
-    @Test
     void testShouldActuallyDeriveKeyFromPassword() {
         // Arrange
         logger.info("Using password: [${PASSWORD}]")
@@ -555,28 +554,19 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         // Arrange
         final String PASSWORD_SPACES = "   ${PASSWORD}   "
 
-        def attemptedPasswords = []
-
-        // Mocked for deterministic output and performance in test -- SCrypt 
is not under test here
-        SCrypt.metaClass.'static'.generate = { byte[] pw, byte[] s, int N, int 
r, int p, int dkLen ->
-            logger.mock("Mock SCrypt.generate(${Hex.encodeHexString(pw)}, 
${Hex.encodeHexString(s)}, ${N}, ${r}, ${p}, ${dkLen})")
-            attemptedPasswords << new String(pw)
-            logger.mock("Returning ${KEY_HEX[0..<dkLen * 2]}")
-            Hex.decodeHex(KEY_HEX[0..<dkLen * 2] as char[])
-        }
+        def attemptedPasswords = [PASSWORD, PASSWORD_SPACES]
 
         // Act
-        [PASSWORD, PASSWORD_SPACES].each { String password ->
+        def derivedKeys = attemptedPasswords.collect { String password ->
             logger.info("Using password: [${password}]")
             String derivedKey = 
ConfigEncryptionTool.deriveKeyFromPassword(password)
             logger.info("Derived key:  [${derivedKey}]")
+            derivedKey
         }
 
         // Assert
         assert attemptedPasswords.size() == 2
-        assert attemptedPasswords.every { it == PASSWORD }
-
-        SCrypt.metaClass.'static' = null
+        assert derivedKeys.every { it == PASSWORD_KEY_HEX }
     }
 
     @Test
@@ -1430,7 +1420,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         logger.info("Original key line from bootstrap.conf: 
${originalKeyLine}")
         assert originalKeyLine == ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX
 
-        final String EXPECTED_KEY_HEX = 
ConfigEncryptionTool.deriveKeyFromPassword(PASSWORD)
+        final String EXPECTED_KEY_HEX = PASSWORD_KEY_HEX
         logger.info("Derived key from password [${PASSWORD}]: 
${EXPECTED_KEY_HEX}")
 
         final String EXPECTED_KEY_LINE = 
ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX + EXPECTED_KEY_HEX
@@ -1512,7 +1502,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         logger.info("Original key line from bootstrap.conf: 
${originalKeyLine}")
         assert originalKeyLine == ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX
 
-        final String EXPECTED_KEY_HEX = 
ConfigEncryptionTool.deriveKeyFromPassword(PASSWORD)
+        final String EXPECTED_KEY_HEX = PASSWORD_KEY_HEX
         logger.info("Derived key from password [${PASSWORD}]: 
${EXPECTED_KEY_HEX}")
 
         final String EXPECTED_KEY_LINE = 
ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX + EXPECTED_KEY_HEX
@@ -1583,6 +1573,184 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
 
         // Assertions defined above
     }
+
+    /**
+     * Helper method to execute key migration test for varying combinations of 
old/new key/password.
+     *
+     * @param scenario a human-readable description of the test scenario
+     * @param scenarioArgs a list of the arguments specific to this scenario 
to be passed to the tool
+     * @param oldPassword the original password
+     * @param newPassword the new password
+     * @param oldKeyHex the original key hex (if present, original password is 
ignored; if not, this is derived)
+     * @param newKeyHex the new key hex (if present, new password is ignored; 
if not, this is derived)
+     */
+    private void performKeyMigration(String scenario, List scenarioArgs, 
String oldPassword = PASSWORD, String newPassword = PASSWORD.reverse(), String 
oldKeyHex = "", String newKeyHex = "") {
+        // Arrange
+        exit.expectSystemExitWithStatus(0)
+
+        // Initial set up
+        File tmpDir = new File("target/tmp/")
+        tmpDir.mkdirs()
+        setFilePermissions(tmpDir, [PosixFilePermission.OWNER_READ, 
PosixFilePermission.OWNER_WRITE, PosixFilePermission.OWNER_EXECUTE, 
PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_WRITE, 
PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, 
PosixFilePermission.OTHERS_WRITE, PosixFilePermission.OTHERS_EXECUTE])
+
+        String bootstrapPath = isUnlimitedStrengthCryptoAvailable() ? 
"src/test/resources/bootstrap_with_master_key_password.conf" :
+                
"src/test/resources/bootstrap_with_master_key_password_128.conf"
+        File originalKeyFile = new File(bootstrapPath)
+        File bootstrapFile = new File("target/tmp/tmp_bootstrap.conf")
+        bootstrapFile.delete()
+
+        Files.copy(originalKeyFile.toPath(), bootstrapFile.toPath())
+        final List<String> originalBootstrapLines = bootstrapFile.readLines()
+        String originalKeyLine = originalBootstrapLines.find {
+            it.startsWith(ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX)
+        }
+
+        // Perform necessary key derivations
+        if (!oldKeyHex) {
+            oldKeyHex = ConfigEncryptionTool.deriveKeyFromPassword(oldPassword)
+            logger.info("Original key derived from password [${oldPassword}]: 
\t${oldKeyHex}")
+        } else {
+            logger.info("Original key provided directly: \t${oldKeyHex}")
+        }
+
+        if (!newKeyHex) {
+            newKeyHex = ConfigEncryptionTool.deriveKeyFromPassword(newPassword)
+            logger.info("Migration key derived from password [${newPassword}]: 
\t${newKeyHex}")
+        } else {
+            logger.info("Migration key provided directly: \t${newKeyHex}")
+        }
+
+        // Reset the source bootstrap.conf file to use the old key (may be 
derived from old password)
+        final String PASSWORD_KEY_LINE = 
ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX + oldKeyHex
+        assert originalKeyLine == PASSWORD_KEY_LINE
+
+        String inputPropertiesPath = isUnlimitedStrengthCryptoAvailable() ?
+                
"src/test/resources/nifi_with_sensitive_properties_protected_aes_password.properties"
 :
+                
"src/test/resources/nifi_with_sensitive_properties_protected_aes_password_128.properties"
+        File inputPropertiesFile = new File(inputPropertiesPath)
+        File outputPropertiesFile = new File("target/tmp/tmp_nifi.properties")
+        outputPropertiesFile.delete()
+
+        // Log original sensitive properties (encrypted with first key)
+        NiFiProperties inputProperties = 
NiFiPropertiesLoader.withKey(oldKeyHex).load(inputPropertiesFile)
+        logger.info("Loaded ${inputProperties.size()} properties from input 
file")
+        ProtectedNiFiProperties protectedInputProperties = new 
ProtectedNiFiProperties(inputProperties)
+        def originalSensitiveValues = 
protectedInputProperties.getSensitivePropertyKeys().collectEntries { String key 
-> [(key): protectedInputProperties.getProperty(key)] }
+        logger.info("Original sensitive values: ${originalSensitiveValues}")
+
+        final String EXPECTED_NEW_KEY_LINE = 
ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX + newKeyHex
+
+        // Act
+        String[] args = ["-n", inputPropertiesFile.path,
+                         "-b", bootstrapFile.path,
+                         "-o", outputPropertiesFile.path,
+                         "-m",
+                         "-v"]
+
+        List<String> localArgs = args + scenarioArgs
+        logger.info("Running [${scenario}] with args: ${localArgs}")
+
+        exit.checkAssertionAfterwards(new Assertion() {
+            public void checkAssertion() {
+                final List<String> updatedPropertiesLines = 
outputPropertiesFile.readLines()
+                logger.info("Updated nifi.properties:")
+                logger.info("\n" * 2 + updatedPropertiesLines.join("\n"))
+
+                // Check that the output values for sensitive properties are 
not the same as the original (i.e. it was re-encrypted)
+                NiFiProperties updatedProperties = new 
NiFiPropertiesLoader().readProtectedPropertiesFromDisk(outputPropertiesFile)
+                assert updatedProperties.size() >= inputProperties.size()
+                originalSensitiveValues.every { String key, String 
originalValue ->
+                    assert updatedProperties.getProperty(key) != originalValue
+                }
+
+                // Check that the new NiFiProperties instance matches the 
output file (values still encrypted)
+                updatedProperties.getPropertyKeys().every { String key ->
+                    assert 
updatedPropertiesLines.contains("${key}=${updatedProperties.getProperty(key)}".toString())
+                }
+
+                // Check that the key was persisted to the bootstrap.conf
+                final List<String> updatedBootstrapLines = 
bootstrapFile.readLines()
+                String updatedKeyLine = updatedBootstrapLines.find {
+                    it.startsWith(ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX)
+                }
+                logger.info("Updated key line: ${updatedKeyLine}")
+
+                assert updatedKeyLine == EXPECTED_NEW_KEY_LINE
+                assert originalBootstrapLines.size() == 
updatedBootstrapLines.size()
+
+                // Clean up
+                outputPropertiesFile.deleteOnExit()
+                bootstrapFile.deleteOnExit()
+                tmpDir.deleteOnExit()
+            }
+        });
+
+        logger.info("Migrating key (${scenario}) with ${localArgs.join(" ")}")
+        ConfigEncryptionTool.main(localArgs as String[])
+
+        // Assert
+
+        // Assertions defined above
+    }
+
+    /**
+     * Ideally all of the combination tests would be a single test with 
iterative argument lists, but due to the System.exit(), it can only be captured 
once per test.
+     */
+    @Test
+    public void testShouldMigrateFromPasswordToPassword() {
+        // Arrange
+        String scenario = "password to password"
+        def args = ["-w", PASSWORD, "-p", PASSWORD.reverse()]
+
+        // Act
+        performKeyMigration(scenario, args, PASSWORD, PASSWORD.reverse())
+
+        // Assert
+
+        // Assertions in common method above
+    }
+
+    @Test
+    public void testShouldMigrateFromPasswordToKey() {
+        // Arrange
+        String scenario = "password to key"
+        def args = ["-w", PASSWORD, "-k", KEY_HEX]
+
+        // Act
+        performKeyMigration(scenario, args, PASSWORD, "", "", KEY_HEX)
+
+        // Assert
+
+        // Assertions in common method above
+    }
+
+    @Test
+    public void testShouldMigrateFromKeyToPassword() {
+        // Arrange
+        String scenario = "key to password"
+        def args = ["-e", PASSWORD_KEY_HEX, "-p", PASSWORD.reverse()]
+
+        // Act
+        performKeyMigration(scenario, args, "", PASSWORD.reverse(), 
PASSWORD_KEY_HEX, "")
+
+        // Assert
+
+        // Assertions in common method above
+    }
+
+    @Test
+    public void testShouldMigrateFromKeyToKey() {
+        // Arrange
+        String scenario = "key to key"
+        def args = ["-e", PASSWORD_KEY_HEX, "-k", KEY_HEX]
+
+        // Act
+        performKeyMigration(scenario, args, "", "", PASSWORD_KEY_HEX, KEY_HEX)
+
+        // Assert
+
+        // Assertions in common method above
+    }
 }
 
 public class TestAppender extends AppenderBase<LoggingEvent> {

http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password.conf
----------------------------------------------------------------------
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password.conf
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password.conf
new file mode 100644
index 0000000..f3a8491
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password.conf
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Java command to use when running NiFi
+java=java
+
+# Username to use when running NiFi. This value will be ignored on Windows.
+run.as=
+
+# Configure where NiFi's lib and conf directories live
+lib.dir=./lib
+conf.dir=./conf
+
+# How long to wait after telling NiFi to shutdown before explicitly killing 
the Process
+graceful.shutdown.seconds=20
+
+# Disable JSR 199 so that we can use JSP's without running a JDK
+java.arg.1=-Dorg.apache.jasper.compiler.disablejsr199=true
+
+# JVM memory settings
+java.arg.2=-Xms512m
+java.arg.3=-Xmx512m
+
+# Enable Remote Debugging
+#java.arg.debug=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
+
+java.arg.4=-Djava.net.preferIPv4Stack=true
+
+# allowRestrictedHeaders is required for Cluster/Node communications to work 
properly
+java.arg.5=-Dsun.net.http.allowRestrictedHeaders=true
+java.arg.6=-Djava.protocol.handler.pkgs=sun.net.www.protocol
+
+# The G1GC is still considered experimental but has proven to be very 
advantageous in providing great
+# performance without significant "stop-the-world" delays.
+java.arg.13=-XX:+UseG1GC
+
+#Set headless mode by default
+java.arg.14=-Djava.awt.headless=true
+
+# Master key in hexadecimal format for encrypted sensitive configuration values
+nifi.bootstrap.sensitive.key=2C576A9585DB862F5ECBEE5B4FFFCCA14B18D8365968D7081651006507AD2BDE
+
+###
+# Notification Services for notifying interested parties when NiFi is stopped, 
started, dies
+###
+
+# XML File that contains the definitions of the notification services
+notification.services.file=./conf/bootstrap-notification-services.xml
+
+# In the case that we are unable to send a notification for an event, how many 
times should we retry?
+notification.max.attempts=5
+
+# Comma-separated list of identifiers that are present in the 
notification.services.file; which services should be used to notify when NiFi 
is started?
+#nifi.start.notification.services=email-notification
+
+# Comma-separated list of identifiers that are present in the 
notification.services.file; which services should be used to notify when NiFi 
is stopped?
+#nifi.stop.notification.services=email-notification
+
+# Comma-separated list of identifiers that are present in the 
notification.services.file; which services should be used to notify when NiFi 
dies?
+#nifi.dead.notification.services=email-notification
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password_128.conf
----------------------------------------------------------------------
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password_128.conf
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password_128.conf
new file mode 100644
index 0000000..929a46a
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/bootstrap_with_master_key_password_128.conf
@@ -0,0 +1,74 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Java command to use when running NiFi
+java=java
+
+# Username to use when running NiFi. This value will be ignored on Windows.
+run.as=
+
+# Configure where NiFi's lib and conf directories live
+lib.dir=./lib
+conf.dir=./conf
+
+# How long to wait after telling NiFi to shutdown before explicitly killing 
the Process
+graceful.shutdown.seconds=20
+
+# Disable JSR 199 so that we can use JSP's without running a JDK
+java.arg.1=-Dorg.apache.jasper.compiler.disablejsr199=true
+
+# JVM memory settings
+java.arg.2=-Xms512m
+java.arg.3=-Xmx512m
+
+# Enable Remote Debugging
+#java.arg.debug=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
+
+java.arg.4=-Djava.net.preferIPv4Stack=true
+
+# allowRestrictedHeaders is required for Cluster/Node communications to work 
properly
+java.arg.5=-Dsun.net.http.allowRestrictedHeaders=true
+java.arg.6=-Djava.protocol.handler.pkgs=sun.net.www.protocol
+
+# The G1GC is still considered experimental but has proven to be very 
advantageous in providing great
+# performance without significant "stop-the-world" delays.
+java.arg.13=-XX:+UseG1GC
+
+#Set headless mode by default
+java.arg.14=-Djava.awt.headless=true
+
+# Master key in hexadecimal format for encrypted sensitive configuration values
+nifi.bootstrap.sensitive.key=2C576A9585DB862F5ECBEE5B4FFFCCA1
+
+###
+# Notification Services for notifying interested parties when NiFi is stopped, 
started, dies
+###
+
+# XML File that contains the definitions of the notification services
+notification.services.file=./conf/bootstrap-notification-services.xml
+
+# In the case that we are unable to send a notification for an event, how many 
times should we retry?
+notification.max.attempts=5
+
+# Comma-separated list of identifiers that are present in the 
notification.services.file; which services should be used to notify when NiFi 
is started?
+#nifi.start.notification.services=email-notification
+
+# Comma-separated list of identifiers that are present in the 
notification.services.file; which services should be used to notify when NiFi 
is stopped?
+#nifi.stop.notification.services=email-notification
+
+# Comma-separated list of identifiers that are present in the 
notification.services.file; which services should be used to notify when NiFi 
dies?
+#nifi.dead.notification.services=email-notification
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password.properties
----------------------------------------------------------------------
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password.properties
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password.properties
new file mode 100644
index 0000000..c3eb2c5
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password.properties
@@ -0,0 +1,128 @@
+# 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.
+
+# Core Properties #
+nifi.version=nifi-test 3.0.0
+nifi.flow.configuration.file=./target/flow.xml.gz
+nifi.flow.configuration.archive.dir=./target/archive/
+nifi.flowcontroller.autoResumeState=true
+nifi.flowcontroller.graceful.shutdown.period=10 sec
+nifi.flowservice.writedelay.interval=2 sec
+nifi.administrative.yield.duration=30 sec
+
+nifi.reporting.task.configuration.file=./target/reporting-tasks.xml
+nifi.controller.service.configuration.file=./target/controller-services.xml
+nifi.templates.directory=./target/templates
+nifi.ui.banner.text=UI Banner Text
+nifi.ui.autorefresh.interval=30 sec
+nifi.nar.library.directory=./target/resources/NiFiProperties/lib/
+nifi.nar.library.directory.alt=./target/resources/NiFiProperties/lib2/
+nifi.nar.working.directory=./target/work/nar/
+
+# H2 Settings
+nifi.database.directory=./target/database_repository
+nifi.h2.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE
+
+# FlowFile Repository
+nifi.flowfile.repository.directory=./target/test-repo
+nifi.flowfile.repository.partitions=1
+nifi.flowfile.repository.checkpoint.interval=2 mins
+nifi.queue.swap.threshold=20000
+nifi.swap.storage.directory=./target/test-repo/swap
+nifi.swap.in.period=5 sec
+nifi.swap.in.threads=1
+nifi.swap.out.period=5 sec
+nifi.swap.out.threads=4
+
+# Content Repository
+nifi.content.claim.max.appendable.size=10 MB
+nifi.content.claim.max.flow.files=100
+nifi.content.repository.directory.default=./target/content_repository
+
+# Provenance Repository Properties
+nifi.provenance.repository.storage.directory=./target/provenance_repository
+nifi.provenance.repository.max.storage.time=24 hours
+nifi.provenance.repository.max.storage.size=1 GB
+nifi.provenance.repository.rollover.time=30 secs
+nifi.provenance.repository.rollover.size=100 MB
+
+# Site to Site properties
+nifi.remote.input.socket.port=9990
+nifi.remote.input.secure=true
+
+# web properties #
+nifi.web.war.directory=./target/lib
+nifi.web.http.host=
+nifi.web.http.port=
+nifi.web.https.host=nifi.nifi.apache.org
+nifi.web.https.port=8443
+nifi.web.jetty.working.directory=./target/work/jetty
+
+# security properties #
+nifi.sensitive.props.key=n6ZO5Y9zv4CGElB2||e0SwwiJqNOZ8drLl30+dbiSYYMgd+Vx7rFjwCYEkJpF4Vh+Tx8+7Oek96kpoxQ
+nifi.sensitive.props.key.protected=aes/gcm/256
+nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL
+nifi.sensitive.props.provider=BC
+nifi.sensitive.props.additional.keys=
+
+nifi.security.keystore=/path/to/keystore.jks
+nifi.security.keystoreType=JKS
+nifi.security.keystorePasswd=LfiLX6USM9NZOyt1||CVG+AxNeAeK4lV0mp0dr54RPboO1QxUOHEXAXUtqqdravyLFz/l3PUA4
+nifi.security.keystorePasswd.protected=aes/gcm/256
+nifi.security.keyPasswd=AJpYEYxT8xx8uXBw||99NyDx1qiZWwY6KfGhwfRgUN6stnaKfTctegOCN0/EmFkwF0lA
+nifi.security.keyPasswd.protected=aes/gcm/256
+nifi.security.truststore=
+nifi.security.truststoreType=
+nifi.security.truststorePasswd=
+nifi.security.needClientAuth=
+nifi.security.user.authorizer=
+
+# cluster common properties (cluster manager and nodes must have same values) #
+nifi.cluster.protocol.heartbeat.interval=5 sec
+nifi.cluster.protocol.is.secure=false
+nifi.cluster.protocol.socket.timeout=30 sec
+nifi.cluster.protocol.connection.handshake.timeout=45 sec
+# if multicast is used, then nifi.cluster.protocol.multicast.xxx properties 
must be configured #
+nifi.cluster.protocol.use.multicast=false
+nifi.cluster.protocol.multicast.address=
+nifi.cluster.protocol.multicast.port=
+nifi.cluster.protocol.multicast.service.broadcast.delay=500 ms
+nifi.cluster.protocol.multicast.service.locator.attempts=3
+nifi.cluster.protocol.multicast.service.locator.attempts.delay=1 sec
+
+# cluster node properties (only configure for cluster nodes) #
+nifi.cluster.is.node=false
+nifi.cluster.node.address=
+nifi.cluster.node.protocol.port=
+nifi.cluster.node.protocol.threads=2
+# if multicast is not used, nifi.cluster.node.unicast.xxx must have same 
values as nifi.cluster.manager.xxx #
+nifi.cluster.node.unicast.manager.address=
+nifi.cluster.node.unicast.manager.protocol.port=
+nifi.cluster.node.unicast.manager.authority.provider.port=
+
+# cluster manager properties (only configure for cluster manager) #
+nifi.cluster.is.manager=false
+nifi.cluster.manager.address=
+nifi.cluster.manager.protocol.port=
+nifi.cluster.manager.authority.provider.port=
+nifi.cluster.manager.authority.provider.threads=10
+nifi.cluster.manager.node.firewall.file=
+nifi.cluster.manager.node.event.history.size=10
+nifi.cluster.manager.node.api.connection.timeout=30 sec
+nifi.cluster.manager.node.api.read.timeout=30 sec
+nifi.cluster.manager.node.api.request.threads=10
+nifi.cluster.manager.flow.retrieval.delay=5 sec
+nifi.cluster.manager.protocol.threads=10
+nifi.cluster.manager.safemode.duration=0 sec
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/nifi/blob/89eb2ce2/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password_128.properties
----------------------------------------------------------------------
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password_128.properties
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password_128.properties
new file mode 100644
index 0000000..e165937
--- /dev/null
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/nifi_with_sensitive_properties_protected_aes_password_128.properties
@@ -0,0 +1,129 @@
+# 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.
+
+# Core Properties #
+nifi.version=nifi-test 3.0.0
+nifi.flow.configuration.file=./target/flow.xml.gz
+nifi.flow.configuration.archive.dir=./target/archive/
+nifi.flowcontroller.autoResumeState=true
+nifi.flowcontroller.graceful.shutdown.period=10 sec
+nifi.flowservice.writedelay.interval=2 sec
+nifi.administrative.yield.duration=30 sec
+
+nifi.reporting.task.configuration.file=./target/reporting-tasks.xml
+nifi.controller.service.configuration.file=./target/controller-services.xml
+nifi.templates.directory=./target/templates
+nifi.ui.banner.text=UI Banner Text
+nifi.ui.banner.text.protected=
+nifi.ui.autorefresh.interval=30 sec
+nifi.nar.library.directory=./target/resources/NiFiProperties/lib/
+nifi.nar.library.directory.alt=./target/resources/NiFiProperties/lib2/
+nifi.nar.working.directory=./target/work/nar/
+
+# H2 Settings
+nifi.database.directory=./target/database_repository
+nifi.h2.url.append=;LOCK_TIMEOUT=25000;WRITE_DELAY=0;AUTO_SERVER=FALSE
+
+# FlowFile Repository
+nifi.flowfile.repository.directory=./target/test-repo
+nifi.flowfile.repository.partitions=1
+nifi.flowfile.repository.checkpoint.interval=2 mins
+nifi.queue.swap.threshold=20000
+nifi.swap.storage.directory=./target/test-repo/swap
+nifi.swap.in.period=5 sec
+nifi.swap.in.threads=1
+nifi.swap.out.period=5 sec
+nifi.swap.out.threads=4
+
+# Content Repository
+nifi.content.claim.max.appendable.size=10 MB
+nifi.content.claim.max.flow.files=100
+nifi.content.repository.directory.default=./target/content_repository
+
+# Provenance Repository Properties
+nifi.provenance.repository.storage.directory=./target/provenance_repository
+nifi.provenance.repository.max.storage.time=24 hours
+nifi.provenance.repository.max.storage.size=1 GB
+nifi.provenance.repository.rollover.time=30 secs
+nifi.provenance.repository.rollover.size=100 MB
+
+# Site to Site properties
+nifi.remote.input.socket.port=9990
+nifi.remote.input.secure=true
+
+# web properties #
+nifi.web.war.directory=./target/lib
+nifi.web.http.host=
+nifi.web.http.port=
+nifi.web.https.host=nifi.nifi.apache.org
+nifi.web.https.port=8443
+nifi.web.jetty.working.directory=./target/work/jetty
+
+# security properties #
+nifi.sensitive.props.key=xwc0atbb2krNWE8i||NLuzY6uraSVnONQEhA6hxfVntOTPhzG7yiKOysopYzfBTYY5Um1oNgnyvrCadw
+nifi.sensitive.props.key.protected=aes/gcm/128
+nifi.sensitive.props.algorithm=PBEWITHMD5AND256BITAES-CBC-OPENSSL
+nifi.sensitive.props.provider=BC
+nifi.sensitive.props.additional.keys=
+
+nifi.security.keystore=/path/to/keystore.jks
+nifi.security.keystoreType=JKS
+nifi.security.keystorePasswd=isL3pLaJJdpZzGa6||y8OnerzMxWfsFEya7STIvLIZNJEbPPGOD/oEhKtZVzIETej0z4ut9V2o
+nifi.security.keystorePasswd.protected=aes/gcm/128
+nifi.security.keyPasswd=m+E0t8rhmjTI7mY9||qpOcS/x25PgLAg1HaP8hEf2ZsrJrdEo+xybeWcLI6iPjdU7RUA
+nifi.security.keyPasswd.protected=aes/gcm/128
+nifi.security.truststore=
+nifi.security.truststoreType=
+nifi.security.truststorePasswd=
+nifi.security.needClientAuth=
+nifi.security.user.authorizer=
+
+# cluster common properties (cluster manager and nodes must have same values) #
+nifi.cluster.protocol.heartbeat.interval=5 sec
+nifi.cluster.protocol.is.secure=false
+nifi.cluster.protocol.socket.timeout=30 sec
+nifi.cluster.protocol.connection.handshake.timeout=45 sec
+# if multicast is used, then nifi.cluster.protocol.multicast.xxx properties 
must be configured #
+nifi.cluster.protocol.use.multicast=false
+nifi.cluster.protocol.multicast.address=
+nifi.cluster.protocol.multicast.port=
+nifi.cluster.protocol.multicast.service.broadcast.delay=500 ms
+nifi.cluster.protocol.multicast.service.locator.attempts=3
+nifi.cluster.protocol.multicast.service.locator.attempts.delay=1 sec
+
+# cluster node properties (only configure for cluster nodes) #
+nifi.cluster.is.node=false
+nifi.cluster.node.address=
+nifi.cluster.node.protocol.port=
+nifi.cluster.node.protocol.threads=2
+# if multicast is not used, nifi.cluster.node.unicast.xxx must have same 
values as nifi.cluster.manager.xxx #
+nifi.cluster.node.unicast.manager.address=
+nifi.cluster.node.unicast.manager.protocol.port=
+nifi.cluster.node.unicast.manager.authority.provider.port=
+
+# cluster manager properties (only configure for cluster manager) #
+nifi.cluster.is.manager=false
+nifi.cluster.manager.address=
+nifi.cluster.manager.protocol.port=
+nifi.cluster.manager.authority.provider.port=
+nifi.cluster.manager.authority.provider.threads=10
+nifi.cluster.manager.node.firewall.file=
+nifi.cluster.manager.node.event.history.size=10
+nifi.cluster.manager.node.api.connection.timeout=30 sec
+nifi.cluster.manager.node.api.read.timeout=30 sec
+nifi.cluster.manager.node.api.request.threads=10
+nifi.cluster.manager.flow.retrieval.delay=5 sec
+nifi.cluster.manager.protocol.threads=10
+nifi.cluster.manager.safemode.duration=0 sec
\ No newline at end of file

Reply via email to