1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.ssl.util;
17
18 import io.netty.util.internal.ThrowableUtil;
19 import io.netty.util.internal.logging.InternalLogger;
20 import io.netty.util.internal.logging.InternalLoggerFactory;
21
22 import java.security.AccessController;
23 import java.security.PrivilegedAction;
24 import java.security.Provider;
25 import java.security.Security;
26 import javax.net.ssl.SSLEngine;
27
28
29
30
31 public final class BouncyCastleUtil {
32 private static final InternalLogger logger = InternalLoggerFactory.getInstance(BouncyCastleUtil.class);
33
34 private static final String BC_PROVIDER_NAME = "BC";
35 private static final String BC_PROVIDER = "org.bouncycastle.jce.provider.BouncyCastleProvider";
36 private static final String BC_FIPS_PROVIDER_NAME = "BCFIPS";
37 private static final String BC_FIPS_PROVIDER = "org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider";
38 private static final String BC_JSSE_PROVIDER_NAME = "BCJSSE";
39 private static final String BC_JSSE_PROVIDER = "org.bouncycastle.jsse.provider.BouncyCastleJsseProvider";
40 private static final String BC_PEMPARSER = "org.bouncycastle.openssl.PEMParser";
41 private static final String BC_JSSE_SSLENGINE = "org.bouncycastle.jsse.BCSSLEngine";
42 private static final String BC_JSSE_ALPN_SELECTOR = "org.bouncycastle.jsse.BCApplicationProtocolSelector";
43
44
45
46
47 public static boolean isBcProvAvailable() {
48 BcProv.ensureLoaded();
49 return BcProv.unavailabilityCauseBcProv == null;
50 }
51
52
53
54
55 public static boolean isBcPkixAvailable() {
56 BcPkix.ensureLoaded();
57 return BcPkix.unavailabilityCauseBcPkix == null;
58 }
59
60
61
62
63 public static boolean isBcTlsAvailable() {
64 BcTls.ensureLoaded();
65 return BcTls.unavailabilityCauseBcTls == null;
66 }
67
68
69
70
71 public static Throwable unavailabilityCauseBcProv() {
72 BcProv.ensureLoaded();
73 return BcProv.unavailabilityCauseBcProv;
74 }
75
76
77
78
79 public static Throwable unavailabilityCauseBcPkix() {
80 BcPkix.ensureLoaded();
81 return BcPkix.unavailabilityCauseBcPkix;
82 }
83
84
85
86
87 public static Throwable unavailabilityCauseBcTls() {
88 BcTls.ensureLoaded();
89 return BcTls.unavailabilityCauseBcTls;
90 }
91
92
93
94
95 public static boolean isBcJsseInUse(SSLEngine engine) {
96 BcTls.ensureLoaded();
97 Class<? extends SSLEngine> bcEngineClass = BcTls.bcSSLEngineClass;
98 return bcEngineClass != null && bcEngineClass.isInstance(engine);
99 }
100
101
102
103
104 public static Provider getBcProviderJce() {
105 BcProv.ensureLoaded();
106 Throwable cause = BcProv.unavailabilityCauseBcProv;
107 Provider provider = BcProv.bcProviderJce;
108 if (cause != null || provider == null) {
109 throw new IllegalStateException(cause);
110 }
111 return provider;
112 }
113
114
115
116
117 public static Provider getBcProviderJsse() {
118 BcTls.ensureLoaded();
119 Throwable cause = BcTls.unavailabilityCauseBcTls;
120 Provider provider = BcTls.bcProviderJsse;
121 if (cause != null || provider == null) {
122 throw new IllegalStateException(cause);
123 }
124 return provider;
125 }
126
127
128
129
130
131
132
133 public static Class<? extends SSLEngine> getBcSSLEngineClass() {
134 BcTls.ensureLoaded();
135 return BcTls.bcSSLEngineClass;
136 }
137
138
139
140
141 static void reset() {
142 BcProv.attemptedLoading = false;
143 BcPkix.attemptedLoading = false;
144 BcTls.attemptedLoading = false;
145 BcProv.unavailabilityCauseBcProv = null;
146 BcPkix.unavailabilityCauseBcPkix = null;
147 BcTls.unavailabilityCauseBcTls = null;
148 BcProv.bcProviderJce = null;
149 BcTls.bcProviderJsse = null;
150 BcTls.bcSSLEngineClass = null;
151 }
152
153 private static final class BcProv {
154 static volatile Throwable unavailabilityCauseBcProv;
155 static volatile Provider bcProviderJce;
156 static volatile boolean attemptedLoading;
157
158 @SuppressWarnings("unchecked")
159 private static void ensureLoaded() {
160 if (!attemptedLoading) {
161 AccessController.doPrivileged((PrivilegedAction<?>) () -> {
162 try {
163
164 Provider provider = Security.getProvider(BC_PROVIDER_NAME);
165 if (provider == null) {
166 provider = Security.getProvider(BC_FIPS_PROVIDER_NAME);
167 }
168 if (provider == null) {
169 ClassLoader classLoader = BouncyCastleUtil.class.getClassLoader();
170 Class<Provider> bcProviderClass;
171 try {
172 bcProviderClass = (Class<Provider>) Class.forName(
173 BC_PROVIDER, true, classLoader);
174 } catch (ClassNotFoundException e) {
175 try {
176 bcProviderClass = (Class<Provider>) Class.forName(
177 BC_FIPS_PROVIDER, true, classLoader);
178 } catch (ClassNotFoundException ex) {
179 ThrowableUtil.addSuppressed(e, ex);
180 throw e;
181 }
182 }
183 provider = bcProviderClass.getConstructor().newInstance();
184 }
185 bcProviderJce = provider;
186 logger.debug("Bouncy Castle provider available");
187 } catch (Throwable e) {
188 String msg = "Bouncy Castle provider (bcprov-jdk18on or bc-fips) was not loaded";
189 if (includeLoadingErrorStackTrace(e)) {
190 logger.debug(msg, e);
191 } else {
192 logger.debug(msg);
193 }
194 unavailabilityCauseBcProv = e;
195 }
196 return null;
197 });
198 attemptedLoading = true;
199 }
200 }
201 }
202
203 private static final class BcPkix {
204 static volatile Throwable unavailabilityCauseBcPkix;
205 static volatile boolean attemptedLoading;
206
207 private static void ensureLoaded() {
208 if (!attemptedLoading) {
209 BcProv.ensureLoaded();
210 AccessController.doPrivileged((PrivilegedAction<?>) () -> {
211 try {
212
213 ClassLoader classLoader = BouncyCastleUtil.class.getClassLoader();
214 Provider provider = BcProv.bcProviderJce;
215 if (provider != null) {
216
217 classLoader = provider.getClass().getClassLoader();
218 }
219 Class.forName(BC_PEMPARSER, true, classLoader);
220 logger.debug("Bouncy Castle PKIX available");
221 } catch (Throwable e) {
222 String msg = "Bouncy Castle PKIX (bcpkix-jdk18on) was not loaded";
223 if (includeLoadingErrorStackTrace(e)) {
224 logger.debug(msg, e);
225 } else {
226 logger.debug(msg);
227 }
228 unavailabilityCauseBcPkix = e;
229 }
230 return null;
231 });
232 attemptedLoading = true;
233 }
234 }
235 }
236
237 private static final class BcTls {
238 static volatile Throwable unavailabilityCauseBcTls;
239 static volatile Provider bcProviderJsse;
240 static volatile Class<? extends SSLEngine> bcSSLEngineClass;
241 static volatile boolean attemptedLoading;
242
243 @SuppressWarnings("unchecked")
244 private static void ensureLoaded() {
245 if (!attemptedLoading) {
246 AccessController.doPrivileged((PrivilegedAction<?>) () -> {
247 try {
248
249 ClassLoader classLoader = BouncyCastleUtil.class.getClassLoader();
250 Provider provider = Security.getProvider(BC_JSSE_PROVIDER_NAME);
251 if (provider != null) {
252
253 classLoader = provider.getClass().getClassLoader();
254 } else {
255 Class<?> providerClass = Class.forName(BC_JSSE_PROVIDER, true, classLoader);
256 provider = (Provider) providerClass.getConstructor().newInstance();
257 }
258 bcSSLEngineClass = (Class<? extends SSLEngine>) Class.forName(
259 BC_JSSE_SSLENGINE, true, classLoader);
260 Class.forName(BC_JSSE_ALPN_SELECTOR, true, classLoader);
261 bcProviderJsse = provider;
262 logger.debug("Bouncy Castle JSSE available");
263 } catch (Throwable e) {
264 String msg = "Bouncy Castle TLS (bctls-jdk18on) was not loaded";
265 if (includeLoadingErrorStackTrace(e)) {
266 logger.debug(msg, e);
267 } else {
268 logger.debug(msg);
269 }
270 unavailabilityCauseBcTls = e;
271 }
272 return null;
273 });
274 attemptedLoading = true;
275 }
276 }
277 }
278
279 private static boolean includeLoadingErrorStackTrace(Throwable e) {
280
281
282
283
284 return logger.isTraceEnabled() || !(e instanceof ClassNotFoundException);
285 }
286
287 private BouncyCastleUtil() {
288 }
289 }