1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.quic;
17
18 import io.netty.buffer.ByteBufAllocator;
19 import io.netty.handler.ssl.ApplicationProtocolNegotiator;
20 import io.netty.handler.ssl.ClientAuth;
21 import io.netty.handler.ssl.SslContext;
22 import io.netty.handler.ssl.SslContextOption;
23 import io.netty.handler.ssl.SslHandler;
24 import io.netty.util.AbstractReferenceCounted;
25 import io.netty.util.Mapping;
26 import io.netty.util.ReferenceCounted;
27 import io.netty.util.internal.EmptyArrays;
28 import io.netty.util.internal.SystemPropertyUtil;
29 import io.netty.util.internal.logging.InternalLogger;
30 import io.netty.util.internal.logging.InternalLoggerFactory;
31 import org.jetbrains.annotations.Nullable;
32
33 import javax.net.ssl.KeyManager;
34 import javax.net.ssl.KeyManagerFactory;
35 import javax.net.ssl.SSLSession;
36 import javax.net.ssl.TrustManager;
37 import javax.net.ssl.TrustManagerFactory;
38 import javax.net.ssl.X509ExtendedKeyManager;
39 import javax.net.ssl.X509ExtendedTrustManager;
40 import javax.net.ssl.X509TrustManager;
41 import java.io.File;
42 import java.io.IOException;
43 import java.security.KeyStore;
44 import java.security.KeyStoreException;
45 import java.security.NoSuchAlgorithmException;
46 import java.security.PrivateKey;
47 import java.security.cert.CertificateException;
48 import java.security.cert.X509Certificate;
49 import java.util.Arrays;
50 import java.util.Collections;
51 import java.util.Enumeration;
52 import java.util.Iterator;
53 import java.util.LinkedHashSet;
54 import java.util.List;
55 import java.util.Map;
56 import java.util.NoSuchElementException;
57 import java.util.Set;
58 import java.util.concurrent.Executor;
59 import java.util.function.BiConsumer;
60 import java.util.function.LongFunction;
61
62 import static io.netty.util.internal.ObjectUtil.checkNotNull;
63
64 final class QuicheQuicSslContext extends QuicSslContext {
65
66 private static final InternalLogger LOGGER = InternalLoggerFactory.getInstance(QuicheQuicSslContext.class);
67
68
69
70
71 private static final String[] DEFAULT_NAMED_GROUPS = { "x25519", "secp256r1", "secp384r1", "secp521r1" };
72 private static final String[] NAMED_GROUPS;
73
74 static final String defaultEndpointVerificationAlgorithm = SslContext.defaultEndpointVerificationAlgorithm;
75
76 static {
77 String[] namedGroups = DEFAULT_NAMED_GROUPS;
78 Set<String> defaultConvertedNamedGroups = new LinkedHashSet<>(namedGroups.length);
79 for (int i = 0; i < namedGroups.length; i++) {
80 defaultConvertedNamedGroups.add(GroupsConverter.toBoringSSL(namedGroups[i]));
81 }
82
83
84
85 if (Quic.isAvailable()) {
86 final long sslCtx = BoringSSL.SSLContext_new();
87 try {
88
89 Iterator<String> defaultGroupsIter = defaultConvertedNamedGroups.iterator();
90 while (defaultGroupsIter.hasNext()) {
91 if (BoringSSL.SSLContext_set1_groups_list(sslCtx, defaultGroupsIter.next()) == 0) {
92
93
94
95 defaultGroupsIter.remove();
96 }
97 }
98
99 String groups = SystemPropertyUtil.get("jdk.tls.namedGroups", null);
100 if (groups != null) {
101 String[] nGroups = groups.split(",");
102 Set<String> supportedNamedGroups = new LinkedHashSet<>(nGroups.length);
103 Set<String> supportedConvertedNamedGroups = new LinkedHashSet<>(nGroups.length);
104
105 Set<String> unsupportedNamedGroups = new LinkedHashSet<>();
106 for (String namedGroup : nGroups) {
107 String converted = GroupsConverter.toBoringSSL(namedGroup);
108
109 if (BoringSSL.SSLContext_set1_groups_list(sslCtx, converted) == 0) {
110 unsupportedNamedGroups.add(namedGroup);
111 } else {
112 supportedConvertedNamedGroups.add(converted);
113 supportedNamedGroups.add(namedGroup);
114 }
115 }
116
117 if (supportedNamedGroups.isEmpty()) {
118 namedGroups = defaultConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
119 LOGGER.info("All configured namedGroups are not supported: {}. Use default: {}.",
120 Arrays.toString(unsupportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS)),
121 Arrays.toString(DEFAULT_NAMED_GROUPS));
122 } else {
123 String[] groupArray = supportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
124 if (unsupportedNamedGroups.isEmpty()) {
125 LOGGER.info("Using configured namedGroups -D 'jdk.tls.namedGroup': {} ",
126 Arrays.toString(groupArray));
127 } else {
128 LOGGER.info("Using supported configured namedGroups: {}. Unsupported namedGroups: {}. ",
129 Arrays.toString(groupArray),
130 Arrays.toString(unsupportedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS)));
131 }
132 namedGroups = supportedConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
133 }
134 } else {
135 namedGroups = defaultConvertedNamedGroups.toArray(EmptyArrays.EMPTY_STRINGS);
136 }
137 } finally {
138 BoringSSL.SSLContext_free(sslCtx);
139 }
140 }
141 NAMED_GROUPS = namedGroups;
142 }
143
144 final ClientAuth clientAuth;
145 private final boolean server;
146 private final String endpointIdentificationAlgorithm;
147 @SuppressWarnings("deprecation")
148 private final ApplicationProtocolNegotiator apn;
149 private long sessionCacheSize;
150 private long sessionTimeout;
151 private final QuicheQuicSslSessionContext sessionCtx;
152 private final QuicheQuicSslEngineMap engineMap = new QuicheQuicSslEngineMap();
153 private final QuicClientSessionCache sessionCache;
154
155 private final BoringSSLSessionTicketCallback sessionTicketCallback = new BoringSSLSessionTicketCallback();
156
157 final NativeSslContext nativeSslContext;
158
159 QuicheQuicSslContext(boolean server, long sessionTimeout, long sessionCacheSize,
160 ClientAuth clientAuth, @Nullable TrustManagerFactory trustManagerFactory,
161 X509Certificate @Nullable [] trustCertCollection,
162 @Nullable KeyManagerFactory keyManagerFactory,
163 X509Certificate @Nullable [] keyCertChain, String password,
164 @Nullable Mapping<? super String, ? extends QuicSslContext> mapping,
165 @Nullable Boolean earlyData, @Nullable BoringSSLKeylog keylog,
166 String[] applicationProtocols, String endpointIdentificationAlgorithm,
167 Map.Entry<SslContextOption<?>, Object>... ctxOptions) {
168 Quic.ensureAvailability();
169 this.server = server;
170 this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
171 this.clientAuth = server ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
172 final X509TrustManager trustManager;
173 final boolean hasExternalTrustManagerFactorySpecified = trustManagerFactory != null;
174 if (trustManagerFactory == null) {
175 try {
176 trustManagerFactory =
177 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
178 trustManagerFactory.init((KeyStore) null);
179 trustManager = chooseTrustManager(trustManagerFactory);
180 } catch (Exception e) {
181 throw new IllegalStateException(e);
182 }
183 } else {
184 trustManager = chooseTrustManager(trustManagerFactory);
185 }
186 if (this.endpointIdentificationAlgorithm != null && !this.endpointIdentificationAlgorithm.isEmpty() &&
187 !(trustManager instanceof X509ExtendedTrustManager)) {
188 throw new UnsupportedOperationException(
189 "Endpoint identification algorithm '" + this.endpointIdentificationAlgorithm + "' is " +
190 "configured but the trust manager does not support extended trust manager verification. " +
191 "Please provide an X509ExtendedTrustManager.");
192 }
193 final X509ExtendedKeyManager keyManager;
194 if (keyManagerFactory == null) {
195 if (server) {
196 throw new IllegalArgumentException("No KeyManagerFactory");
197 }
198 keyManager = null;
199 } else {
200 keyManager = chooseKeyManager(keyManagerFactory);
201 }
202 String[] groups = NAMED_GROUPS;
203 String[] sigalgs = EmptyArrays.EMPTY_STRINGS;
204 Map<String, String> serverKeyTypes = null;
205 Set<String> clientKeyTypes = null;
206
207 if (ctxOptions != null) {
208 for (Map.Entry<SslContextOption<?>, Object> ctxOpt : ctxOptions) {
209 SslContextOption<?> option = ctxOpt.getKey();
210
211 if (option == BoringSSLContextOption.GROUPS) {
212 String[] groupsArray = (String[]) ctxOpt.getValue();
213 Set<String> groupsSet = new LinkedHashSet<String>(groupsArray.length);
214 for (String group : groupsArray) {
215 groupsSet.add(GroupsConverter.toBoringSSL(group));
216 }
217 groups = groupsSet.toArray(EmptyArrays.EMPTY_STRINGS);
218 } else if (option == BoringSSLContextOption.SIGNATURE_ALGORITHMS) {
219 String[] sigalgsArray = (String[]) ctxOpt.getValue();
220 Set<String> sigalgsSet = new LinkedHashSet<String>(sigalgsArray.length);
221 for (String sigalg : sigalgsArray) {
222 sigalgsSet.add(sigalg);
223 }
224 sigalgs = sigalgsSet.toArray(EmptyArrays.EMPTY_STRINGS);
225 } else if (option == BoringSSLContextOption.CLIENT_KEY_TYPES) {
226 clientKeyTypes = (Set<String>) ctxOpt.getValue();
227 } else if (option == BoringSSLContextOption.SERVER_KEY_TYPES) {
228 serverKeyTypes = (Map<String, String>) ctxOpt.getValue();
229 } else {
230 LOGGER.debug("Skipping unsupported " + SslContextOption.class.getSimpleName()
231 + ": " + ctxOpt.getKey());
232 }
233 }
234 }
235 final BoringSSLPrivateKeyMethod privateKeyMethod;
236 if (keyManagerFactory instanceof BoringSSLKeylessManagerFactory) {
237 privateKeyMethod = new BoringSSLAsyncPrivateKeyMethodAdapter(engineMap,
238 ((BoringSSLKeylessManagerFactory) keyManagerFactory).privateKeyMethod);
239 } else {
240 privateKeyMethod = null;
241 }
242 sessionCache = server ? null : new QuicClientSessionCache();
243 int verifyMode = server ? boringSSLVerifyModeForServer(this.clientAuth) : BoringSSL.SSL_VERIFY_PEER;
244 nativeSslContext = new NativeSslContext(BoringSSL.SSLContext_new(server, applicationProtocols,
245 new BoringSSLHandshakeCompleteCallback(engineMap),
246 new BoringSSLCertificateCallback(engineMap, keyManager, password, serverKeyTypes, clientKeyTypes),
247 new BoringSSLCertificateVerifyCallback(engineMap, trustManager),
248 mapping == null ? null : new BoringSSLTlsextServernameCallback(engineMap, mapping),
249 keylog == null ? null : new BoringSSLKeylogCallback(engineMap, keylog),
250 server ? null : new BoringSSLSessionCallback(engineMap, sessionCache), privateKeyMethod,
251 sessionTicketCallback, verifyMode,
252 BoringSSL.subjectNames(trustManager.getAcceptedIssuers())));
253 boolean success = false;
254 try {
255 if (groups.length > 0 && BoringSSL.SSLContext_set1_groups_list(nativeSslContext.ctx, groups) == 0) {
256 String msg = "failed to set curves / groups list: " + Arrays.toString(groups);
257 String lastError = BoringSSL.ERR_last_error();
258 if (lastError != null) {
259
260 msg += ". " + lastError;
261 }
262 throw new IllegalStateException(msg);
263 }
264
265 if (sigalgs.length > 0 && BoringSSL.SSLContext_set1_sigalgs_list(nativeSslContext.ctx, sigalgs) == 0) {
266 String msg = "failed to set signature algorithm list: " + Arrays.toString(sigalgs);
267 String lastError = BoringSSL.ERR_last_error();
268 if (lastError != null) {
269
270 msg += ". " + lastError;
271 }
272 throw new IllegalStateException(msg);
273 }
274
275 apn = new QuicheQuicApplicationProtocolNegotiator(applicationProtocols);
276 if (this.sessionCache != null) {
277
278 this.sessionCache.setSessionCacheSize((int) sessionCacheSize);
279 this.sessionCache.setSessionTimeout((int) sessionTimeout);
280 } else {
281
282 BoringSSL.SSLContext_setSessionCacheSize(
283 nativeSslContext.address(), sessionCacheSize);
284 this.sessionCacheSize = sessionCacheSize;
285
286 BoringSSL.SSLContext_setSessionCacheTimeout(
287 nativeSslContext.address(), sessionTimeout);
288 this.sessionTimeout = sessionTimeout;
289
290
291
292
293
294
295
296 boolean opaqueConfiguration = keyCertChain == null ||
297 (trustCertCollection == null && hasExternalTrustManagerFactorySpecified);
298 if (!BoringSSL.SSLContext_setSessionIdContext(nativeSslContext.address(),
299 deriveSessionIdContext(this.clientAuth, keyCertChain, trustCertCollection,
300 opaqueConfiguration))) {
301
302 String msg = "failed to set session id context";
303 String lastError = BoringSSL.ERR_last_error();
304 if (lastError != null) {
305 msg += ": " + lastError;
306 }
307 throw new IllegalStateException(msg);
308 }
309 }
310 if (server && this.clientAuth == ClientAuth.REQUIRE) {
311
312
313
314 BoringSSL.SSLContext_set_early_data_enabled(nativeSslContext.address(), false);
315 } else if (earlyData != null) {
316 BoringSSL.SSLContext_set_early_data_enabled(nativeSslContext.address(), earlyData);
317 }
318 sessionCtx = new QuicheQuicSslSessionContext(this);
319 success = true;
320 } finally {
321 if (!success) {
322 nativeSslContext.release();
323 }
324 }
325 }
326
327 private X509ExtendedKeyManager chooseKeyManager(KeyManagerFactory keyManagerFactory) {
328 for (KeyManager manager: keyManagerFactory.getKeyManagers()) {
329 if (manager instanceof X509ExtendedKeyManager) {
330 return (X509ExtendedKeyManager) manager;
331 }
332 }
333 throw new IllegalArgumentException("No X509ExtendedKeyManager included");
334 }
335
336 private static X509TrustManager chooseTrustManager(TrustManagerFactory trustManagerFactory) {
337 for (TrustManager manager: trustManagerFactory.getTrustManagers()) {
338 if (manager instanceof X509TrustManager) {
339 return (X509TrustManager) manager;
340 }
341 }
342 throw new IllegalArgumentException("No X509TrustManager included");
343 }
344
345 static X509Certificate @Nullable [] toX509Certificates0(@Nullable File file) throws CertificateException {
346 return toX509Certificates(file);
347 }
348
349 static PrivateKey toPrivateKey0(@Nullable File keyFile, @Nullable String keyPassword) throws Exception {
350 return toPrivateKey(keyFile, keyPassword);
351 }
352
353 static TrustManagerFactory buildTrustManagerFactory0(
354 X509Certificate @Nullable [] certCollection)
355 throws NoSuchAlgorithmException, CertificateException, KeyStoreException, IOException {
356 return buildTrustManagerFactory(certCollection, null, null);
357 }
358
359 private static int boringSSLVerifyModeForServer(ClientAuth mode) {
360 switch (mode) {
361 case NONE:
362 return BoringSSL.SSL_VERIFY_NONE;
363 case REQUIRE:
364 return BoringSSL.SSL_VERIFY_PEER | BoringSSL.SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
365 case OPTIONAL:
366 return BoringSSL.SSL_VERIFY_PEER;
367 default:
368 throw new Error("Unexpected mode: " + mode);
369 }
370 }
371
372 @Nullable
373 QuicheQuicConnection createConnection(LongFunction<Long> connectionCreator, QuicheQuicSslEngine engine) {
374 nativeSslContext.retain();
375 long ssl = BoringSSL.SSL_new(nativeSslContext.address(), isServer(), engine.tlsHostName);
376 engineMap.put(ssl, engine);
377 long connection = connectionCreator.apply(ssl);
378 if (connection == -1) {
379 engineMap.remove(ssl);
380
381
382 nativeSslContext.release();
383 return null;
384 }
385
386 return new QuicheQuicConnection(connection, ssl, engine, nativeSslContext);
387 }
388
389
390
391
392
393
394
395 long add(QuicheQuicSslEngine engine) {
396 nativeSslContext.retain();
397 engine.connection.reattach(nativeSslContext);
398 engineMap.put(engine.connection.ssl, engine);
399 return nativeSslContext.address();
400 }
401
402
403
404
405
406
407 void remove(QuicheQuicSslEngine engine) {
408 QuicheQuicSslEngine removed = engineMap.remove(engine.connection.ssl);
409 assert removed == null || removed == engine;
410 engine.removeSessionFromCacheIfInvalid();
411 }
412
413 @Nullable
414 QuicClientSessionCache getSessionCache() {
415 return sessionCache;
416 }
417
418 @Override
419 public boolean isClient() {
420 return !server;
421 }
422
423 @Override
424 public List<String> cipherSuites() {
425 return Arrays.asList("TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384");
426 }
427
428 @Override
429 public long sessionCacheSize() {
430 if (sessionCache != null) {
431 return sessionCache.getSessionCacheSize();
432 } else {
433 synchronized (this) {
434 return sessionCacheSize;
435 }
436 }
437 }
438
439 @Override
440 public long sessionTimeout() {
441 if (sessionCache != null) {
442 return sessionCache.getSessionTimeout();
443 } else {
444 synchronized (this) {
445 return sessionTimeout;
446 }
447 }
448 }
449
450 @Override
451 public ApplicationProtocolNegotiator applicationProtocolNegotiator() {
452 return apn;
453 }
454
455 @Override
456 public QuicSslEngine newEngine(ByteBufAllocator alloc) {
457 return new QuicheQuicSslEngine(this, null, -1, endpointIdentificationAlgorithm);
458 }
459
460 @Override
461 public QuicSslEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
462 return new QuicheQuicSslEngine(this, peerHost, peerPort, endpointIdentificationAlgorithm);
463 }
464
465 @Override
466 public QuicSslSessionContext sessionContext() {
467 return sessionCtx;
468 }
469
470 @Override
471 protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls) {
472 throw new UnsupportedOperationException();
473 }
474
475 @Override
476 public SslHandler newHandler(ByteBufAllocator alloc, Executor delegatedTaskExecutor) {
477 throw new UnsupportedOperationException();
478 }
479
480 @Override
481 protected SslHandler newHandler(ByteBufAllocator alloc, boolean startTls, Executor executor) {
482 throw new UnsupportedOperationException();
483 }
484
485 @Override
486 protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort, boolean startTls) {
487 throw new UnsupportedOperationException();
488 }
489
490 @Override
491 public SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort,
492 Executor delegatedTaskExecutor) {
493 throw new UnsupportedOperationException();
494 }
495
496 @Override
497 protected SslHandler newHandler(ByteBufAllocator alloc, String peerHost, int peerPort,
498 boolean startTls, Executor delegatedTaskExecutor) {
499 throw new UnsupportedOperationException();
500 }
501
502 @Override
503 protected void finalize() throws Throwable {
504 try {
505 nativeSslContext.release();
506 } finally {
507 super.finalize();
508 }
509 }
510
511 void setSessionTimeout(int seconds) throws IllegalArgumentException {
512 if (sessionCache != null) {
513 sessionCache.setSessionTimeout(seconds);
514 } else {
515 BoringSSL.SSLContext_setSessionCacheTimeout(nativeSslContext.address(), seconds);
516 this.sessionTimeout = seconds;
517 }
518 }
519
520 void setSessionCacheSize(int size) throws IllegalArgumentException {
521 if (sessionCache != null) {
522 sessionCache.setSessionCacheSize(size);
523 } else {
524 BoringSSL.SSLContext_setSessionCacheSize(nativeSslContext.address(), size);
525 sessionCacheSize = size;
526 }
527 }
528
529 void setSessionTicketKeys(SslSessionTicketKey @Nullable [] ticketKeys) {
530 sessionTicketCallback.setSessionTicketKeys(ticketKeys);
531 BoringSSL.SSLContext_setSessionTicketKeys(
532 nativeSslContext.address(), ticketKeys != null && ticketKeys.length != 0);
533 }
534
535 @SuppressWarnings("deprecation")
536 private static final class QuicheQuicApplicationProtocolNegotiator implements ApplicationProtocolNegotiator {
537 private final List<String> protocols;
538
539 QuicheQuicApplicationProtocolNegotiator(String @Nullable ... protocols) {
540 if (protocols == null) {
541 this.protocols = Collections.emptyList();
542 } else {
543 this.protocols = Collections.unmodifiableList(Arrays.asList(protocols));
544 }
545 }
546
547 @Override
548 public List<String> protocols() {
549 return protocols;
550 }
551 }
552
553 private static final class QuicheQuicSslSessionContext implements QuicSslSessionContext {
554 private final QuicheQuicSslContext context;
555
556 QuicheQuicSslSessionContext(QuicheQuicSslContext context) {
557 this.context = context;
558 }
559
560 @Override
561 @Nullable
562 public SSLSession getSession(byte[] sessionId) {
563 return null;
564 }
565
566 @Override
567 public Enumeration<byte[]> getIds() {
568 return new Enumeration<byte[]>() {
569 @Override
570 public boolean hasMoreElements() {
571 return false;
572 }
573
574 @Override
575 public byte[] nextElement() {
576 throw new NoSuchElementException();
577 }
578 };
579 }
580
581 @Override
582 public void setSessionTimeout(int seconds) throws IllegalArgumentException {
583 context.setSessionTimeout(seconds);
584 }
585
586 @Override
587 public int getSessionTimeout() {
588 return (int) context.sessionTimeout();
589 }
590
591 @Override
592 public void setSessionCacheSize(int size) throws IllegalArgumentException {
593 context.setSessionCacheSize(size);
594 }
595
596 @Override
597 public int getSessionCacheSize() {
598 return (int) context.sessionCacheSize();
599 }
600
601 @Override
602 public void setTicketKeys(SslSessionTicketKey @Nullable ... keys) {
603 context.setSessionTicketKeys(keys);
604 }
605 }
606
607 static final class NativeSslContext extends AbstractReferenceCounted {
608 private final long ctx;
609
610 NativeSslContext(long ctx) {
611 this.ctx = ctx;
612 }
613
614 long address() {
615 return ctx;
616 }
617
618 @Override
619 protected void deallocate() {
620 BoringSSL.SSLContext_free(ctx);
621 }
622
623 @Override
624 public ReferenceCounted touch(Object hint) {
625 return this;
626 }
627
628 @Override
629 public String toString() {
630 return "NativeSslContext{" +
631 "ctx=" + ctx +
632 '}';
633 }
634 }
635
636 private static final class BoringSSLAsyncPrivateKeyMethodAdapter implements BoringSSLPrivateKeyMethod {
637 private final QuicheQuicSslEngineMap engineMap;
638 private final BoringSSLAsyncPrivateKeyMethod privateKeyMethod;
639
640 BoringSSLAsyncPrivateKeyMethodAdapter(QuicheQuicSslEngineMap engineMap,
641 BoringSSLAsyncPrivateKeyMethod privateKeyMethod) {
642 this.engineMap = engineMap;
643 this.privateKeyMethod = privateKeyMethod;
644 }
645
646 @Override
647 public void sign(long ssl, int signatureAlgorithm, byte[] input, BiConsumer<byte[], Throwable> callback) {
648 final QuicheQuicSslEngine engine = engineMap.get(ssl);
649 if (engine == null) {
650
651 callback.accept(null, null);
652 } else {
653 privateKeyMethod.sign(engine, signatureAlgorithm, input).addListener(f -> {
654 Throwable cause = f.cause();
655 if (cause != null) {
656 callback.accept(null, cause);
657 } else {
658 callback.accept((byte[]) f.getNow(), null);
659 }
660 });
661 }
662 }
663
664 @Override
665 public void decrypt(long ssl, byte[] input, BiConsumer<byte[], Throwable> callback) {
666 final QuicheQuicSslEngine engine = engineMap.get(ssl);
667 if (engine == null) {
668
669 callback.accept(null, null);
670 } else {
671 privateKeyMethod.decrypt(engine, input).addListener(f -> {
672 Throwable cause = f.cause();
673 if (cause != null) {
674 callback.accept(null, cause);
675 } else {
676 callback.accept((byte[]) f.getNow(), null);
677 }
678 });
679 }
680 }
681 }
682 }