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