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.netty.handler.ssl;
17  
18  
19  import javax.net.ssl.SSLContext;
20  import javax.net.ssl.SSLEngine;
21  import javax.net.ssl.SSLParameters;
22  import java.lang.reflect.Method;
23  import java.security.AccessController;
24  import java.security.PrivilegedExceptionAction;
25  import java.util.List;
26  import java.util.function.BiFunction;
27  
28  import io.netty.util.internal.EmptyArrays;
29  import io.netty.util.internal.PlatformDependent;
30  import io.netty.util.internal.SuppressJava6Requirement;
31  import io.netty.util.internal.logging.InternalLogger;
32  import io.netty.util.internal.logging.InternalLoggerFactory;
33  
34  @SuppressJava6Requirement(reason = "Usage guarded by java version check")
35  final class JdkAlpnSslUtils {
36      private static final InternalLogger logger = InternalLoggerFactory.getInstance(JdkAlpnSslUtils.class);
37      private static final Method SET_APPLICATION_PROTOCOLS;
38      private static final Method GET_APPLICATION_PROTOCOL;
39      private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL;
40      private static final Method SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
41      private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
42  
43      static {
44          Method getHandshakeApplicationProtocol;
45          Method getApplicationProtocol;
46          Method setApplicationProtocols;
47          Method setHandshakeApplicationProtocolSelector;
48          Method getHandshakeApplicationProtocolSelector;
49  
50          try {
51              SSLContext context = SSLContext.getInstance(JdkSslContext.PROTOCOL);
52              context.init(null, null, null);
53              SSLEngine engine = context.createSSLEngine();
54              getHandshakeApplicationProtocol = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
55                  @Override
56                  public Method run() throws Exception {
57                      return SSLEngine.class.getMethod("getHandshakeApplicationProtocol");
58                  }
59              });
60              getHandshakeApplicationProtocol.invoke(engine);
61              getApplicationProtocol = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
62                  @Override
63                  public Method run() throws Exception {
64                      return SSLEngine.class.getMethod("getApplicationProtocol");
65                  }
66              });
67              getApplicationProtocol.invoke(engine);
68  
69              setApplicationProtocols = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
70                  @Override
71                  public Method run() throws Exception {
72                      return SSLParameters.class.getMethod("setApplicationProtocols", String[].class);
73                  }
74              });
75              setApplicationProtocols.invoke(engine.getSSLParameters(), new Object[]{EmptyArrays.EMPTY_STRINGS});
76  
77              setHandshakeApplicationProtocolSelector =
78                      AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
79                  @Override
80                  public Method run() throws Exception {
81                      return SSLEngine.class.getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class);
82                  }
83              });
84              setHandshakeApplicationProtocolSelector.invoke(engine, new BiFunction<SSLEngine, List<String>, String>() {
85                  @Override
86                  public String apply(SSLEngine sslEngine, List<String> strings) {
87                      return null;
88                  }
89              });
90  
91              getHandshakeApplicationProtocolSelector =
92                      AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
93                  @Override
94                  public Method run() throws Exception {
95                      return SSLEngine.class.getMethod("getHandshakeApplicationProtocolSelector");
96                  }
97              });
98              getHandshakeApplicationProtocolSelector.invoke(engine);
99          } catch (Throwable t) {
100             int version = PlatformDependent.javaVersion();
101             if (version >= 9) {
102                 // We only log when run on java9+ as this is expected on some earlier java8 versions
103                 logger.error("Unable to initialize JdkAlpnSslUtils, but the detected java version was: {}", version, t);
104             }
105             getHandshakeApplicationProtocol = null;
106             getApplicationProtocol = null;
107             setApplicationProtocols = null;
108             setHandshakeApplicationProtocolSelector = null;
109             getHandshakeApplicationProtocolSelector = null;
110         }
111         GET_HANDSHAKE_APPLICATION_PROTOCOL = getHandshakeApplicationProtocol;
112         GET_APPLICATION_PROTOCOL = getApplicationProtocol;
113         SET_APPLICATION_PROTOCOLS = setApplicationProtocols;
114         SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = setHandshakeApplicationProtocolSelector;
115         GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = getHandshakeApplicationProtocolSelector;
116     }
117 
118     private JdkAlpnSslUtils() {
119     }
120 
121     static boolean supportsAlpn() {
122         return GET_APPLICATION_PROTOCOL != null;
123     }
124 
125     static String getApplicationProtocol(SSLEngine sslEngine) {
126         try {
127             return (String) GET_APPLICATION_PROTOCOL.invoke(sslEngine);
128         } catch (UnsupportedOperationException ex) {
129             throw ex;
130         } catch (Exception ex) {
131             throw new IllegalStateException(ex);
132         }
133     }
134 
135     static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
136         try {
137             return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invoke(sslEngine);
138         } catch (UnsupportedOperationException ex) {
139             throw ex;
140         } catch (Exception ex) {
141             throw new IllegalStateException(ex);
142         }
143     }
144 
145     static void setApplicationProtocols(SSLEngine engine, List<String> supportedProtocols) {
146         SSLParameters parameters = engine.getSSLParameters();
147 
148         String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
149         try {
150             SET_APPLICATION_PROTOCOLS.invoke(parameters, new Object[]{protocolArray});
151         } catch (UnsupportedOperationException ex) {
152             throw ex;
153         } catch (Exception ex) {
154             throw new IllegalStateException(ex);
155         }
156         engine.setSSLParameters(parameters);
157     }
158 
159     static void setHandshakeApplicationProtocolSelector(
160             SSLEngine engine, BiFunction<SSLEngine, List<String>, String> selector) {
161         try {
162             SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine, selector);
163         } catch (UnsupportedOperationException ex) {
164             throw ex;
165         } catch (Exception ex) {
166             throw new IllegalStateException(ex);
167         }
168     }
169 
170     @SuppressWarnings("unchecked")
171     static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
172         try {
173             return (BiFunction<SSLEngine, List<String>, String>)
174                     GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine);
175         } catch (UnsupportedOperationException ex) {
176             throw ex;
177         } catch (Exception ex) {
178             throw new IllegalStateException(ex);
179         }
180     }
181 }