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