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