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