netty series: making TLS support http2

Posted by Butthead on Wed, 20 Oct 2021 02:15:39 +0200

brief introduction

We know that although the HTTPS protocol does not force the use of HTTPS, for most browsers, if they want to use HTTPS, they must use HTTPS. Therefore, we need to know how to support HTTPS in netty's TLS.

Extended protocols NPN and ALPN of TLS

Http2 protocol is developed from spdy protocol. Both spdy and http2 have developed the extension of TLS protocol in order to work in HTTPS environment.

They are called NPN(Next Protocol Negotiation) and ALPN (Application Layer Protocol Negotiation).

They specify the protocol for application data communication between client and server after TLS protocol handshake. ALPN can list the application layer data protocols supported by the client when the client shakes hands with the server for the first time, and the server can directly select them. Therefore, ALPN can have less interaction process than NPN.

So what are the protocols supported by spdy and http2 respectively?

netty provides an ApplicationProtocolNames class in which corresponding protocols are defined. ALPN corresponds to http2 and http1.1, while sydy corresponds to spdy/1, spdy/2 and spdy/3:

    /**
     * HTTP version 2
     */
    public static final String HTTP_2 = "h2";

    /**
     * {@code "http/1.1"}: HTTP version 1.1
     */
    public static final String HTTP_1_1 = "http/1.1";

    /**
     * {@code "spdy/3.1"}: SPDY version 3.1
     */
    public static final String SPDY_3_1 = "spdy/3.1";

    /**
     * {@code "spdy/3"}: SPDY version 3
     */
    public static final String SPDY_3 = "spdy/3";

    /**
     * {@code "spdy/2"}: SPDY version 2
     */
    public static final String SPDY_2 = "spdy/2";

    /**
     * {@code "spdy/1"}: SPDY version 1
     */
    public static final String SPDY_1 = "spdy/1";

SslProvider

At present, there are two SSL implementations in netty, one is JDK and the other is OPENSSL. Different implementations support TLS protocol extension differently. It provides an isAlpnSupported method to judge whether ALPN is supported according to different incoming provider s.

    public static boolean isAlpnSupported(final SslProvider provider) {
        switch (provider) {
            case JDK:
                return JdkAlpnApplicationProtocolNegotiator.isAlpnSupported();
            case OPENSSL:
            case OPENSSL_REFCNT:
                return OpenSsl.isAlpnSupported();
            default:
                throw new Error("Unknown SslProvider: " + provider);
        }
    }

If you use JDK8, you may get the following error prompt after running:

ALPN is only supported in Java9 or if you use conscrypt as your provider or have the jetty alpn stuff on the class path.

In other words, if JDK is used as the default SSL provider, it does not support ALPN. You must upgrade to Java 9.0

According to the prompt, if you add conscrypt to the classpath:

        <dependency>
            <groupId>org.conscrypt</groupId>
            <artifactId>conscrypt-openjdk-uber</artifactId>
            <version>2.5.2</version>
        </dependency>

After running, you will get the following error:

Unable to wrap SSLEngine of type 'sun.security.ssl.SSLEngineImpl'

What shall I do? The answer is to use Open SSL. You also need to add:

        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-tcnative-boringssl-static</artifactId>
            <version>2.0.40.Final</version>
        </dependency>

Tested and executed perfectly.

ApplicationProtocolConfig

ApplicationProtocolConfig is a protocol configuration class passed to SSLEngine by netty. It mainly has four properties:

    private final List<String> supportedProtocols;
    private final Protocol protocol;
    private final SelectorFailureBehavior selectorBehavior;
    private final SelectedListenerFailureBehavior selectedBehavior;

Supported protocols are supported data transmission protocols, such as HTTP 2, HTTP 1.1 or spdy/1, spdy/2, spdy/3, etc.

Protocol is an extension protocol of TLS, such as ALPN or NPN.

selectorBehavior is the expression method when selecting an agreement. There are three ways:

FATAL_ALERT: if the node that selects the application protocol does not find a match, the handshake will fail.
NO_ADVERTISE: if the node that selects the application protocol does not find a match, it will pretend not to support TLS extension in the handshake.
CHOOSE_MY_LAST_PROTOCOL: if no match is found for the node that selects the application protocol, the last recommended protocol will be used.

selectedBehavior is the expression after notifying the selected protocol. There are also three ways:

ACCEPT: if the node does not support the application protocol selected by the other node, the node does not support the TLS extension by default, and then continue to shake hands.
FATAL_ALERT: if the node does not support the application protocol selected by the other node, the handshake fails.
CHOOSE_MY_LAST_PROTOCOL: if the node does not support the application protocol selected by the other node, the last recommended protocol will be used.

Build SslContext

With the provider and ApplicationProtocolConfig, you can build the SslContext. First create an SSL provider:

 SslProvider provider =  SslProvider.isAlpnSupported(SslProvider.OPENSSL)  ? SslProvider.OPENSSL : SslProvider.JDK;
           

By default, JDK is used as ssl provider. If you use OpenSSL, you can use OpenSSL.

We use SslContextBuilder.forServer to create SslContext. This method needs to pass in certificate and privateKey. For simplicity, we use self signed SelfSignedCertificate:

 SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();

You can also set sslProvider, ciphers, applicationProtocolConfig and other information:

sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey())
                .sslProvider(provider)
                //Supported cipher s
                .ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
                .applicationProtocolConfig(new ApplicationProtocolConfig(
                    Protocol.ALPN,
                    // Currently, OpenSsl and JDK providers only support NO_ADVERTISE
                    SelectorFailureBehavior.NO_ADVERTISE,
                    // At present, OpenSsl and JDK providers only support ACCEPT
                    SelectedListenerFailureBehavior.ACCEPT,
                    ApplicationProtocolNames.HTTP_2,
                    ApplicationProtocolNames.HTTP_1_1))
                .build();

ProtocolNegotiationHandler

Finally, we need to deal with different protocols according to the negotiation. netty provides an ApplicationProtocolNegotiationHandler. If customized, we only need to inherit this class. For example, we process HTTP1 and HTTP2 requests respectively according to the name of the protocol:

   public class MyNegotiationHandler extends ApplicationProtocolNegotiationHandler {
       public MyNegotiationHandler() {
           super(ApplicationProtocolNames.HTTP_1_1);
       }
  
       protected void configurePipeline(ChannelHandlerContext ctx, String protocol) {
           if (ApplicationProtocolNames.HTTP_2.equals(protocol) {
               configureHttp2(ctx);
           } else if (ApplicationProtocolNames.HTTP_1_1.equals(protocol)) {
               configureHttp1(ctx);
           } else {
               throw new IllegalStateException("unknown protocol: " + protocol);
           }
       }
   }

Then add it to ChannelPipeline:

   public class MyInitializer extends ChannelInitializer<Channel> {
       private final SslContext sslCtx;
  
       public MyInitializer(SslContext sslCtx) {
           this.sslCtx = sslCtx;
       }
  
       protected void initChannel(Channel ch) {
           ChannelPipeline p = ch.pipeline();
           p.addLast(sslCtx.newHandler(...)); // Adds SslHandler
           p.addLast(new MyNegotiationHandler());
       }
   }

summary

The above is the complete process of configuring TLS to support HTTP2 in netty.

Examples of this article can be referred to: learn-netty4

This article has been included in http://www.flydean.com/26-netty-secure-http2/

The most popular interpretation, the most profound dry goods, the most concise tutorial, and many tips you don't know are waiting for you to find!

Welcome to my official account: "those things in procedure", understand technology, know you better!

Topics: Java Netty https