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.internal.tcnative.CertificateCallback;
19 import io.netty.internal.tcnative.SSL;
20 import io.netty.internal.tcnative.SSLContext;
21 import io.netty.util.internal.EmptyArrays;
22
23 import javax.net.ssl.KeyManagerFactory;
24 import javax.net.ssl.SNIServerName;
25 import javax.net.ssl.SSLException;
26 import javax.net.ssl.TrustManagerFactory;
27 import javax.net.ssl.X509ExtendedTrustManager;
28 import javax.net.ssl.X509TrustManager;
29 import javax.security.auth.x500.X500Principal;
30 import java.security.KeyStore;
31 import java.security.PrivateKey;
32 import java.security.cert.X509Certificate;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Set;
37
38
39
40
41
42
43
44
45
46 public final class ReferenceCountedOpenSslClientContext extends ReferenceCountedOpenSslContext {
47
48 private static final String[] SUPPORTED_KEY_TYPES = {
49 OpenSslKeyMaterialManager.KEY_TYPE_RSA,
50 OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA,
51 OpenSslKeyMaterialManager.KEY_TYPE_EC,
52 OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA,
53 OpenSslKeyMaterialManager.KEY_TYPE_EC_EC
54 };
55
56 private final OpenSslSessionContext sessionContext;
57
58 ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
59 X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
60 KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
61 CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
62 String[] protocols, long sessionCacheSize, long sessionTimeout,
63 boolean enableOcsp, String keyStore, String endpointIdentificationAlgorithm,
64 List<SNIServerName> serverNames,
65 ResumptionController resumptionController,
66 Map.Entry<SslContextOption<?>, Object>[] options,
67 List<OpenSslCredential> credentials) throws SSLException {
68 this(trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, ciphers,
69 cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, false, enableOcsp, keyStore,
70 endpointIdentificationAlgorithm, serverNames, resumptionController, options, credentials);
71 }
72
73 ReferenceCountedOpenSslClientContext(X509Certificate[] trustCertCollection, TrustManagerFactory trustManagerFactory,
74 X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
75 KeyManagerFactory keyManagerFactory, Iterable<String> ciphers,
76 CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
77 String[] protocols, long sessionCacheSize, long sessionTimeout,
78 boolean startTls, boolean enableOcsp, String keyStore,
79 String endpointIdentificationAlgorithm, List<SNIServerName> serverNames,
80 ResumptionController resumptionController,
81 Map.Entry<SslContextOption<?>, Object>[] options,
82 List<OpenSslCredential> credentials) throws SSLException {
83 super(ciphers, cipherFilter, toNegotiator(apn), SSL.SSL_MODE_CLIENT, keyCertChain,
84 ClientAuth.NONE, protocols, startTls, endpointIdentificationAlgorithm, enableOcsp, true,
85 serverNames, resumptionController, options, credentials);
86 boolean success = false;
87 try {
88 sessionContext = newSessionContext(this, ctx, engines, trustCertCollection, trustManagerFactory,
89 keyCertChain, key, keyPassword, keyManagerFactory, keyStore,
90 sessionCacheSize, sessionTimeout, resumptionController,
91 isJdkSignatureFallbackEnabled(options));
92 success = true;
93 } finally {
94 if (!success) {
95 release();
96 }
97 }
98 }
99
100 @Override
101 public OpenSslSessionContext sessionContext() {
102 return sessionContext;
103 }
104
105 static OpenSslSessionContext newSessionContext(ReferenceCountedOpenSslContext thiz, long ctx,
106 OpenSslEngineMap engines,
107 X509Certificate[] trustCertCollection,
108 TrustManagerFactory trustManagerFactory,
109 X509Certificate[] keyCertChain, PrivateKey key,
110 String keyPassword, KeyManagerFactory keyManagerFactory,
111 String keyStore, long sessionCacheSize, long sessionTimeout,
112 ResumptionController resumptionController,
113 boolean fallbackToJdkProviders)
114 throws SSLException {
115 if (key == null && keyCertChain != null || key != null && keyCertChain == null) {
116 throw new IllegalArgumentException(
117 "Either both keyCertChain and key needs to be null or none of them");
118 }
119 OpenSslKeyMaterialProvider keyMaterialProvider = null;
120 try {
121 try {
122
123
124 if (keyManagerFactory == null && key != null && key.getEncoded() == null) {
125 if (!fallbackToJdkProviders) {
126 throw new SSLException("Private key requiring alternative signature provider detected " +
127 "(such as hardware security key, smart card, or remote signing service) but " +
128 "alternative key fallback is disabled.");
129 }
130 keyMaterialProvider = setupSecurityProviderSignatureSource(thiz, ctx, keyCertChain, key,
131 materialManager -> new OpenSslClientCertificateCallback(
132 engines, materialManager));
133 } else if (!OpenSsl.useKeyManagerFactory()) {
134 if (keyManagerFactory != null) {
135 throw new IllegalArgumentException(
136 "KeyManagerFactory not supported");
137 }
138 if (keyCertChain != null) {
139 setKeyMaterial(ctx, keyCertChain, key, keyPassword);
140 }
141 } else {
142
143 if (keyManagerFactory == null && keyCertChain != null) {
144 keyManagerFactory = certChainToKeyManagerFactory(keyCertChain, key, keyPassword, keyStore);
145 }
146 if (keyManagerFactory != null) {
147 keyMaterialProvider = providerFor(keyManagerFactory, keyPassword);
148 }
149
150 if (keyMaterialProvider != null) {
151 OpenSslKeyMaterialManager materialManager =
152 new OpenSslKeyMaterialManager(keyMaterialProvider, thiz.hasTmpDhKeys);
153 SSLContext.setCertificateCallback(ctx, new OpenSslClientCertificateCallback(
154 engines, materialManager));
155 }
156 }
157 } catch (Exception e) {
158 throw new SSLException("failed to set certificate and key", e);
159 }
160
161
162
163
164
165
166
167 SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_OPTIONAL, VERIFY_DEPTH);
168
169 try {
170 if (trustCertCollection != null) {
171 trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
172 } else if (trustManagerFactory == null) {
173 trustManagerFactory = TrustManagerFactory.getInstance(
174 TrustManagerFactory.getDefaultAlgorithm());
175 trustManagerFactory.init((KeyStore) null);
176 }
177 final X509TrustManager manager = chooseTrustManager(
178 trustManagerFactory.getTrustManagers(), resumptionController);
179
180
181
182
183
184
185
186 if (thiz.endpointIdentificationAlgorithm != null && !thiz.endpointIdentificationAlgorithm.isEmpty() &&
187 !useExtendedTrustManager(manager)) {
188 throw new UnsupportedOperationException(
189 "Endpoint identification algorithm '" + thiz.endpointIdentificationAlgorithm + "' is " +
190 "configured but the trust manager does not support extended trust manager verification. " +
191 "Please provide an X509ExtendedTrustManager or use the SslProvider.JDK.");
192 }
193
194 setVerifyCallback(ctx, engines, manager);
195 } catch (Exception e) {
196 if (keyMaterialProvider != null) {
197 keyMaterialProvider.destroy();
198 }
199 throw new SSLException("unable to setup trustmanager", e);
200 }
201 OpenSslClientSessionContext context = new OpenSslClientSessionContext(thiz, keyMaterialProvider);
202 context.setSessionCacheEnabled(CLIENT_ENABLE_SESSION_CACHE);
203 if (sessionCacheSize > 0) {
204 context.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
205 }
206 if (sessionTimeout > 0) {
207 context.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
208 }
209
210 if (CLIENT_ENABLE_SESSION_TICKET) {
211 context.setTicketKeys();
212 }
213
214 keyMaterialProvider = null;
215 return context;
216 } finally {
217 if (keyMaterialProvider != null) {
218 keyMaterialProvider.destroy();
219 }
220 }
221 }
222
223 private static void setVerifyCallback(long ctx,
224 OpenSslEngineMap engines,
225 X509TrustManager manager) {
226
227 if (useExtendedTrustManager(manager)) {
228 SSLContext.setCertVerifyCallback(ctx,
229 new ExtendedTrustManagerVerifyCallback(engines, (X509ExtendedTrustManager) manager));
230 } else {
231 SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engines, manager));
232 }
233 }
234
235 static final class OpenSslClientSessionContext extends OpenSslSessionContext {
236 OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
237 super(context, provider, SSL.SSL_SESS_CACHE_CLIENT, new OpenSslClientSessionCache(context.engines));
238 }
239 }
240
241 private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier {
242 private final X509TrustManager manager;
243
244 TrustManagerVerifyCallback(OpenSslEngineMap engines, X509TrustManager manager) {
245 super(engines);
246 this.manager = manager;
247 }
248
249 @Override
250 void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth)
251 throws Exception {
252 manager.checkServerTrusted(peerCerts, auth);
253 }
254 }
255
256 private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier {
257 private final X509ExtendedTrustManager manager;
258
259 ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engines,
260 X509ExtendedTrustManager manager) {
261 super(engines);
262 this.manager = manager;
263 }
264
265 @Override
266 void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth)
267 throws Exception {
268 manager.checkServerTrusted(peerCerts, auth, engine);
269 }
270 }
271
272 private static final class OpenSslClientCertificateCallback implements CertificateCallback {
273 private final OpenSslEngineMap engines;
274 private final OpenSslKeyMaterialManager keyManagerHolder;
275
276 OpenSslClientCertificateCallback(OpenSslEngineMap engines,
277 OpenSslKeyMaterialManager keyManagerHolder) {
278 this.engines = engines;
279 this.keyManagerHolder = keyManagerHolder;
280 }
281
282 @Override
283 public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception {
284 final ReferenceCountedOpenSslEngine engine = engines.get(ssl);
285
286 if (engine == null) {
287 return;
288 }
289 try {
290 final String[] keyTypes = supportedClientKeyTypes(keyTypeBytes);
291 final X500Principal[] issuers;
292 if (asn1DerEncodedPrincipals == null) {
293 issuers = null;
294 } else {
295 issuers = new X500Principal[asn1DerEncodedPrincipals.length];
296 for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
297 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
298 }
299 }
300 keyManagerHolder.setKeyMaterialClientSide(engine, keyTypes, issuers);
301 } catch (Throwable cause) {
302 engine.initHandshakeException(cause);
303 if (cause instanceof Exception) {
304 throw (Exception) cause;
305 }
306 throw new SSLException(cause);
307 }
308 }
309
310
311
312
313
314
315
316
317
318 private static String[] supportedClientKeyTypes(byte[] clientCertificateTypes) {
319 if (clientCertificateTypes == null) {
320
321 return SUPPORTED_KEY_TYPES.clone();
322 }
323 Set<String> result = new HashSet<>(clientCertificateTypes.length);
324 for (byte keyTypeCode : clientCertificateTypes) {
325 String keyType = clientKeyType(keyTypeCode);
326 if (keyType == null) {
327
328 continue;
329 }
330 result.add(keyType);
331 }
332 return result.toArray(EmptyArrays.EMPTY_STRINGS);
333 }
334
335 private static String clientKeyType(byte clientCertificateType) {
336
337 switch (clientCertificateType) {
338 case CertificateCallback.TLS_CT_RSA_SIGN:
339 return OpenSslKeyMaterialManager.KEY_TYPE_RSA;
340 case CertificateCallback.TLS_CT_RSA_FIXED_DH:
341 return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA;
342 case CertificateCallback.TLS_CT_ECDSA_SIGN:
343 return OpenSslKeyMaterialManager.KEY_TYPE_EC;
344 case CertificateCallback.TLS_CT_RSA_FIXED_ECDH:
345 return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA;
346 case CertificateCallback.TLS_CT_ECDSA_FIXED_ECDH:
347 return OpenSslKeyMaterialManager.KEY_TYPE_EC_EC;
348 default:
349 return null;
350 }
351 }
352 }
353 }