1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 }