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