2019-04-08 20:17:44 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2019 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License, version
|
|
|
|
* 2.0 (the "License"); you may not use this file except in compliance with the
|
|
|
|
* License. You may obtain a copy of the License at:
|
|
|
|
*
|
2020-10-23 14:44:18 +02:00
|
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
2019-04-08 20:17:44 +02:00
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations under
|
|
|
|
* the License.
|
|
|
|
*/
|
|
|
|
package io.netty.handler.ssl;
|
|
|
|
|
|
|
|
import io.netty.bootstrap.Bootstrap;
|
|
|
|
import io.netty.bootstrap.ServerBootstrap;
|
|
|
|
import io.netty.buffer.ByteBufAllocator;
|
|
|
|
import io.netty.buffer.Unpooled;
|
|
|
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
|
|
|
import io.netty.channel.Channel;
|
|
|
|
import io.netty.channel.ChannelHandler;
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
|
|
import io.netty.channel.ChannelInitializer;
|
|
|
|
import io.netty.channel.ChannelPipeline;
|
|
|
|
import io.netty.channel.EventLoopGroup;
|
|
|
|
import io.netty.channel.MultithreadEventLoopGroup;
|
|
|
|
import io.netty.channel.SimpleChannelInboundHandler;
|
|
|
|
import io.netty.channel.local.LocalAddress;
|
|
|
|
import io.netty.channel.local.LocalChannel;
|
|
|
|
import io.netty.channel.local.LocalHandler;
|
|
|
|
import io.netty.channel.local.LocalServerChannel;
|
|
|
|
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
|
|
|
|
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|
|
|
import io.netty.util.ReferenceCountUtil;
|
2021-07-08 16:19:22 +02:00
|
|
|
import io.netty.util.concurrent.Future;
|
|
|
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
2019-04-08 20:17:44 +02:00
|
|
|
import io.netty.util.concurrent.Promise;
|
|
|
|
import org.hamcrest.Matchers;
|
2021-07-02 15:06:06 +02:00
|
|
|
import org.junit.jupiter.api.AfterAll;
|
|
|
|
import org.junit.jupiter.api.BeforeAll;
|
|
|
|
import org.junit.jupiter.params.ParameterizedTest;
|
|
|
|
import org.junit.jupiter.params.provider.MethodSource;
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
import javax.net.ssl.KeyManagerFactory;
|
|
|
|
import javax.net.ssl.SSLContext;
|
|
|
|
import javax.net.ssl.SSLEngine;
|
|
|
|
import javax.net.ssl.SSLHandshakeException;
|
|
|
|
import java.net.SocketAddress;
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
|
import java.security.Signature;
|
|
|
|
import java.security.SignatureException;
|
|
|
|
import java.security.spec.MGF1ParameterSpec;
|
|
|
|
import java.security.spec.PSSParameterSpec;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collection;
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.concurrent.Executor;
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
2021-07-08 16:19:22 +02:00
|
|
|
import java.util.concurrent.ThreadLocalRandom;
|
2019-04-08 20:17:44 +02:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
2019-05-15 07:24:01 +02:00
|
|
|
import static io.netty.handler.ssl.OpenSslTestUtils.checkShouldUseKeyManagerFactory;
|
2020-10-18 14:30:52 +02:00
|
|
|
import static org.hamcrest.MatcherAssert.assertThat;
|
2021-07-02 15:06:06 +02:00
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
2021-07-08 16:19:22 +02:00
|
|
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
public class OpenSslPrivateKeyMethodTest {
|
|
|
|
private static final String RFC_CIPHER_NAME = "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
|
|
|
|
private static EventLoopGroup GROUP;
|
|
|
|
private static SelfSignedCertificate CERT;
|
|
|
|
private static ExecutorService EXECUTOR;
|
|
|
|
|
2021-07-02 15:06:06 +02:00
|
|
|
static Collection<Object[]> parameters() {
|
2019-04-08 20:17:44 +02:00
|
|
|
List<Object[]> dst = new ArrayList<Object[]>();
|
2021-07-08 16:19:22 +02:00
|
|
|
for (int a = 0; a < 2; a++) {
|
|
|
|
for (int b = 0; b < 2; b++) {
|
|
|
|
for (int c = 0; c < 2; c++) {
|
|
|
|
dst.add(new Object[] { a == 0, b == 0, c == 0 });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-04-08 20:17:44 +02:00
|
|
|
return dst;
|
|
|
|
}
|
|
|
|
|
2021-07-02 15:06:06 +02:00
|
|
|
@BeforeAll
|
2019-04-08 20:17:44 +02:00
|
|
|
public static void init() throws Exception {
|
2019-05-15 07:24:01 +02:00
|
|
|
checkShouldUseKeyManagerFactory();
|
|
|
|
|
2021-07-08 16:19:22 +02:00
|
|
|
assumeTrue(OpenSsl.isBoringSSL());
|
2019-04-08 20:17:44 +02:00
|
|
|
// Check if the cipher is supported at all which may not be the case for various JDK versions and OpenSSL API
|
|
|
|
// implementations.
|
|
|
|
assumeCipherAvailable(SslProvider.OPENSSL);
|
|
|
|
assumeCipherAvailable(SslProvider.JDK);
|
|
|
|
|
|
|
|
GROUP = new MultithreadEventLoopGroup(LocalHandler.newFactory());
|
|
|
|
CERT = new SelfSignedCertificate();
|
2020-01-30 09:28:24 +01:00
|
|
|
EXECUTOR = Executors.newCachedThreadPool(DelegateThread::new);
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
2021-07-02 15:06:06 +02:00
|
|
|
@AfterAll
|
2019-04-08 20:17:44 +02:00
|
|
|
public static void destroy() {
|
2019-04-09 08:31:39 +02:00
|
|
|
if (OpenSsl.isBoringSSL()) {
|
|
|
|
GROUP.shutdownGracefully();
|
|
|
|
CERT.delete();
|
|
|
|
EXECUTOR.shutdown();
|
|
|
|
}
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static void assumeCipherAvailable(SslProvider provider) throws NoSuchAlgorithmException {
|
|
|
|
boolean cipherSupported = false;
|
|
|
|
if (provider == SslProvider.JDK) {
|
|
|
|
SSLEngine engine = SSLContext.getDefault().createSSLEngine();
|
|
|
|
for (String c: engine.getSupportedCipherSuites()) {
|
2021-07-07 21:15:43 +02:00
|
|
|
if (RFC_CIPHER_NAME.equals(c)) {
|
|
|
|
cipherSupported = true;
|
|
|
|
break;
|
|
|
|
}
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cipherSupported = OpenSsl.isCipherSuiteAvailable(RFC_CIPHER_NAME);
|
|
|
|
}
|
2021-07-08 16:19:22 +02:00
|
|
|
assumeTrue(cipherSupported, "Unsupported cipher: " + RFC_CIPHER_NAME);
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static SslHandler newSslHandler(SslContext sslCtx, ByteBufAllocator allocator, Executor executor) {
|
|
|
|
if (executor == null) {
|
|
|
|
return sslCtx.newHandler(allocator);
|
|
|
|
} else {
|
|
|
|
return sslCtx.newHandler(allocator, executor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private SslContext buildServerContext(OpenSslPrivateKeyMethod method) throws Exception {
|
|
|
|
List<String> ciphers = Collections.singletonList(RFC_CIPHER_NAME);
|
|
|
|
|
|
|
|
final KeyManagerFactory kmf = OpenSslX509KeyManagerFactory.newKeyless(CERT.cert());
|
|
|
|
|
2021-07-07 21:15:43 +02:00
|
|
|
return SslContextBuilder.forServer(kmf)
|
2019-04-08 20:17:44 +02:00
|
|
|
.sslProvider(SslProvider.OPENSSL)
|
|
|
|
.ciphers(ciphers)
|
|
|
|
// As this is not a TLSv1.3 cipher we should ensure we talk something else.
|
2021-07-07 21:15:43 +02:00
|
|
|
.protocols(SslProtocols.TLS_v1_2)
|
2021-02-02 08:29:21 +01:00
|
|
|
.option(OpenSslContextOption.PRIVATE_KEY_METHOD, method)
|
2019-04-08 20:17:44 +02:00
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
|
|
|
private SslContext buildClientContext() throws Exception {
|
|
|
|
return SslContextBuilder.forClient()
|
|
|
|
.sslProvider(SslProvider.JDK)
|
|
|
|
.ciphers(Collections.singletonList(RFC_CIPHER_NAME))
|
|
|
|
// As this is not a TLSv1.3 cipher we should ensure we talk something else.
|
2021-07-07 21:15:43 +02:00
|
|
|
.protocols(SslProtocols.TLS_v1_2)
|
2019-04-08 20:17:44 +02:00
|
|
|
.trustManager(InsecureTrustManagerFactory.INSTANCE)
|
|
|
|
.build();
|
|
|
|
}
|
|
|
|
|
2021-07-07 08:27:31 +02:00
|
|
|
private static Executor delegateExecutor(boolean delegate) {
|
2021-07-07 21:15:43 +02:00
|
|
|
return delegate ? EXECUTOR : null;
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
2021-07-08 16:19:22 +02:00
|
|
|
private SslContext buildServerContext(OpenSslAsyncPrivateKeyMethod method) throws Exception {
|
|
|
|
List<String> ciphers = Collections.singletonList(RFC_CIPHER_NAME);
|
|
|
|
|
|
|
|
final KeyManagerFactory kmf = OpenSslX509KeyManagerFactory.newKeyless(CERT.cert());
|
|
|
|
|
|
|
|
return SslContextBuilder.forServer(kmf)
|
|
|
|
.sslProvider(SslProvider.OPENSSL)
|
|
|
|
.ciphers(ciphers)
|
|
|
|
// As this is not a TLSv1.3 cipher we should ensure we talk something else.
|
|
|
|
.protocols(SslProtocols.TLS_v1_2)
|
|
|
|
.option(OpenSslContextOption.ASYNC_PRIVATE_KEY_METHOD, method)
|
|
|
|
.build();
|
|
|
|
}
|
2019-04-08 20:17:44 +02:00
|
|
|
|
2021-07-02 15:06:06 +02:00
|
|
|
private static void assertThread(boolean delegate) {
|
2019-04-08 20:17:44 +02:00
|
|
|
if (delegate && OpenSslContext.USE_TASKS) {
|
|
|
|
assertEquals(DelegateThread.class, Thread.currentThread().getClass());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-08 16:19:22 +02:00
|
|
|
@ParameterizedTest(name = "{index}: delegate = {0}, async = {1}, newThread={2}")
|
2021-07-02 15:06:06 +02:00
|
|
|
@MethodSource("parameters")
|
2021-07-08 16:19:22 +02:00
|
|
|
public void testPrivateKeyMethod(final boolean delegate, boolean async, boolean newThread) throws Exception {
|
2019-04-08 20:17:44 +02:00
|
|
|
final AtomicBoolean signCalled = new AtomicBoolean();
|
2021-07-08 16:19:22 +02:00
|
|
|
OpenSslPrivateKeyMethod keyMethod = new OpenSslPrivateKeyMethod() {
|
2019-04-08 20:17:44 +02:00
|
|
|
@Override
|
|
|
|
public byte[] sign(SSLEngine engine, int signatureAlgorithm, byte[] input) throws Exception {
|
|
|
|
signCalled.set(true);
|
2021-07-02 15:06:06 +02:00
|
|
|
assertThread(delegate);
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
assertEquals(CERT.cert().getPublicKey(),
|
|
|
|
engine.getSession().getLocalCertificates()[0].getPublicKey());
|
|
|
|
|
|
|
|
// Delegate signing to Java implementation.
|
|
|
|
final Signature signature;
|
|
|
|
// Depending on the Java version it will pick one or the other.
|
|
|
|
if (signatureAlgorithm == OpenSslPrivateKeyMethod.SSL_SIGN_RSA_PKCS1_SHA256) {
|
|
|
|
signature = Signature.getInstance("SHA256withRSA");
|
|
|
|
} else if (signatureAlgorithm == OpenSslPrivateKeyMethod.SSL_SIGN_RSA_PSS_RSAE_SHA256) {
|
|
|
|
signature = Signature.getInstance("RSASSA-PSS");
|
|
|
|
signature.setParameter(new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256,
|
|
|
|
32, 1));
|
|
|
|
} else {
|
|
|
|
throw new AssertionError("Unexpected signature algorithm " + signatureAlgorithm);
|
|
|
|
}
|
|
|
|
signature.initSign(CERT.key());
|
|
|
|
signature.update(input);
|
|
|
|
return signature.sign();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] decrypt(SSLEngine engine, byte[] input) {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
2021-07-08 16:19:22 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
final SslContext sslServerContext = async ? buildServerContext(
|
|
|
|
new OpenSslPrivateKeyMethodAdapter(keyMethod, newThread)) : buildServerContext(keyMethod);
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
final SslContext sslClientContext = buildClientContext();
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
final Promise<Object> serverPromise = GROUP.next().newPromise();
|
|
|
|
final Promise<Object> clientPromise = GROUP.next().newPromise();
|
|
|
|
|
|
|
|
ChannelHandler serverHandler = new ChannelInitializer<Channel>() {
|
|
|
|
@Override
|
|
|
|
protected void initChannel(Channel ch) {
|
|
|
|
ChannelPipeline pipeline = ch.pipeline();
|
2021-07-02 15:06:06 +02:00
|
|
|
pipeline.addLast(newSslHandler(sslServerContext, ch.alloc(), delegateExecutor(delegate)));
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
pipeline.addLast(new SimpleChannelInboundHandler<Object>() {
|
|
|
|
@Override
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) {
|
2021-09-02 10:46:54 +02:00
|
|
|
serverPromise.cancel();
|
2019-04-08 20:17:44 +02:00
|
|
|
ctx.fireChannelInactive();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2019-11-01 07:23:07 +01:00
|
|
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) {
|
2019-04-08 20:17:44 +02:00
|
|
|
if (serverPromise.trySuccess(null)) {
|
|
|
|
ctx.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'O', 'N', 'G'}));
|
|
|
|
}
|
|
|
|
ctx.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
|
|
if (!serverPromise.tryFailure(cause)) {
|
|
|
|
ctx.fireExceptionCaught(cause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
LocalAddress address = new LocalAddress("test-" + SslProvider.OPENSSL
|
|
|
|
+ '-' + SslProvider.JDK + '-' + RFC_CIPHER_NAME + '-' + delegate);
|
|
|
|
|
|
|
|
Channel server = server(address, serverHandler);
|
|
|
|
try {
|
|
|
|
ChannelHandler clientHandler = new ChannelInitializer<Channel>() {
|
|
|
|
@Override
|
|
|
|
protected void initChannel(Channel ch) {
|
|
|
|
ChannelPipeline pipeline = ch.pipeline();
|
2021-07-02 15:06:06 +02:00
|
|
|
pipeline.addLast(newSslHandler(sslClientContext, ch.alloc(), delegateExecutor(delegate)));
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
pipeline.addLast(new SimpleChannelInboundHandler<Object>() {
|
|
|
|
@Override
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) {
|
2021-09-02 10:46:54 +02:00
|
|
|
clientPromise.cancel();
|
2019-04-08 20:17:44 +02:00
|
|
|
ctx.fireChannelInactive();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2019-11-01 07:23:07 +01:00
|
|
|
public void messageReceived(ChannelHandlerContext ctx, Object msg) {
|
2019-04-08 20:17:44 +02:00
|
|
|
clientPromise.trySuccess(null);
|
|
|
|
ctx.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
|
|
|
|
if (!clientPromise.tryFailure(cause)) {
|
|
|
|
ctx.fireExceptionCaught(cause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Channel client = client(server, clientHandler);
|
|
|
|
try {
|
|
|
|
client.writeAndFlush(Unpooled.wrappedBuffer(new byte[] {'P', 'I', 'N', 'G'}))
|
2021-07-07 21:15:43 +02:00
|
|
|
.syncUninterruptibly();
|
2019-04-08 20:17:44 +02:00
|
|
|
|
2021-09-02 10:46:54 +02:00
|
|
|
Future<Object> clientFuture = clientPromise.asFuture();
|
|
|
|
Future<Object> serverFuture = serverPromise.asFuture();
|
|
|
|
assertTrue(clientFuture.await(5L, TimeUnit.SECONDS), "client timeout");
|
|
|
|
assertTrue(serverFuture.await(5L, TimeUnit.SECONDS), "server timeout");
|
2019-04-08 20:17:44 +02:00
|
|
|
|
2021-09-02 10:46:54 +02:00
|
|
|
clientFuture.sync();
|
|
|
|
serverFuture.sync();
|
2019-04-08 20:17:44 +02:00
|
|
|
assertTrue(signCalled.get());
|
|
|
|
} finally {
|
|
|
|
client.close().sync();
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
server.close().sync();
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
ReferenceCountUtil.release(sslClientContext);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
ReferenceCountUtil.release(sslServerContext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-02 15:06:06 +02:00
|
|
|
@ParameterizedTest(name = "{index}: delegate = {0}")
|
|
|
|
@MethodSource("parameters")
|
|
|
|
public void testPrivateKeyMethodFailsBecauseOfException(final boolean delegate) throws Exception {
|
|
|
|
testPrivateKeyMethodFails(delegate, false);
|
2019-04-29 08:31:14 +02:00
|
|
|
}
|
|
|
|
|
2021-07-02 15:06:06 +02:00
|
|
|
@ParameterizedTest(name = "{index}: delegate = {0}")
|
|
|
|
@MethodSource("parameters")
|
|
|
|
public void testPrivateKeyMethodFailsBecauseOfNull(final boolean delegate) throws Exception {
|
|
|
|
testPrivateKeyMethodFails(delegate, true);
|
2019-04-29 08:31:14 +02:00
|
|
|
}
|
2021-07-02 15:06:06 +02:00
|
|
|
|
|
|
|
private void testPrivateKeyMethodFails(final boolean delegate, final boolean returnNull) throws Exception {
|
2019-04-08 20:17:44 +02:00
|
|
|
final SslContext sslServerContext = buildServerContext(new OpenSslPrivateKeyMethod() {
|
|
|
|
@Override
|
|
|
|
public byte[] sign(SSLEngine engine, int signatureAlgorithm, byte[] input) throws Exception {
|
2021-07-02 15:06:06 +02:00
|
|
|
assertThread(delegate);
|
2019-04-29 08:31:14 +02:00
|
|
|
if (returnNull) {
|
|
|
|
return null;
|
|
|
|
}
|
2019-04-08 20:17:44 +02:00
|
|
|
throw new SignatureException();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public byte[] decrypt(SSLEngine engine, byte[] input) {
|
|
|
|
throw new UnsupportedOperationException();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
final SslContext sslClientContext = buildClientContext();
|
|
|
|
|
|
|
|
SslHandler serverSslHandler = newSslHandler(
|
2021-07-02 15:06:06 +02:00
|
|
|
sslServerContext, UnpooledByteBufAllocator.DEFAULT, delegateExecutor(delegate));
|
2019-04-08 20:17:44 +02:00
|
|
|
SslHandler clientSslHandler = newSslHandler(
|
2021-07-02 15:06:06 +02:00
|
|
|
sslClientContext, UnpooledByteBufAllocator.DEFAULT, delegateExecutor(delegate));
|
2019-04-08 20:17:44 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
LocalAddress address = new LocalAddress("test-" + SslProvider.OPENSSL
|
|
|
|
+ '-' + SslProvider.JDK + '-' + RFC_CIPHER_NAME + '-' + delegate);
|
|
|
|
|
|
|
|
Channel server = server(address, serverSslHandler);
|
|
|
|
try {
|
|
|
|
Channel client = client(server, clientSslHandler);
|
|
|
|
try {
|
|
|
|
Throwable clientCause = clientSslHandler.handshakeFuture().await().cause();
|
|
|
|
Throwable serverCause = serverSslHandler.handshakeFuture().await().cause();
|
|
|
|
assertNotNull(clientCause);
|
|
|
|
assertThat(serverCause, Matchers.instanceOf(SSLHandshakeException.class));
|
|
|
|
} finally {
|
|
|
|
client.close().sync();
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
server.close().sync();
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
ReferenceCountUtil.release(sslClientContext);
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
ReferenceCountUtil.release(sslServerContext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Channel server(LocalAddress address, ChannelHandler handler) throws Exception {
|
|
|
|
ServerBootstrap bootstrap = new ServerBootstrap()
|
|
|
|
.channel(LocalServerChannel.class)
|
|
|
|
.group(GROUP)
|
|
|
|
.childHandler(handler);
|
|
|
|
|
2021-08-03 19:43:38 +02:00
|
|
|
return bootstrap.bind(address).get();
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static Channel client(Channel server, ChannelHandler handler) throws Exception {
|
|
|
|
SocketAddress remoteAddress = server.localAddress();
|
|
|
|
|
|
|
|
Bootstrap bootstrap = new Bootstrap()
|
|
|
|
.channel(LocalChannel.class)
|
|
|
|
.group(GROUP)
|
|
|
|
.handler(handler);
|
|
|
|
|
2021-08-03 19:43:38 +02:00
|
|
|
return bootstrap.connect(remoteAddress).get();
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static final class DelegateThread extends Thread {
|
|
|
|
DelegateThread(Runnable target) {
|
|
|
|
super(target);
|
|
|
|
}
|
|
|
|
}
|
2021-07-08 16:19:22 +02:00
|
|
|
|
|
|
|
private static final class OpenSslPrivateKeyMethodAdapter implements OpenSslAsyncPrivateKeyMethod {
|
|
|
|
private final OpenSslPrivateKeyMethod keyMethod;
|
|
|
|
private final boolean newThread;
|
|
|
|
|
|
|
|
OpenSslPrivateKeyMethodAdapter(OpenSslPrivateKeyMethod keyMethod, boolean newThread) {
|
|
|
|
this.keyMethod = keyMethod;
|
|
|
|
this.newThread = newThread;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Future<byte[]> sign(final SSLEngine engine, final int signatureAlgorithm, final byte[] input) {
|
|
|
|
final Promise<byte[]> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
|
|
|
try {
|
|
|
|
if (newThread) {
|
|
|
|
// Let's run these in an extra thread to ensure that this would also work if the promise is
|
|
|
|
// notified later.
|
|
|
|
new DelegateThread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
// Let's sleep for some time to ensure we would notify in an async fashion
|
|
|
|
Thread.sleep(ThreadLocalRandom.current().nextLong(100, 500));
|
|
|
|
promise.setSuccess(keyMethod.sign(engine, signatureAlgorithm, input));
|
|
|
|
} catch (Throwable cause) {
|
|
|
|
promise.setFailure(cause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).start();
|
|
|
|
} else {
|
|
|
|
promise.setSuccess(keyMethod.sign(engine, signatureAlgorithm, input));
|
|
|
|
}
|
|
|
|
} catch (Throwable cause) {
|
|
|
|
promise.setFailure(cause);
|
|
|
|
}
|
2021-09-02 10:46:54 +02:00
|
|
|
return promise.asFuture();
|
2021-07-08 16:19:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Future<byte[]> decrypt(final SSLEngine engine, final byte[] input) {
|
|
|
|
final Promise<byte[]> promise = ImmediateEventExecutor.INSTANCE.newPromise();
|
|
|
|
try {
|
|
|
|
if (newThread) {
|
|
|
|
// Let's run these in an extra thread to ensure that this would also work if the promise is
|
|
|
|
// notified later.
|
|
|
|
new DelegateThread(new Runnable() {
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
try {
|
|
|
|
// Let's sleep for some time to ensure we would notify in an async fashion
|
|
|
|
Thread.sleep(ThreadLocalRandom.current().nextLong(100, 500));
|
|
|
|
promise.setSuccess(keyMethod.decrypt(engine, input));
|
|
|
|
} catch (Throwable cause) {
|
|
|
|
promise.setFailure(cause);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).start();
|
|
|
|
} else {
|
|
|
|
promise.setSuccess(keyMethod.decrypt(engine, input));
|
|
|
|
}
|
|
|
|
} catch (Throwable cause) {
|
|
|
|
promise.setFailure(cause);
|
|
|
|
}
|
2021-09-02 10:46:54 +02:00
|
|
|
return promise.asFuture();
|
2021-07-08 16:19:22 +02:00
|
|
|
}
|
|
|
|
}
|
2019-04-08 20:17:44 +02:00
|
|
|
}
|