NIFI-8511 Added KeyStore implementation of KeyProvider#5110
NIFI-8511 Added KeyStore implementation of KeyProvider#5110exceptionfactory wants to merge 2 commits intoapache:mainfrom
Conversation
...commons/nifi-security-kms/src/main/java/org/apache/nifi/security/kms/KeyProviderFactory.java
Show resolved
Hide resolved
...curity-kms/src/main/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReader.java
Outdated
Show resolved
Hide resolved
...curity-kms/src/main/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReader.java
Outdated
Show resolved
Hide resolved
...curity-kms/src/main/java/org/apache/nifi/security/kms/reader/StandardFileBasedKeyReader.java
Show resolved
Hide resolved
...s/nifi-security-kms/src/test/java/org/apache/nifi/security/kms/FileBasedKeyProviderTest.java
Show resolved
Hide resolved
...mmons/nifi-security-utils/src/test/java/org/apache/nifi/security/util/KeyStoreUtilsTest.java
Show resolved
Hide resolved
| * @param configuration Key Provider Configuration | ||
| * @return Key Provider | ||
| */ | ||
| public static KeyProvider getKeyProvider(final KeyProviderConfiguration<?> configuration) { |
There was a problem hiding this comment.
I've got an idea to avoid downcasting and branching here. The KeyProviderConfiguration interface could contain a new method:
KeyProvider getKeyProvider(final KeyProviderCreator keyProviderCreator);
A key provider creator class could handle creating the different providers based on different configurations:
public class KeyProviderCreator {
private static final String SECRET_KEY_ALGORITHM = "AES";
public KeyProvider getKeyProvider(final StaticKeyProviderConfiguration configuration) {
final Map<String, SecretKey> secretKeys;
try {
secretKeys = getSecretKeys(configuration.getKeys());
return new StaticKeyProvider(secretKeys);
} catch (final DecoderException e) {
throw new KeyReaderException("Decoding Hexadecimal Secret Keys failed", e);
}
}
public KeyProvider getKeyProvider(final FileBasedKeyProviderConfiguration configuration) {
final Path keyProviderPath = Paths.get(configuration.getLocation());
return new FileBasedKeyProvider(keyProviderPath, configuration.getRootKey());
}
public KeyProvider getKeyProvider(final KeyStoreKeyProviderConfiguration configuration) {
final KeyStore keyStore = configuration.getKeyStore();
return new KeyStoreKeyProvider(keyStore, configuration.getKeyPassword());
}
private 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;
}
}`
The configuration classes can utilize the creator class to make providers:
@Override public KeyProvider getKeyProvider(KeyProviderCreator keyProviderCreator) { return keyProviderCreator.getKeyProvider(this); }
And then in the factory, there's no branching remaining:
`public class KeyProviderFactory {
private static final KeyProviderCreator creator = new KeyProviderCreator();
public static KeyProvider getKeyProvider(final KeyProviderConfiguration<?> configuration) {
return configuration.getKeyProvider(creator);
}
}`
What do you think of this approach? It adds a bit of extra complexity to other classes but withdraws some from the factory. Also, this way if anyone adds a new configuration, they will be obliged to implement the provider method.
There was a problem hiding this comment.
Thanks for the suggestion, that is an interesting idea. The purpose of the KeyProviderFactory is to abstract the details of instantiating a KeyProvider, so going in the direction of KeyProviderCreator seems to create another level of abstraction. The basic purpose of the KeyProviderConfiguration classes is to describe the properties necessary to create the associated KeyProvider. A different approach could be to have one KeyProviderConfiguration with all possible properties, but it would still require branching to determine the required properties. There are probably other options for streamlining and abstracting KeyProvider creation, but the current implementation is a variation on the previous approach.
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/kms/CryptoUtils.java
Outdated
Show resolved
Hide resolved
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java
Show resolved
Hide resolved
...sitory/src/main/java/org/apache/nifi/provenance/EncryptedWriteAheadProvenanceRepository.java
Outdated
Show resolved
Hide resolved
- KeyStoreKeyProvider supports PKCS12 and BCFKS - Refactored KeyProvider and implementations to nifi-security-kms - Updated Admin Guide and User Guide with KeyStoreKeyProvider details
5701b3c to
098367b
Compare
|
Rebased to resolve merge conflicts. |
|
Tested this out with a pkcs12 keystore containing a secret key (
and verified that the data is encrypted in the provenance repo data in ./provenance_repository. Thought there was an issue with querying which turned out to be an authZ issue. I also ran into the below exception when opening the provenance UI:
which was a result of having an existing unencrypted provenance database. I stopped NiFi, deleted ./provenance_repository/* and started back up and provenance was working once I fixed the authz issue. +1, will merge. |
- 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 apache#5110.
- 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 apache#5110.
Description of PR
NIFI-8511 Adds an encrypted repository KeyProvider implementation using a
java.security.KeyStoreinstance containingjavax.crypto.SecretKeyentries. The implementation supports PKCS12 or BCFKS keystores.The Bouncy Castle implementation of PKCS12 does not support SecretKey entries, so
KeyStoreUtilsincludes additional mappings and methods for reading keystore files containing SecretKey entries. Changes also include refactoring theKeyProviderinterface and implementations into a separatenifi-security-kmsmodule to streamline and clarify dependencies. The User Guide includes additional sections with examplenifi.propertiessections to configure theKeyStoreKeyProvider.In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:
For all changes:
Is there a JIRA ticket associated with this PR? Is it referenced
in the commit message?
Does your PR title start with NIFI-XXXX where XXXX is the JIRA number you are trying to resolve? Pay particular attention to the hyphen "-" character.
Has your PR been rebased against the latest commit within the target branch (typically
main)?Is your initial contribution a single, squashed commit? Additional commits in response to PR reviewer feedback should be made on this branch and pushed to allow change tracking. Do not
squashor use--forcewhen pushing to allow for clean monitoring of changes.For code changes:
mvn -Pcontrib-check clean installat the rootnififolder?LICENSEfile, including the mainLICENSEfile undernifi-assembly?NOTICEfile, including the mainNOTICEfile found undernifi-assembly?.displayNamein addition to .name (programmatic access) for each of the new properties?For documentation related changes:
Note:
Please ensure that once the PR is submitted, you check GitHub Actions CI for build issues and submit an update to your PR as soon as possible.