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