You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by th...@apache.org on 2022/10/18 03:56:42 UTC

[nifi] branch main updated: NIFI-10625 Added support for HTTP/2 in Registry

This is an automated email from the ASF dual-hosted git repository.

thenatog pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new b753c1c72d NIFI-10625 Added support for HTTP/2 in Registry
b753c1c72d is described below

commit b753c1c72d85c482bcc8bf125023107d79f54ed4
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Tue Oct 11 14:29:42 2022 -0500

    NIFI-10625 Added support for HTTP/2 in Registry
    
    - Added nifi-security-ssl for generalized SSLContext creation
    - Removed static keystore and truststore test files from nifi-registry-jetty
    
    Signed-off-by: Nathan Gough <th...@gmail.com>
    
    This closes #6514.
---
 nifi-commons/nifi-security-ssl/pom.xml             |  25 +++
 .../ssl/BuilderConfigurationException.java         |  41 ++++
 .../apache/nifi/security/ssl/KeyStoreBuilder.java  |  31 +++
 .../nifi/security/ssl/SslContextBuilder.java       |  31 +++
 .../nifi/security/ssl/StandardKeyStoreBuilder.java | 117 +++++++++++
 .../security/ssl/StandardSslContextBuilder.java    | 171 +++++++++++++++
 .../security/ssl/StandardKeyStoreBuilderTest.java  |  57 +++++
 .../ssl/StandardSslContextBuilderTest.java         |  78 +++++++
 nifi-commons/pom.xml                               |   1 +
 nifi-registry/nifi-registry-assembly/pom.xml       |   1 +
 .../src/main/asciidoc/administration-guide.adoc    |   7 +
 .../nifi-registry-core/nifi-registry-jetty/pom.xml |  29 +++
 .../apache/nifi/registry/jetty/JettyServer.java    | 164 ++-------------
 .../ApplicationServerConnectorFactory.java         | 231 +++++++++++++++++++++
 .../registry/jetty/JettyServerGroovyTest.groovy    | 165 ---------------
 .../ApplicationServerConnectorFactoryTest.java     | 146 +++++++++++++
 .../test/resources/keystoreDifferentPasswords.jks  | Bin 3128 -> 0 bytes
 .../src/test/resources/keystoreSamePassword.jks    | Bin 3128 -> 0 bytes
 .../src/test/resources/truststore.jks              | Bin 935 -> 0 bytes
 .../properties/NiFiRegistryProperties.java         |  12 ++
 .../main/resources/conf/nifi-registry.properties   |   1 +
 nifi-registry/pom.xml                              |  12 ++
 22 files changed, 1008 insertions(+), 312 deletions(-)

diff --git a/nifi-commons/nifi-security-ssl/pom.xml b/nifi-commons/nifi-security-ssl/pom.xml
new file mode 100644
index 0000000000..b8340373fa
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/pom.xml
@@ -0,0 +1,25 @@
+<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">
+    <!--
+      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.
+    -->
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-commons</artifactId>
+        <version>1.19.0-SNAPSHOT</version>
+    </parent>
+    <artifactId>nifi-security-ssl</artifactId>
+    <description>Shared TLS security components without additional dependencies</description>
+</project>
+
diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java
new file mode 100644
index 0000000000..74ad3a96e4
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java
@@ -0,0 +1,41 @@
+/*
+ * 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.ssl;
+
+/**
+ * Exception indicating runtime failure to create configured objects
+ */
+public class BuilderConfigurationException extends RuntimeException {
+    /**
+     * Builder Configuration Exception Constructor with standard properties
+     *
+     * @param message Exception Message
+     * @param cause Exception Cause
+     */
+    public BuilderConfigurationException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Builder Configuration Exception Constructor without Throwable cause
+     *
+     * @param message Exception Message
+     */
+    public BuilderConfigurationException(final String message) {
+        super(message);
+    }
+}
diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.java
new file mode 100644
index 0000000000..ecc2911b7c
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.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.ssl;
+
+import java.security.KeyStore;
+
+/**
+ * Builder interface for instances of java.security.KeyStore
+ */
+public interface KeyStoreBuilder {
+    /**
+     * Build Key Store based on configured properties
+     *
+     * @return Key Store
+     */
+    KeyStore build();
+}
diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.java
new file mode 100644
index 0000000000..620299ad76
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.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.ssl;
+
+import javax.net.ssl.SSLContext;
+
+/**
+ * Builder interface for instances of javax.net.ssl.SSLContext
+ */
+public interface SslContextBuilder {
+    /**
+     * Build SSLContext using configured properties
+     *
+     * @return SSLContext
+     */
+    SSLContext build();
+}
diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java
new file mode 100644
index 0000000000..2f5271bd51
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java
@@ -0,0 +1,117 @@
+/*
+ * 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.ssl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.util.Objects;
+
+/**
+ * Standard implementation of Key Store Builder
+ */
+public class StandardKeyStoreBuilder implements KeyStoreBuilder {
+    private String provider;
+
+    private String type = KeyStore.getDefaultType();
+
+    private InputStream inputStream;
+
+    private char[] password;
+
+    /**
+     * Build Key Store using configured properties
+     *
+     * @return Key Store
+     */
+    @Override
+    public KeyStore build() {
+        final KeyStore keyStore = getKeyStore();
+
+        if (inputStream == null) {
+            throw new BuilderConfigurationException("Key Store InputStream not configured");
+        }
+
+        try {
+            keyStore.load(inputStream, password);
+        } catch (final IOException|NoSuchAlgorithmException|CertificateException e) {
+            throw new BuilderConfigurationException("Key Store loading failed", e);
+        }
+
+        return keyStore;
+    }
+
+    /**
+     * Set Key Store Provider for Key Store implementation
+     *
+     * @param provider Key Store Provider
+     * @return Builder
+     */
+    public StandardKeyStoreBuilder provider(final String provider) {
+        this.provider = Objects.requireNonNull(provider, "Key Store Provider required");
+        return this;
+    }
+
+    /**
+     * Set Key Store Type defaults to platform configuration derived from KeyStore.getDefaultType()
+     *
+     * @param type Key Store Type
+     * @return Builder
+     */
+    public StandardKeyStoreBuilder type(final String type) {
+        this.type = Objects.requireNonNull(type, "Key Store Type required");
+        return this;
+    }
+
+    /**
+     * Set Key Store Password
+     *
+     * @param password Key Store Password
+     * @return Builder
+     */
+    public StandardKeyStoreBuilder password(final char[] password) {
+        this.password = Objects.requireNonNull(password, "Key Store Password required");
+        return this;
+    }
+
+    /**
+     * Set Key Store InputStream to be loaded
+     *
+     * @param inputStream Key Store InputStream
+     * @return Builder
+     */
+    public StandardKeyStoreBuilder inputStream(final InputStream inputStream) {
+        this.inputStream = Objects.requireNonNull(inputStream, "Key Store InputStream required");
+        return this;
+    }
+
+    private KeyStore getKeyStore() {
+        try {
+            return provider == null ? KeyStore.getInstance(type) : KeyStore.getInstance(type, provider);
+        } catch (final KeyStoreException e) {
+            final String message = String.format("Key Store Type [%s] creation failed", type);
+            throw new BuilderConfigurationException(message, e);
+        } catch (final NoSuchProviderException e) {
+            final String message = String.format("Key Store Type [%s] Provider [%s] creation failed", type, provider);
+            throw new BuilderConfigurationException(message, e);
+        }
+    }
+}
diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java
new file mode 100644
index 0000000000..9d82ea9c72
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java
@@ -0,0 +1,171 @@
+/*
+ * 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.ssl;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.Objects;
+
+/**
+ * Standard implementation of SSL Context Builder
+ */
+public class StandardSslContextBuilder implements SslContextBuilder {
+    private static final String DEFAULT_PROTOCOL = "TLS";
+
+    private String protocol = DEFAULT_PROTOCOL;
+
+    private KeyStore keyStore;
+
+    private char[] keyPassword;
+
+    private KeyStore trustStore;
+
+    /**
+     * Build and initialize an SSL Context using configured Key Manager and Trust Manager sources
+     *
+     * @return SSL Context
+     */
+    @Override
+    public SSLContext build() {
+        final SSLContext sslContext = getSslContext();
+        final SecureRandom secureRandom = new SecureRandom();
+        final KeyManager[] keyManagers = getKeyManagers();
+        final TrustManager[] trustManagers = getTrustManagers();
+
+        try {
+            sslContext.init(keyManagers, trustManagers, secureRandom);
+        } catch (final KeyManagementException e) {
+            throw new BuilderConfigurationException("SSLContext initialization failed", e);
+        }
+
+        return sslContext;
+    }
+
+    /**
+     * Set TLS Protocol defaults to TLS without a specific version number
+     *
+     * @param protocol TLS Protocol
+     * @return Builder
+     */
+    public StandardSslContextBuilder protocol(final String protocol) {
+        this.protocol = Objects.requireNonNull(protocol, "Protocol required");
+        return this;
+    }
+
+    /**
+     * Set Key Store with Private Key and Certificate Entry
+     *
+     * @param keyStore Key Store
+     * @return Builder
+     */
+    public StandardSslContextBuilder keyStore(final KeyStore keyStore) {
+        this.keyStore = Objects.requireNonNull(keyStore, "Key Store required");
+        return this;
+    }
+
+    /**
+     * Set Key Password for reading Private Key entries from Key Store
+     *
+     * @param keyPassword Key Password
+     * @return Builder
+     */
+    public StandardSslContextBuilder keyPassword(final char[] keyPassword) {
+        this.keyPassword = Objects.requireNonNull(keyPassword, "Key Password required");
+        return this;
+    }
+
+    /**
+     * Set Trust Store with Certificate Entries
+     *
+     * @param trustStore Trust Store
+     * @return Builder
+     */
+    public StandardSslContextBuilder trustStore(final KeyStore trustStore) {
+        this.trustStore = Objects.requireNonNull(trustStore, "Trust Store required");
+        return this;
+    }
+
+    private KeyManager[] getKeyManagers() {
+        final KeyManager[] keyManagers;
+        if (keyStore == null) {
+            keyManagers = null;
+        } else {
+            final KeyManagerFactory keyManagerFactory = getKeyManagerFactory();
+            try {
+                keyManagerFactory.init(keyStore, keyPassword);
+            } catch (final KeyStoreException|NoSuchAlgorithmException|UnrecoverableKeyException e) {
+                throw new BuilderConfigurationException("Key Manager initialization failed", e);
+            }
+            keyManagers = keyManagerFactory.getKeyManagers();
+        }
+        return keyManagers;
+    }
+
+    private TrustManager[] getTrustManagers() {
+        final TrustManager[] trustManagers;
+        if (trustStore == null) {
+            trustManagers = null;
+        } else {
+            final TrustManagerFactory trustManagerFactory = getTrustManagerFactory();
+            try {
+                trustManagerFactory.init(trustStore);
+            } catch (final KeyStoreException e) {
+                throw new BuilderConfigurationException("Trust Manager initialization failed", e);
+            }
+            trustManagers = trustManagerFactory.getTrustManagers();
+        }
+        return trustManagers;
+    }
+
+    private KeyManagerFactory getKeyManagerFactory() {
+        final String algorithm = KeyManagerFactory.getDefaultAlgorithm();
+        try {
+            return KeyManagerFactory.getInstance(algorithm);
+        } catch (final NoSuchAlgorithmException e) {
+            final String message = String.format("KeyManagerFactory creation failed with algorithm [%s]", algorithm);
+            throw new BuilderConfigurationException(message, e);
+        }
+    }
+
+    private TrustManagerFactory getTrustManagerFactory() {
+        final String algorithm = TrustManagerFactory.getDefaultAlgorithm();
+        try {
+            return TrustManagerFactory.getInstance(algorithm);
+        } catch (final NoSuchAlgorithmException e) {
+            final String message = String.format("TrustManagerFactory creation failed with algorithm [%s]", algorithm);
+            throw new BuilderConfigurationException(message, e);
+        }
+    }
+
+    private SSLContext getSslContext() {
+        try {
+            return SSLContext.getInstance(protocol);
+        } catch (final NoSuchAlgorithmException e) {
+            final String message = String.format("SSLContext creation failed with protocol [%s]", protocol);
+            throw new BuilderConfigurationException(message, e);
+        }
+    }
+}
diff --git a/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java
new file mode 100644
index 0000000000..f18be8ebe1
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ssl;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.KeyStore;
+import java.util.UUID;
+
+import static java.nio.file.Files.createTempFile;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+class StandardKeyStoreBuilderTest {
+
+    private static final String TYPE = KeyStore.getDefaultType();
+
+    private static final char[] PASSWORD = UUID.randomUUID().toString().toCharArray();
+
+    @Test
+    void testBuild() throws Exception {
+        final Path path = createTempFile(StandardKeyStoreBuilderTest.class.getSimpleName(), TYPE);
+        path.toFile().deleteOnExit();
+
+        final KeyStore sourceKeyStore = KeyStore.getInstance(TYPE);
+        sourceKeyStore.load(null);
+        try (final OutputStream outputStream = Files.newOutputStream(path)) {
+            sourceKeyStore.store(outputStream, PASSWORD);
+        }
+
+        final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
+        builder.type(TYPE);
+        builder.password(PASSWORD);
+        try (final InputStream inputStream = Files.newInputStream(path)) {
+            builder.inputStream(inputStream);
+            final KeyStore keyStore = builder.build();
+            assertNotNull(keyStore);
+        }
+    }
+}
diff --git a/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.java b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.java
new file mode 100644
index 0000000000..ea00afcb84
--- /dev/null
+++ b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.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.ssl;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import javax.net.ssl.SSLContext;
+
+import java.security.KeyStore;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+@ExtendWith(MockitoExtension.class)
+class StandardSslContextBuilderTest {
+    private static final String TLS_PROTOCOL = "TLS";
+
+    @Mock
+    KeyStore trustStore;
+
+    @Test
+    void testBuild() {
+        final StandardSslContextBuilder builder = new StandardSslContextBuilder();
+
+        final SSLContext sslContext = builder.build();
+
+        assertNotNull(sslContext);
+    }
+
+    @Test
+    void testBuildProtocol() {
+        final StandardSslContextBuilder builder = new StandardSslContextBuilder();
+        builder.protocol(TLS_PROTOCOL);
+
+        final SSLContext sslContext = builder.build();
+
+        assertNotNull(sslContext);
+    }
+
+    @Test
+    void testBuildKeyStore() throws Exception {
+        final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        keyStore.load(null);
+
+        final StandardSslContextBuilder builder = new StandardSslContextBuilder();
+        builder.keyStore(keyStore);
+
+        final SSLContext sslContext = builder.build();
+
+        assertNotNull(sslContext);
+    }
+
+    @Test
+    void testBuildTrustStore() {
+        final StandardSslContextBuilder builder = new StandardSslContextBuilder();
+        builder.trustStore(trustStore);
+
+        final SSLContext sslContext = builder.build();
+
+        assertNotNull(sslContext);
+    }
+}
diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml
index 07769c4abe..5d4cebe621 100644
--- a/nifi-commons/pom.xml
+++ b/nifi-commons/pom.xml
@@ -57,6 +57,7 @@
         <module>nifi-security-kerberos</module>
         <module>nifi-security-kms</module>
         <module>nifi-security-socket-ssl</module>
+        <module>nifi-security-ssl</module>
         <module>nifi-security-utils-api</module>
         <module>nifi-security-utils</module>
         <module>nifi-single-user-utils</module>
diff --git a/nifi-registry/nifi-registry-assembly/pom.xml b/nifi-registry/nifi-registry-assembly/pom.xml
index ea27204bb3..def70a61f9 100644
--- a/nifi-registry/nifi-registry-assembly/pom.xml
+++ b/nifi-registry/nifi-registry-assembly/pom.xml
@@ -165,6 +165,7 @@
         <nifi.registry.web.http.port>18080</nifi.registry.web.http.port>
         <nifi.registry.web.https.host />
         <nifi.registry.web.https.port />
+        <nifi.registry.web.https.application.protocols>http/1.1</nifi.registry.web.https.application.protocols>
         <nifi.registry.jetty.work.dir>./work/jetty</nifi.registry.jetty.work.dir>
         <nifi.registry.web.jetty.threads>200</nifi.registry.web.jetty.threads>
         <nifi.registry.web.should.send.server.version>true</nifi.registry.web.should.send.server.version>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc b/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc
index c14e86b242..86e37357bc 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc
@@ -1013,6 +1013,13 @@ These properties pertain to the web-based User Interface.
 |`nifi.registry.web.http.port`|The HTTP port. The default value is `18080`.
 |`nifi.registry.web.https.host`|The HTTPS host. It is blank by default.
 |`nifi.registry.web.https.port`|The HTTPS port. It is blank by default. When configuring NiFi Registry to run securely, this port should be configured.
+|`nifi.registry.web.https.application.protocols`|The space-separated list of application protocols supported when running with HTTPS enabled.
+
+The default value is `http/1.1`.
+
+The value can be set to `h2 http/1.1` to support Application Layer Protocol Negotiation (ALPN) for HTTP/2 or HTTP/1.1 based on client capabilities.
+
+The value can be set to `h2` to require HTTP/2 and disable HTTP/1.1.
 |`nifi.registry.web.jetty.working.directory`|The location of the Jetty working directory. The default value is `./work/jetty`.
 |`nifi.registry.web.jetty.threads`|The number of Jetty threads. The default value is `200`.
 |====
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml b/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml
index 694e6ad348..60c2dbeb7b 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml
+++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml
@@ -28,6 +28,29 @@
             <artifactId>nifi-registry-properties</artifactId>
             <version>1.19.0-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-jetty-configuration</artifactId>
+            <version>1.19.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-ssl</artifactId>
+            <version>1.19.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils-api</artifactId>
+            <version>1.19.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty.http2</groupId>
+            <artifactId>http2-server</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse.jetty</groupId>
+            <artifactId>jetty-alpn-server</artifactId>
+        </dependency>
         <dependency>
             <groupId>org.eclipse.jetty</groupId>
             <artifactId>jetty-server</artifactId>
@@ -62,5 +85,11 @@
             <artifactId>apache-jstl</artifactId>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils</artifactId>
+            <version>1.19.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java
index 6d8b47ce1a..f868a39270 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java
@@ -17,6 +17,8 @@
 package org.apache.nifi.registry.jetty;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.jetty.configuration.connector.ServerConnectorFactory;
+import org.apache.nifi.registry.jetty.connector.ApplicationServerConnectorFactory;
 import org.apache.nifi.registry.jetty.headers.ContentSecurityPolicyFilter;
 import org.apache.nifi.registry.jetty.headers.StrictTransportSecurityFilter;
 import org.apache.nifi.registry.jetty.headers.XFrameOptionsFilter;
@@ -26,17 +28,12 @@ import org.apache.nifi.registry.security.crypto.CryptoKeyProvider;
 import org.eclipse.jetty.annotations.AnnotationConfiguration;
 import org.eclipse.jetty.server.Connector;
 import org.eclipse.jetty.server.Handler;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpConnectionFactory;
-import org.eclipse.jetty.server.SecureRequestCustomizer;
 import org.eclipse.jetty.server.Server;
 import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.server.SslConnectionFactory;
 import org.eclipse.jetty.server.handler.HandlerCollection;
 import org.eclipse.jetty.servlet.DefaultServlet;
 import org.eclipse.jetty.servlet.FilterHolder;
 import org.eclipse.jetty.servlet.ServletHolder;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
 import org.eclipse.jetty.util.thread.QueuedThreadPool;
 import org.eclipse.jetty.webapp.Configuration;
 import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
@@ -72,16 +69,12 @@ public class JettyServer {
 
     private static final Logger logger = LoggerFactory.getLogger(JettyServer.class);
     private static final String WEB_DEFAULTS_XML = "org/apache/nifi-registry/web/webdefault.xml";
-    private static final int HEADER_BUFFER_SIZE = 16 * 1024; // 16kb
-    private static final String CIPHER_SUITE_SEPARATOR_PATTERN = ",\\s*";
 
+    private static final String ALL_PATHS = "/*";
 
-    private static final FileFilter WAR_FILTER = new FileFilter() {
-        @Override
-        public boolean accept(File pathname) {
-            final String nameToTest = pathname.getName().toLowerCase();
-            return nameToTest.endsWith(".war") && pathname.isFile();
-        }
+    private static final FileFilter WAR_FILTER = pathname -> {
+        final String nameToTest = pathname.getName().toLowerCase();
+        return nameToTest.endsWith(".war") && pathname.isFile();
     };
 
     private final NiFiRegistryProperties properties;
@@ -89,9 +82,7 @@ public class JettyServer {
     private final String docsLocation;
     private final Server server;
 
-    private WebAppContext webUiContext;
     private WebAppContext webApiContext;
-    private WebAppContext webDocsContext;
 
     public JettyServer(final NiFiRegistryProperties properties, final CryptoKeyProvider cryptoKeyProvider, final String docsLocation) {
         final QueuedThreadPool threadPool = new QueuedThreadPool(properties.getWebThreads());
@@ -156,128 +147,9 @@ public class JettyServer {
     }
 
     private void configureConnectors() {
-        // create the http configuration
-        final HttpConfiguration httpConfiguration = new HttpConfiguration();
-        httpConfiguration.setRequestHeaderSize(HEADER_BUFFER_SIZE);
-        httpConfiguration.setResponseHeaderSize(HEADER_BUFFER_SIZE);
-        httpConfiguration.setSendServerVersion(properties.shouldSendServerVersion());
-
-        if (properties.getPort() != null) {
-            final Integer port = properties.getPort();
-            if (port < 0 || (int) Math.pow(2, 16) <= port) {
-                throw new IllegalStateException("Invalid HTTP port: " + port);
-            }
-
-            logger.info("Configuring Jetty for HTTP on port: " + port);
-
-            // create the connector
-            final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration));
-
-            // set host and port
-            if (StringUtils.isNotBlank(properties.getHttpHost())) {
-                http.setHost(properties.getHttpHost());
-            }
-            http.setPort(port);
-
-            // add this connector
-            server.addConnector(http);
-        } else if (properties.getSslPort() != null) {
-            final Integer port = properties.getSslPort();
-            if (port < 0 || (int) Math.pow(2, 16) <= port) {
-                throw new IllegalStateException("Invalid HTTPs port: " + port);
-            }
-
-            if (StringUtils.isBlank(properties.getKeyStorePath())) {
-                throw new IllegalStateException(NiFiRegistryProperties.SECURITY_KEYSTORE
-                        + " must be provided to configure Jetty for HTTPs");
-            }
-
-            logger.info("Configuring Jetty for HTTPs on port: " + port);
-
-            // add some secure config
-            final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration);
-            httpsConfiguration.setSecureScheme("https");
-            httpsConfiguration.setSecurePort(properties.getSslPort());
-            httpsConfiguration.addCustomizer(new SecureRequestCustomizer());
-
-            // build the connector
-            final ServerConnector https = new ServerConnector(server,
-                    new SslConnectionFactory(createSslContextFactory(), "http/1.1"),
-                    new HttpConnectionFactory(httpsConfiguration));
-
-            // set host and port
-            if (StringUtils.isNotBlank(properties.getHttpsHost())) {
-                https.setHost(properties.getHttpsHost());
-            }
-            https.setPort(port);
-
-            // add this connector
-            server.addConnector(https);
-        }
-    }
-
-    private static String[] getCipherSuites(final String cipherSuitesProperty) {
-        return cipherSuitesProperty.split(CIPHER_SUITE_SEPARATOR_PATTERN);
-    }
-
-    private SslContextFactory createSslContextFactory() {
-        final SslContextFactory.Server contextFactory = new SslContextFactory.Server();
-
-        // if needClientAuth is false then set want to true so we can optionally use certs
-        if (properties.getNeedClientAuth()) {
-            logger.info("Setting Jetty's SSLContextFactory needClientAuth to true");
-            contextFactory.setNeedClientAuth(true);
-        } else {
-            logger.info("Setting Jetty's SSLContextFactory wantClientAuth to true");
-            contextFactory.setWantClientAuth(true);
-        }
-
-        /* below code sets JSSE system properties when values are provided */
-        // keystore properties
-        if (StringUtils.isNotBlank(properties.getKeyStorePath())) {
-            contextFactory.setKeyStorePath(properties.getKeyStorePath());
-        }
-        if (StringUtils.isNotBlank(properties.getKeyStoreType())) {
-            contextFactory.setKeyStoreType(properties.getKeyStoreType());
-        }
-
-
-        final String keystorePassword = properties.getKeyStorePassword();
-        final String keyPassword = properties.getKeyPassword();
-
-        if (StringUtils.isEmpty(keystorePassword)) {
-            throw new IllegalArgumentException("The keystore password cannot be null or empty");
-        } else {
-            // if no key password was provided, then assume the key password is the same as the keystore password.
-            final String defaultKeyPassword = (StringUtils.isBlank(keyPassword)) ? keystorePassword : keyPassword;
-            contextFactory.setKeyStorePassword(keystorePassword);
-            contextFactory.setKeyManagerPassword(defaultKeyPassword);
-        }
-
-        // truststore properties
-        if (StringUtils.isNotBlank(properties.getTrustStorePath())) {
-            contextFactory.setTrustStorePath(properties.getTrustStorePath());
-        }
-        if (StringUtils.isNotBlank(properties.getTrustStoreType())) {
-            contextFactory.setTrustStoreType(properties.getTrustStoreType());
-        }
-        if (StringUtils.isNotBlank(properties.getTrustStorePassword())) {
-            contextFactory.setTrustStorePassword(properties.getTrustStorePassword());
-        }
-
-        final String includeCipherSuites = properties.getHttpsCipherSuitesInclude();
-        if (StringUtils.isNotBlank(includeCipherSuites)) {
-            final String[] cipherSuites = getCipherSuites(includeCipherSuites);
-            contextFactory.setIncludeCipherSuites(cipherSuites);
-        }
-
-        final String excludeCipherSuites = properties.getHttpsCipherSuitesExclude();
-        if (StringUtils.isNotBlank(excludeCipherSuites)) {
-            final String[] cipherSuites = getCipherSuites(excludeCipherSuites);
-            contextFactory.setExcludeCipherSuites(cipherSuites);
-        }
-
-        return contextFactory;
+        final ServerConnectorFactory serverConnectorFactory = new ApplicationServerConnectorFactory(server, properties);
+        final ServerConnector serverConnector = serverConnectorFactory.getServerConnector();
+        server.addConnector(serverConnector);
     }
 
     private void loadWars() throws IOException {
@@ -309,7 +181,7 @@ public class JettyServer {
             throw new IllegalStateException("Unable to locate NiFi Registry Web Docs");
         }
 
-        webUiContext = loadWar(webUiWar, "/nifi-registry");
+        WebAppContext webUiContext = loadWar(webUiWar, "/nifi-registry");
         webUiContext.getInitParams().put("oidc-supported", String.valueOf(properties.isOidcEnabled()));
 
         webApiContext = loadWar(webApiWar, "/nifi-registry-api", getWebApiAdditionalClasspath());
@@ -322,7 +194,7 @@ public class JettyServer {
         webApiContext.setAttribute("org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern", ".*/spring-[^/]*\\.jar$");
 
         final String docsContextPath = "/nifi-registry-docs";
-        webDocsContext = loadWar(webDocsWar, docsContextPath);
+        WebAppContext webDocsContext = loadWar(webDocsWar, docsContextPath);
         addDocsServlets(webDocsContext);
 
         final HandlerCollection handlers = new HandlerCollection();
@@ -371,20 +243,18 @@ public class JettyServer {
         webappContext.setMaxFormContentSize(600000);
 
         // add HTTP security headers to all responses
-        final String ALL_PATHS = "/*";
         ArrayList<Class<? extends Filter>> filters = new ArrayList<>(Arrays.asList(XFrameOptionsFilter.class, ContentSecurityPolicyFilter.class, XSSProtectionFilter.class));
-        if(properties.isHTTPSConfigured()) {
+        if (properties.isHTTPSConfigured()) {
             filters.add(StrictTransportSecurityFilter.class);
         }
 
-        filters.forEach( (filter) -> addFilters(filter, ALL_PATHS, webappContext));
+        filters.forEach( (filter) -> addFilters(filter, webappContext));
 
         // start out assuming the system ClassLoader will be the parent, but if additional resources were specified then
         // inject a new ClassLoader in between the system and webapp ClassLoaders that contains the additional resources
         ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader();
         if (additionalResources != null && additionalResources.length > 0) {
-            URLClassLoader additionalClassLoader = new URLClassLoader(additionalResources, ClassLoader.getSystemClassLoader());
-            parentClassLoader = additionalClassLoader;
+            parentClassLoader = new URLClassLoader(additionalResources, ClassLoader.getSystemClassLoader());
         }
 
         webappContext.setClassLoader(new WebAppClassLoader(parentClassLoader, webappContext));
@@ -393,10 +263,10 @@ public class JettyServer {
         return webappContext;
     }
 
-    private void addFilters(Class<? extends Filter> clazz, String path, WebAppContext webappContext) {
+    private void addFilters(Class<? extends Filter> clazz, final WebAppContext webappContext) {
         FilterHolder holder = new FilterHolder(clazz);
         holder.setName(clazz.getSimpleName());
-        webappContext.addFilter(holder, path, EnumSet.allOf(DispatcherType.class));
+        webappContext.addFilter(holder, ALL_PATHS, EnumSet.allOf(DispatcherType.class));
     }
 
     private URL[] getWebApiAdditionalClasspath() {
@@ -451,7 +321,7 @@ public class JettyServer {
             logger.info("]");
         }
 
-        return resources.toArray(new URL[resources.size()]);
+        return resources.toArray(new URL[0]);
     }
 
     private void addDocsServlets(WebAppContext docsContext) {
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java
new file mode 100644
index 0000000000..640c3cd96c
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java
@@ -0,0 +1,231 @@
+/*
+ * 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.registry.jetty.connector;
+
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.jetty.configuration.connector.ApplicationLayerProtocol;
+import org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory;
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
+import org.apache.nifi.security.ssl.StandardSslContextBuilder;
+import org.apache.nifi.security.util.TlsPlatform;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Registry Application extension of Standard Jetty Server Connector Factory
+ */
+public class ApplicationServerConnectorFactory extends StandardServerConnectorFactory {
+
+    private static final int HEADER_SIZE = 16384;
+
+    private static final String CIPHER_SUITE_SEPARATOR_PATTERN = ",\\s*";
+
+    private final String includeCipherSuites;
+
+    private final String excludeCipherSuites;
+
+    private final String host;
+
+    private SslContextFactory.Server sslContextFactory;
+
+    public ApplicationServerConnectorFactory(final Server server, final NiFiRegistryProperties properties) {
+        super(server, getPort(properties));
+
+        host = getHost(properties);
+        includeCipherSuites = properties.getHttpsCipherSuitesInclude();
+        excludeCipherSuites = properties.getHttpsCipherSuitesExclude();
+
+        if (properties.isHTTPSConfigured()) {
+            if (properties.getNeedClientAuth()) {
+                setNeedClientAuth(true);
+            } else {
+                setWantClientAuth(true);
+            }
+
+            final SSLContext sslContext = buildSslContext(properties);
+            setSslContext(sslContext);
+
+            setApplicationLayerProtocols(properties);
+
+            // Set Transport Layer Security Protocols based on platform configuration
+            setIncludeSecurityProtocols(TlsPlatform.getPreferredProtocols().toArray(new String[0]));
+        }
+    }
+
+    /**
+     * Get Server Connector using configured properties
+     *
+     * @return Server Connector
+     */
+    @Override
+    public ServerConnector getServerConnector() {
+        final ServerConnector serverConnector = super.getServerConnector();
+        serverConnector.setHost(host);
+        return serverConnector;
+    }
+
+    /**
+     * Get Jetty Server SSL Context Factory and reuse the same instance for multiple invocations
+     *
+     * @return Jetty Server SSL Context Factory
+     */
+    @Override
+    protected SslContextFactory.Server getSslContextFactory() {
+        if (sslContextFactory == null) {
+            sslContextFactory = super.getSslContextFactory();
+
+            if (StringUtils.isNotBlank(includeCipherSuites)) {
+                final String[] cipherSuites = getCipherSuites(includeCipherSuites);
+                sslContextFactory.setIncludeCipherSuites(cipherSuites);
+            }
+            if (StringUtils.isNotBlank(excludeCipherSuites)) {
+                final String[] cipherSuites = getCipherSuites(excludeCipherSuites);
+                sslContextFactory.setExcludeCipherSuites(cipherSuites);
+            }
+        }
+
+        return sslContextFactory;
+    }
+
+    /**
+     * Get HTTP Configuration with additional settings
+     *
+     * @return HTTP Configuration
+     */
+    @Override
+    protected HttpConfiguration getHttpConfiguration() {
+        final HttpConfiguration httpConfiguration = super.getHttpConfiguration();
+
+        httpConfiguration.setRequestHeaderSize(HEADER_SIZE);
+        httpConfiguration.setResponseHeaderSize(HEADER_SIZE);
+
+        return httpConfiguration;
+    }
+
+    private String[] getCipherSuites(final String cipherSuitesProperty) {
+        return cipherSuitesProperty.split(CIPHER_SUITE_SEPARATOR_PATTERN);
+    }
+
+    private SSLContext buildSslContext(final NiFiRegistryProperties properties) {
+        final KeyStore keyStore = buildKeyStore(properties);
+        final char[] keyPassword = getKeyPassword(properties);
+        final KeyStore trustStore = buildTrustStore(properties);
+
+        return new StandardSslContextBuilder()
+                .keyStore(keyStore)
+                .keyPassword(keyPassword)
+                .trustStore(trustStore)
+                .build();
+    }
+
+    private char[] getKeyPassword(final NiFiRegistryProperties properties) {
+        final String keyStorePassword = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD);
+        final String keyPassword = properties.getProperty(NiFiRegistryProperties.SECURITY_KEY_PASSWD, keyStorePassword);
+        return keyPassword.toCharArray();
+    }
+
+    private KeyStore buildKeyStore(final NiFiRegistryProperties properties) {
+        final String keyStore = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE);
+        final String keyStoreType = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE);
+        final String keyStorePassword = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD);
+        return buildStore(keyStore, keyStoreType, keyStorePassword);
+    }
+
+    private KeyStore buildTrustStore(final NiFiRegistryProperties properties) {
+        final String trustStore = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_TRUSTSTORE);
+        final String trustStoreType = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE);
+        final String trustStorePassword = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD);
+        return buildStore(trustStore, trustStoreType, trustStorePassword);
+    }
+
+    private KeyStore buildStore(
+            final String store,
+            final String storeType,
+            final String storePassword
+    ) {
+        final Path trustStorePath = Paths.get(store);
+        try (final InputStream inputStream = Files.newInputStream(trustStorePath)) {
+            return new StandardKeyStoreBuilder()
+                    .type(storeType)
+                    .password(storePassword.toCharArray())
+                    .inputStream(inputStream)
+                    .build();
+        } catch (final IOException e) {
+            final String message = String.format("Store Path [%s] read failed", store);
+            throw new IllegalStateException(message, e);
+        }
+    }
+
+    private String getRequiredProperty(final NiFiRegistryProperties properties, final String property) {
+        final String requiredProperty = properties.getProperty(property);
+        if (requiredProperty == null || requiredProperty.isEmpty()) {
+            throw new IllegalStateException(String.format("Required Property [%s] not configured", property));
+        }
+        return requiredProperty;
+    }
+
+    private void setApplicationLayerProtocols(final NiFiRegistryProperties properties) {
+        final Set<String> protocols = properties.getWebHttpsApplicationProtocols();
+
+        final Set<ApplicationLayerProtocol> applicationLayerProtocols = Arrays.stream(ApplicationLayerProtocol.values())
+                .filter(
+                        applicationLayerProtocol -> protocols.contains(applicationLayerProtocol.getProtocol())
+                )
+                .collect(Collectors.toSet());
+        setApplicationLayerProtocols(applicationLayerProtocols);
+    }
+
+    private static String getHost(final NiFiRegistryProperties properties) {
+        final String host;
+
+        if (properties.isHTTPSConfigured()) {
+            host = properties.getHttpsHost();
+        } else {
+            host = properties.getHttpHost();
+        }
+
+        return host;
+    }
+
+    private static int getPort(final NiFiRegistryProperties properties) {
+        final Integer httpsPort = properties.getSslPort();
+        final Integer httpPort = properties.getPort();
+
+        if (ObjectUtils.allNull(httpsPort, httpPort)) {
+            throw new IllegalStateException("Invalid port configuration: Neither nifi.registry.web.https.port nor nifi.registry.web.http.port specified");
+        } else if (ObjectUtils.allNotNull(httpsPort, httpPort)) {
+            throw new IllegalStateException("Invalid port configuration: Both nifi.registry.web.https.port and nifi.registry.web.http.port specified");
+        }
+
+        return ObjectUtils.defaultIfNull(httpsPort, httpPort);
+    }
+}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy
deleted file mode 100644
index e0c1c2f23e..0000000000
--- a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy
+++ /dev/null
@@ -1,165 +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.registry.jetty
-
-import org.apache.nifi.registry.properties.NiFiRegistryProperties
-import org.eclipse.jetty.util.ssl.SslContextFactory
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.ExpectedException
-import org.junit.runner.RunWith
-import org.mockito.junit.MockitoJUnitRunner
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-import org.eclipse.jetty.server.Server
-
-@RunWith(MockitoJUnitRunner.class)
-class JettyServerGroovyTest extends GroovyTestCase {
-
-    private static final Logger logger = LoggerFactory.getLogger(JettyServerGroovyTest.class)
-
-    private static final keyPassword = "keyPassword"
-    private static final keystorePassword = "keystorePassword"
-    private static final truststorePassword = "truststorePassword"
-    private static final matchingPassword = "thePassword"
-
-    @Test
-    void testCreateSslContextFactoryWithKeystoreAndKeypassword() throws Exception {
-
-        // Arrange
-        NiFiRegistryProperties properties = new NiFiRegistryProperties()
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreDifferentPasswords.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEY_PASSWD, keyPassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, keystorePassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS")
-
-        Server internalServer = new Server()
-        JettyServer testServer = new JettyServer(internalServer, properties)
-
-        // Act
-        SslContextFactory sslContextFactory = testServer.createSslContextFactory()
-        sslContextFactory.start()
-
-        // Assert
-        assertNotNull(sslContextFactory)
-        assertNotNull(sslContextFactory.getSslContext())
-    }
-
-    @Test
-    void testCreateSslContextFactoryWithOnlyKeystorePassword() throws Exception {
-
-        // Arrange
-        NiFiRegistryProperties properties = new NiFiRegistryProperties()
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, matchingPassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS")
-
-        Server internalServer = new Server()
-        JettyServer testServer = new JettyServer(internalServer, properties)
-
-        // Act
-        SslContextFactory sslContextFactory = testServer.createSslContextFactory()
-        sslContextFactory.start()
-
-        // Assert
-        assertNotNull(sslContextFactory)
-        assertNotNull(sslContextFactory.getSslContext())
-    }
-
-    @Test
-    void testCreateSslContextFactoryWithMatchingPasswordsDefined() throws Exception {
-
-        // Arrange
-        NiFiRegistryProperties properties = new NiFiRegistryProperties()
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEY_PASSWD, matchingPassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, matchingPassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS")
-
-        Server internalServer = new Server()
-        JettyServer testServer = new JettyServer(internalServer, properties)
-
-        // Act
-        SslContextFactory sslContextFactory = testServer.createSslContextFactory()
-        sslContextFactory.start()
-
-        // Assert
-        assertNotNull(sslContextFactory)
-        assertNotNull(sslContextFactory.getSslContext())
-    }
-
-    @Rule public ExpectedException exception = ExpectedException.none()
-
-    @Test
-    void testCreateSslContextFactoryWithNoKeystorePasswordFails() throws Exception {
-
-        // Arrange
-        exception.expect(IllegalArgumentException.class)
-        exception.expectMessage("The keystore password cannot be null or empty")
-
-        NiFiRegistryProperties properties = new NiFiRegistryProperties()
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS")
-
-        Server internalServer = new Server()
-        JettyServer testServer = new JettyServer(internalServer, properties)
-
-        // Act but expect exception
-        SslContextFactory sslContextFactory = testServer.createSslContextFactory()
-    }
-
-    @Test
-    void testCreateSslContextFactoryWithCipherSuites() throws Exception {
-
-        // Arrange
-        NiFiRegistryProperties properties = new NiFiRegistryProperties()
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks")
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, matchingPassword)
-        properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS")
-        properties.setProperty(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_INCLUDE, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384")
-        properties.setProperty(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_EXCLUDE, "BAD_CIPHER")
-
-        Server internalServer = new Server()
-        JettyServer testServer = new JettyServer(internalServer, properties)
-
-        // Act
-        SslContextFactory sslContextFactory = testServer.createSslContextFactory()
-        sslContextFactory.start()
-
-        // Assert this
-        assertNotNull(sslContextFactory)
-        assertNotNull(sslContextFactory.getSslContext())
-        assertEquals("INCLUDE_CIPHER_SUITES", sslContextFactory.getIncludeCipherSuites(), ["TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"])
-        assertEquals("EXCLUDE_CIPHER_SUITES", sslContextFactory.getExcludeCipherSuites(), ["BAD_CIPHER"])
-    }
-
-}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java
new file mode 100644
index 0000000000..fa4b12fa80
--- /dev/null
+++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java
@@ -0,0 +1,146 @@
+/*
+ * 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.registry.jetty.connector;
+
+import org.apache.nifi.registry.properties.NiFiRegistryProperties;
+import org.apache.nifi.remote.io.socket.NetworkUtils;
+import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
+import org.apache.nifi.security.util.TlsConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class ApplicationServerConnectorFactoryTest {
+
+    private static final String SSL_PROTOCOL = "ssl";
+
+    private static final String H2_PROTOCOL = "h2";
+
+    private static final String INCLUDE_CIPHERS = ".*GCM.*";
+
+    private static final String EXCLUDE_CIPHERS = "*.CBC.*";
+
+    private static final String LOCALHOST = "127.0.0.1";
+
+    static TlsConfiguration tlsConfiguration;
+
+    Server server;
+
+    @BeforeAll
+    static void setTlsConfiguration() {
+        tlsConfiguration = new TemporaryKeyStoreBuilder().build();
+    }
+
+    @BeforeEach
+    void setServer() {
+        server = new Server();
+    }
+
+    @Test
+    void testGetServerConnectorRequiredProperties() {
+        final int port = NetworkUtils.getAvailableTcpPort();
+        final Properties configuredProperties = new Properties();
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTP_PORT, Integer.toString(port));
+
+        final NiFiRegistryProperties properties = getProperties(configuredProperties);
+        final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties);
+
+        final ServerConnector serverConnector = factory.getServerConnector();
+
+        assertNotNull(serverConnector);
+        assertNull(serverConnector.getHost());
+    }
+
+    @Test
+    void testGetServerConnectorHostProperty() {
+        final int port = NetworkUtils.getAvailableTcpPort();
+        final Properties configuredProperties = new Properties();
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTP_PORT, Integer.toString(port));
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTP_HOST, LOCALHOST);
+
+        final NiFiRegistryProperties properties = getProperties(configuredProperties);
+        final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties);
+
+        final ServerConnector serverConnector = factory.getServerConnector();
+
+        assertNotNull(serverConnector);
+        assertEquals(LOCALHOST, serverConnector.getHost());
+    }
+
+    @Test
+    void testGetServerConnectorSslProperties() {
+        final int port = NetworkUtils.getAvailableTcpPort();
+        final Properties configuredProperties = getSecurityProperties();
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_PORT, Integer.toString(port));
+
+        final NiFiRegistryProperties properties = getProperties(configuredProperties);
+        final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties);
+
+        final ServerConnector serverConnector = factory.getServerConnector();
+
+        assertNotNull(serverConnector);
+        assertNull(serverConnector.getHost());
+        assertTrue(serverConnector.getProtocols().contains(SSL_PROTOCOL));
+    }
+
+    @Test
+    void testGetServerConnectorHttp2Properties() {
+        final int port = NetworkUtils.getAvailableTcpPort();
+        final Properties configuredProperties = getSecurityProperties();
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_PORT, Integer.toString(port));
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_APPLICATION_PROTOCOLS, H2_PROTOCOL);
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_INCLUDE, INCLUDE_CIPHERS);
+        configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_EXCLUDE, EXCLUDE_CIPHERS);
+
+        final NiFiRegistryProperties properties = getProperties(configuredProperties);
+        final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties);
+
+        final ServerConnector serverConnector = factory.getServerConnector();
+
+        assertNotNull(serverConnector);
+        assertNull(serverConnector.getHost());
+
+        final List<String> protocols = serverConnector.getProtocols();
+        assertTrue(protocols.contains(SSL_PROTOCOL));
+        assertTrue(protocols.contains(H2_PROTOCOL));
+    }
+
+    private Properties getSecurityProperties() {
+        final Properties securityProperties = new Properties();
+        securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
+        securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
+        securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
+        securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
+        securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
+        securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
+        return securityProperties;
+    }
+
+    private NiFiRegistryProperties getProperties(final Properties configuredProperties) {
+        return new NiFiRegistryProperties(configuredProperties);
+    }
+}
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks
deleted file mode 100644
index 98c8903ad9..0000000000
Binary files a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks and /dev/null differ
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks
deleted file mode 100644
index aeedd7f952..0000000000
Binary files a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks and /dev/null differ
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks
deleted file mode 100644
index 47c8e45c3c..0000000000
Binary files a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks and /dev/null differ
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
index 0b908715ce..eeff77de5b 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
@@ -53,6 +53,7 @@ public class NiFiRegistryProperties extends ApplicationProperties {
     public static final String WEB_HTTPS_HOST = "nifi.registry.web.https.host";
     public static final String WEB_HTTPS_CIPHERSUITES_INCLUDE = "nifi.registry.web.https.ciphersuites.include";
     public static final String WEB_HTTPS_CIPHERSUITES_EXCLUDE = "nifi.registry.web.https.ciphersuites.exclude";
+    public static final String WEB_HTTPS_APPLICATION_PROTOCOLS = "nifi.registry.web.https.application.protocols";
     public static final String WEB_WORKING_DIR = "nifi.registry.web.jetty.working.directory";
     public static final String WEB_THREADS = "nifi.registry.web.jetty.threads";
     public static final String WEB_SHOULD_SEND_SERVER_VERSION = "nifi.registry.web.should.send.server.version";
@@ -119,6 +120,7 @@ public class NiFiRegistryProperties extends ApplicationProperties {
 
     // Defaults
     public static final String DEFAULT_WEB_WORKING_DIR = "./work/jetty";
+    public static final String DEFAULT_WEB_HTTPS_APPLICATION_PROTOCOLS = "http/1.1";
     public static final String DEFAULT_WAR_DIR = "./lib";
     public static final String DEFAULT_PROVIDERS_CONFIGURATION_FILE = "./conf/providers.xml";
     public static final String DEFAULT_REGISTRY_ALIAS_CONFIGURATION_FILE = "./conf/registry-aliases.xml";
@@ -229,6 +231,16 @@ public class NiFiRegistryProperties extends ApplicationProperties {
         return new File(getProperty(WEB_WORKING_DIR, DEFAULT_WEB_WORKING_DIR));
     }
 
+    /**
+     * Get Web HTTPS Application Protocols defaults to HTTP/1.1
+     *
+     * @return Set of configured HTTPS Application Protocols
+     */
+    public Set<String> getWebHttpsApplicationProtocols() {
+        final String protocols = getProperty(WEB_HTTPS_APPLICATION_PROTOCOLS, DEFAULT_WEB_HTTPS_APPLICATION_PROTOCOLS);
+        return Arrays.stream(protocols.split("\\s+")).collect(Collectors.toSet());
+    }
+
     public File getExtensionsWorkingDirectory() {
         return  new File(getProperty(EXTENSIONS_WORKING_DIR, DEFAULT_EXTENSIONS_WORKING_DIR));
     }
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
index db5b429dec..c7a6b9fd64 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
+++ b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties
@@ -19,6 +19,7 @@ nifi.registry.web.http.host=${nifi.registry.web.http.host}
 nifi.registry.web.http.port=${nifi.registry.web.http.port}
 nifi.registry.web.https.host=${nifi.registry.web.https.host}
 nifi.registry.web.https.port=${nifi.registry.web.https.port}
+nifi.registry.web.https.application.protocols=${nifi.registry.web.https.application.protocols}
 nifi.registry.web.jetty.working.directory=${nifi.registry.jetty.work.dir}
 nifi.registry.web.jetty.threads=${nifi.registry.web.jetty.threads}
 nifi.registry.web.should.send.server.version=${nifi.registry.web.should.send.server.version}
diff --git a/nifi-registry/pom.xml b/nifi-registry/pom.xml
index 753d4647d4..4dfa2f11ee 100644
--- a/nifi-registry/pom.xml
+++ b/nifi-registry/pom.xml
@@ -93,6 +93,18 @@
                 <artifactId>apache-jstl</artifactId>
                 <scope>compile</scope>
             </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty.http2</groupId>
+                <artifactId>http2-server</artifactId>
+                <version>${jetty.version}</version>
+                <scope>compile</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.eclipse.jetty</groupId>
+                <artifactId>jetty-alpn-server</artifactId>
+                <version>${jetty.version}</version>
+                <scope>compile</scope>
+            </dependency>
         <!-- lib/java11 -->
             <dependency>
                 <groupId>jakarta.xml.bind</groupId>