From 7374361b5c6398a92577479d297e55dbe7ea2f63 Mon Sep 17 00:00:00 2001 From: Nathan Gough Date: Tue, 10 Mar 2020 21:22:29 +1100 Subject: [PATCH] NIFI-7223 - Fixed a minor issue where the OkHttpReplicationClient class loaded blank properties as empty string instead of an expected null value. Added a isNotBlank check. Added unit tests for replication client and HTTPNotificationService. NIFI-7223 - Renamed some variables and methods. NIFI-7223 - Removed unused dependency. Corrected security properties in administration-guide. --- nifi-bootstrap/pom.xml | 5 + .../http/TestHttpNotificationServiceSSL.java | 190 +++++++++++++++++- .../main/asciidoc/administration-guide.adoc | 5 +- .../okhttp/OkHttpReplicationClient.java | 11 +- .../okhttp/OkHttpReplicationClientTest.groovy | 75 +++++++ .../src/test/resources/conf/keystore.jks | Bin 0 -> 3088 bytes .../src/test/resources/conf/truststore.jks | Bin 0 -> 911 bytes 7 files changed, 273 insertions(+), 13 deletions(-) create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/resources/conf/keystore.jks create mode 100644 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/resources/conf/truststore.jks diff --git a/nifi-bootstrap/pom.xml b/nifi-bootstrap/pom.xml index c92a5fa100..d1bfb0998d 100644 --- a/nifi-bootstrap/pom.xml +++ b/nifi-bootstrap/pom.xml @@ -73,5 +73,10 @@ language governing permissions and limitations under the License. --> jna-platform 4.4.0 + + ch.qos.logback + logback-classic + test + diff --git a/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/TestHttpNotificationServiceSSL.java b/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/TestHttpNotificationServiceSSL.java index 5ab8cab496..2f54f1cec4 100644 --- a/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/TestHttpNotificationServiceSSL.java +++ b/nifi-bootstrap/src/test/java/org/apache/nifi/bootstrap/http/TestHttpNotificationServiceSSL.java @@ -16,13 +16,21 @@ */ package org.apache.nifi.bootstrap.http; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; import okhttp3.mockwebserver.MockWebServer; +import org.apache.nifi.bootstrap.NotificationServiceManager; import org.apache.nifi.security.util.SslContextFactory; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.mockito.internal.util.io.IOUtil; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; +import ch.qos.logback.classic.Logger; import javax.net.ssl.SSLContext; +import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; import java.nio.file.Files; @@ -32,11 +40,47 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.util.List; -public class TestHttpNotificationServiceSSL extends TestHttpNotificationServiceCommon{ +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +public class TestHttpNotificationServiceSSL extends TestHttpNotificationServiceCommon { static final String CONFIGURATION_FILE_TEXT = "\n"+ + "\n"+ + " \n"+ + " http-notification\n"+ + " org.apache.nifi.bootstrap.notification.http.HttpNotificationService\n"+ + " ${test.server}\n"+ + " ./src/test/resources/truststore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " ./src/test/resources/keystore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " passwordpassword\n"+ + " ${literal('testing')}\n"+ + " \n"+ + ""; + + static final String CONFIGURATION_FILE_TEXT_NO_KEYSTORE_PASSWORD = "\n"+ + "\n"+ + " \n"+ + " http-notification\n"+ + " org.apache.nifi.bootstrap.notification.http.HttpNotificationService\n"+ + " ${test.server}\n"+ + " ./src/test/resources/truststore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " ./src/test/resources/keystore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " ${literal('testing')}\n"+ + " \n"+ + ""; + + static final String CONFIGURATION_FILE_TEXT_NO_KEY_PASSWORD = "\n"+ "\n"+ " \n"+ " http-notification\n"+ @@ -52,9 +96,42 @@ public class TestHttpNotificationServiceSSL extends TestHttpNotificationService " \n"+ ""; + static final String CONFIGURATION_FILE_TEXT_BLANK_KEY_PASSWORD = "\n"+ + "\n"+ + " \n"+ + " http-notification\n"+ + " org.apache.nifi.bootstrap.notification.http.HttpNotificationService\n"+ + " ${test.server}\n"+ + " ./src/test/resources/truststore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " ./src/test/resources/keystore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " \n"+ + " ${literal('testing')}\n"+ + " \n"+ + ""; - @BeforeClass - public static void startServer() throws IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + static final String CONFIGURATION_FILE_TEXT_BLANK_KEYSTORE_PASSWORD = "\n"+ + "\n"+ + " \n"+ + " http-notification\n"+ + " org.apache.nifi.bootstrap.notification.http.HttpNotificationService\n"+ + " ${test.server}\n"+ + " ./src/test/resources/truststore.jks\n"+ + " JKS\n"+ + " passwordpassword\n"+ + " ./src/test/resources/keystore.jks\n"+ + " JKS\n"+ + " \n"+ + " passwordpassword\n"+ + " ${literal('testing')}\n"+ + " \n"+ + ""; + + @Before + public void startServer() throws IOException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException { tempConfigFilePath = "./target/TestHttpNotificationService-config.xml"; Files.deleteIfExists(Paths.get(tempConfigFilePath)); @@ -78,10 +155,109 @@ public class TestHttpNotificationServiceSSL extends TestHttpNotificationService IOUtil.writeText(configFileOutput, new File(tempConfigFilePath)); } - @AfterClass - public static void shutdownServer() throws IOException { + @After + public void shutdownServer() throws IOException { Files.deleteIfExists(Paths.get(tempConfigFilePath)); mockWebServer.shutdown(); } + @Test + public void testStartNotificationSucceedsNoKeystorePasswd() throws ParserConfigurationException, SAXException, IOException { + Logger notificationServiceLogger = (Logger) LoggerFactory.getLogger(NotificationServiceManager.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + notificationServiceLogger.addAppender(listAppender); + + String configFileOutput = CONFIGURATION_FILE_TEXT_NO_KEYSTORE_PASSWORD.replace("${test.server}", String.valueOf(mockWebServer.url("/"))); + IOUtil.writeText(configFileOutput, new File(tempConfigFilePath)); + + NotificationServiceManager notificationServiceManager = new NotificationServiceManager(); + notificationServiceManager.setMaxNotificationAttempts(1); + notificationServiceManager.loadNotificationServices(new File(tempConfigFilePath)); + + List logsList = listAppender.list; + boolean notificationServiceFailed = false; + for(ILoggingEvent logMessage : logsList) { + if(logMessage.getFormattedMessage().contains("is not valid for the following reasons")) { + notificationServiceFailed = true; + } + } + + assertFalse(notificationServiceFailed); + } + + @Test + public void testStartNotificationSucceedsNoKeyPasswd() throws ParserConfigurationException, SAXException, IOException { + Logger notificationServiceLogger = (Logger) LoggerFactory.getLogger(NotificationServiceManager.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + notificationServiceLogger.addAppender(listAppender); + + String configFileOutput = CONFIGURATION_FILE_TEXT_NO_KEY_PASSWORD.replace("${test.server}", String.valueOf(mockWebServer.url("/"))); + IOUtil.writeText(configFileOutput, new File(tempConfigFilePath)); + + NotificationServiceManager notificationServiceManager = new NotificationServiceManager(); + notificationServiceManager.setMaxNotificationAttempts(1); + notificationServiceManager.loadNotificationServices(new File(tempConfigFilePath)); + + List logsList = listAppender.list; + boolean notificationServiceFailed = false; + for(ILoggingEvent logMessage : logsList) { + if(logMessage.getFormattedMessage().contains("is not valid for the following reasons")) { + notificationServiceFailed = true; + } + } + + assertFalse(notificationServiceFailed); + } + + @Test + public void testStartNotificationFailsBlankKeystorePasswdCorrectKeypasswd() throws ParserConfigurationException, SAXException, IOException { + Logger notificationServiceLogger = (Logger) LoggerFactory.getLogger(NotificationServiceManager.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + notificationServiceLogger.addAppender(listAppender); + + String configFileOutput = CONFIGURATION_FILE_TEXT_BLANK_KEYSTORE_PASSWORD.replace("${test.server}", String.valueOf(mockWebServer.url("/"))); + IOUtil.writeText(configFileOutput, new File(tempConfigFilePath)); + + NotificationServiceManager notificationServiceManager = new NotificationServiceManager(); + notificationServiceManager.setMaxNotificationAttempts(1); + notificationServiceManager.loadNotificationServices(new File(tempConfigFilePath)); + + List logsList = listAppender.list; + boolean notificationServiceFailed = false; + for(ILoggingEvent logMessage : logsList) { + if(logMessage.getFormattedMessage().contains("'Keystore Password' validated against '' is invalid because Keystore Password cannot be empty")) { + notificationServiceFailed = true; + } + } + + assertTrue(notificationServiceFailed); + } + + @Test + public void testStartNotificationFailsCorrectKeystorePasswdBlankKeypasswd() throws ParserConfigurationException, SAXException, IOException { + Logger notificationServiceLogger = (Logger) LoggerFactory.getLogger(NotificationServiceManager.class); + ListAppender listAppender = new ListAppender<>(); + listAppender.start(); + notificationServiceLogger.addAppender(listAppender); + + String configFileOutput = CONFIGURATION_FILE_TEXT_BLANK_KEY_PASSWORD.replace("${test.server}", String.valueOf(mockWebServer.url("/"))); + IOUtil.writeText(configFileOutput, new File(tempConfigFilePath)); + + NotificationServiceManager notificationServiceManager = new NotificationServiceManager(); + notificationServiceManager.setMaxNotificationAttempts(1); + notificationServiceManager.loadNotificationServices(new File(tempConfigFilePath)); + + List logsList = listAppender.list; + boolean notificationServiceFailed = false; + for(ILoggingEvent logMessage : logsList) { + if(logMessage.getFormattedMessage().contains("'Key Password' validated against '' is invalid because Key Password cannot be empty")) { + notificationServiceFailed = true; + } + } + + assertTrue(notificationServiceFailed); + } } diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc b/nifi-docs/src/main/asciidoc/administration-guide.adoc index 5c90725396..c97aa896a5 100644 --- a/nifi-docs/src/main/asciidoc/administration-guide.adoc +++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc @@ -2252,8 +2252,9 @@ It has the following properties available: |`Truststore Type`||The Type of the Truststore. Either `JKS` or `PKCS12` |`Truststore Password`||The password for the Truststore |`Keystore Filename`||The fully-qualified filename of the Keystore -|`Keystore Type`||The password for the Keystore -|`Keystore Password`||The password for the key. If this is not specified, but the Keystore Filename, Password, and Type are specified, then the Keystore Password will be assumed to be the same as the Key Password. +|`Keystore Type`||The Type of the Keystore. Either `JKS` or `PKCS12` +|`Keystore Password`||The password for the Keystore +|`Key Password`||The password for the key. If this is not specified, but the Keystore Filename, Password, and Type are specified, then the Key Password will be assumed to be the same as the Keystore Password. |`SSL Protocol`||The algorithm to use for this SSL context. This can either be `SSL` or `TLS`. |==== diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java index 2352026d1d..ff63836681 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClient.java @@ -334,11 +334,11 @@ public class OkHttpReplicationClient implements HttpReplicationClient { try { Tuple sslContextTuple = createTrustSslContextWithTrustManagers( properties.getProperty(NiFiProperties.SECURITY_KEYSTORE), - properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD) != null ? properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray() : null, - properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD) != null ? properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD).toCharArray() : null, + StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD)) ? properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD).toCharArray() : null, + StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD)) ? properties.getProperty(NiFiProperties.SECURITY_KEY_PASSWD).toCharArray() : null, properties.getProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE), properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE), - properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD) != null ? properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray() : null, + StringUtils.isNotBlank(properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD)) ? properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD).toCharArray() : null, properties.getProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE), WANT, sslContext.getProtocol()); @@ -346,7 +346,10 @@ public class OkHttpReplicationClient implements HttpReplicationClient { .filter(trustManager -> trustManager instanceof X509TrustManager) .map(trustManager -> (X509TrustManager) trustManager).collect(Collectors.toList()); return new Tuple<>(sslContextTuple.getKey().getSocketFactory(), x509TrustManagers.get(0)); - } catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) { + } catch(UnrecoverableKeyException e) { + logger.error("Key password may be incorrect or not set. Check your keystore passwords." + e.getMessage()); + return null; + } catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | IOException e) { return null; } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy index cad27f182b..712f80a9eb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/groovy/org/apache/nifi/cluster/coordination/http/replication/okhttp/OkHttpReplicationClientTest.groovy @@ -26,6 +26,8 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.slf4j.Logger import org.slf4j.LoggerFactory +import sun.security.ssl.DummyX509KeyManager +import sun.security.ssl.SunX509KeyManagerImpl @RunWith(JUnit4.class) class OkHttpReplicationClientTest extends GroovyTestCase { @@ -135,4 +137,77 @@ class OkHttpReplicationClientTest extends GroovyTestCase { assert headers."Content-Length" == "123" } } + + @Test + void testShouldUseKeystorePasswdIfKeypasswdIsBlank() { + // Arrange + Map flowfileEncryptionProps = [ + (NiFiProperties.SECURITY_TRUSTSTORE): "./src/test/resources/conf/truststore.jks", + (NiFiProperties.SECURITY_TRUSTSTORE_TYPE): "JKS", + (NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword", + (NiFiProperties.SECURITY_KEYSTORE): "./src/test/resources/conf/keystore.jks", + (NiFiProperties.SECURITY_KEYSTORE_TYPE): "JKS", + (NiFiProperties.SECURITY_KEYSTORE_PASSWD): "passwordpassword", + (NiFiProperties.SECURITY_KEY_PASSWD): "", + (NiFiProperties.WEB_HTTPS_HOST): "localhost", + (NiFiProperties.WEB_HTTPS_PORT): "51552", + ] + NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(flowfileEncryptionProps)) + + // Act + OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties) + + // Assert + assertNotNull(client.okHttpClient.sslSocketFactory) + assertEquals(SunX509KeyManagerImpl.class, client.okHttpClient.sslSocketFactory.context.getX509KeyManager().getClass()) + assertNotNull(client.okHttpClient.sslSocketFactory.context.getX509KeyManager().credentialsMap["nifi-key"]) + } + + @Test + void testShouldFailIfKeyPasswdIsSetButKeystorePasswdIsBlank() { + // Arrange + Map flowfileEncryptionProps = [ + (NiFiProperties.SECURITY_TRUSTSTORE): "./src/test/resources/conf/truststore.jks", + (NiFiProperties.SECURITY_TRUSTSTORE_TYPE): "JKS", + (NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword", + (NiFiProperties.SECURITY_KEYSTORE): "./src/test/resources/conf/keystore.jks", + (NiFiProperties.SECURITY_KEYSTORE_TYPE): "JKS", + (NiFiProperties.SECURITY_KEYSTORE_PASSWD): "", + (NiFiProperties.SECURITY_KEY_PASSWD): "passwordpassword", + (NiFiProperties.WEB_HTTPS_HOST): "localhost", + (NiFiProperties.WEB_HTTPS_PORT): "51552", + ] + NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(flowfileEncryptionProps)) + + // Act + OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties) + + // Assert + // The replication client will fail to initialize if the keystore password is missing, and will use + // a default empty DummyX509KeyManager instead. This is considered a failure to start the service. + assertSame(DummyX509KeyManager.class, client.okHttpClient.sslSocketFactory.context.getX509KeyManager().getClass()) + } + + @Test + void testShouldFailIfKeyPasswdIsBlankAndKeystorePasswd() { + // Arrange + Map flowfileEncryptionProps = [ + (NiFiProperties.SECURITY_TRUSTSTORE): "./src/test/resources/conf/truststore.jks", + (NiFiProperties.SECURITY_TRUSTSTORE_TYPE): "JKS", + (NiFiProperties.SECURITY_TRUSTSTORE_PASSWD): "passwordpassword", + (NiFiProperties.SECURITY_KEYSTORE): "./src/test/resources/conf/keystore.jks", + (NiFiProperties.SECURITY_KEYSTORE_TYPE): "JKS", + (NiFiProperties.SECURITY_KEYSTORE_PASSWD): "", + (NiFiProperties.SECURITY_KEY_PASSWD): "", + (NiFiProperties.WEB_HTTPS_HOST): "localhost", + (NiFiProperties.WEB_HTTPS_PORT): "51552", + ] + NiFiProperties mockNiFiProperties = new StandardNiFiProperties(new Properties(flowfileEncryptionProps)) + + // Act + OkHttpReplicationClient client = new OkHttpReplicationClient(mockNiFiProperties) + + // Assert + assertEquals(DummyX509KeyManager.class, client.okHttpClient.sslSocketFactory.context.getX509KeyManager().getClass()) + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/resources/conf/keystore.jks b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/resources/conf/keystore.jks new file mode 100644 index 0000000000000000000000000000000000000000..246fe888efbb981188c9dcca109b5d2c52f2b435 GIT binary patch literal 3088 zcmb`|c{tSF9suz9%_a@TzDvU}%JBY1_7`DFP1&+#U$ZAATQ9;`LzV{F%6MfdJ87|v zE2AhhmLf7Oyo_p0MWJw2_jzyc^W6K~`^Wpod7kq<=RDu@$M<}`XY<|WI|u{<-5$XA zvE!7#kH1Qw_qpu_?~N4xO$Y=51D%~@XpW4zpN#`sJCe8iw3r=g#nrL- zc}e<${}VaCYCr4gf{ezT(R=D z-nlpW#qF(FTF*1$2O#WiubmF;Hrxmy6*igNRlfI9l~@uN!qhs~FEEGiMBlUNgZzPX(j)?E&4g~kNMFchr_X$68d zLt}BE%T`bPS@41`N-iwN`9RK`z@zznQIT?}DVA}T$?N6wuhG{w2?-ZIV8Z4AapH~> zM=w+HO5Fp%YPU&g%=71;K{*z_W9CqOV7L|boJ98Vakn zj}6I7pIDmnvrHDGH%#ao7SjJ1`5HddZo{Zee{!RGW`+H+BrM;}xmtv)l}OxJAZh8- zwT`Bb9j|;Yw_=HDi5fgOoK;}HBaAL{SGgCDyYgC&a%im9w+!Rh4OO{I+3JX2BP;2q`9{!Jli?d3(E_2u<*?$NuwN zAx~K%jw`&zdYQkM#CBOGN@Z)7Q<2%226`=Tiu8=+T0z;V zY7d2carE2qJ_kfpxZ~T^YKhz^<)mJ)c!*1P+p8$2xI%eB(zQC(9^*_&h|gtE3?{fC_^JA?rpHl7SEY8Zz?j!0+^9w##-C~jZ9C0k?>zlY?s;}AZqkO zbydnL6y`L;WU);zfw*aVg|DuDb_E^dVtr1h)_rk&z2RQwX?`5pO;@!2(%#Elv zUe?<*C&k;IN?al00~>7(D+{R{ic4NlN$k7d`Kle9?n&9GU&+1B-px6N;$i0b^mLdH z$(AH9DJAw4%+ES<-~96R++imu41fUT@mIn4Vo+wg1TuVZQMj;q`m}DIxpU)D>FgW# zCt@$))iSz4*>BtOaB)yHPFQ#tq@;HhsC0~}gtS`Jb~GUw{o7yR_5m~iY{B6$C~Otv z{uT?tp&;Z(Y6Z9`D2&{({aqpuTrlXLGvG&Rfp4kF|9${JO@A)o_WRi`ApkLd%?d`^ zwVRHvkb+>ylrv{t<84w-ewm=TU#?Km9_vmc&9~O%6%D7U^XiNP=!ZoKGTfS%Z}Nw) zH{x9_j=9q}jQmOY)74O+sC%~#z>AO)@0IaZlNPG}wW`Bg=)g&(tXNyq_!yt+OrN=~ zI($%TXQXiyc7nf@_hT*hgCy0khUrx^=WzesaX3$8g63@cB7^&p{McFHy1vdY==?h9 zr$i9-K5UdfLvmB==xlIx+U1VV&1Bu2@vaB97}@x`Igcs&i$SY;of|i20_+_wEhLNk z+=yMjh0Gcl{PF_zdC}bg6b%M;P1(`c(n3>q$pDAw=cgoWJ+UFRdnX}(x-;8$N7{Br z_{kkw;BRtA_^UgDhJ(-XdZNP~%U{gf7FCwHzJh|Rf_+h9s``swy%q}DLj3H6C zm&5jubG)kdY)^pIY2viya-LOlTTWE{Q#6J~$`{9ISBVxD5%5QXmr|5=0xtV6gXHrVgG0f?qugFkf{fs5ABY4YFOs`kuIZEzZ*rUX}v9X zN><>P7`!CrHo!+=rw-Pd*t`9=BZ6as?F1UlV0w}EQMEuBMHopBcmt14|g&sQmlf^%aznq zUn5%e{qm(@k9XT@{BRcw#{5+cunF?~P=f$r+me1V`5(#sPm|wG|95zQ?aSX?xh;m} z?PMiEdK%D&u3H6GI^rhnY*xAGv<;Iqj>{WtWW@EZletfeN9l*o^*{aEh^`J6S`PPx zb!lA+afuH^Y<;QMQN8n<1{6v(4GYP(NR)PaHRS}EU0EA{9Fv&6>jAFDwB;aj&{_Ji z%^1H5<93JiC5io0Rj&h9D-N^eGlyk6Ijv?(^|B%=o%YPcg*(0Cu1 literal 0 HcmV?d00001 diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/resources/conf/truststore.jks b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/test/resources/conf/truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..87f4be1cb74419252a6dd4a8cb8ed6063e7ade2f GIT binary patch literal 911 zcmezO_TO6u1_mY|W(3omd6{XMy2+_UB|wqXzwHA57+53pObsj<7?^zxnwUKenwSh1 zFf%bSF>!(9`2~U67z}vXIJMe5+P?ELGIFyr7$_U^8gR2Qhq7?`|G6{iVN=9$4_qR+voW1o`bX1kq_?@ zm7?Yu-O|hoDsa9N@MUVyrd<=O*InmV-0wL}LeXNm*vwgmI&)i2xoutU%O^gq+-#-h zgcU!vCxtetZirTB`cPrNOwe`BjfgY1S)#*|1p91Qo|habzwWRw{qs>-^K}Z`hO4)3 zSIUzY)UVZ%QmoRgXzEd9>P=X}&%!9YU~gQOux6db!Fj1Vv2}&_t)uD|R&4O>6_I{d zsCSIx%+#5m7tCg~+3?%uOvdTe*F$Rt zebbjM$oo^i)iz_A4D$trP*WymMh3>k^#-*De832h{z<6{wH5fM3WFu8l*4Bb6fbc?q7ui3Ma?czxTd62X+i-dt#19k;q{i-Zt1|n=6 z+H8!htnAE8a26wS6amu*Fp3x%;$M|c<$Br5aA-Zh`^uo~Wl`!k!$N+rAKm}gt?NVV zYn6)fmHTq0T%UdUsC(tf>wh-zugDkvQkKrV&wNr*Y-2X-|KIB@^>$R5GBG*(lon0! zZ;^?AbUT{i$=9D(FVwb7kUOEi+Gn#X+u0bI3n5nobT4k-BQr_T{`T_6SwEI~yi@Kw zFTeN0R^RvQOEeC9N z9oJ^^ymM}2j4=Bzl|5_bntl+HlHs5ImG#RU$C=4?=Kr%5`)8e9xTbMSRd_+e)hDMV z{$6UT4g6+eRVVNyVx3Chngx@VtPkMaA?X>r^jGNWKqZspMPe?;zuNh^J${uwG4xsu E0JdaNg#Z8m literal 0 HcmV?d00001