Skip to content

Commit

Permalink
Fix ssl protocol config for ssl context
Browse files Browse the repository at this point in the history
Using the raw ssl context instead of the netty handlers
missed the configuration of protocols.

The ssl testing is broken out into submodules and a new
exhaustive set of negotiation circumstances are tested.

This affected the bolt server which instantiated its
handlers using the context.
  • Loading branch information
martinfurmanski committed Feb 27, 2018
1 parent 7414bcf commit d00310c
Show file tree
Hide file tree
Showing 9 changed files with 916 additions and 476 deletions.
Expand Up @@ -74,10 +74,10 @@ private SslConnectionFactory createSslConnectionFactory( SslPolicy sslPolicy )
sslContextFactory.setIncludeCipherSuites( ciphers.toArray( new String[ciphers.size()] ) );
}

List<String> protocols = sslPolicy.getTlsVersions();
String[] protocols = sslPolicy.getTlsVersions();
if ( protocols != null )
{
sslContextFactory.setIncludeProtocols( protocols.toArray( new String[protocols.size()] ) );
sslContextFactory.setIncludeProtocols( protocols );
}

switch ( sslPolicy.getClientAuth() )
Expand Down
12 changes: 7 additions & 5 deletions community/ssl/src/main/java/org/neo4j/ssl/SslPolicy.java
Expand Up @@ -43,7 +43,7 @@ public class SslPolicy

/* cryptographic parameters */
private final List<String> ciphers;
private final List<String> tlsVersions;
private final String[] tlsVersions;
private final ClientAuth clientAuth;

private final TrustManagerFactory trustManagerFactory;
Expand All @@ -55,7 +55,7 @@ public SslPolicy( PrivateKey privateKey, X509Certificate[] keyCertChain,
{
this.privateKey = privateKey;
this.keyCertChain = keyCertChain;
this.tlsVersions = tlsVersions;
this.tlsVersions = tlsVersions == null ? null : tlsVersions.toArray( new String[tlsVersions.size()] );
this.ciphers = ciphers;
this.clientAuth = clientAuth;
this.trustManagerFactory = trustManagerFactory;
Expand All @@ -67,6 +67,7 @@ public SslContext nettyServerContext() throws SSLException
return SslContextBuilder.forServer( privateKey, keyCertChain )
.sslProvider( sslProvider )
.clientAuth( forNetty( clientAuth ) )
.protocols( tlsVersions )
.ciphers( ciphers )
.trustManager( trustManagerFactory )
.build();
Expand All @@ -77,6 +78,7 @@ public SslContext nettyClientContext() throws SSLException
return SslContextBuilder.forClient()
.sslProvider( sslProvider )
.keyManager( privateKey, keyCertChain )
.protocols( tlsVersions )
.ciphers( ciphers )
.trustManager( trustManagerFactory )
.build();
Expand Down Expand Up @@ -112,7 +114,7 @@ private SslHandler makeNettyHandler( Channel channel, SslContext sslContext )
SSLEngine sslEngine = sslContext.newEngine( channel.alloc() );
if ( tlsVersions != null )
{
sslEngine.setEnabledProtocols( tlsVersions.toArray( new String[tlsVersions.size()] ) );
sslEngine.setEnabledProtocols( tlsVersions );
}
return new SslHandler( sslEngine );
}
Expand Down Expand Up @@ -154,7 +156,7 @@ public List<String> getCipherSuites()
return ciphers;
}

public List<String> getTlsVersions()
public String[] getTlsVersions()
{
return tlsVersions;
}
Expand All @@ -170,7 +172,7 @@ public String toString()
return "SslPolicy{" +
"keyCertChain=" + describeCertChain() +
", ciphers=" + ciphers +
", tlsVersions=" + tlsVersions +
", tlsVersions=" + Arrays.toString( tlsVersions ) +
", clientAuth=" + clientAuth +
'}';
}
Expand Down
155 changes: 155 additions & 0 deletions integrationtests/src/test/java/org/neo4j/ssl/SecureClient.java
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2002-2018 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.neo4j.ssl;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;

import javax.net.ssl.SSLEngine;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.Matchers.equalTo;
import static org.neo4j.test.assertion.Assert.assertEventually;

class SecureClient
{
private Bootstrap bootstrap;
private ClientInitializer clientInitializer;
private NioEventLoopGroup eventLoopGroup;
private Channel channel;
private Bucket bucket = new Bucket();

SecureClient( SslContext sslContext )
{
eventLoopGroup = new NioEventLoopGroup();
clientInitializer = new ClientInitializer( sslContext, bucket );
bootstrap = new Bootstrap()
.group( eventLoopGroup )
.channel( NioSocketChannel.class )
.handler( clientInitializer );
}

@SuppressWarnings( "SameParameterValue" )
void connect( int port )
{
ChannelFuture channelFuture = bootstrap.connect( "localhost", port ).awaitUninterruptibly();
channel = channelFuture.channel();
}

void disconnect()
{
if ( channel != null )
{
channel.close().awaitUninterruptibly();
eventLoopGroup.shutdownGracefully( 0, 0, SECONDS );
}

bucket.collectedData.release();
}

void assertResponse( ByteBuf expected ) throws InterruptedException
{
assertEventually( channel.toString(), () -> bucket.collectedData, equalTo( expected ), 5, SECONDS );
}

Channel channel()
{
return channel;
}

Future<Channel> sslHandshakeFuture()
{
return clientInitializer.handshakeFuture;
}

String ciphers()
{
return clientInitializer.sslEngine.getSession().getCipherSuite();
}

String protocol()
{
return clientInitializer.sslEngine.getSession().getProtocol();
}

static class Bucket extends SimpleChannelInboundHandler<ByteBuf>
{
private final ByteBuf collectedData;

Bucket()
{
collectedData = ByteBufAllocator.DEFAULT.buffer();
}

@Override
protected void channelRead0( ChannelHandlerContext ctx, ByteBuf msg ) throws Exception
{
collectedData.writeBytes( msg );
}

@Override
public void exceptionCaught( ChannelHandlerContext ctx, Throwable cause ) throws Exception
{
//cause.printStackTrace(); // for debugging
}
}

public static class ClientInitializer extends ChannelInitializer<SocketChannel>
{
private SslContext sslContext;
private final Bucket bucket;
private Future<Channel> handshakeFuture;
private SSLEngine sslEngine;

ClientInitializer( SslContext sslContext, Bucket bucket )
{
this.sslContext = sslContext;
this.bucket = bucket;
}

@Override
protected void initChannel( SocketChannel channel ) throws Exception
{
ChannelPipeline pipeline = channel.pipeline();

this.sslEngine = sslContext.newEngine( channel.alloc() );
this.sslEngine.setUseClientMode( true );

SslHandler sslHandler = new SslHandler( sslEngine );
handshakeFuture = sslHandler.handshakeFuture();

pipeline.addLast( sslHandler );
pipeline.addLast( bucket );
}
}
}

0 comments on commit d00310c

Please sign in to comment.