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