mtien-apache commented on a change in pull request #4767: URL: https://github.com/apache/nifi/pull/4767#discussion_r564115761
########## File path: nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpTwoWaySSL.java ########## @@ -51,4 +59,28 @@ public static void beforeClass() throws Exception { url = server.getSecureUrl(); } + @AfterClass + public static void afterClass() throws Exception { Review comment: @exceptionfactory The super class method does not delete the files, so this method is necessary. ########## File path: nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/util/TestInvokeHttpCommon.java ########## @@ -63,6 +56,12 @@ import org.junit.Assert; import org.junit.Test; +import static org.apache.commons.codec.binary.Base64.encodeBase64; Review comment: Oops, I must've accidentally optimized the imports. I'll change it back. ########## File path: nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java ########## @@ -366,4 +461,133 @@ public static String sslServerSocketToString(SSLServerSocket sslServerSocket) { .append("useClientMode", sslServerSocket.getUseClientMode()) .toString(); } + + /** + * 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 + * @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 { + + try (final FileOutputStream outputStream = new FileOutputStream(keyStorePath)) { + final KeyPair keyPair = KeyPairGenerator.getInstance(KEY_ALGORITHM).generateKeyPair(); + + final X509Certificate selfSignedCert = CertificateUtils.generateSelfSignedX509Certificate( + keyPair, CERT_DN, SIGNING_ALGORITHM, CERT_DURATION_DAYS + ); + + final KeyStore keyStore = loadEmptyKeyStore(keyStoreType); + keyStore.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new Certificate[]{selfSignedCert}); + keyStore.store(outputStream, keyStorePassword.toCharArray()); + + return selfSignedCert; + } + } + + /** + * Loads the Truststore with the given values. + * + * @param cert the certificate + * @param alias the certificate alias + * @param password the truststore password + * @param path the truststore path + * @param truststoreType the truststore type + */ + private static void createTrustStore(final X509Certificate cert, + final String alias, final String password, final String path, final KeystoreType truststoreType) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException { + + try (final FileOutputStream outputStream = new FileOutputStream(path)) { + final KeyStore trustStore = loadEmptyKeyStore(truststoreType); + trustStore.setCertificateEntry(alias, cert); + trustStore.store(outputStream, password.toCharArray()); + } catch (IOException e) { + throw new UncheckedIOException(TRUSTSTORE_ERROR_MSG, e); + } + } + + /** + * Generates a temporary keystore file and returns the path. + * + * @param keystoreType the Keystore type + * @return a Path + */ + private static Path generateTempKeystorePath(KeystoreType keystoreType) throws IOException { + return Files.createTempFile(TEST_KEYSTORE_PREFIX, getKeystoreExtension(keystoreType)); + } + + /** + * Generates a temporary truststore file and returns the path. + * + * @param truststoreType the Truststore type + * @return a Path + */ + private static Path generateTempTruststorePath(KeystoreType truststoreType) throws IOException { + return Files.createTempFile(TEST_TRUSTSTORE_PREFIX, getKeystoreExtension(truststoreType)); + } + + /** + * Loads and returns an empty Keystore backed by the appropriate provider. + * + * @param keyStoreType the keystore type + * @return an empty keystore + * @throws KeyStoreException if a keystore of the given type cannot be instantiated + */ + private static KeyStore loadEmptyKeyStore(KeystoreType keyStoreType) throws KeyStoreException, CertificateException, NoSuchAlgorithmException { + final KeyStore keyStore; + try { + keyStore = KeyStore.getInstance( + Objects.requireNonNull(getKeystoreType(keyStoreType.toString())) + .toString()); + keyStore.load(null, null); + return keyStore; + } catch (IOException e) { + logger.error("Encountered an error loading keystore: {}", e.getLocalizedMessage()); + throw new UncheckedIOException("Error loading keystore", e); + } + } + + /** + * Returns the Keystore type in the correct format given the Keystore type. + * + * @param keystoreType the keystore type as a String + * @return the keystore type + */ + private static KeystoreType getKeystoreType(String keystoreType) { + // if true Review comment: Removed. ########## File path: nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/KeyStoreUtils.java ########## @@ -366,4 +461,133 @@ public static String sslServerSocketToString(SSLServerSocket sslServerSocket) { .append("useClientMode", sslServerSocket.getUseClientMode()) .toString(); } + + /** + * 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 + * @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 { + + try (final FileOutputStream outputStream = new FileOutputStream(keyStorePath)) { + final KeyPair keyPair = KeyPairGenerator.getInstance(KEY_ALGORITHM).generateKeyPair(); + + final X509Certificate selfSignedCert = CertificateUtils.generateSelfSignedX509Certificate( + keyPair, CERT_DN, SIGNING_ALGORITHM, CERT_DURATION_DAYS + ); + + final KeyStore keyStore = loadEmptyKeyStore(keyStoreType); + keyStore.setKeyEntry(alias, keyPair.getPrivate(), keyPassword.toCharArray(), new Certificate[]{selfSignedCert}); + keyStore.store(outputStream, keyStorePassword.toCharArray()); + + return selfSignedCert; + } + } + + /** + * Loads the Truststore with the given values. + * + * @param cert the certificate + * @param alias the certificate alias + * @param password the truststore password + * @param path the truststore path + * @param truststoreType the truststore type + */ + private static void createTrustStore(final X509Certificate cert, + final String alias, final String password, final String path, final KeystoreType truststoreType) + throws KeyStoreException, NoSuchAlgorithmException, CertificateException { + + try (final FileOutputStream outputStream = new FileOutputStream(path)) { + final KeyStore trustStore = loadEmptyKeyStore(truststoreType); + trustStore.setCertificateEntry(alias, cert); + trustStore.store(outputStream, password.toCharArray()); + } catch (IOException e) { + throw new UncheckedIOException(TRUSTSTORE_ERROR_MSG, e); + } + } + + /** + * Generates a temporary keystore file and returns the path. + * + * @param keystoreType the Keystore type + * @return a Path + */ + private static Path generateTempKeystorePath(KeystoreType keystoreType) throws IOException { + return Files.createTempFile(TEST_KEYSTORE_PREFIX, getKeystoreExtension(keystoreType)); + } + + /** + * Generates a temporary truststore file and returns the path. + * + * @param truststoreType the Truststore type + * @return a Path + */ + private static Path generateTempTruststorePath(KeystoreType truststoreType) throws IOException { + return Files.createTempFile(TEST_TRUSTSTORE_PREFIX, getKeystoreExtension(truststoreType)); + } + + /** + * Loads and returns an empty Keystore backed by the appropriate provider. + * + * @param keyStoreType the keystore type + * @return an empty keystore + * @throws KeyStoreException if a keystore of the given type cannot be instantiated + */ + private static KeyStore loadEmptyKeyStore(KeystoreType keyStoreType) throws KeyStoreException, CertificateException, NoSuchAlgorithmException { + final KeyStore keyStore; + try { + keyStore = KeyStore.getInstance( + Objects.requireNonNull(getKeystoreType(keyStoreType.toString())) + .toString()); + keyStore.load(null, null); + return keyStore; + } catch (IOException e) { + logger.error("Encountered an error loading keystore: {}", e.getLocalizedMessage()); + throw new UncheckedIOException("Error loading keystore", e); + } + } + + /** + * Returns the Keystore type in the correct format given the Keystore type. + * + * @param keystoreType the keystore type as a String + * @return the keystore type + */ + private static KeystoreType getKeystoreType(String keystoreType) { + // if true + if (KeystoreType.isValidKeystoreType(keystoreType)) { + return KeystoreType.valueOf(keystoreType.toUpperCase()); + } else { + logger.debug("Invalid Keystore Type [{}]: Supported Types {}", keystoreType, Arrays.asList(KeystoreType.values())); Review comment: Warning makes sense. Changed. ########## File path: nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestInvokeHttpTwoWaySSL.java ########## @@ -28,15 +34,17 @@ */ public class TestInvokeHttpTwoWaySSL extends TestInvokeHttpSSL { - @BeforeClass public static void beforeClass() throws Exception { Assume.assumeTrue("Test only runs on *nix", !SystemUtils.IS_OS_WINDOWS); // useful for verbose logging output // don't commit this with this property enabled, or any 'mvn test' will be really verbose // System.setProperty("org.slf4j.simpleLogger.log.nifi.processors.standard", "debug"); - // create the SSL properties, which basically store keystore / trustore information + // create TLS configuration with a new keystore and truststore + tlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore(); Review comment: @exceptionfactory the super class does not create the TLS configuration, so I need to generate it from here. ########## File path: nifi-nar-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestListenHTTP.java ########## @@ -105,22 +101,45 @@ private int availablePort; @BeforeClass - public static void setUpSuite() { + public static void setUpSuite() throws IOException, GeneralSecurityException { Assume.assumeTrue("Test only runs on *nix", !SystemUtils.IS_OS_WINDOWS); + + clientTlsConfiguration = KeyStoreUtils.createTlsConfigAndNewKeystoreTruststore(); + + trustOnlyTlsConfiguration = new StandardTlsConfiguration( + null, null, null, null, + clientTlsConfiguration.getTruststorePath(), clientTlsConfiguration.getTruststorePassword(), + clientTlsConfiguration.getTruststoreType(), TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion()); + } + + @AfterClass + public static void afterClass() throws Exception { + if (clientTlsConfiguration != null) { + try { + if (StringUtils.isNotBlank(clientTlsConfiguration.getKeystorePath())) { + java.nio.file.Files.deleteIfExists(Paths.get(clientTlsConfiguration.getKeystorePath())); Review comment: @exceptionfactory For some reason, my IDE won't let me import the class and forcing me to use the qualified class name. Let me see what settings I can change to fix this. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org