View Javadoc
1   /*
2    * Copyright 2021 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  import io.netty.util.internal.EmptyArrays;
19  import io.netty.util.internal.PlatformDependent;
20  import io.netty.util.internal.logging.InternalLogger;
21  import io.netty.util.internal.logging.InternalLoggerFactory;
22  
23  import java.lang.reflect.InvocationHandler;
24  import java.lang.reflect.Method;
25  import java.lang.reflect.Proxy;
26  import java.security.AccessController;
27  import java.security.PrivilegedExceptionAction;
28  import java.util.List;
29  import java.util.function.BiFunction;
30  import javax.net.ssl.SSLContext;
31  import javax.net.ssl.SSLEngine;
32  
33  import static io.netty.handler.ssl.SslUtils.getSSLContext;
34  
35  final class BouncyCastleAlpnSslUtils {
36      private static final InternalLogger logger = InternalLoggerFactory.getInstance(BouncyCastleAlpnSslUtils.class);
37      private static final Method SET_PARAMETERS;
38      private static final Method GET_PARAMETERS;
39      private static final Method SET_APPLICATION_PROTOCOLS;
40      private static final Method GET_APPLICATION_PROTOCOL;
41      private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL;
42      private static final Method SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
43      private static final Method GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR;
44      private static final Class BC_APPLICATION_PROTOCOL_SELECTOR;
45      private static final Method BC_APPLICATION_PROTOCOL_SELECTOR_SELECT;
46  
47      static {
48          Class bcSslEngine;
49          Method getParameters;
50          Method setParameters;
51          Method setApplicationProtocols;
52          Method getApplicationProtocol;
53          Method getHandshakeApplicationProtocol;
54          Method setHandshakeApplicationProtocolSelector;
55          Method getHandshakeApplicationProtocolSelector;
56          Method bcApplicationProtocolSelectorSelect;
57          Class bcApplicationProtocolSelector;
58  
59          try {
60              bcSslEngine = Class.forName("org.bouncycastle.jsse.BCSSLEngine");
61              final Class testBCSslEngine = bcSslEngine;
62  
63              bcApplicationProtocolSelector =
64                      Class.forName("org.bouncycastle.jsse.BCApplicationProtocolSelector");
65  
66              final Class testBCApplicationProtocolSelector = bcApplicationProtocolSelector;
67  
68              bcApplicationProtocolSelectorSelect = AccessController.doPrivileged(
69                      new PrivilegedExceptionAction<Method>() {
70                  @Override
71                  public Method run() throws Exception {
72                      return testBCApplicationProtocolSelector.getMethod("select", Object.class, List.class);
73                  }
74              });
75  
76              SSLContext context = getSSLContext("BCJSSE");
77              SSLEngine engine = context.createSSLEngine();
78  
79              getParameters = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
80                  @Override
81                  public Method run() throws Exception {
82                      return testBCSslEngine.getMethod("getParameters");
83                  }
84              });
85  
86              final Object bcSslParameters = getParameters.invoke(engine);
87              final Class<?> bCSslParametersClass = bcSslParameters.getClass();
88  
89              setParameters = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
90                  @Override
91                  public Method run() throws Exception {
92                      return testBCSslEngine.getMethod("setParameters", bCSslParametersClass);
93                  }
94              });
95              setParameters.invoke(engine, bcSslParameters);
96  
97              setApplicationProtocols = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
98                  @Override
99                  public Method run() throws Exception {
100                     return bCSslParametersClass.getMethod("setApplicationProtocols", String[].class);
101                 }
102             });
103             setApplicationProtocols.invoke(bcSslParameters, new Object[]{EmptyArrays.EMPTY_STRINGS});
104 
105             getApplicationProtocol = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
106                 @Override
107                 public Method run() throws Exception {
108                     return testBCSslEngine.getMethod("getApplicationProtocol");
109                 }
110             });
111             getApplicationProtocol.invoke(engine);
112 
113             getHandshakeApplicationProtocol = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
114                 @Override
115                 public Method run() throws Exception {
116                     return testBCSslEngine.getMethod("getHandshakeApplicationProtocol");
117                 }
118             });
119             getHandshakeApplicationProtocol.invoke(engine);
120 
121             setHandshakeApplicationProtocolSelector =
122                     AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
123                         @Override
124                         public Method run() throws Exception {
125                             return testBCSslEngine.getMethod("setBCHandshakeApplicationProtocolSelector",
126                                     testBCApplicationProtocolSelector);
127                         }
128                     });
129 
130             getHandshakeApplicationProtocolSelector =
131                     AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
132                         @Override
133                         public Method run() throws Exception {
134                             return testBCSslEngine.getMethod("getBCHandshakeApplicationProtocolSelector");
135                         }
136                     });
137             getHandshakeApplicationProtocolSelector.invoke(engine);
138 
139         } catch (Throwable t) {
140             logger.error("Unable to initialize BouncyCastleAlpnSslUtils.", t);
141             setParameters = null;
142             getParameters = null;
143             setApplicationProtocols = null;
144             getApplicationProtocol = null;
145             getHandshakeApplicationProtocol = null;
146             setHandshakeApplicationProtocolSelector = null;
147             getHandshakeApplicationProtocolSelector = null;
148             bcApplicationProtocolSelectorSelect = null;
149             bcApplicationProtocolSelector = null;
150         }
151         SET_PARAMETERS = setParameters;
152         GET_PARAMETERS = getParameters;
153         SET_APPLICATION_PROTOCOLS = setApplicationProtocols;
154         GET_APPLICATION_PROTOCOL = getApplicationProtocol;
155         GET_HANDSHAKE_APPLICATION_PROTOCOL = getHandshakeApplicationProtocol;
156         SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = setHandshakeApplicationProtocolSelector;
157         GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = getHandshakeApplicationProtocolSelector;
158         BC_APPLICATION_PROTOCOL_SELECTOR_SELECT = bcApplicationProtocolSelectorSelect;
159         BC_APPLICATION_PROTOCOL_SELECTOR = bcApplicationProtocolSelector;
160     }
161 
162     private BouncyCastleAlpnSslUtils() {
163     }
164 
165     static String getApplicationProtocol(SSLEngine sslEngine) {
166         try {
167             return (String) GET_APPLICATION_PROTOCOL.invoke(sslEngine);
168         } catch (UnsupportedOperationException ex) {
169             throw ex;
170         } catch (Exception ex) {
171             throw new IllegalStateException(ex);
172         }
173     }
174 
175     static void setApplicationProtocols(SSLEngine engine, List<String> supportedProtocols) {
176         String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
177         try {
178             Object bcSslParameters = GET_PARAMETERS.invoke(engine);
179             SET_APPLICATION_PROTOCOLS.invoke(bcSslParameters, new Object[]{protocolArray});
180             SET_PARAMETERS.invoke(engine, bcSslParameters);
181         } catch (UnsupportedOperationException ex) {
182             throw ex;
183         } catch (Exception ex) {
184             throw new IllegalStateException(ex);
185         }
186         if (PlatformDependent.javaVersion() >= 9) {
187             JdkAlpnSslUtils.setApplicationProtocols(engine, supportedProtocols);
188         }
189     }
190 
191     static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
192         try {
193             return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invoke(sslEngine);
194         } catch (UnsupportedOperationException ex) {
195             throw ex;
196         } catch (Exception ex) {
197             throw new IllegalStateException(ex);
198         }
199     }
200 
201     static void setHandshakeApplicationProtocolSelector(
202             SSLEngine engine, final BiFunction<SSLEngine, List<String>, String> selector) {
203         try {
204             Object selectorProxyInstance = Proxy.newProxyInstance(
205                     BouncyCastleAlpnSslUtils.class.getClassLoader(),
206                     new Class[]{BC_APPLICATION_PROTOCOL_SELECTOR},
207                     new InvocationHandler() {
208                         @Override
209                         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
210                             if (method.getName().equals("select")) {
211                                 try {
212                                     return selector.apply((SSLEngine) args[0], (List<String>) args[1]);
213                                 } catch (ClassCastException e) {
214                                     throw new RuntimeException("BCApplicationProtocolSelector select method " +
215                                             "parameter of invalid type.", e);
216                                 }
217                             } else {
218                                 throw new UnsupportedOperationException(String.format("Method '%s' not supported.",
219                                         method.getName()));
220                             }
221                         }
222                     });
223 
224             SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine, selectorProxyInstance);
225         } catch (UnsupportedOperationException ex) {
226             throw ex;
227         } catch (Exception ex) {
228             throw new IllegalStateException(ex);
229         }
230     }
231 
232     @SuppressWarnings("unchecked")
233     static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
234         try {
235             final Object selector = GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine);
236             return new BiFunction<SSLEngine, List<String>, String>() {
237 
238                 @Override
239                 public String apply(SSLEngine sslEngine, List<String> strings) {
240                     try {
241                         return (String) BC_APPLICATION_PROTOCOL_SELECTOR_SELECT.invoke(selector, sslEngine,
242                                 strings);
243                     } catch (Exception e) {
244                         throw new RuntimeException("Could not call getHandshakeApplicationProtocolSelector", e);
245                     }
246                 }
247             };
248 
249         } catch (UnsupportedOperationException ex) {
250             throw ex;
251         } catch (Exception ex) {
252             throw new IllegalStateException(ex);
253         }
254     }
255 
256 }