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.netty5.handler.ssl;
17  
18  
19  import io.netty5.util.internal.EmptyArrays;
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.reflect.Method;
27  import java.lang.reflect.Proxy;
28  import java.security.AccessController;
29  import java.security.PrivilegedExceptionAction;
30  import java.util.List;
31  import java.util.function.BiFunction;
32  
33  import static io.netty5.handler.ssl.SslUtils.getSSLContext;
34  
35  final class BouncyCastleAlpnSslUtils {
36      private static final InternalLogger logger = InternalLoggerFactory.getInstance(BouncyCastleAlpnSslUtils.class);
37      private static final Class<?> BC_SSL_PARAMETERS;
38      private static final Method SET_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          Class<?> bcSslParameters;
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              bcSslParameters = Class.forName("org.bouncycastle.jsse.BCSSLParameters");
64              Object bcSslParametersInstance = bcSslParameters.newInstance();
65              final Class<?> testBCSslParameters = bcSslParameters;
66  
67              bcApplicationProtocolSelector =
68                      Class.forName("org.bouncycastle.jsse.BCApplicationProtocolSelector");
69  
70              final Class<?> testBCApplicationProtocolSelector = bcApplicationProtocolSelector;
71  
72              bcApplicationProtocolSelectorSelect = AccessController.doPrivileged(
73                      (PrivilegedExceptionAction<Method>) () ->
74                              testBCApplicationProtocolSelector.getMethod("select", Object.class, List.class));
75  
76              SSLContext context = getSSLContext("BCJSSE");
77              SSLEngine engine = context.createSSLEngine();
78              setParameters = AccessController.doPrivileged((PrivilegedExceptionAction<Method>)
79                      () -> testBCSslEngine.getMethod("setParameters", testBCSslParameters));
80              setParameters.invoke(engine, bcSslParametersInstance);
81  
82              setApplicationProtocols = AccessController.doPrivileged((PrivilegedExceptionAction<Method>)
83                      () -> testBCSslParameters.getMethod("setApplicationProtocols", String[].class));
84              setApplicationProtocols.invoke(bcSslParametersInstance, new Object[]{EmptyArrays.EMPTY_STRINGS});
85  
86              getApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<Method>)
87                      () -> testBCSslEngine.getMethod("getApplicationProtocol"));
88              getApplicationProtocol.invoke(engine);
89  
90              getHandshakeApplicationProtocol = AccessController.doPrivileged((PrivilegedExceptionAction<Method>)
91                      () -> testBCSslEngine.getMethod("getHandshakeApplicationProtocol"));
92              getHandshakeApplicationProtocol.invoke(engine);
93  
94              setHandshakeApplicationProtocolSelector =
95                      AccessController.doPrivileged((PrivilegedExceptionAction<Method>)
96                              () -> testBCSslEngine.getMethod("setBCHandshakeApplicationProtocolSelector",
97                              testBCApplicationProtocolSelector));
98  
99              getHandshakeApplicationProtocolSelector =
100                     AccessController.doPrivileged((PrivilegedExceptionAction<Method>)
101                             () -> testBCSslEngine.getMethod("getBCHandshakeApplicationProtocolSelector"));
102             getHandshakeApplicationProtocolSelector.invoke(engine);
103 
104         } catch (Throwable t) {
105             logger.error("Unable to initialize BouncyCastleAlpnSslUtils.", t);
106             bcSslParameters = null;
107             setParameters = null;
108             setApplicationProtocols = null;
109             getApplicationProtocol = null;
110             getHandshakeApplicationProtocol = null;
111             setHandshakeApplicationProtocolSelector = null;
112             getHandshakeApplicationProtocolSelector = null;
113             bcApplicationProtocolSelectorSelect = null;
114             bcApplicationProtocolSelector = null;
115         }
116         BC_SSL_PARAMETERS = bcSslParameters;
117         SET_PARAMETERS = setParameters;
118         SET_APPLICATION_PROTOCOLS = setApplicationProtocols;
119         GET_APPLICATION_PROTOCOL = getApplicationProtocol;
120         GET_HANDSHAKE_APPLICATION_PROTOCOL = getHandshakeApplicationProtocol;
121         SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = setHandshakeApplicationProtocolSelector;
122         GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR = getHandshakeApplicationProtocolSelector;
123         BC_APPLICATION_PROTOCOL_SELECTOR_SELECT = bcApplicationProtocolSelectorSelect;
124         BC_APPLICATION_PROTOCOL_SELECTOR = bcApplicationProtocolSelector;
125     }
126 
127     private BouncyCastleAlpnSslUtils() {
128     }
129 
130     static String getApplicationProtocol(SSLEngine sslEngine) {
131         try {
132             return (String) GET_APPLICATION_PROTOCOL.invoke(sslEngine);
133         } catch (UnsupportedOperationException ex) {
134             throw ex;
135         } catch (Exception ex) {
136             throw new IllegalStateException(ex);
137         }
138     }
139 
140     static void setApplicationProtocols(SSLEngine engine, List<String> supportedProtocols) {
141         SSLParameters parameters = engine.getSSLParameters();
142 
143         String[] protocolArray = supportedProtocols.toArray(EmptyArrays.EMPTY_STRINGS);
144         try {
145             Object bcSslParameters = BC_SSL_PARAMETERS.newInstance();
146             SET_APPLICATION_PROTOCOLS.invoke(bcSslParameters, new Object[]{protocolArray});
147             SET_PARAMETERS.invoke(engine, bcSslParameters);
148         } catch (UnsupportedOperationException ex) {
149             throw ex;
150         } catch (Exception ex) {
151             throw new IllegalStateException(ex);
152         }
153         engine.setSSLParameters(parameters);
154     }
155 
156     static String getHandshakeApplicationProtocol(SSLEngine sslEngine) {
157         try {
158             return (String) GET_HANDSHAKE_APPLICATION_PROTOCOL.invoke(sslEngine);
159         } catch (UnsupportedOperationException ex) {
160             throw ex;
161         } catch (Exception ex) {
162             throw new IllegalStateException(ex);
163         }
164     }
165 
166     static void setHandshakeApplicationProtocolSelector(
167             SSLEngine engine, final BiFunction<SSLEngine, List<String>, String> selector) {
168         try {
169             Object selectorProxyInstance = Proxy.newProxyInstance(
170                     BouncyCastleAlpnSslUtils.class.getClassLoader(),
171                     new Class[]{BC_APPLICATION_PROTOCOL_SELECTOR},
172                     (proxy, method, args) -> {
173                         if ("select".equals(method.getName())) {
174                             try {
175                                 return selector.apply((SSLEngine) args[0], (List<String>) args[1]);
176                             } catch (ClassCastException e) {
177                                 throw new RuntimeException("BCApplicationProtocolSelector select method " +
178                                         "parameter of invalid type.", e);
179                             }
180                         } else {
181                             throw new UnsupportedOperationException(String.format("Method '%s' not supported.",
182                                     method.getName()));
183                         }
184                     });
185 
186             SET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine, selectorProxyInstance);
187         } catch (UnsupportedOperationException ex) {
188             throw ex;
189         } catch (Exception ex) {
190             throw new IllegalStateException(ex);
191         }
192     }
193 
194     static BiFunction<SSLEngine, List<String>, String> getHandshakeApplicationProtocolSelector(SSLEngine engine) {
195         try {
196             final Object selector = GET_HANDSHAKE_APPLICATION_PROTOCOL_SELECTOR.invoke(engine);
197             return (sslEngine, strings) -> {
198                 try {
199                     return (String) BC_APPLICATION_PROTOCOL_SELECTOR_SELECT.invoke(selector, sslEngine,
200                             strings);
201                 } catch (Exception e) {
202                     throw new RuntimeException("Could not call getHandshakeApplicationProtocolSelector", e);
203                 }
204             };
205 
206         } catch (UnsupportedOperationException ex) {
207             throw ex;
208         } catch (Exception ex) {
209             throw new IllegalStateException(ex);
210         }
211     }
212 }