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