View Javadoc
1   /*
2    * Copyright 2017 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.handler.ssl;
17  
18  import io.netty5.util.internal.EmptyArrays;
19  import io.netty5.util.internal.PlatformDependent;
20  import io.netty5.util.internal.logging.InternalLogger;
21  import io.netty5.util.internal.logging.InternalLoggerFactory;
22  
23  import javax.net.ssl.SSLContext;
24  import javax.net.ssl.SSLEngine;
25  import javax.net.ssl.SSLParameters;
26  import java.lang.invoke.MethodHandle;
27  import java.lang.invoke.MethodHandles;
28  import java.lang.invoke.MethodType;
29  import java.security.AccessController;
30  import java.security.PrivilegedExceptionAction;
31  import java.util.List;
32  import java.util.function.BiFunction;
33  
34  final class JdkAlpnSslUtils {
35      private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkAlpnSslUtils.class);
36      private static final MethodHandle SET_APPLICATION_PROTOCOLS;
37      private static final MethodHandle GET_APPLICATION_PROTOCOL;
38      private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL;
39      private static final MethodHandle SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
40      private static final MethodHandle GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
41  
42      static {
43          MethodHandle getHandshakeApplicationProtocol;
44          MethodHandle getApplicationProtocol;
45          MethodHandle setApplicationProtocols;
46          MethodHandle setHandshakeApplicationProtocolSelector;
47          MethodHandle getHandshakeApplicationProtocolSelector;
48  
49          try {
50              SSLContext context = SSLContext.getInstance(JdkSslContext.PROTOCOL);
51              context.init(null, null, null);
52              SSLEngine engine = context.createSSLEngine();
53              MethodHandles.Lookup lookup = MethodHandles.lookup();
54              getHandshakeApplicationProtocol =
55                      AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
56                      lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocol",
57                              MethodType.methodType(String.class)));
58              // Invoke and specify the return type so the compiler doesnt try to use void
59              String getHandshakeApplicationProtocolRes = (String) getHandshakeApplicationProtocol.invokeExact(engine);
60  
61              getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
62                      lookup.findVirtual(SSLEngine.class, "getApplicationProtocol",
63                              MethodType.methodType(String.class)));
64              // Invoke and specify the return type so the compiler doesnt try to use void
65              String getApplicationProtocolRes = (String) getApplicationProtocol.invokeExact(engine);
66  
67              setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
68                      lookup.findVirtual(SSLParameters.class, "setApplicationProtocols",
69                              MethodType.methodType(void.class, String[].class)));
70              setApplicationProtocols.invokeExact(engine.getSSLParameters(), EmptyArrays.EMPTY_STRINGS);
71  
72              setHandshakeApplicationProtocolSelector =
73                      AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
74                              lookup.findVirtual(SSLEngine.class, "setHandshakeApplicationProtocolSelector",
75                                      MethodType.methodType(void.class, BiFunction.class)));
76              setHandshakeApplicationProtocolSelector.invokeExact(engine,
77                      (BiFunction<SSLEngine, List<String>, String>) (sslEngine, strings) -> null);
78  
79              getHandshakeApplicationProtocolSelector =
80                      AccessController.doPrivileged((PrivilegedExceptionAction<MethodHandle>) () ->
81                              lookup.findVirtual(SSLEngine.class, "getHandshakeApplicationProtocolSelector",
82                                      MethodType.methodType(BiFunction.class)));
83              // Invoke and specify the return type so the compiler doesn't try to use void
84              @SuppressWarnings("unchecked")
85              BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelectorRes =
86                      (BiFunction<SSLEngine, List<String>, String>)
87                              getHandshakeApplicationProtocolSelector.invokeExact(engine);
88          } catch (Throwable t) {
89              // This is expected to work since Java 9, so log as error.
90              int version = PlatformDependent.javaVersion();
91              logger.error("Unable to initialize JdkAlpnSslUtils. Detected java version was: {}", version, t);
92              getHandshakeApplicationProtocol = null;
93              getApplicationProtocol = null;
94              setApplicationProtocols = null;
95              setHandshakeApplicationProtocolSelector = null;
96              getHandshakeApplicationProtocolSelector = null;
97          }
98          GET_HANDSHAKE_APPLICATION_PROTOCOL = getHandshakeApplicationProtocol;
99          GET_APPLICATION_PROTOCOL = getApplicationProtocol;
100         SET_APPLICATION_PROTOCOLS = setApplicationProtocols;
101         SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = setHandshakeApplicationProtocolSelector;
102         GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = getHandshakeApplicationProtocolSelector;
103     }
104 
105     private JdkAlpnSslUtils() {
106     }
107 
108     static boolean supportsAlpn() {
109         return GET_APPLICATION_PROTOCOL != null;
110     }
111 
112     static String getApplicationProtocol(SSLEngine sslEngine) {
113         try {
114             return (String) GET_APPLICATION_PROTOCOL.invokeExact(sslEngine);
115         } catch (UnsupportedOperationException ex) {
116             throw ex;
117         } catch (Throwable ex) {
118             throw new IllegalStateException(ex);
119         }
120     }
121 
122     static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
123         try {
124             return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invokeExact(sslEngine);
125         } catch (UnsupportedOperationException ex) {
126             throw ex;
127         } catch (Throwable ex) {
128             throw new IllegalStateException(ex);
129         }
130     }
131 
132     static void setApplicationProtocols(SSLEngine engine, List<String> supportedProtocols) {
133         SSLParameters parameters = engine.getSSLParameters();
134 
135         String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
136         try {
137             SET_APPLICATION_PROTOCOLS.invokeExact(parameters, protocolArray);
138         } catch (UnsupportedOperationException ex) {
139             throw ex;
140         } catch (Throwable ex) {
141             throw new IllegalStateException(ex);
142         }
143         engine.setSSLParameters(parameters);
144     }
145 
146     static void setHandshakeApplicationProtocolSelector(
147             SSLEngine engine, BiFunction<SSLEngine, List<String>, String> selector) {
148         try {
149             SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine, selector);
150         } catch (UnsupportedOperationException ex) {
151             throw ex;
152         } catch (Throwable ex) {
153             throw new IllegalStateException(ex);
154         }
155     }
156 
157     @SuppressWarnings("unchecked")
158     static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
159         try {
160             return (BiFunction<SSLEngine, List<String>, String>)
161                     GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invokeExact(engine);
162         } catch (UnsupportedOperationException ex) {
163             throw ex;
164         } catch (Throwable ex) {
165             throw new IllegalStateException(ex);
166         }
167     }
168 }