Netty and TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8

910 views
Skip to first unread message

Rogan Dawes

unread,
Sep 26, 2017, 3:40:55 AM9/26/17
to Netty discussions
Hi,

I'm trying to write a proxy to intercept COAP connections from an embedded device. I am able to clone the TLS certificates that it thinks it is connecting to, obviously using my own keys (for the TLS server side), and insert the CA cert into the firmware of this device. By this, I mean that an ASN.1 dump of my CA certificate and the "expected" CA certificate show differences only in the actual key values, not any other parameters of the keys or certificates.

However, so far I have been unable to convince netty to negotiate a connection using the only supported algorithm offered by the device, being TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8. When I do, I get handshake errors that the handshaker was unable to establish a common cipher suite.

I have tried using the native JDK 8 options (build 1.8.0_102-b14), as well as tried specifying use of the Openssl provider via 


sslContext = SslContextBuilder.forServer(km.getPrivateKey(target), km.getCertificateChain(target))

.sslContextProvider(new BouncyCastleProvider()).build();


I have also tried:


sslContext = SslContextBuilder.forServer(km.getPrivateKey(target), km.getCertificateChain(target))

.sslProvider(SslProvider.OPENSSL).build();


where km is an X509KeyManager instance that holds the relevant keys.


Any suggestions?


Rogan


Rogan Dawes

unread,
Sep 29, 2017, 3:27:57 AM9/29/17
to Netty discussions
I have been able to get a handshake established with some direct threads using the conventional InputStream/OutputStream approach, making use of the BouncyCastle crypto libraries (both BouncyCastleProvider and BouncyCastleJsseProvider are required).

However, netty seems to have no mechanism to specify use of the BouncyCastle providers when initialising the SSLContext, only allowing the default JDK Provider or the OpenSSL provider. One problem with the OpenSSL provider is that this particular algorithm is only supported in OpenSSL 1.1, not any of the 1.02x branches.

So, I tried inserting the BouncyCastle providers are position 1, which led to problems initialising the SecureRandom instances, etc, etc.

Any thoughts on making it easy/possible to use BouncyCastle as the preferred Provider?

Thanks!

Rogan

Rogan Dawes

unread,
Oct 5, 2017, 7:06:20 AM10/5/17
to Netty discussions
Ok, so I figured out I could specify 

sslContextBuilder.sslContextProvider(Security.getProvider("BCJSSE"));


Now it seems that I'm not configuring something correctly for EC algorithms, or else Netty and BouncyCastle are not playing nicely together. Below is a slightly modified EchoServer. It pre-loads the BouncyCastle providers, and uses a pre-generated EC key and certificate, since SelfSignedCertificate is hard coded to use RSA.

package io.netty.example.echo;


import java.io.ByteArrayInputStream;

import java.security.Security;


import javax.net.ssl.SSLEngine;


import org.bouncycastle.jce.provider.BouncyCastleProvider;

import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;


import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelHandler.Sharable;

import io.netty.channel.ChannelHandlerContext;

import io.netty.channel.ChannelInboundHandlerAdapter;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.ChannelPipeline;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.logging.LogLevel;

import io.netty.handler.logging.LoggingHandler;

import io.netty.handler.ssl.SslContext;

import io.netty.handler.ssl.SslContextBuilder;

import io.netty.handler.ssl.SslHandler;


/**

 * Echoes back any received data from a client.

 */

public final class EchoServer {


private static byte[] KEY = 

("-----BEGIN PRIVATE KEY-----\n"

+ "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgujVVI0eva1wiEgIK\n"

+ "lBon2bp0ZFnS8JCuYe3djnfvPG6hRANCAARXIyQz9p/u9IdnLX/hKokNTD5VLMTX\n"

+ "OwA+sTCBY4i2iyZBr0IJQ2ckcoOaljMIFDL/ZKsZKM0hJsoylUD9ZVW1\n" 

+ "-----END PRIVATE KEY-----\n").getBytes();

private static byte[] CERT = 

("-----BEGIN CERTIFICATE-----\n" 

+ "MIIBHDCBwqADAgECAgRZ1gxLMAoGCCqGSM49BAMCMBYxFDASBgNVBAMMC2V4YW1w\n"

+ "bGUub3JnMB4XDTE3MTAwNTEwNDIxOFoXDTE4MTAwNTEwNDIxOFowFjEUMBIGA1UE\n"

+ "AwwLZXhhbXBsZS5vcmcwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARXIyQz9p/u\n"

+ "9IdnLX/hKokNTD5VLMTXOwA+sTCBY4i2iyZBr0IJQ2ckcoOaljMIFDL/ZKsZKM0h\n"

+ "JsoylUD9ZVW1MAoGCCqGSM49BAMCA0kAMEYCIQD2/7J9u4Cz5ewdgXAe7jM9B3w2\n"

+ "R8Cg4Tph4i9629mF1QIhAL59cMvwwEuN7HxYYZoZNB3nGOoMVFXwVvdZwuMhOo5Z\n" 

+ "-----END CERTIFICATE-----").getBytes();


public static void main(String[] args) throws Exception {

// Configure SSL.

Security.addProvider(new BouncyCastleProvider());

BouncyCastleJsseProvider bcjsp = new BouncyCastleJsseProvider();

Security.addProvider(bcjsp);


ByteArrayInputStream cert = new ByteArrayInputStream(CERT);

ByteArrayInputStream key = new ByteArrayInputStream(KEY);

final SslContext sslCtx = SslContextBuilder.forServer(cert, key).sslContextProvider(bcjsp).build();


// Configure the server.

EventLoopGroup bossGroup = new NioEventLoopGroup(1);

EventLoopGroup workerGroup = new NioEventLoopGroup();

try {

ServerBootstrap b = new ServerBootstrap();

b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)

.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {

@Override

public void initChannel(SocketChannel ch) throws Exception {

ChannelPipeline p = ch.pipeline();

SslHandler s = sslCtx.newHandler(ch.alloc());

SSLEngine e = s.engine();

e.setEnabledCipherSuites(e.getSupportedCipherSuites());

p.addLast(s);

p.addLast(new LoggingHandler(LogLevel.INFO));

p.addLast(new EchoServerHandler());

}

});


// Start the server.

ChannelFuture f = b.bind(4433).sync();


// Wait until the server socket is closed.

f.channel().closeFuture().sync();

} finally {

// Shut down all event loops to terminate all threads.

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}


@Sharable

public static class EchoServerHandler extends ChannelInboundHandlerAdapter {


@Override

public void channelRead(ChannelHandlerContext ctx, Object msg) {

ctx.write(msg);

}


@Override

public void channelReadComplete(ChannelHandlerContext ctx) {

ctx.flush();

}


@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {

// Close the connection when an exception is raised.

cause.printStackTrace();

ctx.close();

}

}

}


I try to connect to it using OpenSSL (1.1 has better support for EC algorithms, it seems, in particular):

openssl s_client

And I get:

Oct 05, 2017 1:00:48 PM org.bouncycastle.jsse.provider.ProvTlsServer notifyAlertRaised

INFO: Server raised fatal(2) handshake_failure(40) alert: Failed to read record

org.bouncycastle.tls.TlsFatalAlert: handshake_failure(40)

at org.bouncycastle.tls.AbstractTlsServer.getSelectedCipherSuite(Unknown Source)

at org.bouncycastle.jsse.provider.ProvTlsServer.getSelectedCipherSuite(Unknown Source)

at org.bouncycastle.tls.TlsServerProtocol.sendServerHelloMessage(Unknown Source)

at org.bouncycastle.tls.TlsServerProtocol.handleHandshakeMessage(Unknown Source)

at org.bouncycastle.tls.TlsProtocol.processHandshakeQueue(Unknown Source)

at org.bouncycastle.tls.TlsProtocol.processRecord(Unknown Source)

at org.bouncycastle.tls.RecordStream.readRecord(Unknown Source)

at org.bouncycastle.tls.TlsProtocol.safeReadRecord(Unknown Source)

at org.bouncycastle.tls.TlsProtocol.offerInput(Unknown Source)

at org.bouncycastle.jsse.provider.ProvSSLEngine.unwrap(Unknown Source)

at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)

at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:281)

at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1215)

at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1127)

at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1162)

at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)

at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)

at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)

at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)

at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1359)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)

at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)

at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:935)

at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)

at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:645)

at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:580)

at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:497)

at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:459)

at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)

at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)

at java.lang.Thread.run(Thread.java:748)

 
I have confirmed that this works using old-style Threads, etc, and can provide that code here if needed.

Any idea what I am doing wrong? If I remove the "builder.sslContextProvider(bcjsp)", it also works, BUT only BouncyCastle has support for "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", which need! Sigh!

Rogan

On Tuesday, September 26, 2017 at 9:40:55 AM UTC+2, Rogan Dawes wrote:

Rogan Dawes

unread,
Oct 25, 2017, 10:03:41 AM10/25/17
to Netty discussions
Seems like I'm talking to myself here :-(

Anyway, current status is that BouncyCastle requires use of the PKIX implementation of a KeyManager, and this is not the default. In order to do this, it looks like I have to replicate a whole lot of code that already exists in SslContextBuilder and SslContext, unless I do:

Security.setProperty("ssl.KeyManagerFactory.algorithm","PKIX");


But that feels like a "big stick" approach, being a global change! :-(

Also also noted that I have to change JdkSslServerContext (and place a copy in my own code), because when initialising the SSLContext, it passes a null value instead of a SecureRandom instance.:

            SSLContext ctx = sslContextProvider == null ? SSLContext.getInstance(PROTOCOL)

                : SSLContext.getInstance(PROTOCOL, sslContextProvider);

            ctx.init(keyManagerFactory.getKeyManagers(),

                     trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),

                     null);

 

BouncyCastle does not like that at all.

Rogan

Norman Maurer

unread,
Oct 25, 2017, 10:05:01 AM10/25/17
to ne...@googlegroups.com
Hey Rogan,

Maybe you want to provide a PR for netty to not pass I note null stuff ?

Sorry but I never used Bouncycastle so I am not a big help.


-- 
You received this message because you are subscribed to the Google Groups "Netty discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to netty+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/netty/08e97b0d-4be2-4ab3-8c6b-65455a0c5d57%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rogan Dawes

unread,
Oct 25, 2017, 1:14:38 PM10/25/17
to ne...@googlegroups.com
Yeah, will do.

Rogan

Norman Maurer

unread,
Oct 25, 2017, 1:16:36 PM10/25/17
to ne...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages