1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty5.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.netty5.util.internal.EmptyArrays;
22
23 import javax.net.ssl.KeyManagerFactory;
24 import javax.net.ssl.SSLException;
25 import javax.net.ssl.TrustManagerFactory;
26 import javax.net.ssl.X509ExtendedTrustManager;
27 import javax.net.ssl.X509TrustManager;
28 import javax.security.auth.x500.X500Principal;
29 import java.security.KeyStore;
30 import java.security.PrivateKey;
31 import java.security.cert.X509Certificate;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.HashSet;
35 import java.util.LinkedHashSet;
36 import java.util.Map;
37 import java.util.Set;
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<>(
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,
64 Map.Entry<SslContextOption<?>, Object>... options) throws SSLException {
65 super(ciphers, cipherFilter, toNegotiator(apn), SSL.SSL_MODE_CLIENT, keyCertChain,
66 ClientAuth.NONE, protocols, false, 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.supportsKeyManagerFactory()) {
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
160 if (useExtendedTrustManager(manager)) {
161 SSLContext.setCertVerifyCallback(ctx,
162 new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager));
163 } else {
164 SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager));
165 }
166 } catch (Exception e) {
167 if (keyMaterialProvider != null) {
168 keyMaterialProvider.destroy();
169 }
170 throw new SSLException("unable to setup trustmanager", e);
171 }
172 OpenSslClientSessionContext context = new OpenSslClientSessionContext(thiz, keyMaterialProvider);
173 context.setSessionCacheEnabled(CLIENT_ENABLE_SESSION_CACHE);
174 if (sessionCacheSize > 0) {
175 context.setSessionCacheSize((int) Math.min(sessionCacheSize, Integer.MAX_VALUE));
176 }
177 if (sessionTimeout > 0) {
178 context.setSessionTimeout((int) Math.min(sessionTimeout, Integer.MAX_VALUE));
179 }
180
181 if (CLIENT_ENABLE_SESSION_TICKET) {
182 context.setTicketKeys();
183 }
184
185 keyMaterialProvider = null;
186 return context;
187 } finally {
188 if (keyMaterialProvider != null) {
189 keyMaterialProvider.destroy();
190 }
191 }
192 }
193
194 static final class OpenSslClientSessionContext extends OpenSslSessionContext {
195 OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
196 super(context, provider, SSL.SSL_SESS_CACHE_CLIENT, new OpenSslClientSessionCache(context.engineMap));
197 }
198 }
199
200 private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier {
201 private final X509TrustManager manager;
202
203 TrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509TrustManager manager) {
204 super(engineMap);
205 this.manager = manager;
206 }
207
208 @Override
209 void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth)
210 throws Exception {
211 manager.checkServerTrusted(peerCerts, auth);
212 }
213 }
214
215 private static final class ExtendedTrustManagerVerifyCallback extends AbstractCertificateVerifier {
216 private final X509ExtendedTrustManager manager;
217
218 ExtendedTrustManagerVerifyCallback(OpenSslEngineMap engineMap, X509ExtendedTrustManager manager) {
219 super(engineMap);
220 this.manager = manager;
221 }
222
223 @Override
224 void verify(ReferenceCountedOpenSslEngine engine, X509Certificate[] peerCerts, String auth)
225 throws Exception {
226 manager.checkServerTrusted(peerCerts, auth, engine);
227 }
228 }
229
230 private static final class OpenSslClientCertificateCallback implements CertificateCallback {
231 private final OpenSslEngineMap engineMap;
232 private final OpenSslKeyMaterialManager keyManagerHolder;
233
234 OpenSslClientCertificateCallback(OpenSslEngineMap engineMap, OpenSslKeyMaterialManager keyManagerHolder) {
235 this.engineMap = engineMap;
236 this.keyManagerHolder = keyManagerHolder;
237 }
238
239 @Override
240 public void handle(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) throws Exception {
241 final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
242
243 if (engine == null) {
244 return;
245 }
246 try {
247 final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes);
248 final String[] keyTypes = keyTypesSet.toArray(EmptyArrays.EMPTY_STRINGS);
249 final X500Principal[] issuers;
250 if (asn1DerEncodedPrincipals == null) {
251 issuers = null;
252 } else {
253 issuers = new X500Principal[asn1DerEncodedPrincipals.length];
254 for (int i = 0; i < asn1DerEncodedPrincipals.length; i++) {
255 issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
256 }
257 }
258 keyManagerHolder.setKeyMaterialClientSide(engine, keyTypes, issuers);
259 } catch (Throwable cause) {
260 engine.initHandshakeException(cause);
261 if (cause instanceof Exception) {
262 throw (Exception) cause;
263 }
264 throw new SSLException(cause);
265 }
266 }
267
268
269
270
271
272
273
274
275
276 private static Set<String> supportedClientKeyTypes(byte[] clientCertificateTypes) {
277 if (clientCertificateTypes == null) {
278
279 return SUPPORTED_KEY_TYPES;
280 }
281 Set<String> result = new HashSet<>(clientCertificateTypes.length);
282 for (byte keyTypeCode : clientCertificateTypes) {
283 String keyType = clientKeyType(keyTypeCode);
284 if (keyType == null) {
285
286 continue;
287 }
288 result.add(keyType);
289 }
290 return result;
291 }
292
293 private static String clientKeyType(byte clientCertificateType) {
294
295 switch (clientCertificateType) {
296 case CertificateCallback.TLS_CT_RSA_SIGN:
297 return OpenSslKeyMaterialManager.KEY_TYPE_RSA;
298 case CertificateCallback.TLS_CT_RSA_FIXED_DH:
299 return OpenSslKeyMaterialManager.KEY_TYPE_DH_RSA;
300 case CertificateCallback.TLS_CT_ECDSA_SIGN:
301 return OpenSslKeyMaterialManager.KEY_TYPE_EC;
302 case CertificateCallback.TLS_CT_RSA_FIXED_ECDH:
303 return OpenSslKeyMaterialManager.KEY_TYPE_EC_RSA;
304 case CertificateCallback.TLS_CT_ECDSA_FIXED_ECDH:
305 return OpenSslKeyMaterialManager.KEY_TYPE_EC_EC;
306 default:
307 return null;
308 }
309 }
310 }
311 }