NIFI-8511 Added KeyStore implementation of KeyProvider

- KeyStoreKeyProvider supports PKCS12 and BCFKS
- Refactored KeyProvider and implementations to nifi-security-kms
- Updated Admin Guide and User Guide with KeyStoreKeyProvider details

NIFI-8511 Improved documentation and streamlined several methods

Signed-off-by: Nathan Gough <thenatog@gmail.com>

This closes #5110.
diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index 329f916..91b9f73 100644
--- a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++ b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -102,6 +102,7 @@
     public static final String CONTENT_REPOSITORY_ENCRYPTION_KEY_ID = "nifi.content.repository.encryption.key.id";
     public static final String CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS = "nifi.content.repository.encryption.key.provider.implementation";
     public static final String CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_LOCATION = "nifi.content.repository.encryption.key.provider.location";
+    public static final String CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_PASSWORD = "nifi.content.repository.encryption.key.provider.password";
 
     // flowfile repository properties
     public static final String FLOWFILE_REPOSITORY_IMPLEMENTATION = "nifi.flowfile.repository.implementation";
@@ -113,6 +114,7 @@
     public static final String FLOWFILE_REPOSITORY_ENCRYPTION_KEY_ID = "nifi.flowfile.repository.encryption.key.id";
     public static final String FLOWFILE_REPOSITORY_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS = "nifi.flowfile.repository.encryption.key.provider.implementation";
     public static final String FLOWFILE_REPOSITORY_ENCRYPTION_KEY_PROVIDER_LOCATION = "nifi.flowfile.repository.encryption.key.provider.location";
+    public static final String FLOWFILE_REPOSITORY_ENCRYPTION_KEY_PROVIDER_PASSWORD = "nifi.flowfile.repository.encryption.key.provider.password";
     public static final String FLOWFILE_SWAP_MANAGER_IMPLEMENTATION = "nifi.swap.manager.implementation";
     public static final String QUEUE_SWAP_THRESHOLD = "nifi.queue.swap.threshold";
 
@@ -135,6 +137,7 @@
     public static final String PROVENANCE_REPO_ENCRYPTION_KEY_ID = "nifi.provenance.repository.encryption.key.id";
     public static final String PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS = "nifi.provenance.repository.encryption.key.provider.implementation";
     public static final String PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_LOCATION = "nifi.provenance.repository.encryption.key.provider.location";
+    public static final String PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_PASSWORD = "nifi.provenance.repository.encryption.key.provider.password";
     public static final String PROVENANCE_REPO_DEBUG_FREQUENCY = "nifi.provenance.repository.debug.frequency";
 
     // status repository properties
@@ -1614,8 +1617,7 @@
 
     /**
      * Returns a map of keyId -> key in hex loaded from the {@code nifi.properties} file if a
-     * {@code StaticKeyProvider} is defined. If {@code FileBasedKeyProvider} is defined, use
-     * {@code CryptoUtils#readKeys()} instead -- this method will return an empty map.
+     * {@code StaticKeyProvider} is defined. If {@code FileBasedKeyProvider} is defined this method will return an empty map.
      *
      * @return a Map of the keys identified by key ID
      */
@@ -1746,8 +1748,7 @@
 
     /**
      * Returns a map of keyId -> key in hex loaded from the {@code nifi.properties} file if a
-     * {@code StaticKeyProvider} is defined. If {@code FileBasedKeyProvider} is defined, use
-     * {@code CryptoUtils#readKeys()} instead -- this method will return an empty map.
+     * {@code StaticKeyProvider} is defined. If {@code FileBasedKeyProvider} is defined this method will return an empty map.
      *
      * @return a Map of the keys identified by key ID
      */
@@ -1778,8 +1779,7 @@
 
     /**
      * Returns a map of keyId -> key in hex loaded from the {@code nifi.properties} file if a
-     * {@code StaticKeyProvider} is defined. If {@code FileBasedKeyProvider} is defined, use
-     * {@code CryptoUtils#readKeys()} instead -- this method will return an empty map.
+     * {@code StaticKeyProvider} is defined. If {@code FileBasedKeyProvider} is defined this method will return an empty map.
      *
      * @return a Map of the keys identified by key ID
      */
diff --git a/nifi-commons/nifi-security-kms/pom.xml b/nifi-commons/nifi-security-kms/pom.xml
new file mode 100644
index 0000000..a11d3a9
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-commons</artifactId>
+        <version>1.14.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-security-kms</artifactId>
+    <dependencies>
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.15</version>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/FileBasedKeyProvider.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/FileBasedKeyProvider.java
new file mode 100644
index 0000000..3157c0d
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/FileBasedKeyProvider.java
@@ -0,0 +1,34 @@
+/*
+ * 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.kms;
+
+import org.apache.nifi.security.kms.reader.StandardFileBasedKeyReader;
+import org.apache.nifi.security.kms.reader.FileBasedKeyReader;
+
+import java.nio.file.Path;
+import javax.crypto.SecretKey;
+
+/**
+ * File Based Key Provider reads encrypted Secret Keys from a properties file containing one or more entries
+ */
+public class FileBasedKeyProvider extends StaticKeyProvider {
+    private static final FileBasedKeyReader READER = new StandardFileBasedKeyReader();
+
+    public FileBasedKeyProvider(final Path location, final SecretKey rootKey) {
+        super(READER.readSecretKeys(location, rootKey));
+    }
+}
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/KeyProvider.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProvider.java
similarity index 73%
rename from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/KeyProvider.java
rename to nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProvider.java
index f842a9b..cc9a1c9 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/KeyProvider.java
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProvider.java
@@ -19,10 +19,8 @@
 import java.security.KeyManagementException;
 import java.util.List;
 import javax.crypto.SecretKey;
-import javax.naming.OperationNotSupportedException;
 
 public interface KeyProvider {
-
     /**
      * Returns the key identified by this ID or throws an exception if one is not available.
      *
@@ -46,17 +44,4 @@
      * @return a List of keyIds (empty list if none are available)
      */
      List<String> getAvailableKeyIds();
-
-    /**
-     * Adds the key to the provider and associates it with the given ID. Some implementations may not allow this operation.
-     *
-     * @param keyId the key identifier
-     * @param key the key
-     * @return true if the key was successfully added
-     * @throws OperationNotSupportedException if this implementation doesn't support adding keys
-     * @throws KeyManagementException if the key is invalid, the ID conflicts, etc.
-     */
-     boolean addKey(String keyId, SecretKey key) throws OperationNotSupportedException, KeyManagementException;
-
-     // TODO: Add #getActiveKeyId() method
 }
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java
new file mode 100644
index 0000000..3cf6886
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java
@@ -0,0 +1,85 @@
+/*
+ * 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.kms;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.nifi.security.kms.configuration.FileBasedKeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.KeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.KeyStoreKeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.StaticKeyProviderConfiguration;
+import org.apache.commons.codec.binary.Hex;
+import org.apache.nifi.security.kms.reader.KeyReaderException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Key Provider Factory
+ */
+public class KeyProviderFactory {
+    private static final String SECRET_KEY_ALGORITHM = "AES";
+
+    /**
+     * Get Key Provider based on Configuration
+     *
+     * @param configuration Key Provider Configuration
+     * @return Key Provider
+     */
+    public static KeyProvider getKeyProvider(final KeyProviderConfiguration<?> configuration) {
+        KeyProvider keyProvider;
+
+        if (configuration instanceof StaticKeyProviderConfiguration) {
+            final StaticKeyProviderConfiguration providerConfiguration = (StaticKeyProviderConfiguration) configuration;
+            final Map<String, SecretKey> secretKeys;
+            try {
+                secretKeys = getSecretKeys(providerConfiguration.getKeys());
+                keyProvider = new StaticKeyProvider(secretKeys);
+            } catch (final DecoderException e) {
+                throw new KeyReaderException("Decoding Hexadecimal Secret Keys failed", e);
+            }
+        } else if (configuration instanceof FileBasedKeyProviderConfiguration) {
+            final FileBasedKeyProviderConfiguration providerConfiguration = (FileBasedKeyProviderConfiguration) configuration;
+            final Path keyProviderPath = Paths.get(providerConfiguration.getLocation());
+            keyProvider = new FileBasedKeyProvider(keyProviderPath, providerConfiguration.getRootKey());
+        } else if (configuration instanceof KeyStoreKeyProviderConfiguration) {
+            final KeyStoreKeyProviderConfiguration providerConfiguration = (KeyStoreKeyProviderConfiguration) configuration;
+            final KeyStore keyStore = providerConfiguration.getKeyStore();
+            keyProvider = new KeyStoreKeyProvider(keyStore, providerConfiguration.getKeyPassword());
+        } else {
+            throw new UnsupportedOperationException(String.format("Key Provider [%s] not supported", configuration.getKeyProviderClass().getName()));
+        }
+
+        return keyProvider;
+    }
+
+    private static Map<String, SecretKey> getSecretKeys(final Map<String, String> keys) throws DecoderException {
+        final Map<String, SecretKey> secretKeys = new HashMap<>();
+
+        for (final Map.Entry<String, String> keyEntry : keys.entrySet()) {
+            final byte[] encodedSecretKey = Hex.decodeHex(keyEntry.getValue());
+            final SecretKey secretKey = new SecretKeySpec(encodedSecretKey, SECRET_KEY_ALGORITHM);
+            secretKeys.put(keyEntry.getKey(), secretKey);
+        }
+
+        return secretKeys;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyStoreKeyProvider.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyStoreKeyProvider.java
new file mode 100644
index 0000000..ad777d1
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyStoreKeyProvider.java
@@ -0,0 +1,64 @@
+/*
+ * 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.kms;
+
+import org.apache.nifi.security.kms.reader.KeyReaderException;
+
+import javax.crypto.SecretKey;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * KeyStore implementation of Key Provider
+ */
+public class KeyStoreKeyProvider extends StaticKeyProvider {
+    /**
+     * KeyStore Key Provider constructor with KeyStore and password used to read Secret Key entries
+     *
+     * @param keyStore KeyStore
+     * @param keyPassword Password for reading Secret Key entries
+     */
+    public KeyStoreKeyProvider(final KeyStore keyStore, final char[] keyPassword) {
+        super(readSecretKeys(requireNonNull(keyStore, "KeyStore required"), requireNonNull(keyPassword, "Password required")));
+    }
+
+    private static Map<String, SecretKey> readSecretKeys(final KeyStore keyStore, final char[] keyPassword) throws KeyReaderException {
+        final Map<String, SecretKey> secretKeys = new HashMap<>();
+
+        try {
+            final Enumeration<String> aliases = keyStore.aliases();
+            while (aliases.hasMoreElements()) {
+                final String alias = aliases.nextElement();
+                final Key key = keyStore.getKey(alias, keyPassword);
+                if (key instanceof SecretKey) {
+                    final SecretKey secretKey = (SecretKey) key;
+                    secretKeys.put(alias, secretKey);
+                }
+            }
+        } catch (final GeneralSecurityException e) {
+            throw new KeyReaderException("Reading KeyStore failed", e);
+        }
+
+        return secretKeys;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/StaticKeyProvider.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/StaticKeyProvider.java
new file mode 100644
index 0000000..2445194
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/StaticKeyProvider.java
@@ -0,0 +1,78 @@
+/*
+ * 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.kms;
+
+import java.security.KeyManagementException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import javax.crypto.SecretKey;
+
+/**
+ * Static Key Provider stores Secret Keys in memory based on initialized configuration properties
+ */
+public class StaticKeyProvider implements KeyProvider {
+    private final Map<String, SecretKey> keys;
+
+    /**
+     * Static Key Provider constructor of Map of Key Identifier to Secret Key
+     *
+     * @param keys Map of Key Identifier to Secret Key
+     */
+    public StaticKeyProvider(final Map<String, SecretKey> keys) {
+        this.keys = Collections.unmodifiableMap(Objects.requireNonNull(keys, "Keys required"));
+    }
+
+    /**
+     * Returns the key identified by this ID or throws an exception if one is not available.
+     *
+     * @param keyId the key identifier
+     * @return the key
+     * @throws KeyManagementException Thrown when Secret Key not found for Key Identifier
+     */
+    @Override
+    public SecretKey getKey(final String keyId) throws KeyManagementException {
+        final SecretKey secretKey = keys.get(keyId);
+        if (secretKey == null) {
+            throw new KeyManagementException(String.format("Secret Key [%s] not found", keyId));
+        }
+        return secretKey;
+    }
+
+    /**
+     * Returns true if the key exists and is available. Null or empty IDs will return false.
+     *
+     * @param keyId the key identifier
+     * @return true if the key can be used
+     */
+    @Override
+    public boolean keyExists(final String keyId) {
+        return keys.containsKey(keyId);
+    }
+
+    /**
+     * Returns a singleton list of the available key identifier.
+     *
+     * @return a List containing the {@code KEY_ID}
+     */
+    @Override
+    public List<String> getAvailableKeyIds() {
+        return new ArrayList<>(keys.keySet());
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/FileBasedKeyProviderConfiguration.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/FileBasedKeyProviderConfiguration.java
new file mode 100644
index 0000000..b4da6c9
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/FileBasedKeyProviderConfiguration.java
@@ -0,0 +1,48 @@
+/*
+ * 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.kms.configuration;
+
+import org.apache.nifi.security.kms.FileBasedKeyProvider;
+
+import javax.crypto.SecretKey;
+
+/**
+ * Configuration for File-based Key Provider
+ */
+public class FileBasedKeyProviderConfiguration implements KeyProviderConfiguration<FileBasedKeyProvider> {
+    private final String location;
+
+    private final SecretKey rootKey;
+
+    public FileBasedKeyProviderConfiguration(final String location, final SecretKey rootKey) {
+        this.location = location;
+        this.rootKey = rootKey;
+    }
+
+    @Override
+    public Class<FileBasedKeyProvider> getKeyProviderClass() {
+        return FileBasedKeyProvider.class;
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public SecretKey getRootKey() {
+        return rootKey;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/KeyProviderConfiguration.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/KeyProviderConfiguration.java
new file mode 100644
index 0000000..e6e7f14
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/KeyProviderConfiguration.java
@@ -0,0 +1,31 @@
+/*
+ * 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.kms.configuration;
+
+import org.apache.nifi.security.kms.KeyProvider;
+
+/**
+ * Key Provider Configuration
+ */
+public interface KeyProviderConfiguration<T extends KeyProvider> {
+    /**
+     * Get Key Provider Class
+     *
+     * @return Key Provider implementation Class
+     */
+    Class<T> getKeyProviderClass();
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/KeyStoreKeyProviderConfiguration.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/KeyStoreKeyProviderConfiguration.java
new file mode 100644
index 0000000..8259ec0
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/KeyStoreKeyProviderConfiguration.java
@@ -0,0 +1,49 @@
+/*
+ * 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.kms.configuration;
+
+import org.apache.nifi.security.kms.KeyStoreKeyProvider;
+
+import java.security.KeyStore;
+import java.util.Objects;
+
+/**
+ * Configuration for KeyStore Key Provider
+ */
+public class KeyStoreKeyProviderConfiguration implements KeyProviderConfiguration<KeyStoreKeyProvider> {
+    private final KeyStore keyStore;
+
+    private final char[] keyPassword;
+
+    public KeyStoreKeyProviderConfiguration(final KeyStore keyStore, final char[] keyPassword) {
+        this.keyStore = Objects.requireNonNull(keyStore, "Key Store required");
+        this.keyPassword = Objects.requireNonNull(keyPassword, "Key Password required");
+    }
+
+    @Override
+    public Class<KeyStoreKeyProvider> getKeyProviderClass() {
+        return KeyStoreKeyProvider.class;
+    }
+
+    public KeyStore getKeyStore() {
+        return keyStore;
+    }
+
+    public char[] getKeyPassword() {
+        return keyPassword;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/StaticKeyProviderConfiguration.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/StaticKeyProviderConfiguration.java
new file mode 100644
index 0000000..168fa1b
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/configuration/StaticKeyProviderConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * 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.kms.configuration;
+
+import org.apache.nifi.security.kms.StaticKeyProvider;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Configuration for Static Key Provider
+ */
+public class StaticKeyProviderConfiguration implements KeyProviderConfiguration<StaticKeyProvider> {
+    private final Map<String, String> keys;
+
+    public StaticKeyProviderConfiguration(final Map<String, String> keys) {
+        this.keys = Collections.unmodifiableMap(keys);
+    }
+
+    @Override
+    public Class<StaticKeyProvider> getKeyProviderClass() {
+        return StaticKeyProvider.class;
+    }
+
+    public Map<String, String> getKeys() {
+        return keys;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/FileBasedKeyReader.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/FileBasedKeyReader.java
new file mode 100644
index 0000000..641bea8
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/FileBasedKeyReader.java
@@ -0,0 +1,35 @@
+/*
+ * 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.kms.reader;
+
+import javax.crypto.SecretKey;
+import java.nio.file.Path;
+import java.util.Map;
+
+/**
+ * File Based Key Reader
+ */
+public interface FileBasedKeyReader {
+    /**
+     * Read Secret Keys from File Path and decrypt using provided Root Key
+     *
+     * @param path File Path
+     * @param rootKey Root Key
+     * @return Map of Key Identifier to Secret key
+     */
+    Map<String, SecretKey> readSecretKeys(Path path, SecretKey rootKey);
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/KeyReaderException.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/KeyReaderException.java
new file mode 100644
index 0000000..7395de6
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/KeyReaderException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.kms.reader;
+
+/**
+ * Key Reader Exception
+ */
+public class KeyReaderException extends RuntimeException {
+    /**
+     * Key Reader Exception with message and associated cause
+     *
+     * @param message Exception message
+     * @param cause Exception cause
+     */
+    public KeyReaderException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReader.java b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReader.java
new file mode 100644
index 0000000..d5464ae
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReader.java
@@ -0,0 +1,115 @@
+/*
+ * 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.kms.reader;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Properties;
+
+/**
+ * Standard File Based Key Reader reads Secret Keys from Properties files encrypted using AES-GCM with Tag Size of 128
+ */
+public class StandardFileBasedKeyReader implements FileBasedKeyReader {
+    protected static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+
+    protected static final int IV_LENGTH_BYTES = 16;
+
+    protected static final int TAG_SIZE_BITS = 128;
+
+    private static final Base64.Decoder DECODER = Base64.getDecoder();
+
+    private static final String SECRET_KEY_ALGORITHM = "AES";
+
+    /**
+     * Read Secret Keys using provided Root Secret Key
+     *
+     * @param path File Path contains a properties file with Key Identifier and Base64-encoded encrypted values
+     * @param rootKey Root Secret Key
+     * @return Map of Key Identifier to decrypted Secret Key
+     */
+    @Override
+    public Map<String, SecretKey> readSecretKeys(final Path path, final SecretKey rootKey) {
+        Objects.requireNonNull(path, "Path required");
+        Objects.requireNonNull(rootKey, "Root Key required");
+        final Map<String, SecretKey> secretKeys = new HashMap<>();
+
+        final Properties properties = getProperties(path);
+        for (final String keyId : properties.stringPropertyNames()) {
+            final String encodedProperty = properties.getProperty(keyId);
+            final SecretKey secretKey = readSecretKey(keyId, encodedProperty, rootKey);
+            secretKeys.put(keyId, secretKey);
+        }
+        return secretKeys;
+    }
+
+    private Properties getProperties(final Path path) {
+        final Properties properties = new Properties();
+        try (final FileInputStream inputStream = new FileInputStream(path.toFile())) {
+            properties.load(inputStream);
+        } catch (final IOException e) {
+            throw new KeyReaderException(String.format("Reading Secret Keys Failed [%s]", path), e);
+        }
+        return properties;
+    }
+
+    private SecretKey readSecretKey(final String keyId, final String encodedProperty, final SecretKey rootKey) {
+        final byte[] encryptedProperty = DECODER.decode(encodedProperty);
+        final Cipher cipher = getCipher(keyId, encryptedProperty, rootKey);
+        final byte[] encryptedSecretKey = Arrays.copyOfRange(encryptedProperty, IV_LENGTH_BYTES, encryptedProperty.length);
+        try {
+            final byte[] secretKey = cipher.doFinal(encryptedSecretKey);
+            return new SecretKeySpec(secretKey, SECRET_KEY_ALGORITHM);
+        } catch (final IllegalBlockSizeException|BadPaddingException e) {
+            throw new KeyReaderException(String.format("Key Identifier [%s] decryption failed", keyId), e);
+        }
+    }
+
+    private Cipher getCipher(final String keyId, final byte[] encryptedProperty, final SecretKey rootKey) {
+        final byte[] initializationVector = Arrays.copyOfRange(encryptedProperty, 0, IV_LENGTH_BYTES);
+        final Cipher cipher = getCipher();
+        try {
+            cipher.init(Cipher.DECRYPT_MODE, rootKey, new GCMParameterSpec(TAG_SIZE_BITS, initializationVector));
+        } catch (final InvalidAlgorithmParameterException|InvalidKeyException e) {
+            throw new KeyReaderException(String.format("Cipher initialization failed for Key Identifier [%s]", keyId), e);
+        }
+        return cipher;
+    }
+
+    private Cipher getCipher() {
+        try {
+            return Cipher.getInstance(CIPHER_ALGORITHM);
+        } catch (final NoSuchAlgorithmException|NoSuchPaddingException e) {
+            throw new KeyReaderException(String.format("Cipher Algorithm [%s] initialization failed", CIPHER_ALGORITHM), e);
+        }
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/FileBasedKeyProviderTest.java b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/FileBasedKeyProviderTest.java
new file mode 100644
index 0000000..09daf8a
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/FileBasedKeyProviderTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.kms;
+
+import org.apache.nifi.security.kms.util.SecretKeyUtils;
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.GeneralSecurityException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class FileBasedKeyProviderTest {
+    private static final String KEYS_EXTENSION = ".keys";
+
+    private static final String KEY_ID = UUID.randomUUID().toString();
+
+    @Test
+    public void testGetKey() throws GeneralSecurityException, IOException {
+        final SecretKey rootKey = SecretKeyUtils.getSecretKey();
+        final SecretKey secretKey = SecretKeyUtils.getSecretKey();
+        final Path secretKeysPath = getSecretKeysPath(rootKey, Collections.singletonMap(KEY_ID, secretKey));
+        final FileBasedKeyProvider provider = new FileBasedKeyProvider(secretKeysPath, rootKey);
+
+        final SecretKey secretKeyFound = provider.getKey(KEY_ID);
+        assertEquals(secretKey, secretKeyFound);
+        assertTrue(provider.keyExists(KEY_ID));
+        assertFalse(provider.getAvailableKeyIds().isEmpty());
+    }
+
+    private Path getSecretKeysPath(final SecretKey rootKey, final Map<String, SecretKey> secretKeys) throws IOException, GeneralSecurityException {
+        final Path path = Files.createTempFile(FileBasedKeyProviderTest.class.getSimpleName(), KEYS_EXTENSION);
+        path.toFile().deleteOnExit();
+
+        final Properties properties = SecretKeyUtils.getEncryptedSecretKeys(rootKey, secretKeys);
+        try (final OutputStream outputStream = new FileOutputStream(path.toFile())) {
+            properties.store(outputStream, null);
+        }
+
+        return path;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/KeyProviderFactoryTest.java b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/KeyProviderFactoryTest.java
new file mode 100644
index 0000000..bf3ea04
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/KeyProviderFactoryTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.kms;
+
+import org.apache.commons.codec.binary.Hex;
+import org.apache.nifi.security.kms.configuration.FileBasedKeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.KeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.KeyStoreKeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.StaticKeyProviderConfiguration;
+import org.apache.nifi.security.kms.util.SecretKeyUtils;
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+import java.io.File;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.util.Collections;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+public class KeyProviderFactoryTest {
+
+    @Test
+    public void testGetUnsupportedKeyProvider() {
+        final KeyProviderConfiguration<?> configuration = new UnsupportedKeyProviderConfiguration();
+        assertThrows(UnsupportedOperationException.class, () -> KeyProviderFactory.getKeyProvider(configuration));
+    }
+
+    @Test
+    public void testGetStaticKeyProvider() {
+        final SecretKey secretKey = SecretKeyUtils.getSecretKey();
+        final String encodedSecretKey = Hex.encodeHexString(secretKey.getEncoded());
+        final Map<String, String> keys = Collections.singletonMap(SecretKey.class.getSimpleName(), encodedSecretKey);
+
+        final KeyProviderConfiguration<?> configuration = new StaticKeyProviderConfiguration(keys);
+        final KeyProvider keyProvider = KeyProviderFactory.getKeyProvider(configuration);
+        assertEquals(StaticKeyProvider.class, keyProvider.getClass());
+    }
+
+    @Test
+    public void testGetFileBasedKeyProvider() throws IOException {
+        final File file = File.createTempFile(KeyProviderFactoryTest.class.getSimpleName(), FileBasedKeyProviderConfiguration.class.getSimpleName());
+        file.deleteOnExit();
+        final String location = file.getAbsolutePath();
+        final SecretKey rootKey = SecretKeyUtils.getSecretKey();
+        final KeyProviderConfiguration<?> configuration = new FileBasedKeyProviderConfiguration(location, rootKey);
+        final KeyProvider keyProvider = KeyProviderFactory.getKeyProvider(configuration);
+        assertEquals(FileBasedKeyProvider.class, keyProvider.getClass());
+    }
+
+    @Test
+    public void testGetKeyStoreKeyProvider() throws GeneralSecurityException, IOException {
+        final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        keyStore.load(null, null);
+        final char[] password = String.class.getSimpleName().toCharArray();
+        final KeyProviderConfiguration<?> configuration = new KeyStoreKeyProviderConfiguration(keyStore, password);
+        final KeyProvider keyProvider = KeyProviderFactory.getKeyProvider(configuration);
+        assertEquals(KeyStoreKeyProvider.class, keyProvider.getClass());
+    }
+
+    private static class UnsupportedKeyProviderConfiguration implements KeyProviderConfiguration<KeyProvider> {
+
+        @Override
+        public Class<KeyProvider> getKeyProviderClass() {
+            return KeyProvider.class;
+        }
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/KeyStoreKeyProviderTest.java b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/KeyStoreKeyProviderTest.java
new file mode 100644
index 0000000..5210ad5
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/KeyStoreKeyProviderTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.kms;
+
+import org.apache.nifi.security.kms.util.SecretKeyUtils;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+public class KeyStoreKeyProviderTest {
+    private static final String KEY_ID = UUID.randomUUID().toString();
+
+    private static final SecretKey SECRET_KEY = SecretKeyUtils.getSecretKey();
+
+    private static final char[] PASSWORD = UUID.randomUUID().toString().toCharArray();
+
+    private static final String KEY_STORE_TYPE = "PKCS12";
+
+    private static KeyStore keyStore;
+
+    @BeforeClass
+    public static void setKeyStore() throws GeneralSecurityException, IOException {
+        keyStore = getKeyStore();
+    }
+
+    @Test
+    public void testGetKey() throws KeyManagementException {
+        final KeyStoreKeyProvider provider = new KeyStoreKeyProvider(keyStore, PASSWORD);
+
+        final SecretKey secretKeyFound = provider.getKey(KEY_ID);
+        assertEquals(SECRET_KEY, secretKeyFound);
+    }
+
+    @Test
+    public void testKeyExists() {
+        final KeyStoreKeyProvider provider = new KeyStoreKeyProvider(keyStore, PASSWORD);
+
+        assertTrue(provider.keyExists(KEY_ID));
+    }
+
+    @Test
+    public void testGetAvailableKeys() {
+        final KeyStoreKeyProvider provider = new KeyStoreKeyProvider(keyStore, PASSWORD);
+
+        final List<String> keyIds = provider.getAvailableKeyIds();
+        assertTrue(keyIds.contains(KEY_ID));
+    }
+
+    @Test
+    public void testGetKeyNotFoundManagementException() {
+        final KeyStoreKeyProvider provider = new KeyStoreKeyProvider(keyStore, PASSWORD);
+        assertThrows(KeyManagementException.class, () -> provider.getKey(SecretKey.class.getName()));
+    }
+
+    private static KeyStore getKeyStore() throws GeneralSecurityException, IOException {
+        KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE);
+        keyStore.load(null, null);
+        keyStore.setEntry(KEY_ID, new KeyStore.SecretKeyEntry(SECRET_KEY), new KeyStore.PasswordProtection(PASSWORD));
+        return keyStore;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/StaticKeyProviderTest.java b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/StaticKeyProviderTest.java
new file mode 100644
index 0000000..3f1aaad
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/StaticKeyProviderTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.kms;
+
+import org.apache.nifi.security.kms.util.SecretKeyUtils;
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+import java.security.KeyManagementException;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+public class StaticKeyProviderTest {
+    private static final String KEY_ID = UUID.randomUUID().toString();
+
+    private static final SecretKey SECRET_KEY = SecretKeyUtils.getSecretKey();
+
+    @Test
+    public void testGetKey() throws KeyManagementException {
+        final StaticKeyProvider provider = new StaticKeyProvider(Collections.singletonMap(KEY_ID, SECRET_KEY));
+
+        final SecretKey secretKeyFound = provider.getKey(KEY_ID);
+        assertEquals(SECRET_KEY, secretKeyFound);
+    }
+
+    @Test
+    public void testKeyExists() {
+        final StaticKeyProvider provider = new StaticKeyProvider(Collections.singletonMap(KEY_ID, SECRET_KEY));
+
+        assertTrue(provider.keyExists(KEY_ID));
+    }
+
+    @Test
+    public void testGetAvailableKeys() {
+        final StaticKeyProvider provider = new StaticKeyProvider(Collections.singletonMap(KEY_ID, SECRET_KEY));
+
+        final List<String> keyIds = provider.getAvailableKeyIds();
+        assertTrue(keyIds.contains(KEY_ID));
+    }
+
+    @Test
+    public void testGetKeyNotFoundManagementException() {
+        final StaticKeyProvider provider = new StaticKeyProvider(Collections.emptyMap());
+        assertThrows(KeyManagementException.class, () -> provider.getKey(SecretKey.class.getName()));
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReaderTest.java b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReaderTest.java
new file mode 100644
index 0000000..cd0f9e72
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReaderTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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.kms.reader;
+
+import org.apache.nifi.security.kms.util.SecretKeyUtils;
+import org.junit.Test;
+
+import javax.crypto.SecretKey;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+
+import static org.junit.Assert.assertEquals;
+
+public class StandardFileBasedKeyReaderTest {
+    private static final String KEYS_EXTENSION = ".keys";
+
+    private static final SecretKey ROOT_KEY = SecretKeyUtils.getSecretKey();
+
+    @Test
+    public void testReadSecretKeys() throws Exception {
+        final StandardFileBasedKeyReader reader = new StandardFileBasedKeyReader();
+
+        final SecretKey secretKey = SecretKeyUtils.getSecretKey();
+        final String keyId = SecretKey.class.getSimpleName();
+
+        final Path path = getSecretKeysPath(ROOT_KEY, Collections.singletonMap(keyId, secretKey));
+        final Map<String, SecretKey> secretKeys = reader.readSecretKeys(path, ROOT_KEY);
+        final SecretKey readSecretKey = secretKeys.get(keyId);
+        assertEquals("Secret Key not matched", secretKey, readSecretKey);
+    }
+
+    private Path getSecretKeysPath(final SecretKey rootKey, final Map<String, SecretKey> secretKeys) throws Exception {
+        final Path path = Files.createTempFile(StandardFileBasedKeyReaderTest.class.getSimpleName(), KEYS_EXTENSION);
+        path.toFile().deleteOnExit();
+
+        final Properties properties = SecretKeyUtils.getEncryptedSecretKeys(rootKey, secretKeys);
+        try (final OutputStream outputStream = new FileOutputStream(path.toFile())) {
+            properties.store(outputStream, null);
+        }
+
+        return path;
+    }
+}
diff --git a/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/util/SecretKeyUtils.java b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/util/SecretKeyUtils.java
new file mode 100644
index 0000000..919c55d
--- /dev/null
+++ b/nifi-commons/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/util/SecretKeyUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.kms.util;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Base64;
+import java.util.Map;
+import java.util.Properties;
+
+public class SecretKeyUtils {
+    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
+    private static final Base64.Encoder ENCODER = Base64.getEncoder();
+
+    private static final String CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+
+    private static final String KEY_ALGORITHM = "AES";
+
+    private static final int KEY_LENGTH = 32;
+
+    private static final int IV_LENGTH = 16;
+
+    private static final int TAG_LENGTH = 128;
+
+    /**
+     * Get Encrypted Secret Keys as Properties
+     *
+     * @param rootKey Root Key used to encrypt Secret Keys
+     * @param secretKeys Map of Key Identifier to Secret Key
+     * @return Properties containing encrypted Secret Keys
+     * @throws GeneralSecurityException Thrown on getEncryptedSecretKey()
+     */
+    public static Properties getEncryptedSecretKeys(final SecretKey rootKey, final Map<String, SecretKey> secretKeys) throws GeneralSecurityException {
+        final Properties properties = new Properties();
+        for (final Map.Entry<String, SecretKey> secretKeyEntry : secretKeys.entrySet()) {
+            final SecretKey secretKey = secretKeyEntry.getValue();
+            final String encryptedSecretKey = getEncryptedSecretKey(rootKey, secretKey);
+            properties.setProperty(secretKeyEntry.getKey(), encryptedSecretKey);
+        }
+        return properties;
+    }
+
+    /**
+     * Get Random AES Secret Key
+     *
+     * @return Secret Key
+     */
+    public static SecretKey getSecretKey() {
+        final byte[] encodedKey = new byte[KEY_LENGTH];
+        SECURE_RANDOM.nextBytes(encodedKey);
+        return new SecretKeySpec(encodedKey, KEY_ALGORITHM);
+    }
+
+    /**
+     * Get Encrypted Secret Key using AES-GCM with Base64 encoded string prefixed with initialization vector
+     *
+     * @param rootKey Root Key used to encrypt Secret Key
+     * @param secretKey Secret Key to be encrypted
+     * @return Base64 encoded and encrypted Secret Key
+     * @throws GeneralSecurityException Thrown when unable to encrypt Secret Key
+     */
+    private static String getEncryptedSecretKey(final SecretKey rootKey, final SecretKey secretKey) throws GeneralSecurityException {
+        final Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
+
+        final byte[] initializationVector = new byte[IV_LENGTH];
+        SECURE_RANDOM.nextBytes(initializationVector);
+        cipher.init(Cipher.ENCRYPT_MODE, rootKey, new GCMParameterSpec(TAG_LENGTH, initializationVector));
+        final byte[] encryptedSecretKey = cipher.doFinal(secretKey.getEncoded());
+
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try {
+            outputStream.write(initializationVector);
+            outputStream.write(encryptedSecretKey);
+        } catch (final IOException e) {
+            throw new UncheckedIOException(e);
+        }
+        final byte[] encryptedProperty = outputStream.toByteArray();
+        return ENCODER.encodeToString(encryptedProperty);
+    }
+}
diff --git a/nifi-commons/nifi-security-utils/pom.xml b/nifi-commons/nifi-security-utils/pom.xml
index 25be6ed..ced618a 100644
--- a/nifi-commons/nifi-security-utils/pom.xml
+++ b/nifi-commons/nifi-security-utils/pom.xml
@@ -43,6 +43,11 @@
             <version>1.14.0-SNAPSHOT</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-kms</artifactId>
+            <version>1.14.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
             <groupId>ch.qos.logback</groupId>
             <artifactId>logback-classic</artifactId>
             <scope>provided</scope>
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
index c0b60c8..37909e9 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
@@ -18,33 +18,26 @@
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.security.repository.config.RepositoryEncryptionConfiguration;
-import org.apache.nifi.security.util.EncryptionMethod;
-import org.apache.nifi.security.util.crypto.AESKeyedCipherProvider;
 import org.apache.nifi.util.NiFiBootstrapUtils;
 import org.bouncycastle.util.encoders.DecoderException;
 import org.bouncycastle.util.encoders.Hex;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
-import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.security.KeyManagementException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.Arrays;
-import java.util.Base64;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Pattern;
@@ -53,6 +46,7 @@
     private static final Logger logger = LoggerFactory.getLogger(CryptoUtils.class);
     public static final String STATIC_KEY_PROVIDER_CLASS_NAME = "org.apache.nifi.security.kms.StaticKeyProvider";
     public static final String FILE_BASED_KEY_PROVIDER_CLASS_NAME = "org.apache.nifi.security.kms.FileBasedKeyProvider";
+    public static final String KEY_STORE_KEY_PROVIDER_CLASS_NAME = "org.apache.nifi.security.kms.KeyStoreKeyProvider";
 
     // TODO: Move to RepositoryEncryptionUtils in NIFI-6617
     public static final String LEGACY_SKP_FQCN = "org.apache.nifi.provenance.StaticKeyProvider";
@@ -63,7 +57,6 @@
 
     private static final List<Integer> UNLIMITED_KEY_LENGTHS = Arrays.asList(32, 48, 64);
 
-    public static final int IV_LENGTH = 16;
     public static final String ENCRYPTED_FSR_CLASS_NAME = "org.apache.nifi.controller.repository.crypto.EncryptedFileSystemRepository";
     public static final String EWAFFR_CLASS_NAME = "org.apache.nifi.controller.repository.crypto.EncryptedWriteAheadFlowFileRepository";
 
@@ -127,45 +120,29 @@
      * @param encryptionKeys            a map of key IDs to key material in hex format
      * @return true if the provided configuration is valid
      */
-    public static boolean isValidKeyProvider(String keyProviderImplementation, String keyProviderLocation, String keyId, Map<String, String> encryptionKeys) {
-        logger.debug("Attempting to validate the key provider: keyProviderImplementation = "
-                + keyProviderImplementation + ", keyProviderLocation = "
-                + keyProviderLocation + ", keyId = "
-                + keyId + ", encryptionKeys = "
-                + ((encryptionKeys == null) ? "0" : encryptionKeys.size()));
-
+    public static boolean isValidKeyProvider(String keyProviderImplementation, final String keyProviderLocation, final String keyId, final Map<String, String> encryptionKeys) {
         try {
             keyProviderImplementation = handleLegacyPackages(keyProviderImplementation);
-        } catch (KeyManagementException e) {
-            logger.warn("The attempt to validate the key provider failed keyProviderImplementation = "
-                    + keyProviderImplementation + ", keyProviderLocation = "
-                    + keyProviderLocation + ", keyId = "
-                    + keyId + ", encryptionKeys = "
-                    + ((encryptionKeys == null) ? "0" : encryptionKeys.size()));
-
+        } catch (final KeyManagementException e) {
+            logger.warn("Key Provider [{}] Validation Failed: {}", keyProviderImplementation, e.getMessage());
             return false;
         }
 
-        if (STATIC_KEY_PROVIDER_CLASS_NAME.equals(keyProviderImplementation)) {
-            // Ensure the keyId and key(s) are valid
-            if (encryptionKeys == null) {
+        switch (keyProviderImplementation) {
+            case STATIC_KEY_PROVIDER_CLASS_NAME:
+                if (encryptionKeys == null) {
+                    return false;
+                } else {
+                    boolean everyKeyValid = encryptionKeys.values().stream().allMatch(CryptoUtils::keyIsValid);
+                    return everyKeyValid && StringUtils.isNotEmpty(keyId);
+                }
+            case FILE_BASED_KEY_PROVIDER_CLASS_NAME:
+            case KEY_STORE_KEY_PROVIDER_CLASS_NAME:
+                final Path keyProviderPath = Paths.get(keyProviderLocation);
+                return Files.isReadable(keyProviderPath) && StringUtils.isNotEmpty(keyId);
+            default:
+                logger.warn("Validation Failed: Key Provider [{}] Location [{}] Key ID [{}]", keyProviderImplementation, keyProviderLocation, keyId);
                 return false;
-            } else {
-                boolean everyKeyValid = encryptionKeys.values().stream().allMatch(CryptoUtils::keyIsValid);
-                return everyKeyValid && StringUtils.isNotEmpty(keyId);
-            }
-        } else if (FILE_BASED_KEY_PROVIDER_CLASS_NAME.equals(keyProviderImplementation)) {
-            // Ensure the file can be read and the keyId is populated (does not read file to validate)
-            final File kpf = new File(keyProviderLocation);
-            return kpf.exists() && kpf.canRead() && StringUtils.isNotEmpty(keyId);
-        } else {
-            logger.warn("The attempt to validate the key provider failed keyProviderImplementation = "
-                    + keyProviderImplementation + ", keyProviderLocation = "
-                    + keyProviderLocation + ", keyId = "
-                    + keyId + ", encryptionKeys = "
-                    + ((encryptionKeys == null) ? "0" : encryptionKeys.size()));
-
-            return false;
         }
     }
 
@@ -206,96 +183,6 @@
     }
 
     /**
-     * Returns a {@link SecretKey} formed from the hexadecimal key bytes (validity is checked).
-     *
-     * @param keyHex the key in hex form
-     * @return the SecretKey
-     */
-    public static SecretKey formKeyFromHex(String keyHex) throws KeyManagementException {
-        if (keyIsValid(keyHex)) {
-            return new SecretKeySpec(Hex.decode(keyHex), "AES");
-        } else {
-            throw new KeyManagementException("The provided key material is not valid");
-        }
-    }
-
-    /**
-     * Returns a map containing the key IDs and the parsed key from a key provider definition file.
-     * The values in the file are decrypted using the root key provided. If the file is missing or empty,
-     * cannot be read, or if no valid keys are read, a {@link KeyManagementException} will be thrown.
-     *
-     * @param filepath  the key definition file path
-     * @param rootKey the root key used to decrypt each key definition
-     * @return a Map of key IDs to SecretKeys
-     * @throws KeyManagementException if the file is missing or invalid
-     */
-    public static Map<String, SecretKey> readKeys(String filepath, SecretKey rootKey) throws KeyManagementException {
-        Map<String, SecretKey> keys = new HashMap<>();
-
-        if (StringUtils.isBlank(filepath)) {
-            throw new KeyManagementException("The key provider file is not present and readable");
-        }
-        if (rootKey == null) {
-            throw new KeyManagementException("The root key must be provided to decrypt the individual keys");
-        }
-
-        File file = new File(filepath);
-        if (!file.exists() || !file.canRead()) {
-            throw new KeyManagementException("The key provider file is not present and readable");
-        }
-
-        try (BufferedReader br = new BufferedReader(new FileReader(file))) {
-            AESKeyedCipherProvider rootCipherProvider = new AESKeyedCipherProvider();
-
-            String line;
-            int l = 1;
-            while ((line = br.readLine()) != null) {
-                String[] components = line.split("=", 2);
-                if (components.length != 2 || StringUtils.isAnyEmpty(components)) {
-                    logger.warn("Line " + l + " is not properly formatted -- keyId=Base64EncodedKey...");
-                }
-                String keyId = components[0];
-                if (StringUtils.isNotEmpty(keyId)) {
-                    try {
-                        byte[] base64Bytes = Base64.getDecoder().decode(components[1]);
-                        byte[] ivBytes = Arrays.copyOfRange(base64Bytes, 0, IV_LENGTH);
-
-                        Cipher rootCipher = null;
-                        try {
-                            rootCipher = rootCipherProvider.getCipher(EncryptionMethod.AES_GCM, rootKey, ivBytes, false);
-                        } catch (Exception e) {
-                            throw new KeyManagementException("Error building cipher to decrypt FileBaseKeyProvider definition at " + filepath, e);
-                        }
-                        byte[] individualKeyBytes = rootCipher.doFinal(Arrays.copyOfRange(base64Bytes, IV_LENGTH, base64Bytes.length));
-
-                        SecretKey key = new SecretKeySpec(individualKeyBytes, "AES");
-                        logger.debug("Read and decrypted key for " + keyId);
-                        if (keys.containsKey(keyId)) {
-                            logger.warn("Multiple key values defined for " + keyId + " -- using most recent value");
-                        }
-                        keys.put(keyId, key);
-                    } catch (IllegalArgumentException e) {
-                        logger.error("Encountered an error decoding Base64 for " + keyId + ": " + e.getLocalizedMessage());
-                    } catch (BadPaddingException | IllegalBlockSizeException e) {
-                        logger.error("Encountered an error decrypting key for " + keyId + ": " + e.getLocalizedMessage());
-                    }
-                }
-                l++;
-            }
-
-            if (keys.isEmpty()) {
-                throw new KeyManagementException("The provided file contained no valid keys");
-            }
-
-            logger.info("Read " + keys.size() + " keys from FileBasedKeyProvider " + filepath);
-            return keys;
-        } catch (IOException e) {
-            throw new KeyManagementException("Error reading FileBasedKeyProvider definition at " + filepath, e);
-        }
-
-    }
-
-    /**
      * Returns the root key from the {@code bootstrap.conf} file used to encrypt various sensitive properties and data encryption keys.
      *
      * @return the root key
@@ -367,7 +254,7 @@
      */
     private static byte[] convertCharsToBytes(char[] chars) {
         CharBuffer charBuffer = CharBuffer.wrap(chars);
-        ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
+        ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer);
         return Arrays.copyOfRange(byteBuffer.array(),
                 byteBuffer.position(), byteBuffer.limit());
     }
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/FileBasedKeyProvider.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/FileBasedKeyProvider.java
deleted file mode 100644
index b6c3842..0000000
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/FileBasedKeyProvider.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.kms;
-
-import java.security.KeyManagementException;
-import javax.crypto.SecretKey;
-import javax.naming.OperationNotSupportedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FileBasedKeyProvider extends StaticKeyProvider {
-    private static final Logger logger = LoggerFactory.getLogger(FileBasedKeyProvider.class);
-
-    private final String filepath;
-
-    public FileBasedKeyProvider(String location, SecretKey rootKey) throws KeyManagementException {
-        super(CryptoUtils.readKeys(location, rootKey));
-        this.filepath = location;
-    }
-
-    /**
-     * Adds the key to the provider and associates it with the given ID. Some implementations may not allow this operation.
-     *
-     * @param keyId the key identifier
-     * @param key   the key
-     * @return true if the key was successfully added
-     * @throws OperationNotSupportedException if this implementation doesn't support adding keys
-     * @throws KeyManagementException         if the key is invalid, the ID conflicts, etc.
-     */
-    @Override
-    public boolean addKey(String keyId, SecretKey key) throws OperationNotSupportedException, KeyManagementException {
-        throw new OperationNotSupportedException("This implementation does not allow adding keys. Modify the file backing this provider at " + filepath);
-    }
-}
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java
deleted file mode 100644
index 69bf8e2..0000000
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * 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.kms;
-
-import java.security.KeyManagementException;
-import java.util.Map;
-import java.util.stream.Collectors;
-import javax.crypto.SecretKey;
-import org.apache.nifi.security.repository.config.RepositoryEncryptionConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Factory class to build {@link KeyProvider} instances. Currently supports {@link StaticKeyProvider} and {@link FileBasedKeyProvider}.
- */
-public class KeyProviderFactory {
-    private static final Logger logger = LoggerFactory.getLogger(KeyProviderFactory.class);
-
-    /**
-     * Returns a key provider instantiated from the configuration values in a {@link RepositoryEncryptionConfiguration} object.
-     *
-     * @param rec     the data container for config values (usually extracted from {@link org.apache.nifi.util.NiFiProperties})
-     * @param rootKey the root key used to decrypt wrapped keys
-     * @return the configured key provider
-     * @throws KeyManagementException if the key provider cannot be instantiated
-     */
-    public static KeyProvider buildKeyProvider(RepositoryEncryptionConfiguration rec, SecretKey rootKey) throws KeyManagementException {
-        if (rec == null) {
-            throw new KeyManagementException("The repository encryption configuration values are required to build a key provider");
-        }
-        return buildKeyProvider(rec.getKeyProviderImplementation(), rec.getKeyProviderLocation(), rec.getEncryptionKeyId(), rec.getEncryptionKeys(), rootKey);
-    }
-
-    /**
-     * Returns a key provider instantiated from the configuration values in a {@link RepositoryEncryptionConfiguration} object.
-     *
-     * @param implementationClassName the key provider class name
-     * @param keyProviderLocation     the filepath/URL of the stored keys
-     * @param keyId                   the active key id
-     * @param encryptionKeys          the available encryption keys
-     * @param rootKey                 the root key used to decrypt wrapped keys
-     * @return the configured key provider
-     * @throws KeyManagementException if the key provider cannot be instantiated
-     */
-    public static KeyProvider buildKeyProvider(String implementationClassName, String keyProviderLocation, String keyId, Map<String, String> encryptionKeys,
-                                               SecretKey rootKey) throws KeyManagementException {
-        KeyProvider keyProvider;
-
-        implementationClassName = CryptoUtils.handleLegacyPackages(implementationClassName);
-
-        if (StaticKeyProvider.class.getName().equals(implementationClassName)) {
-            // Get all the keys (map) from config
-            if (CryptoUtils.isValidKeyProvider(implementationClassName, keyProviderLocation, keyId, encryptionKeys)) {
-                Map<String, SecretKey> formedKeys = encryptionKeys.entrySet().stream()
-                        .collect(Collectors.toMap(
-                                Map.Entry::getKey,
-                                e -> {
-                                    try {
-                                        return CryptoUtils.formKeyFromHex(e.getValue());
-                                    } catch (KeyManagementException e1) {
-                                        // This should never happen because the hex has already been validated
-                                        logger.error("Encountered an error: ", e1);
-                                        return null;
-                                    }
-                                }));
-                keyProvider = new StaticKeyProvider(formedKeys);
-            } else {
-                final String msg = "The StaticKeyProvider definition is not valid";
-                logger.error(msg);
-                throw new KeyManagementException(msg);
-            }
-        } else if (FileBasedKeyProvider.class.getName().equals(implementationClassName)) {
-            keyProvider = new FileBasedKeyProvider(keyProviderLocation, rootKey);
-            if (!keyProvider.keyExists(keyId)) {
-                throw new KeyManagementException("The specified key ID " + keyId + " is not in the key definition file");
-            }
-        } else {
-            throw new KeyManagementException("Invalid key provider implementation provided: " + implementationClassName);
-        }
-
-        return keyProvider;
-    }
-
-    /**
-     * Returns true if this {@link KeyProvider} implementation requires the presence of the {@code root key} in order to decrypt the available data encryption keys.
-     *
-     * @param implementationClassName the key provider implementation class
-     * @return true if this implementation requires the root key to operate
-     * @throws KeyManagementException if the provided class name is not a valid key provider implementation
-     */
-    public static boolean requiresRootKey(String implementationClassName) throws KeyManagementException {
-        implementationClassName = CryptoUtils.handleLegacyPackages(implementationClassName);
-        return FileBasedKeyProvider.class.getName().equals(implementationClassName);
-    }
-}
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/StaticKeyProvider.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/StaticKeyProvider.java
deleted file mode 100644
index f14d124..0000000
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/StaticKeyProvider.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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.kms;
-
-import java.security.KeyManagementException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import javax.crypto.SecretKey;
-import javax.naming.OperationNotSupportedException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Reference implementation for static key provider (used during tests).
- */
-public class StaticKeyProvider implements KeyProvider {
-    private static final Logger logger = LoggerFactory.getLogger(StaticKeyProvider.class);
-
-    private Map<String, SecretKey> keys = new HashMap<>();
-
-    public StaticKeyProvider(String keyId, String keyHex) throws KeyManagementException {
-        this.keys.put(keyId, CryptoUtils.formKeyFromHex(keyHex));
-    }
-
-    public StaticKeyProvider(Map<String, SecretKey> keys) throws KeyManagementException {
-        this.keys.putAll(keys);
-    }
-
-    /**
-     * Returns the key identified by this ID or throws an exception if one is not available.
-     *
-     * @param keyId the key identifier
-     * @return the key
-     * @throws KeyManagementException if the key cannot be retrieved
-     */
-    @Override
-    public SecretKey getKey(String keyId) throws KeyManagementException {
-        logger.debug("Attempting to get key: " + keyId);
-        if (keyExists(keyId)) {
-            return keys.get(keyId);
-        } else {
-            throw new KeyManagementException("No key available for " + keyId);
-        }
-    }
-
-    /**
-     * Returns true if the key exists and is available. Null or empty IDs will return false.
-     *
-     * @param keyId the key identifier
-     * @return true if the key can be used
-     */
-    @Override
-    public boolean keyExists(String keyId) {
-        return keys.containsKey(keyId);
-    }
-
-    /**
-     * Returns a singleton list of the available key identifier.
-     *
-     * @return a List containing the {@code KEY_ID}
-     */
-    @Override
-    public List<String> getAvailableKeyIds() {
-        return new ArrayList<>(keys.keySet());
-    }
-
-    /**
-     * Adds the key to the provider and associates it with the given ID. Some implementations may not allow this operation.
-     *
-     * @param keyId the key identifier
-     * @param key   the key
-     * @return true if the key was successfully added
-     * @throws OperationNotSupportedException if this implementation doesn't support adding keys
-     * @throws KeyManagementException         if the key is invalid, the ID conflicts, etc.
-     */
-    @Override
-    public boolean addKey(String keyId, SecretKey key) throws OperationNotSupportedException, KeyManagementException {
-        throw new OperationNotSupportedException("This implementation does not allow adding keys");
-    }
-}
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/RepositoryEncryptorUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/RepositoryEncryptorUtils.java
index dbd0732..4f8ebbc 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/RepositoryEncryptorUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/RepositoryEncryptorUtils.java
@@ -25,19 +25,29 @@
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.security.KeyManagementException;
+import java.security.KeyStore;
 import java.util.Arrays;
-import java.util.List;
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;
 import org.apache.nifi.security.kms.CryptoUtils;
 import org.apache.nifi.security.kms.EncryptionException;
+import org.apache.nifi.security.kms.FileBasedKeyProvider;
 import org.apache.nifi.security.kms.KeyProvider;
 import org.apache.nifi.security.kms.KeyProviderFactory;
+import org.apache.nifi.security.kms.KeyStoreKeyProvider;
+import org.apache.nifi.security.kms.StaticKeyProvider;
+import org.apache.nifi.security.kms.configuration.FileBasedKeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.KeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.KeyStoreKeyProviderConfiguration;
+import org.apache.nifi.security.kms.configuration.StaticKeyProviderConfiguration;
 import org.apache.nifi.security.repository.config.RepositoryEncryptionConfiguration;
 import org.apache.nifi.security.util.EncryptionMethod;
+import org.apache.nifi.security.util.KeyStoreUtils;
+import org.apache.nifi.security.util.TlsException;
 import org.apache.nifi.security.util.crypto.AESKeyedCipherProvider;
 import org.apache.nifi.stream.io.NonCloseableInputStream;
 import org.apache.nifi.util.NiFiProperties;
+import org.apache.nifi.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -46,11 +56,7 @@
 
     private static final int CONTENT_HEADER_SIZE = 2;
     private static final int IV_LENGTH = 16;
-    private static final byte[] EMPTY_IV = new byte[IV_LENGTH];
-    private static final String VERSION = "v1";
-    private static final List<String> SUPPORTED_VERSIONS = Arrays.asList(VERSION);
     private static final int MIN_METADATA_LENGTH = IV_LENGTH + 3 + 3; // 3 delimiters and 3 non-zero elements
-    private static final int METADATA_DEFAULT_LENGTH = (20 + 17 + IV_LENGTH + VERSION.length()) * 2; // Default to twice the expected length
     private static final String EWAPR_CLASS_NAME = "org.apache.nifi.provenance.EncryptedWriteAheadProvenanceRepository";
 
     // TODO: Add Javadoc
@@ -204,53 +210,6 @@
     }
 
     /**
-     * Returns a configured {@link KeyProvider} instance that does not require a {@code root key} to use (usually a {@link org.apache.nifi.security.kms.StaticKeyProvider}).
-     *
-     * @param niFiProperties the {@link NiFiProperties} object
-     * @param repositoryType the {@link RepositoryType} indicator
-     * @return the configured KeyProvider
-     * @throws KeyManagementException if there is a problem with the configuration
-     */
-    private static KeyProvider buildKeyProvider(NiFiProperties niFiProperties, RepositoryType repositoryType) throws KeyManagementException {
-        return buildKeyProvider(niFiProperties, null, repositoryType);
-    }
-
-    /**
-     * Returns a configured {@link KeyProvider} instance that requires a {@code root key} to use
-     * (usually a {@link org.apache.nifi.security.kms.FileBasedKeyProvider} or an encrypted
-     * {@link org.apache.nifi.security.kms.StaticKeyProvider}).
-     *
-     * @param niFiProperties the {@link NiFiProperties} object
-     * @param rootKey      the root encryption key used to encrypt the data encryption keys in the key provider configuration
-     * @param repositoryType the {@link RepositoryType} indicator
-     * @return the configured KeyProvider
-     * @throws KeyManagementException if there is a problem with the configuration
-     */
-    public static KeyProvider buildKeyProvider(NiFiProperties niFiProperties, SecretKey rootKey, RepositoryType repositoryType) throws KeyManagementException {
-        RepositoryEncryptionConfiguration rec = RepositoryEncryptionConfiguration.fromNiFiProperties(niFiProperties, repositoryType);
-
-        return buildKeyProviderFromConfig(rootKey, rec);
-    }
-
-    /**
-     * Returns a configured {@link KeyProvider} instance given the {@link RepositoryEncryptionConfiguration}.
-     *
-     * @param rootKey the root encryption key used to encrypt the data encryption keys in the key provider configuration
-     * @param rec       the repository-specific encryption configuration
-     * @return the configured KeyProvider
-     * @throws KeyManagementException if there is a problem with the configuration
-     */
-    public static KeyProvider buildKeyProviderFromConfig(SecretKey rootKey, RepositoryEncryptionConfiguration rec) throws KeyManagementException {
-        if (rec.getKeyProviderImplementation() == null) {
-            final String keyProviderImplementationClass = determineKeyProviderImplementationClassName(rec.getRepositoryType());
-            throw new KeyManagementException("Cannot create key provider because the NiFi properties are missing the following property: "
-                    + keyProviderImplementationClass);
-        }
-
-        return KeyProviderFactory.buildKeyProvider(rec, rootKey);
-    }
-
-    /**
      * Utility method which returns the {@link KeyProvider} implementation class name for a given repository type.
      *
      * @param repositoryType the {@link RepositoryType} indicator
@@ -284,20 +243,12 @@
      * @throws IOException if there is a problem reading the properties or they are not valid & complete
      */
     public static KeyProvider validateAndBuildRepositoryKeyProvider(NiFiProperties niFiProperties, RepositoryType repositoryType) throws IOException {
-        // Initialize the encryption-specific fields
         if (isRepositoryEncryptionConfigured(niFiProperties, repositoryType)) {
             try {
-                KeyProvider keyProvider;
-                final String keyProviderImplementation = niFiProperties.getProperty(determineKeyProviderImplementationClassName(repositoryType));
-                if (KeyProviderFactory.requiresRootKey(keyProviderImplementation)) {
-                    SecretKey rootKey = CryptoUtils.getRootKey();
-                    keyProvider = buildKeyProvider(niFiProperties, rootKey, repositoryType);
-                } else {
-                    keyProvider = buildKeyProvider(niFiProperties, repositoryType);
-                }
-                return keyProvider;
-            } catch (KeyManagementException e) {
-                String msg = "Encountered an error building the key provider";
+                final RepositoryEncryptionConfiguration configuration = RepositoryEncryptionConfiguration.fromNiFiProperties(niFiProperties, repositoryType);
+                return getKeyProvider(configuration);
+            } catch (final KeyManagementException e) {
+                final String msg = "Encountered an error building the key provider";
                 logger.error(msg, e);
                 throw new IOException(msg, e);
             }
@@ -313,15 +264,51 @@
      * @return the configured KeyProvider
      * @throws IOException if there is a problem reading the properties or they are not valid & complete
      */
-    public static KeyProvider validateAndBuildRepositoryKeyProvider(RepositoryEncryptionConfiguration repositoryEncryptionConfiguration) throws IOException {
-        // Initialize the encryption-specific fields
+    public static KeyProvider validateAndBuildRepositoryKeyProvider(final RepositoryEncryptionConfiguration repositoryEncryptionConfiguration) throws IOException {
         try {
-            SecretKey rootKey = KeyProviderFactory.requiresRootKey(repositoryEncryptionConfiguration.getKeyProviderImplementation()) ? CryptoUtils.getRootKey() : null;
-            return buildKeyProviderFromConfig(rootKey, repositoryEncryptionConfiguration);
-        } catch (KeyManagementException e) {
-            String msg = "Encountered an error building the key provider";
+            return getKeyProvider(repositoryEncryptionConfiguration);
+        } catch (final KeyManagementException e) {
+            final String msg = "Encountered an error building the key provider";
             logger.error(msg, e);
             throw new IOException(msg, e);
         }
     }
+
+    private static KeyProvider getKeyProvider(final RepositoryEncryptionConfiguration repositoryEncryptionConfiguration) throws KeyManagementException {
+        final KeyProviderConfiguration<?> keyProviderConfiguration = getKeyProviderConfiguration(repositoryEncryptionConfiguration);
+        final KeyProvider keyProvider = KeyProviderFactory.getKeyProvider(keyProviderConfiguration);
+
+        final String keyId = repositoryEncryptionConfiguration.getEncryptionKeyId();
+        if (keyProvider.keyExists(keyId)) {
+            return keyProvider;
+        } else {
+            throw new KeyManagementException(String.format("Key Identifier [%s] not found in Key Provider", keyId));
+        }
+    }
+
+    private static KeyProviderConfiguration<?> getKeyProviderConfiguration(final RepositoryEncryptionConfiguration repositoryEncryptionConfiguration) throws KeyManagementException {
+        final String keyProviderImplementation = repositoryEncryptionConfiguration.getKeyProviderImplementation();
+        if (keyProviderImplementation.endsWith(StaticKeyProvider.class.getSimpleName())) {
+            return new StaticKeyProviderConfiguration(repositoryEncryptionConfiguration.getEncryptionKeys());
+        } else if (keyProviderImplementation.endsWith(FileBasedKeyProvider.class.getSimpleName())) {
+            final SecretKey rootKey = CryptoUtils.getRootKey();
+            return new FileBasedKeyProviderConfiguration(repositoryEncryptionConfiguration.getKeyProviderLocation(), rootKey);
+        } else if (keyProviderImplementation.endsWith(KeyStoreKeyProvider.class.getSimpleName())) {
+            final String keyProviderPassword = repositoryEncryptionConfiguration.getKeyProviderPassword();
+            if (StringUtils.isBlank(keyProviderPassword)) {
+                throw new KeyManagementException("Key Provider Password not configured");
+            }
+            final String location = repositoryEncryptionConfiguration.getKeyProviderLocation();
+            final char[] keyStorePassword = repositoryEncryptionConfiguration.getKeyProviderPassword().toCharArray();
+            final String keyStoreType = repositoryEncryptionConfiguration.getKeyStoreType();
+            try {
+                final KeyStore keyStore = KeyStoreUtils.loadSecretKeyStore(location, keyStorePassword, keyStoreType);
+                return new KeyStoreKeyProviderConfiguration(keyStore, keyStorePassword);
+            } catch (final TlsException e) {
+                throw new KeyManagementException("Key Store Provider loading failed", e);
+            }
+        } else {
+            throw new UnsupportedOperationException(String.format("Key Provider Implementation [%s] not supported", keyProviderImplementation));
+        }
+    }
 }
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ContentRepositoryEncryptionConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ContentRepositoryEncryptionConfiguration.java
index 0e4d5c6..355a170 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ContentRepositoryEncryptionConfiguration.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ContentRepositoryEncryptionConfiguration.java
@@ -18,13 +18,10 @@
 
 import java.util.Map;
 import org.apache.nifi.security.repository.RepositoryType;
+import org.apache.nifi.security.util.KeyStoreUtils;
 import org.apache.nifi.util.NiFiProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class ContentRepositoryEncryptionConfiguration extends RepositoryEncryptionConfiguration {
-    private static final Logger logger = LoggerFactory.getLogger(ContentRepositoryEncryptionConfiguration.class);
-
     /**
      * Contructor which accepts a {@link NiFiProperties} object and extracts the relevant
      * property values directly.
@@ -36,7 +33,8 @@
                 niFiProperties.getProperty(NiFiProperties.CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_LOCATION),
                 niFiProperties.getContentRepositoryEncryptionKeyId(),
                 niFiProperties.getContentRepositoryEncryptionKeys(),
-                niFiProperties.getProperty(NiFiProperties.CONTENT_REPOSITORY_IMPLEMENTATION)
+                niFiProperties.getProperty(NiFiProperties.CONTENT_REPOSITORY_IMPLEMENTATION),
+                niFiProperties.getProperty(NiFiProperties.CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_PASSWORD)
         );
     }
 
@@ -48,17 +46,21 @@
      * @param encryptionKeyId the active encryption key id
      * @param encryptionKeys the map of available keys
      * @param repositoryImplementation the repository implementation class
+     * @param keyProviderPassword Key Provider Password used based on provider implementation
      */
-    public ContentRepositoryEncryptionConfiguration(String keyProviderImplementation,
-                                                    String keyProviderLocation,
-                                                    String encryptionKeyId,
-                                                    Map<String, String> encryptionKeys,
-                                                    String repositoryImplementation) {
+    public ContentRepositoryEncryptionConfiguration(final String keyProviderImplementation,
+                                                    final String keyProviderLocation,
+                                                    final String encryptionKeyId,
+                                                    final Map<String, String> encryptionKeys,
+                                                    final String repositoryImplementation,
+                                                    final String keyProviderPassword) {
         this.keyProviderImplementation = keyProviderImplementation;
         this.keyProviderLocation = keyProviderLocation;
         this.encryptionKeyId = encryptionKeyId;
         this.encryptionKeys = encryptionKeys;
         this.repositoryImplementation = repositoryImplementation;
         this.repositoryType = RepositoryType.CONTENT;
+        this.keyStoreType = KeyStoreUtils.getKeystoreTypeFromExtension(keyProviderLocation).getType();
+        this.keyProviderPassword = keyProviderPassword;
     }
 }
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/FlowFileRepositoryEncryptionConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/FlowFileRepositoryEncryptionConfiguration.java
index 74e4132..0c5666c 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/FlowFileRepositoryEncryptionConfiguration.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/FlowFileRepositoryEncryptionConfiguration.java
@@ -18,13 +18,10 @@
 
 import java.util.Map;
 import org.apache.nifi.security.repository.RepositoryType;
+import org.apache.nifi.security.util.KeyStoreUtils;
 import org.apache.nifi.util.NiFiProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class FlowFileRepositoryEncryptionConfiguration extends RepositoryEncryptionConfiguration {
-    private static final Logger logger = LoggerFactory.getLogger(FlowFileRepositoryEncryptionConfiguration.class);
-
     /**
      * Constructor which accepts a {@link NiFiProperties} object and extracts the relevant
      * property values directly.
@@ -36,7 +33,8 @@
                 niFiProperties.getProperty(NiFiProperties.FLOWFILE_REPOSITORY_ENCRYPTION_KEY_PROVIDER_LOCATION),
                 niFiProperties.getFlowFileRepoEncryptionKeyId(),
                 niFiProperties.getFlowFileRepoEncryptionKeys(),
-                niFiProperties.getProperty(NiFiProperties.FLOWFILE_REPOSITORY_WAL_IMPLEMENTATION)
+                niFiProperties.getProperty(NiFiProperties.FLOWFILE_REPOSITORY_WAL_IMPLEMENTATION),
+                niFiProperties.getProperty(NiFiProperties.FLOWFILE_REPOSITORY_ENCRYPTION_KEY_PROVIDER_PASSWORD)
         );
     }
 
@@ -51,17 +49,21 @@
      * @param encryptionKeyId the active encryption key id
      * @param encryptionKeys the map of available keys
      * @param repositoryImplementation the write ahead log implementation
+     * @param keyProviderPassword Key Provider Password
      */
-    public FlowFileRepositoryEncryptionConfiguration(String keyProviderImplementation,
-                                                     String keyProviderLocation,
-                                                     String encryptionKeyId,
-                                                     Map<String, String> encryptionKeys,
-                                                     String repositoryImplementation) {
+    public FlowFileRepositoryEncryptionConfiguration(final String keyProviderImplementation,
+                                                     final String keyProviderLocation,
+                                                     final String encryptionKeyId,
+                                                     final Map<String, String> encryptionKeys,
+                                                     final String repositoryImplementation,
+                                                     final String keyProviderPassword) {
         this.keyProviderImplementation = keyProviderImplementation;
         this.keyProviderLocation = keyProviderLocation;
         this.encryptionKeyId = encryptionKeyId;
         this.encryptionKeys = encryptionKeys;
         this.repositoryImplementation = repositoryImplementation;
         this.repositoryType = RepositoryType.FLOWFILE;
+        this.keyStoreType = KeyStoreUtils.getKeystoreTypeFromExtension(keyProviderLocation).getType();
+        this.keyProviderPassword = keyProviderPassword;
     }
 }
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ProvenanceRepositoryEncryptionConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ProvenanceRepositoryEncryptionConfiguration.java
index 4f85a75..66c6283 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ProvenanceRepositoryEncryptionConfiguration.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/ProvenanceRepositoryEncryptionConfiguration.java
@@ -18,13 +18,10 @@
 
 import java.util.Map;
 import org.apache.nifi.security.repository.RepositoryType;
+import org.apache.nifi.security.util.KeyStoreUtils;
 import org.apache.nifi.util.NiFiProperties;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 public class ProvenanceRepositoryEncryptionConfiguration extends RepositoryEncryptionConfiguration {
-    private static final Logger logger = LoggerFactory.getLogger(ProvenanceRepositoryEncryptionConfiguration.class);
-
     /**
      * Constructor which accepts a {@link NiFiProperties} object and extracts the relevant
      * property values directly.
@@ -36,7 +33,8 @@
                 niFiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_LOCATION),
                 niFiProperties.getProvenanceRepoEncryptionKeyId(),
                 niFiProperties.getProvenanceRepoEncryptionKeys(),
-                niFiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_IMPLEMENTATION_CLASS)
+                niFiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_IMPLEMENTATION_CLASS),
+                niFiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_PASSWORD)
         );
     }
 
@@ -48,17 +46,21 @@
      * @param encryptionKeyId the active encryption key id
      * @param encryptionKeys the map of available keys
      * @param repositoryImplementation the repository implementation class
+     * @param keyProviderPassword Key Provider Password used based on provider implementation
      */
-    public ProvenanceRepositoryEncryptionConfiguration(String keyProviderImplementation,
-                                                       String keyProviderLocation,
-                                                       String encryptionKeyId,
-                                                       Map<String, String> encryptionKeys,
-                                                       String repositoryImplementation) {
+    public ProvenanceRepositoryEncryptionConfiguration(final String keyProviderImplementation,
+                                                       final String keyProviderLocation,
+                                                       final String encryptionKeyId,
+                                                       final Map<String, String> encryptionKeys,
+                                                       final String repositoryImplementation,
+                                                       final String keyProviderPassword) {
         this.keyProviderImplementation = keyProviderImplementation;
         this.keyProviderLocation = keyProviderLocation;
         this.encryptionKeyId = encryptionKeyId;
         this.encryptionKeys = encryptionKeys;
         this.repositoryImplementation = repositoryImplementation;
         this.repositoryType = RepositoryType.CONTENT;
+        this.keyStoreType = KeyStoreUtils.getKeystoreTypeFromExtension(keyProviderLocation).getType();
+        this.keyProviderPassword = keyProviderPassword;
     }
 }
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/RepositoryEncryptionConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/RepositoryEncryptionConfiguration.java
index 39da7ff..8b3cd2c 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/RepositoryEncryptionConfiguration.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/repository/config/RepositoryEncryptionConfiguration.java
@@ -17,8 +17,6 @@
 package org.apache.nifi.security.repository.config;
 
 import java.util.Map;
-import javax.crypto.SecretKey;
-import org.apache.nifi.security.kms.CryptoUtils;
 import org.apache.nifi.security.kms.FileBasedKeyProvider;
 import org.apache.nifi.security.kms.KeyProvider;
 import org.apache.nifi.security.kms.StaticKeyProvider;
@@ -37,6 +35,8 @@
     Map<String, String> encryptionKeys;
     String repositoryImplementation;
     RepositoryType repositoryType;
+    String keyStoreType;
+    String keyProviderPassword;
 
     /**
      * Returns the class name of the {@link KeyProvider} implementation used.
@@ -72,7 +72,6 @@
      * {@link StaticKeyProvider}. For
      * {@link FileBasedKeyProvider}, this method will return an
      * empty map because the keys must be loaded using the {@code root key} to decrypt them
-     * via {@link CryptoUtils#readKeys(String, SecretKey)}.
      *
      * @return a map of key ids & keys
      * @see NiFiProperties#getContentRepositoryEncryptionKeys()
@@ -100,6 +99,24 @@
         return repositoryType;
     }
 
+    /**
+     * Get Key Store Type for Key Store implementation
+     *
+     * @return Key Store Type
+     */
+    public String getKeyStoreType() {
+        return keyStoreType;
+    }
+
+    /**
+     * Get Key Provider Password
+     *
+     * @return Key Provider Password
+     */
+    public String getKeyProviderPassword() {
+        return keyProviderPassword;
+    }
+
     public static RepositoryEncryptionConfiguration fromNiFiProperties(NiFiProperties niFiProperties, RepositoryType repositoryType) {
         switch (repositoryType) {
             case CONTENT:
diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java
index ddee951..278265b 100644
--- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java
+++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java
@@ -27,23 +27,24 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
-import java.security.Key;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.UnrecoverableKeyException;
 import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Optional;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.TrustManagerFactory;
 import org.apache.commons.codec.binary.Hex;
@@ -57,6 +58,7 @@
     private static final Logger logger = LoggerFactory.getLogger(KeyStoreUtils.class);
 
     public static final String SUN_PROVIDER_NAME = "SUN";
+    public static final String SUN_JSSE_PROVIDER_NAME = "SunJSSE";
     private static final String JKS_EXT = ".jks";
     private static final String PKCS12_EXT = ".p12";
     private static final String BCFKS_EXT = ".bcfks";
@@ -75,6 +77,7 @@
 
     private static final Map<String, String> KEY_STORE_TYPE_PROVIDERS = new HashMap<>();
     private static final Map<KeystoreType, String> KEY_STORE_EXTENSIONS = new HashMap<>();
+    private static final Map<KeystoreType, String> SECRET_KEY_STORE_PROVIDERS = new HashMap<>();
 
     static {
         Security.addProvider(new BouncyCastleProvider());
@@ -82,6 +85,9 @@
         KEY_STORE_TYPE_PROVIDERS.put(KeystoreType.BCFKS.getType(), BouncyCastleProvider.PROVIDER_NAME);
         KEY_STORE_TYPE_PROVIDERS.put(KeystoreType.PKCS12.getType(), BouncyCastleProvider.PROVIDER_NAME);
         KEY_STORE_TYPE_PROVIDERS.put(KeystoreType.JKS.getType(), SUN_PROVIDER_NAME);
+
+        SECRET_KEY_STORE_PROVIDERS.put(KeystoreType.BCFKS, BouncyCastleProvider.PROVIDER_NAME);
+        SECRET_KEY_STORE_PROVIDERS.put(KeystoreType.PKCS12, SUN_JSSE_PROVIDER_NAME);
     }
 
     static {
@@ -96,7 +102,7 @@
      * @param keyStoreType the keyStoreType
      * @return Key Store Provider Name or null when not found
      */
-    public static String getKeyStoreProvider(String keyStoreType) {
+    public static String getKeyStoreProvider(final String keyStoreType) {
         final String storeType = StringUtils.upperCase(keyStoreType);
         return KEY_STORE_TYPE_PROVIDERS.get(storeType);
     }
@@ -108,7 +114,7 @@
      * @return an empty KeyStore
      * @throws KeyStoreException if a KeyStore of the given type cannot be instantiated
      */
-    public static KeyStore getKeyStore(String keyStoreType) throws KeyStoreException {
+    public static KeyStore getKeyStore(final String keyStoreType) throws KeyStoreException {
         final String keyStoreProvider = getKeyStoreProvider(keyStoreType);
         if (StringUtils.isNotEmpty(keyStoreProvider)) {
             try {
@@ -121,6 +127,26 @@
     }
 
     /**
+     * Returns an empty KeyStore for Secret Keys backed by the appropriate provider
+     *
+     * @param keystoreTypeName Keystore Type Name
+     * @return an empty KeyStore
+     * @throws KeyStoreException if a KeyStore of the given type cannot be instantiated
+     */
+    public static KeyStore getSecretKeyStore(final String keystoreTypeName) throws KeyStoreException {
+        final KeystoreType keystoreType = getKeystoreType(keystoreTypeName);
+        final String provider = SECRET_KEY_STORE_PROVIDERS.get(keystoreType);
+        if (provider == null) {
+            throw new KeyStoreException(String.format("Keystore Type [%s] does not support Secret Keys", keystoreType.getType()));
+        }
+        try {
+            return KeyStore.getInstance(keystoreType.getType(), provider);
+        } catch (final NoSuchProviderException e) {
+            throw new KeyStoreException(String.format("KeyStore Type [%s] Provider [%s] not found", keystoreType.getType(), provider), e);
+        }
+    }
+
+    /**
      * Returns a loaded {@link KeyStore} given the provided configuration values.
      *
      * @param keystorePath     the file path to the keystore
@@ -144,6 +170,27 @@
     }
 
     /**
+     * Load {@link KeyStore} containing Secret Key entries using configured Security Provider
+     *
+     * @param keystorePath File path to KeyStore
+     * @param keystorePassword Password for loading KeyStore
+     * @param keystoreTypeName Keystore Type Name
+     * @return KeyStore loaded using specified configuration
+     * @throws TlsException Thrown when unable to load KeyStore or unsupported Keystore Type
+     */
+    public static KeyStore loadSecretKeyStore(final String keystorePath, final char[] keystorePassword, final String keystoreTypeName) throws TlsException {
+        try {
+            final KeyStore keyStore = getSecretKeyStore(keystoreTypeName);
+            try (final InputStream keyStoreStream = new FileInputStream(keystorePath)) {
+                keyStore.load(keyStoreStream, keystorePassword);
+            }
+            return keyStore;
+        } catch (final GeneralSecurityException|IOException e) {
+            throw new TlsException(String.format("Loading Secret Keystore [%s] Type [%s] Failed", keystorePath, keystoreTypeName), e);
+        }
+    }
+
+    /**
      * Creates a temporary default Keystore and Truststore and returns it wrapped in a TLS configuration.
      *
      * @return a {@link org.apache.nifi.security.util.TlsConfiguration}
@@ -418,7 +465,7 @@
             // Determine the default alias
             String alias = ks.aliases().nextElement();
             try {
-                Key privateKeyEntry = ks.getKey(alias, keyPassword);
+                ks.getKey(alias, keyPassword);
                 return true;
             } catch (UnrecoverableKeyException e) {
                 logger.warn("Tried to access a key in keystore " + keystore + " with a key password that failed");
@@ -437,6 +484,36 @@
         }
     }
 
+    /**
+     * Get Keystore Type based on file extension defaults to returning PKCS12
+     *
+     * @param keystorePath Path to KeyStore
+     * @return Keystore Type defaults to PKCS12
+     */
+    public static KeystoreType getKeystoreTypeFromExtension(final String keystorePath) {
+        KeystoreType keystoreType = KeystoreType.PKCS12;
+
+        for (final Map.Entry<KeystoreType, String> keystoreTypeEntry : KEY_STORE_EXTENSIONS.entrySet()) {
+            final String extension = keystoreTypeEntry.getValue();
+            if (StringUtils.endsWithIgnoreCase(keystorePath, extension)) {
+                keystoreType = keystoreTypeEntry.getKey();
+                break;
+            }
+        }
+
+        return keystoreType;
+    }
+
+    /**
+     * Is Secret Key Entry supported for specified Keystore Type
+     *
+     * @param keystoreType Keystore Type
+     * @return Secret Key Entry supported status
+     */
+    public static boolean isSecretKeyEntrySupported(final KeystoreType keystoreType) {
+        return SECRET_KEY_STORE_PROVIDERS.containsKey(keystoreType);
+    }
+
     public static String sslContextToString(SSLContext sslContext) {
         return new ToStringBuilder(sslContext)
                 .append("protocol", sslContext.getProtocol())
@@ -444,14 +521,6 @@
                 .toString();
     }
 
-    public static String sslParametersToString(SSLParameters sslParameters) {
-        return new ToStringBuilder(sslParameters)
-                .append("protocols", sslParameters.getProtocols())
-                .append("wantClientAuth", sslParameters.getWantClientAuth())
-                .append("needClientAuth", sslParameters.getNeedClientAuth())
-                .toString();
-    }
-
     public static String sslServerSocketToString(SSLServerSocket sslServerSocket) {
         return new ToStringBuilder(sslServerSocket)
                 .append("enabledProtocols", sslServerSocket.getEnabledProtocols())
@@ -469,22 +538,6 @@
      * @param keyPassword      the key password
      * @param keyStorePath     the keystore path
      * @param keyStoreType     the keystore type
-     * @return a {@link X509Certificate}
-     */
-    private static X509Certificate createKeyStoreAndGetX509Certificate(
-            final String alias, final String keyStorePassword, final String keyPassword, final String keyStorePath,
-            final KeystoreType keyStoreType) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException {
-        return createKeyStoreAndGetX509Certificate(alias, keyStorePassword, keyPassword, keyStorePath, keyStoreType, CERT_DURATION_DAYS,
-                null);
-    }
-    /**
-     * Loads the Keystore and returns a X509 Certificate with the given values.
-     *
-     * @param alias            the certificate alias
-     * @param keyStorePassword the keystore password
-     * @param keyPassword      the key password
-     * @param keyStorePath     the keystore path
-     * @param keyStoreType     the keystore type
      * @param dnsSubjectAlternativeNames An optional array of dnsName SANs
      * @param certDurationDays     the duration of the validity of the certificate, in days
      * @return a {@link X509Certificate}
@@ -588,4 +641,12 @@
         new SecureRandom().nextBytes(password);
         return Hex.encodeHexString(password);
     }
+
+    private static KeystoreType getKeystoreType(final String keystoreTypeName) {
+        final String keystoreTypeFilter = keystoreTypeName.toUpperCase();
+        final Optional<KeystoreType> foundKeystoreType = Arrays.stream(KeystoreType.values())
+                .filter(keystoreType -> keystoreType.getType().equals(keystoreTypeFilter))
+                .findFirst();
+        return foundKeystoreType.orElseThrow(() -> new IllegalArgumentException(String.format("Keystore Type [%s] not found", keystoreTypeFilter)));
+    }
 }
diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/CryptoUtilsTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/CryptoUtilsTest.groovy
index 6065c5b..1472791 100644
--- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/CryptoUtilsTest.groovy
+++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/CryptoUtilsTest.groovy
@@ -33,18 +33,11 @@
 import org.slf4j.LoggerFactory
 
 import javax.crypto.Cipher
-import javax.crypto.SecretKey
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
 import java.nio.charset.StandardCharsets
 import java.nio.file.Files
 import java.nio.file.attribute.PosixFilePermission
-import java.security.KeyManagementException
-import java.security.SecureRandom
 import java.security.Security
 
-import static groovy.test.GroovyAssert.shouldFail
-
 @RunWith(JUnit4.class)
 class CryptoUtilsTest {
     private static final Logger logger = LoggerFactory.getLogger(CryptoUtilsTest.class)
@@ -341,154 +334,6 @@
     }
 
     @Test
-    void testShouldReadKeys() {
-        // Arrange
-        String rootKeyHex = KEY_HEX
-        SecretKey rootKey = new SecretKeySpec(Hex.decode(rootKeyHex), "AES")
-
-        // Generate the file
-        String keyFileName = "keys.nkp"
-        File keyFile = tempFolder.newFile(keyFileName)
-        final int KEY_COUNT = 5
-        List<String> lines = []
-        KEY_COUNT.times { int i ->
-            lines.add("key${i + 1}=${generateEncryptedKey(rootKey)}")
-        }
-
-        keyFile.text = lines.join("\n")
-
-        logger.info("File contents: \n${keyFile.text}")
-
-        // Act
-        def readKeys = CryptoUtils.readKeys(keyFile.path, rootKey)
-        logger.info("Read ${readKeys.size()} keys from ${keyFile.path}")
-
-        // Assert
-        assert readKeys.size() == KEY_COUNT
-    }
-
-    @Test
-    void testShouldReadKeysWithDuplicates() {
-        // Arrange
-        String rootKeyHex = KEY_HEX
-        SecretKey rootKey = new SecretKeySpec(Hex.decode(rootKeyHex), "AES")
-
-        // Generate the file
-        String keyFileName = "keys.nkp"
-        File keyFile = tempFolder.newFile(keyFileName)
-        final int KEY_COUNT = 3
-        List<String> lines = []
-        KEY_COUNT.times { int i ->
-            lines.add("key${i + 1}=${generateEncryptedKey(rootKey)}")
-        }
-
-        lines.add("key3=${generateEncryptedKey(rootKey)}")
-
-        keyFile.text = lines.join("\n")
-
-        logger.info("File contents: \n${keyFile.text}")
-
-        // Act
-        def readKeys = CryptoUtils.readKeys(keyFile.path, rootKey)
-        logger.info("Read ${readKeys.size()} keys from ${keyFile.path}")
-
-        // Assert
-        assert readKeys.size() == KEY_COUNT
-    }
-
-    @Test
-    void testShouldReadKeysWithSomeMalformed() {
-        // Arrange
-        String rootKeyHex = KEY_HEX
-        SecretKey rootKey = new SecretKeySpec(Hex.decode(rootKeyHex), "AES")
-
-        // Generate the file
-        String keyFileName = "keys.nkp"
-        File keyFile = tempFolder.newFile(keyFileName)
-        final int KEY_COUNT = 5
-        List<String> lines = []
-        KEY_COUNT.times { int i ->
-            lines.add("key${i + 1}=${generateEncryptedKey(rootKey)}")
-        }
-
-        // Insert the malformed keys in the middle
-        lines.add(2, "keyX1==${generateEncryptedKey(rootKey)}")
-        lines.add(4, "=${generateEncryptedKey(rootKey)}")
-        lines.add(6, "keyX3=non Base64-encoded data")
-
-        keyFile.text = lines.join("\n")
-
-        logger.info("File contents: \n${keyFile.text}")
-
-        // Act
-        def readKeys = CryptoUtils.readKeys(keyFile.path, rootKey)
-        logger.info("Read ${readKeys.size()} keys from ${keyFile.path}")
-
-        // Assert
-        assert readKeys.size() == KEY_COUNT
-    }
-
-    @Test
-    void testShouldNotReadKeysIfAllMalformed() {
-        // Arrange
-        String rootKeyHex = KEY_HEX
-        SecretKey rootKey = new SecretKeySpec(Hex.decode(rootKeyHex), "AES")
-
-        // Generate the file
-        String keyFileName = "keys.nkp"
-        File keyFile = tempFolder.newFile(keyFileName)
-        final int KEY_COUNT = 5
-        List<String> lines = []
-
-        // All of these keys are malformed
-        KEY_COUNT.times { int i ->
-            lines.add("key${i + 1}=${generateEncryptedKey(rootKey)[0..<-4]}")
-        }
-
-        keyFile.text = lines.join("\n")
-
-        logger.info("File contents: \n${keyFile.text}")
-
-        // Act
-        def msg = shouldFail(KeyManagementException) {
-            def readKeys = CryptoUtils.readKeys(keyFile.path, rootKey)
-            logger.info("Read ${readKeys.size()} keys from ${keyFile.path}")
-        }
-
-        // Assert
-        assert msg.getMessage() == "The provided file contained no valid keys"
-    }
-
-    @Test
-    void testShouldNotReadKeysIfEmptyOrMissing() {
-        // Arrange
-        String rootKeyHex = KEY_HEX
-        SecretKey rootKey = new SecretKeySpec(Hex.decode(rootKeyHex), "AES")
-
-        // Generate the file
-        String keyFileName = "empty.nkp"
-        File keyFile = tempFolder.newFile(keyFileName)
-        logger.info("File contents: \n${keyFile.text}")
-
-        // Act
-        def missingMsg = shouldFail(KeyManagementException) {
-            def readKeys = CryptoUtils.readKeys(keyFile.path, rootKey)
-            logger.info("Read ${readKeys.size()} keys from ${keyFile.path}")
-        }
-        logger.expected("Missing file: ${missingMsg}")
-
-        def emptyMsg = shouldFail(KeyManagementException) {
-            def readKeys = CryptoUtils.readKeys(null, rootKey)
-            logger.info("Read ${readKeys.size()} keys from ${null}")
-        }
-        logger.expected("Empty file: ${emptyMsg}")
-
-        // Assert
-        assert missingMsg.getMessage() == "The provided file contained no valid keys"
-        assert emptyMsg.getMessage() == "The key provider file is not present and readable"
-    }
-
-    @Test
     void testShouldEvaluateConstantTimeEqualsForStrings() {
         // Arrange
         String plaintext = "This is a short string."
@@ -684,20 +529,4 @@
         long end = System.nanoTime()
         end - start
     }
-
-    private static String generateEncryptedKey(SecretKey rootKey) {
-        byte[] ivBytes = new byte[16]
-        byte[] keyBytes = new byte[isUnlimitedStrengthCryptoAvailable() ? 32 : 16]
-
-        SecureRandom sr = new SecureRandom()
-        sr.nextBytes(ivBytes)
-        sr.nextBytes(keyBytes)
-
-        Cipher rootCipher = Cipher.getInstance("AES/GCM/NoPadding", "BC")
-        rootCipher.init(Cipher.ENCRYPT_MODE, rootKey, new IvParameterSpec(ivBytes))
-        byte[] cipherBytes = rootCipher.doFinal(keyBytes)
-
-        Base64.encoder.encodeToString(CryptoUtils.concatByteArrays(ivBytes, cipherBytes))
-    }
-
 }
diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/KeyProviderFactoryTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/KeyProviderFactoryTest.groovy
deleted file mode 100644
index 9fdcac3..0000000
--- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/kms/KeyProviderFactoryTest.groovy
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * 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.kms
-
-import org.apache.nifi.util.NiFiProperties
-import org.bouncycastle.jce.provider.BouncyCastleProvider
-import org.bouncycastle.util.encoders.Hex
-import org.junit.After
-import org.junit.AfterClass
-import org.junit.Before
-import org.junit.BeforeClass
-import org.junit.ClassRule
-import org.junit.Test
-import org.junit.rules.TemporaryFolder
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import javax.crypto.Cipher
-import javax.crypto.SecretKey
-import javax.crypto.spec.IvParameterSpec
-import javax.crypto.spec.SecretKeySpec
-import java.security.KeyManagementException
-import java.security.SecureRandom
-import java.security.Security
-
-import static groovy.test.GroovyAssert.shouldFail
-
-@RunWith(JUnit4.class)
-class KeyProviderFactoryTest {
-    private static final Logger logger = LoggerFactory.getLogger(KeyProviderFactoryTest.class)
-
-    private static final String KEY_ID = "K1"
-    private static final String KEY_HEX_128 = "0123456789ABCDEFFEDCBA9876543210"
-    private static final String KEY_HEX_256 = KEY_HEX_128 * 2
-    private static final String KEY_HEX = isUnlimitedStrengthCryptoAvailable() ? KEY_HEX_256 : KEY_HEX_128
-
-    private static final String LEGACY_SKP_FQCN = "org.apache.nifi.provenance.StaticKeyProvider"
-    private static final String LEGACY_FBKP_FQCN = "org.apache.nifi.provenance.FileBasedKeyProvider"
-
-    private static final String ORIGINAL_PROPERTIES_PATH = System.getProperty(NiFiProperties.PROPERTIES_FILE_PATH)
-
-    private static final SecretKey ROOT_KEY = new SecretKeySpec(Hex.decode(KEY_HEX), "AES")
-
-    @ClassRule
-    public static TemporaryFolder tempFolder = new TemporaryFolder()
-
-    @BeforeClass
-    static void setUpOnce() throws Exception {
-        Security.addProvider(new BouncyCastleProvider())
-
-        logger.metaClass.methodMissing = { String name, args ->
-            logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}")
-        }
-
-        logger.info("Original \$PROPERTIES_FILE_PATH is ${ORIGINAL_PROPERTIES_PATH}")
-        String testPath = new File("src/test/resources/${isUnlimitedStrengthCryptoAvailable() ? "256" : "128"}/conf/.").getAbsolutePath()
-        logger.info("Temporarily setting to ${testPath}")
-        System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, testPath)
-    }
-
-    @Before
-    void setUp() throws Exception {
-        tempFolder.create()
-    }
-
-    @After
-    void tearDown() throws Exception {
-        tempFolder?.delete()
-    }
-
-    @AfterClass
-    static void tearDownOnce() throws Exception {
-        if (ORIGINAL_PROPERTIES_PATH) {
-            logger.info("Restored \$PROPERTIES_FILE_PATH to ${ORIGINAL_PROPERTIES_PATH}")
-            System.setProperty(NiFiProperties.PROPERTIES_FILE_PATH, ORIGINAL_PROPERTIES_PATH)
-        }
-    }
-
-    private static boolean isUnlimitedStrengthCryptoAvailable() {
-        Cipher.getMaxAllowedKeyLength("AES") > 128
-    }
-
-    private static void populateKeyDefinitionsFile(String path = "src/test/resources/conf/filebased.kp") {
-        String rootKeyHex = KEY_HEX
-        SecretKey rootKey = new SecretKeySpec(Hex.decode(rootKeyHex), "AES")
-
-        // Generate the file
-        File keyFile = new File(path)
-        final int KEY_COUNT = 1
-        List<String> lines = []
-        KEY_COUNT.times { int i ->
-            lines.add("K${i + 1}=${generateEncryptedKey(rootKey)}")
-        }
-
-        keyFile.text = lines.join("\n")
-    }
-
-    private static String generateEncryptedKey(SecretKey rootKey) {
-        byte[] ivBytes = new byte[16]
-        byte[] keyBytes = new byte[isUnlimitedStrengthCryptoAvailable() ? 32 : 16]
-
-        SecureRandom sr = new SecureRandom()
-        sr.nextBytes(ivBytes)
-        sr.nextBytes(keyBytes)
-
-        Cipher rootCipher = Cipher.getInstance("AES/GCM/NoPadding", "BC")
-        rootCipher.init(Cipher.ENCRYPT_MODE, rootKey, new IvParameterSpec(ivBytes))
-        byte[] cipherBytes = rootCipher.doFinal(keyBytes)
-
-        Base64.encoder.encodeToString(CryptoUtils.concatByteArrays(ivBytes, cipherBytes))
-    }
-
-    @Test
-    void testShouldBuildStaticKeyProvider() {
-        // Arrange
-        String staticProvider = StaticKeyProvider.class.name
-        String providerLocation = null
-
-        // Act
-        KeyProvider keyProvider = KeyProviderFactory.buildKeyProvider(staticProvider, providerLocation, KEY_ID, [(KEY_ID): KEY_HEX], null)
-        logger.info("Key Provider ${staticProvider} with location ${providerLocation} and keyId ${KEY_ID} / ${KEY_HEX} formed: ${keyProvider}")
-
-        // Assert
-        assert keyProvider instanceof StaticKeyProvider
-        assert keyProvider.getAvailableKeyIds() == [KEY_ID]
-    }
-
-    @Test
-    void testShouldBuildStaticKeyProviderWithLegacyPackage() {
-        // Arrange
-        String staticProvider = LEGACY_SKP_FQCN
-        String providerLocation = null
-
-        // Act
-        KeyProvider keyProvider = KeyProviderFactory.buildKeyProvider(staticProvider, providerLocation, KEY_ID, [(KEY_ID): KEY_HEX], null)
-        logger.info("Key Provider ${staticProvider} with location ${providerLocation} and keyId ${KEY_ID} / ${KEY_HEX} formed: ${keyProvider}")
-
-        // Assert
-        assert keyProvider instanceof StaticKeyProvider
-        assert keyProvider.getAvailableKeyIds() == [KEY_ID]
-    }
-
-    @Test
-    void testShouldBuildFileBasedKeyProvider() {
-        // Arrange
-        String fileBasedProvider = FileBasedKeyProvider.class.name
-        File fileBasedProviderFile = tempFolder.newFile("filebased.kp")
-        String providerLocation = fileBasedProviderFile.path
-        populateKeyDefinitionsFile(providerLocation)
-        logger.info("Created temporary file based key provider: ${providerLocation}")
-
-        // Act
-        KeyProvider keyProvider = KeyProviderFactory.buildKeyProvider(fileBasedProvider, providerLocation, KEY_ID, [(KEY_ID): KEY_HEX], ROOT_KEY)
-        logger.info("Key Provider ${fileBasedProvider} with location ${providerLocation} and keyId ${KEY_ID} / ${KEY_HEX} formed: ${keyProvider}")
-
-        // Assert
-        assert keyProvider instanceof FileBasedKeyProvider
-        assert keyProvider.getAvailableKeyIds() == [KEY_ID]
-    }
-
-    @Test
-    void testShouldBuildFileBasedKeyProviderWithLegacyPackage() {
-        // Arrange
-        String fileBasedProvider = LEGACY_FBKP_FQCN
-        File fileBasedProviderFile = tempFolder.newFile("filebased.kp")
-        String providerLocation = fileBasedProviderFile.path
-        populateKeyDefinitionsFile(providerLocation)
-        logger.info("Created temporary file based key provider: ${providerLocation}")
-
-        // Act
-        KeyProvider keyProvider = KeyProviderFactory.buildKeyProvider(fileBasedProvider, providerLocation, KEY_ID, [(KEY_ID): KEY_HEX], ROOT_KEY)
-        logger.info("Key Provider ${fileBasedProvider} with location ${providerLocation} and keyId ${KEY_ID} / ${KEY_HEX} formed: ${keyProvider}")
-
-        // Assert
-        assert keyProvider instanceof FileBasedKeyProvider
-        assert keyProvider.getAvailableKeyIds() == [KEY_ID]
-    }
-
-    @Test
-    void testShouldNotBuildFileBasedKeyProviderWithoutRootKey() {
-        // Arrange
-        String fileBasedProvider = FileBasedKeyProvider.class.name
-        File fileBasedProviderFile = tempFolder.newFile("filebased.kp")
-        String providerLocation = fileBasedProviderFile.path
-        populateKeyDefinitionsFile(providerLocation)
-        logger.info("Created temporary file based key provider: ${providerLocation}")
-
-        // Act
-        def msg = shouldFail(KeyManagementException) {
-            KeyProvider keyProvider = KeyProviderFactory.buildKeyProvider(fileBasedProvider, providerLocation, KEY_ID, [(KEY_ID): KEY_HEX], null)
-            logger.info("Key Provider ${fileBasedProvider} with location ${providerLocation} and keyId ${KEY_ID} / ${KEY_HEX} formed: ${keyProvider}")
-        }
-
-        // Assert
-        assert msg =~ "The root key must be provided to decrypt the individual keys"
-    }
-
-    @Test
-    void testShouldNotBuildUnknownKeyProvider() {
-        // Arrange
-        String providerImplementation = "org.apache.nifi.provenance.ImaginaryKeyProvider"
-        String providerLocation = null
-
-        // Act
-        def msg = shouldFail(KeyManagementException) {
-            KeyProvider keyProvider = KeyProviderFactory.buildKeyProvider(providerImplementation, providerLocation, KEY_ID, [(KEY_ID): KEY_HEX], null)
-            logger.info("Key Provider ${providerImplementation} with location ${providerLocation} and keyId ${KEY_ID} / ${KEY_HEX} formed: ${keyProvider}")
-        }
-
-        // Assert
-        assert msg =~ "Invalid key provider implementation provided"
-    }
-}
diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java
index a4db650..12d76dd 100644
--- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java
+++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java
@@ -21,6 +21,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
@@ -31,9 +32,13 @@
 import java.security.cert.X509Certificate;
 import java.util.UUID;
 
+import org.apache.commons.lang3.StringUtils;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -46,13 +51,20 @@
     private static final String ALIAS = "alias";
     private static final String KEY_ALGORITHM = "RSA";
     private static final String SUBJECT_DN = "CN=localhost";
+    private static final String SECRET_KEY_ALGORITHM = "AES";
+    private static final String KEY_PROTECTION_ALGORITHM = "PBEWithHmacSHA256AndAES_256";
+    private static final String HYPHEN_SEPARATOR = "-";
+
     private static KeyPair keyPair;
     private static X509Certificate certificate;
+    private static SecretKey secretKey;
 
     @BeforeClass
     public static void generateKeysAndCertificates() throws NoSuchAlgorithmException, CertificateException {
         keyPair = KeyPairGenerator.getInstance(KEY_ALGORITHM).generateKeyPair();
         certificate = CertificateUtils.generateSelfSignedX509Certificate(keyPair, SUBJECT_DN, SIGNING_ALGORITHM, DURATION_DAYS);
+        final byte[] encodedKey = StringUtils.remove(UUID.randomUUID().toString(), HYPHEN_SEPARATOR).getBytes(StandardCharsets.UTF_8);
+        secretKey = new SecretKeySpec(encodedKey, SECRET_KEY_ALGORITHM);
     }
 
     @Test
@@ -91,6 +103,21 @@
         }
     }
 
+    @Test
+    public void testKeystoreTypesSecretKeyEntry() throws GeneralSecurityException, IOException {
+        for (final KeystoreType keystoreType : KeystoreType.values()) {
+            if (KeyStoreUtils.isSecretKeyEntrySupported(keystoreType)) {
+                final KeyStore sourceKeyStore = KeyStoreUtils.getSecretKeyStore(keystoreType.getType());
+                final KeyStore destinationKeyStore = KeyStoreUtils.getSecretKeyStore(keystoreType.getType());
+                try {
+                    assertSecretKeyStoredLoaded(sourceKeyStore, destinationKeyStore);
+                } catch (final GeneralSecurityException e) {
+                    throw new GeneralSecurityException(String.format("Keystore Type [%s] Failed", keystoreType), e);
+                }
+            }
+        }
+    }
+
     private void assertCertificateEntryStoredLoaded(final KeyStore sourceKeyStore, final KeyStore destinationKeyStore) throws GeneralSecurityException, IOException {
         sourceKeyStore.load(null, null);
         sourceKeyStore.setCertificateEntry(ALIAS, certificate);
@@ -115,6 +142,16 @@
         assertEquals(String.format("[%s] Public Key not matched", sourceKeyStore.getType()), keyPair.getPublic(), entryCertificateChain[0].getPublicKey());
     }
 
+    private void assertSecretKeyStoredLoaded(final KeyStore sourceKeyStore, final KeyStore destinationKeyStore) throws GeneralSecurityException, IOException {
+        sourceKeyStore.load(null, null);
+        final KeyStore.ProtectionParameter protection = getProtectionParameter(sourceKeyStore.getType());
+        sourceKeyStore.setEntry(ALIAS, new KeyStore.SecretKeyEntry(secretKey), protection);
+
+        final KeyStore copiedKeyStore = copyKeyStore(sourceKeyStore, destinationKeyStore);
+        final KeyStore.Entry entry = copiedKeyStore.getEntry(ALIAS, protection);
+        assertTrue(String.format("[%s] Secret Key entry not found", sourceKeyStore.getType()), entry instanceof KeyStore.SecretKeyEntry);
+    }
+
     private KeyStore copyKeyStore(final KeyStore sourceKeyStore, final KeyStore destinationKeyStore) throws GeneralSecurityException, IOException {
         final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         sourceKeyStore.store(byteArrayOutputStream, STORE_PASSWORD);
@@ -122,4 +159,13 @@
         destinationKeyStore.load(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), STORE_PASSWORD);
         return destinationKeyStore;
     }
+
+    private KeyStore.ProtectionParameter getProtectionParameter(final String keyStoreType) {
+        if (KeystoreType.PKCS12.getType().equals(keyStoreType)) {
+            // Select Key Protection Algorithm for PKCS12 to avoid unsupported algorithm on Java 1.8.0.292
+            return new KeyStore.PasswordProtection(KEY_PASSWORD, KEY_PROTECTION_ALGORITHM, null);
+        } else {
+            return new KeyStore.PasswordProtection(KEY_PASSWORD);
+        }
+    }
 }
diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml
index 368a67a..59a0b58 100644
--- a/nifi-commons/pom.xml
+++ b/nifi-commons/pom.xml
@@ -43,6 +43,7 @@
         <module>nifi-rocksdb-utils</module>
         <module>nifi-schema-utils</module>
         <module>nifi-security-kerberos</module>
+        <module>nifi-security-kms</module>
         <module>nifi-security-socket-ssl</module>
         <module>nifi-security-utils-api</module>
         <module>nifi-security-utils</module>
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index a231d46..38e5765 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -2865,8 +2865,9 @@
 
 |====
 |*Property*|*Description*
-|`nifi.flowfile.repository.encryption.key.provider.implementation`|This is the fully-qualified class name of the **key provider**. A key provider is the datastore interface for accessing the encryption key to protect the content claims. There are currently two implementations -- `StaticKeyProvider` which reads a key directly from _nifi.properties_, and `FileBasedKeyProvider` which reads *n* many keys from an encrypted file. The interface is extensible, and HSM-backed or other providers are expected in the future.
+|`nifi.flowfile.repository.encryption.key.provider.implementation`|This is the fully-qualified class name of the **key provider**. A key provider is the datastore interface for accessing the encryption key to protect the content claims. There are currently three implementations: `StaticKeyProvider` which reads a key directly from _nifi.properties_, `FileBasedKeyProvider` which reads keys from an encrypted file, and `KeyStoreKeyProvider` which reads keys from a standard `java.security.KeyStore`.
 |`nifi.flowfile.repository.encryption.key.provider.location`|The path to the key definition resource (empty for `StaticKeyProvider`, `./keys.nkp` or similar path for `FileBasedKeyProvider`). For future providers like an HSM, this may be a connection string or URL.
+|`nifi.flowfile.repository.encryption.key.provider.password`|The password used for decrypting the key definition resource, such as the keystore for `KeyStoreKeyProvider`.
 |`nifi.flowfile.repository.encryption.key.id`|The active key ID to use for encryption (e.g. `Key1`).
 |`nifi.flowfile.repository.encryption.key`|The key to use for `StaticKeyProvider`. The key format is hex-encoded (`0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210`) but can also be encrypted using the `./encrypt-config.sh` tool in NiFi Toolkit (see the <<toolkit-guide.adoc#encrypt_config_tool,Encrypt-Config Tool>> section in the link:toolkit-guide.html[NiFi Toolkit Guide] for more information).
 |`nifi.flowfile.repository.encryption.key.id.`*|Allows for additional keys to be specified for the `StaticKeyProvider`. For example, the line `nifi.flowfile.repository.encryption.key.id.Key2=012...210` would provide an available key `Key2`.
@@ -3047,8 +3048,9 @@
 
 |====
 |*Property*|*Description*
- |`nifi.content.repository.encryption.key.provider.implementation`|This is the fully-qualified class name of the **key provider**. A key provider is the datastore interface for accessing the encryption key to protect the content claims. There are currently two implementations -- `StaticKeyProvider` which reads a key directly from _nifi.properties_, and `FileBasedKeyProvider` which reads *n* many keys from an encrypted file. The interface is extensible, and HSM-backed or other providers are expected in the future.
+ |`nifi.content.repository.encryption.key.provider.implementation`|This is the fully-qualified class name of the **key provider**. A key provider is the datastore interface for accessing the encryption key to protect the content claims. There are currently three implementations: `StaticKeyProvider` which reads a key directly from _nifi.properties_, `FileBasedKeyProvider` which reads keys from an encrypted file, and `KeyStoreKeyProvider` which reads keys from a standard `java.security.KeyStore`.
  |`nifi.content.repository.encryption.key.provider.location`|The path to the key definition resource (empty for `StaticKeyProvider`, `./keys.nkp` or similar path for `FileBasedKeyProvider`). For future providers like an HSM, this may be a connection string or URL.
+ |`nifi.content.repository.encryption.key.provider.password`|The password used for decrypting the key definition resource, such as the keystore for `KeyStoreKeyProvider`.
  |`nifi.content.repository.encryption.key.id`|The active key ID to use for encryption (e.g. `Key1`).
  |`nifi.content.repository.encryption.key`|The key to use for `StaticKeyProvider`. The key format is hex-encoded (`0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210`) but can also be encrypted using the `./encrypt-config.sh` tool in NiFi Toolkit (see the <<toolkit-guide.adoc#encrypt_config_tool,Encrypt-Config Tool>> section in the link:toolkit-guide.html[NiFi Toolkit Guide] for more information).
  |`nifi.content.repository.encryption.key.id.`*|Allows for additional keys to be specified for the `StaticKeyProvider`. For example, the line `nifi.content.repository.encryption.key.id.Key2=012...210` would provide an available key `Key2`.
@@ -3173,8 +3175,9 @@
 
 |====
 |*Property*|*Description*
- |`nifi.provenance.repository.encryption.key.provider.implementation`|This is the fully-qualified class name of the **key provider**. A key provider is the datastore interface for accessing the encryption key to protect the provenance events. There are currently two implementations -- `StaticKeyProvider` which reads a key directly from _nifi.properties_, and `FileBasedKeyProvider` which reads *n* many keys from an encrypted file. The interface is extensible, and HSM-backed or other providers are expected in the future.
+ |`nifi.provenance.repository.encryption.key.provider.implementation`|This is the fully-qualified class name of the **key provider**. A key provider is the datastore interface for accessing the encryption key to protect the provenance events. There are currently three implementations: `StaticKeyProvider` which reads a key directly from _nifi.properties_, `FileBasedKeyProvider` which reads keys from an encrypted file, and `KeyStoreKeyProvider` which reads keys from a standard `java.security.KeyStore`.
  |`nifi.provenance.repository.encryption.key.provider.location`|The path to the key definition resource (empty for `StaticKeyProvider`, `./keys.nkp` or similar path for `FileBasedKeyProvider`). For future providers like an HSM, this may be a connection string or URL.
+ |`nifi.provenance.repository.encryption.key.provider.password`|The password used for decrypting the key definition resource, such as the keystore for `KeyStoreKeyProvider`.
  |`nifi.provenance.repository.encryption.key.id`|The active key ID to use for encryption (e.g. `Key1`).
  |`nifi.provenance.repository.encryption.key`|The key to use for `StaticKeyProvider`. The key format is hex-encoded (`0123456789ABCDEFFEDCBA98765432100123456789ABCDEFFEDCBA9876543210`) but can also be encrypted using the `./encrypt-config.sh` tool in NiFi Toolkit (see the <<toolkit-guide.adoc#encrypt_config_tool,Encrypt-Config Tool>> section in the link:toolkit-guide.html[NiFi Toolkit Guide] for more information).
  |`nifi.provenance.repository.encryption.key.id.`*|Allows for additional keys to be specified for the `StaticKeyProvider`. For example, the line `nifi.provenance.repository.encryption.key.id.Key2=012...210` would provide an available key `Key2`.
diff --git a/nifi-docs/src/main/asciidoc/user-guide.adoc b/nifi-docs/src/main/asciidoc/user-guide.adoc
index feaa9ac..0316a0e 100644
--- a/nifi-docs/src/main/asciidoc/user-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/user-guide.adoc
@@ -2934,6 +2934,34 @@
 
 Each line defines a key ID and then the Base64-encoded cipher text of a 16 byte IV and wrapped AES-128, AES-192, or AES-256 key depending on the JCE policies available. The individual keys are wrapped by AES/GCM encryption using the **root key** defined by `nifi.bootstrap.sensitive.key` in _conf/bootstrap.conf_.
 
+===== KeyStoreKeyProvider
+The `KeyStoreKeyProvider` implementation reads from a standard `java.security.KeyStore` using the configured password to load AES Secret Key entries.
+
+The provider supports the following Keystore Types:
+
+* BCFKS
+* PKCS12
+
+The keystore filename extension must be either `.p12` indicating PKCS12 or `.bcfks` indicating BCFKS.
+
+The `keytool` command can be used to generate an AES-256 Secret Key stored in a PKCS12 file for repository encryption:
+
+...
+keytool -genseckey -alias primary-key -keyalg AES -keysize 256 -keystore repository.p12 -storetype PKCS12
+...
+
+Enter a keystore password when prompted. The same value must be used for both the keystore password and key password.
+The keystore password will be used in the provider configuration properties.
+
+The following configuration properties support using a PKCS12 keystore with a Secret Key:
+
+...
+nifi.provenance.repository.encryption.key.provider.implementation=org.apache.nifi.security.kms.KeyStoreKeyProvider
+nifi.provenance.repository.encryption.key.provider.location=./conf/repository.p12
+nifi.provenance.repository.encryption.key.provider.password=KEYSTORE_PASSWORD
+nifi.provenance.repository.encryption.key.id=primary-key
+...
+
 [[provenance-repository-key-rotation]]
 ===== Key Rotation
 Simply update _nifi.properties_ to reference a new key ID in `nifi.provenance.repository.encryption.key.id`. Previously-encrypted events can still be decrypted as long as that key is still available in the key definition file or `nifi.provenance.repository.encryption.key.id.<OldKeyID>` as the key ID is serialized alongside the encrypted record.
@@ -3013,6 +3041,34 @@
 
 Each line defines a key ID and then the Base64-encoded cipher text of a 16 byte IV and wrapped AES-128, AES-192, or AES-256 key depending on the JCE policies available. The individual keys are wrapped by AES/GCM encryption using the **root key** defined by `nifi.bootstrap.sensitive.key` in _conf/bootstrap.conf_.
 
+==== KeyStoreKeyProvider
+The `KeyStoreKeyProvider` implementation reads from a standard `java.security.KeyStore` using the configured password to load AES Secret Key entries.
+
+The provider supports the following Keystore Types:
+
+* BCFKS
+* PKCS12
+
+The keystore filename extension must be either `.p12` indicating PKCS12 or `.bcfks` indicating BCFKS.
+
+The `keytool` command can be used to generate an AES-256 Secret Key stored in a PKCS12 file for repository encryption:
+
+...
+keytool -genseckey -alias primary-key -keyalg AES -keysize 256 -keystore repository.p12 -storetype PKCS12
+...
+
+Enter a keystore password when prompted. The same value must be used for both the keystore password and key password.
+The keystore password will be used in the provider configuration properties.
+
+The following configuration properties support using a PKCS12 keystore with a Secret Key:
+
+...
+nifi.content.repository.encryption.key.provider.implementation=org.apache.nifi.security.kms.KeyStoreKeyProvider
+nifi.content.repository.encryption.key.provider.location=./conf/repository.p12
+nifi.content.repository.encryption.key.provider.password=KEYSTORE_PASSWORD
+nifi.content.repository.encryption.key.id=primary-key
+...
+
 .Data Protection vs. Key Protection
 ****
 Even though the flowfile content is encrypted using `AES/CTR` to handle streaming data, if using the _Config Encrypt
@@ -3099,6 +3155,34 @@
 
 Each line defines a key ID and then the Base64-encoded cipher text of a 16 byte IV and wrapped AES-128, AES-192, or AES-256 key depending on the JCE policies available. The individual keys are wrapped by AES/GCM encryption using the **root key** defined by `nifi.bootstrap.sensitive.key` in _conf/bootstrap.conf_.
 
+==== KeyStoreKeyProvider
+The `KeyStoreKeyProvider` implementation reads from a standard `java.security.KeyStore` using the configured password to load AES Secret Key entries.
+
+The provider supports the following Keystore Types:
+
+* BCFKS
+* PKCS12
+
+The keystore filename extension must be either `.p12` indicating PKCS12 or `.bcfks` indicating BCFKS.
+
+The `keytool` command can be used to generate an AES-256 Secret Key stored in a PKCS12 file for repository encryption:
+
+...
+keytool -genseckey -alias primary-key -keyalg AES -keysize 256 -keystore repository.p12 -storetype PKCS12
+...
+
+Enter a keystore password when prompted. The same value must be used for both the keystore password and key password.
+The keystore password will be used in the provider configuration properties.
+
+The following configuration properties support using a PKCS12 keystore with a Secret Key:
+
+...
+nifi.flowfile.repository.encryption.key.provider.implementation=org.apache.nifi.security.kms.KeyStoreKeyProvider
+nifi.flowfile.repository.encryption.key.provider.location=./conf/repository.p12
+nifi.flowfile.repository.encryption.key.provider.password=KEYSTORE_PASSWORD
+nifi.flowfile.repository.encryption.key.id=primary-key
+...
+
 [[flowfile-repository-key-rotation]]
 ==== Key Rotation
 Simply update _nifi.properties_ to reference a new key ID in `nifi.flowfile.repository.encryption.key.id`. Previously-encrypted flowfile records can still be decrypted as long as that key is still available in the key definition file or `nifi.flowfile.repository.encryption.key.id.<OldKeyID>` as the key ID is serialized alongside the encrypted record.
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedSchemaRepositoryRecordSerdeTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedSchemaRepositoryRecordSerdeTest.groovy
index 9e0cf32..d6a5418 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedSchemaRepositoryRecordSerdeTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-flowfile-repo-serialization/src/test/groovy/org/apache/nifi/controller/repository/EncryptedSchemaRepositoryRecordSerdeTest.groovy
@@ -276,7 +276,7 @@
 
         // Configure the serde with multiple keys available
         def multipleKeys = KEYS + [K2: "0F" * 32]
-        FlowFileRepositoryEncryptionConfiguration multipleKeyFFREC = new FlowFileRepositoryEncryptionConfiguration(KPI, KPL, KEY_ID, multipleKeys, REPO_IMPL)
+        FlowFileRepositoryEncryptionConfiguration multipleKeyFFREC = new FlowFileRepositoryEncryptionConfiguration(KPI, KPL, KEY_ID, multipleKeys, REPO_IMPL, null)
 
         esrrs = new EncryptedSchemaRepositoryRecordSerde(wrappedSerDe, multipleKeyFFREC)
         assert esrrs.getActiveKeyId() == "K1"
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/wali/EncryptedSequentialAccessWriteAheadLogTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/wali/EncryptedSequentialAccessWriteAheadLogTest.groovy
index 61d0b7a..f50a160 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/wali/EncryptedSequentialAccessWriteAheadLogTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/wali/EncryptedSequentialAccessWriteAheadLogTest.groovy
@@ -92,7 +92,7 @@
         dataOutputStream = new DataOutputStream(byteArrayOutputStream)
         wrappedSerDe = new SchemaRepositoryRecordSerde(claimManager, new NoOpFieldCache())
 
-        flowFileREC = new FlowFileRepositoryEncryptionConfiguration(KPI, KPL, KEY_ID, KEYS, REPO_IMPL)
+        flowFileREC = new FlowFileRepositoryEncryptionConfiguration(KPI, KPL, KEY_ID, KEYS, REPO_IMPL, null)
 
         esrrs = new EncryptedSchemaRepositoryRecordSerde(wrappedSerDe, flowFileREC)
     }
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java
index 368ff03..ace5016 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/main/java/org/apache/nifi/properties/ProtectedNiFiProperties.java
@@ -48,8 +48,16 @@
     public static final String ADDITIONAL_SENSITIVE_PROPERTIES_KEY = "nifi.sensitive.props.additional.keys";
 
     // Default list of "sensitive" property keys
-    public static final List<String> DEFAULT_SENSITIVE_PROPERTIES = new ArrayList<>(asList(SECURITY_KEY_PASSWD,
-            SECURITY_KEYSTORE_PASSWD, SECURITY_TRUSTSTORE_PASSWD, SENSITIVE_PROPS_KEY, PROVENANCE_REPO_ENCRYPTION_KEY));
+    public static final List<String> DEFAULT_SENSITIVE_PROPERTIES = new ArrayList<>(asList(
+            SECURITY_KEY_PASSWD,
+            SECURITY_KEYSTORE_PASSWD,
+            SECURITY_TRUSTSTORE_PASSWD,
+            SENSITIVE_PROPS_KEY,
+            PROVENANCE_REPO_ENCRYPTION_KEY,
+            PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_PASSWORD,
+            FLOWFILE_REPOSITORY_ENCRYPTION_KEY_PROVIDER_PASSWORD,
+            CONTENT_REPOSITORY_ENCRYPTION_KEY_PROVIDER_PASSWORD
+    ));
 
     public ProtectedNiFiProperties() {
         this(new NiFiProperties());
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy
index f902dbc..6c49f6d 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/groovy/org/apache/nifi/properties/ProtectedNiFiPropertiesGroovyTest.groovy
@@ -41,7 +41,10 @@
             "nifi.security.keystorePasswd",
             "nifi.security.keyPasswd",
             "nifi.security.truststorePasswd",
-            "nifi.provenance.repository.encryption.key"
+            "nifi.provenance.repository.encryption.key",
+            "nifi.provenance.repository.encryption.key.provider.password",
+            "nifi.flowfile.repository.encryption.key.provider.password",
+            "nifi.content.repository.encryption.key.provider.password"
     ]
 
     final def COMMON_ADDITIONAL_SENSITIVE_PROPERTIES = [
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index 772fe12..cf0c531 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -65,6 +65,7 @@
 nifi.flowfile.repository.always.sync=${nifi.flowfile.repository.always.sync}
 nifi.flowfile.repository.encryption.key.provider.implementation=${nifi.flowfile.repository.encryption.key.provider.implementation}
 nifi.flowfile.repository.encryption.key.provider.location=${nifi.flowfile.repository.encryption.key.provider.location}
+nifi.flowfile.repository.encryption.key.provider.password=
 nifi.flowfile.repository.encryption.key.id=${nifi.flowfile.repository.encryption.key.id}
 nifi.flowfile.repository.encryption.key=${nifi.flowfile.repository.encryption.key}
 nifi.flowfile.repository.retain.orphaned.flowfiles=${nifi.flowfile.repository.retain.orphaned.flowfiles}
@@ -83,6 +84,7 @@
 nifi.content.viewer.url=${nifi.content.viewer.url}
 nifi.content.repository.encryption.key.provider.implementation=${nifi.content.repository.encryption.key.provider.implementation}
 nifi.content.repository.encryption.key.provider.location=${nifi.content.repository.encryption.key.provider.location}
+nifi.content.repository.encryption.key.provider.password=
 nifi.content.repository.encryption.key.id=${nifi.content.repository.encryption.key.id}
 nifi.content.repository.encryption.key=${nifi.content.repository.encryption.key}
 
@@ -90,6 +92,7 @@
 nifi.provenance.repository.implementation=${nifi.provenance.repository.implementation}
 nifi.provenance.repository.encryption.key.provider.implementation=${nifi.provenance.repository.encryption.key.provider.implementation}
 nifi.provenance.repository.encryption.key.provider.location=${nifi.provenance.repository.encryption.key.provider.location}
+nifi.provenance.repository.encryption.key.provider.password=
 nifi.provenance.repository.encryption.key.id=${nifi.provenance.repository.encryption.key.id}
 nifi.provenance.repository.encryption.key=${nifi.provenance.repository.encryption.key}
 
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/pom.xml b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/pom.xml
index 3d83217..d8a8051 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/pom.xml
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/pom.xml
@@ -47,6 +47,21 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils</artifactId>
+            <version>1.14.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils-api</artifactId>
+            <version>1.14.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-kms</artifactId>
+            <version>1.14.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-properties</artifactId>
             <version>1.14.0-SNAPSHOT</version>
         </dependency>
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
index 24d80d6..198f362 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
@@ -25,14 +25,14 @@
 import org.apache.nifi.provenance.toc.StandardTocWriter;
 import org.apache.nifi.provenance.toc.TocUtil;
 import org.apache.nifi.provenance.toc.TocWriter;
-import org.apache.nifi.security.kms.CryptoUtils;
 import org.apache.nifi.security.kms.KeyProvider;
-import org.apache.nifi.security.kms.KeyProviderFactory;
+import org.apache.nifi.security.repository.RepositoryEncryptorUtils;
+import org.apache.nifi.security.repository.config.ProvenanceRepositoryEncryptionConfiguration;
+import org.apache.nifi.security.repository.config.RepositoryEncryptionConfiguration;
 import org.apache.nifi.util.NiFiProperties;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.crypto.SecretKey;
 import java.io.IOException;
 import java.security.KeyManagementException;
 
@@ -84,13 +84,7 @@
         ProvenanceEventEncryptor provenanceEventEncryptor;
         if (getConfig().supportsEncryption()) {
             try {
-                KeyProvider keyProvider;
-                if (KeyProviderFactory.requiresRootKey(getConfig().getKeyProviderImplementation())) {
-                    SecretKey rootKey = CryptoUtils.getRootKey();
-                    keyProvider = buildKeyProvider(rootKey);
-                } else {
-                    keyProvider = buildKeyProvider();
-                }
+                final KeyProvider keyProvider = buildKeyProvider();
                 provenanceEventEncryptor = new AESProvenanceEventEncryptor();
                 provenanceEventEncryptor.initialize(keyProvider);
             } catch (KeyManagementException e) {
@@ -130,22 +124,16 @@
         super.init(recordWriterFactory, recordReaderFactory, eventReporter, authorizer, resourceFactory);
     }
 
-    private KeyProvider buildKeyProvider() throws KeyManagementException {
-        return buildKeyProvider(null);
-    }
-
-    private KeyProvider buildKeyProvider(SecretKey rootKey) throws KeyManagementException {
-        RepositoryConfiguration config = super.getConfig();
-        if (config == null) {
-            throw new KeyManagementException("The repository configuration is missing");
-        }
-
-        final String implementationClassName = config.getKeyProviderImplementation();
-        if (implementationClassName == null) {
-            throw new KeyManagementException("Cannot create Key Provider because the NiFi Properties is missing the following property: "
-                    + NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS);
-        }
-
-        return KeyProviderFactory.buildKeyProvider(implementationClassName, config.getKeyProviderLocation(), config.getKeyId(), config.getEncryptionKeys(), rootKey);
+    private KeyProvider buildKeyProvider() throws IOException {
+        final RepositoryConfiguration config = getConfig();
+        final RepositoryEncryptionConfiguration configuration = new ProvenanceRepositoryEncryptionConfiguration(
+                config.getKeyProviderImplementation(),
+                config.getKeyProviderLocation(),
+                config.getKeyId(),
+                config.getEncryptionKeys(),
+                getClass().getName(),
+                config.getKeyProviderPassword()
+        );
+        return RepositoryEncryptorUtils.validateAndBuildRepositoryKeyProvider(configuration);
     }
 }
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/RepositoryConfiguration.java b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/RepositoryConfiguration.java
index 53549aa..77c080a 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/RepositoryConfiguration.java
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-persistent-provenance-repository/src/main/java/org/apache/nifi/provenance/RepositoryConfiguration.java
@@ -59,6 +59,7 @@
     private String keyId;
     private String keyProviderImplementation;
     private String keyProviderLocation;
+    private String keyProviderPassword;
 
     private List<SearchableField> searchableFields = new ArrayList<>();
     private List<SearchableField> searchableAttributes = new ArrayList<>();
@@ -409,6 +410,13 @@
         this.keyProviderLocation = keyProviderLocation;
     }
 
+    public String getKeyProviderPassword() {
+        return keyProviderPassword;
+    }
+
+    public void setKeyProviderPassword(final String keyProviderPassword) {
+        this.keyProviderPassword = keyProviderPassword;
+    }
 
     public int getDebugFrequency() {
         return debugFrequency;
@@ -519,6 +527,7 @@
             config.setKeyId(nifiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_ID));
             config.setKeyProviderImplementation(nifiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_IMPLEMENTATION_CLASS));
             config.setKeyProviderLocation(nifiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_LOCATION));
+            config.setKeyProviderPassword(nifiProperties.getProperty(NiFiProperties.PROVENANCE_REPO_ENCRYPTION_KEY_PROVIDER_PASSWORD));
         }
 
         return config;
diff --git a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-provenance-repository-nar/pom.xml b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-provenance-repository-nar/pom.xml
index a2b7504..0e93b5a 100644
--- a/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-provenance-repository-nar/pom.xml
+++ b/nifi-nar-bundles/nifi-provenance-repository-bundle/nifi-provenance-repository-nar/pom.xml
@@ -34,5 +34,11 @@
             <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-volatile-provenance-repository</artifactId>
         </dependency>
+        <!-- Override provided scope to include in NAR -->
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils-api</artifactId>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 </project>