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