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