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