Skip to content

Commit fa15d8c

Browse files
committed
Merge remote-tracking branch 'upstream/1.0' into 1.1
2 parents 3a5da0d + e2e4574 commit fa15d8c

File tree

11 files changed

+500
-171
lines changed

11 files changed

+500
-171
lines changed

driver/src/main/java/org/neo4j/driver/internal/security/SecurityPlan.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,17 @@
2222
import org.neo4j.driver.internal.net.BoltServerAddress;
2323
import org.neo4j.driver.v1.*;
2424

25+
import javax.net.ssl.KeyManager;
26+
import javax.net.ssl.SSLContext;
2527
import javax.net.ssl.TrustManager;
2628
import javax.net.ssl.TrustManagerFactory;
2729
import java.io.File;
2830
import java.io.IOException;
2931
import java.security.GeneralSecurityException;
32+
import java.security.KeyManagementException;
3033
import java.security.KeyStore;
34+
import java.security.KeyStoreException;
35+
import java.security.NoSuchAlgorithmException;
3136

3237
import static org.neo4j.driver.internal.util.CertificateTool.loadX509Cert;
3338

@@ -50,37 +55,48 @@ public static SecurityPlan forSignedCertificates( File certFile )
5055
// Create TrustManager from TrustedKeyStore
5156
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( "SunX509" );
5257
trustManagerFactory.init( trustedKeyStore );
53-
return new SecurityPlan( true, trustManagerFactory.getTrustManagers() );
58+
59+
SSLContext sslContext = SSLContext.getInstance( "TLS" );
60+
sslContext.init( new KeyManager[0], trustManagerFactory.getTrustManagers(), null );
61+
62+
return new SecurityPlan( true, sslContext);
63+
}
64+
65+
public static SecurityPlan forSystemCertificates() throws NoSuchAlgorithmException, KeyStoreException
66+
{
67+
return new SecurityPlan( true, SSLContext.getDefault() );
5468
}
5569

70+
5671
public static SecurityPlan forTrustOnFirstUse( File knownHosts, BoltServerAddress address, Logger logger )
57-
throws IOException
72+
throws IOException, KeyManagementException, NoSuchAlgorithmException
5873
{
59-
return new SecurityPlan( true, new TrustOnFirstUseTrustManager( address, knownHosts, logger ) );
74+
SSLContext sslContext = SSLContext.getInstance( "TLS" );
75+
sslContext.init( new KeyManager[0], new TrustManager[]{new TrustOnFirstUseTrustManager( address, knownHosts, logger )}, null );
76+
77+
return new SecurityPlan( true, sslContext);
6078
}
6179

6280
public static SecurityPlan insecure()
6381
{
64-
return new SecurityPlan( false );
82+
return new SecurityPlan( false, null );
6583
}
6684

6785
private final boolean requiresEncryption;
68-
private final TrustManager[] trustManagers;
86+
private final SSLContext sslContext;
6987

70-
public SecurityPlan( boolean requiresEncryption, TrustManager... trustManagers )
88+
private SecurityPlan( boolean requiresEncryption, SSLContext sslContext)
7189
{
7290
this.requiresEncryption = requiresEncryption;
73-
this.trustManagers = trustManagers;
91+
this.sslContext = sslContext;
7492
}
7593

7694
public boolean requiresEncryption()
7795
{
7896
return requiresEncryption;
7997
}
8098

81-
public TrustManager[] trustManagers()
82-
{
83-
return trustManagers;
84-
}
99+
100+
public SSLContext sslContext() {return sslContext;}
85101

86102
}

driver/src/main/java/org/neo4j/driver/internal/security/TLSSocketChannel.java

Lines changed: 37 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
import org.neo4j.driver.internal.net.BoltServerAddress;
3333
import org.neo4j.driver.v1.Logger;
3434
import org.neo4j.driver.internal.util.BytePrinter;
35+
import org.neo4j.driver.internal.util.BytePrinter;
36+
import org.neo4j.driver.v1.Config.TrustStrategy;
37+
import org.neo4j.driver.v1.Logger;
3538
import org.neo4j.driver.v1.exceptions.ClientException;
3639

3740
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.FINISHED;
@@ -67,8 +70,7 @@ public class TLSSocketChannel implements ByteChannel
6770
public TLSSocketChannel( BoltServerAddress address, SecurityPlan securityPlan, ByteChannel channel, Logger logger )
6871
throws GeneralSecurityException, IOException
6972
{
70-
this(channel, logger, createSSLEngine( address, createSSLContext( securityPlan ) ) );
71-
73+
this( channel, logger, createSSLEngine( address, securityPlan.sslContext() ) );
7274
}
7375

7476
public TLSSocketChannel( ByteChannel channel, Logger logger, SSLEngine sslEngine ) throws GeneralSecurityException, IOException
@@ -163,8 +165,8 @@ private HandshakeStatus runDelegatedTasks()
163165
* To verify if deciphering is done successfully, we could check if any bytes has been read into {@code buffer},
164166
* as the deciphered bytes will only be saved into {@code buffer} when deciphering is carried out successfully.
165167
*
166-
* @param buffer
167-
* @return
168+
* @param buffer to read data into.
169+
* @return The status of the current handshake.
168170
* @throws IOException
169171
*/
170172
private HandshakeStatus unwrap( ByteBuffer buffer ) throws IOException
@@ -261,7 +263,7 @@ private HandshakeStatus unwrap( ByteBuffer buffer ) throws IOException
261263
* a loop
262264
*
263265
* @param buffer contains the bytes to send to channel
264-
* @return
266+
* @return The status of the current handshake
265267
* @throws IOException
266268
*/
267269
private HandshakeStatus wrap( ByteBuffer buffer ) throws IOException
@@ -277,7 +279,7 @@ private HandshakeStatus wrap( ByteBuffer buffer ) throws IOException
277279
case OK:
278280
handshakeStatus = runDelegatedTasks();
279281
cipherOut.flip();
280-
while(cipherOut.hasRemaining())
282+
while ( cipherOut.hasRemaining() )
281283
{
282284
channel.write( cipherOut );
283285
}
@@ -287,19 +289,30 @@ private HandshakeStatus wrap( ByteBuffer buffer ) throws IOException
287289
// Enlarge the buffer and return the old status
288290
int curNetSize = cipherOut.capacity();
289291
int netSize = sslEngine.getSession().getPacketBufferSize();
290-
if ( curNetSize >= netSize || buffer.capacity() > netSize )
292+
if ( netSize > curNetSize )
291293
{
292-
// TODO
293-
throw new ClientException(
294-
String.format( "Failed to enlarge network buffer from %s to %s. This is either because the " +
295-
"new size is however less than the old size, or because the application " +
296-
"buffer size %s is so big that the application data still cannot fit into the " +
297-
"new network buffer.", curNetSize, netSize, buffer.capacity() ) );
294+
// enlarge the peer application data buffer
295+
cipherOut = ByteBuffer.allocate( netSize );
296+
logger.debug( "Enlarged network output buffer from %s to %s. " +
297+
"This operation should be a rare operation.", curNetSize, netSize );
298+
}
299+
else
300+
{
301+
// flush as much data as possible
302+
cipherOut.flip();
303+
int written = channel.write( cipherOut );
304+
if (written == 0)
305+
{
306+
throw new ClientException(
307+
String.format(
308+
"Failed to enlarge network buffer from %s to %s. This is either because the " +
309+
"new size is however less than the old size, or because the application " +
310+
"buffer size %s is so big that the application data still cannot fit into the " +
311+
"new network buffer.", curNetSize, netSize, buffer.capacity() ) );
312+
}
313+
cipherOut.compact();
314+
logger.debug( "Network output buffer couldn't be enlarged, flushing data to the channel instead." );
298315
}
299-
300-
cipherOut = ByteBuffer.allocate( netSize );
301-
logger.debug( "Enlarged network output buffer from %s to %s. " +
302-
"This operation should be a rare operation.", curNetSize, netSize );
303316
break;
304317
default:
305318
throw new ClientException( "Got unexpected status " + status );
@@ -320,9 +333,9 @@ private HandshakeStatus wrap( ByteBuffer buffer ) throws IOException
320333
* After the method call, the new position of {@code from.pos} will be {@code from.pos + p}, and similarly,
321334
* the new position of {@code to.pos} will be {@code to.pos + p}
322335
*
323-
* @param from
324-
* @param to
325-
* @return
336+
* @param from buffer to copy from
337+
* @param to buffer to copy to
338+
* @return the number of transferred bytes
326339
*/
327340
static int bufferCopy( ByteBuffer from, ByteBuffer to )
328341
{
@@ -339,25 +352,10 @@ static int bufferCopy( ByteBuffer from, ByteBuffer to )
339352
return maxTransfer;
340353
}
341354

342-
/**
343-
* Create an SSLContext based on a given SecurityPlan.
344-
*
345-
* @param securityPlan
346-
* @return
347-
* @throws GeneralSecurityException
348-
* @throws IOException
349-
*/
350-
private static SSLContext createSSLContext( SecurityPlan securityPlan ) throws GeneralSecurityException, IOException
351-
{
352-
SSLContext sslContext = SSLContext.getInstance( "TLS" );
353-
sslContext.init( new KeyManager[0], securityPlan.trustManagers(), null );
354-
return sslContext;
355-
}
356-
357355
/**
358356
* Create SSLEngine with the SSLContext just created.
359-
* @param address
360-
* @param sslContext
357+
* @param address the host to connect to
358+
* @param sslContext the current ssl context
361359
*/
362360
private static SSLEngine createSSLEngine( BoltServerAddress address, SSLContext sslContext )
363361
{
@@ -445,10 +443,10 @@ public void close() throws IOException
445443
channel.close();
446444
logger.debug( "~~ [CLOSED SECURE CHANNEL]" );
447445
}
448-
catch(IOException e)
446+
catch ( IOException e )
449447
{
450448
// Treat this as ok - the connection is closed, even if the TLS session did not exit cleanly.
451-
logger.warn( "TLS socket could not be closed cleanly: '"+e.getMessage()+"'", e );
449+
logger.warn( "TLS socket could not be closed cleanly: '" + e.getMessage() + "'", e );
452450
}
453451
}
454452

driver/src/main/java/org/neo4j/driver/v1/Config.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import org.neo4j.driver.v1.util.Immutable;
2828

2929
import static java.lang.System.getProperty;
30-
import static org.neo4j.driver.v1.Config.TrustStrategy.*;
30+
import static org.neo4j.driver.v1.Config.TrustStrategy.trustOnFirstUse;
3131

3232
/**
3333
* A configuration class to config driver properties.
@@ -245,7 +245,7 @@ public ConfigBuilder withEncryptionLevel( EncryptionLevel level )
245245
/**
246246
* Specify how to determine the authenticity of an encryption certificate provided by the Neo4j instance we are connecting to.
247247
* This defaults to {@link TrustStrategy#trustOnFirstUse(File)}.
248-
* See {@link TrustStrategy#trustSignedBy(File)} for using certificate signatures instead to verify
248+
* See {@link TrustStrategy#trustCustomCertificateSignedBy(File)} for using certificate signatures instead to verify
249249
* trust.
250250
* <p>
251251
* This is an important setting to understand, because unless we know that the remote server we have an encrypted connection to
@@ -296,12 +296,20 @@ public static class TrustStrategy
296296
public enum Strategy
297297
{
298298
TRUST_ON_FIRST_USE,
299-
TRUST_SIGNED_CERTIFICATES
299+
@Deprecated
300+
TRUST_SIGNED_CERTIFICATES,
301+
TRUST_CUSTOM_CA_SIGNED_CERTIFICATES,
302+
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
300303
}
301304

302305
private final Strategy strategy;
303306
private final File certFile;
304307

308+
private TrustStrategy( Strategy strategy )
309+
{
310+
this( strategy, null );
311+
}
312+
305313
private TrustStrategy( Strategy strategy, File certFile )
306314
{
307315
this.strategy = strategy;
@@ -322,6 +330,15 @@ public File certFile()
322330
return certFile;
323331
}
324332

333+
/**
334+
* Use {@link #trustCustomCertificateSignedBy(File)} instead.
335+
*/
336+
@Deprecated
337+
public static TrustStrategy trustSignedBy( File certFile )
338+
{
339+
return new TrustStrategy( Strategy.TRUST_SIGNED_CERTIFICATES, certFile );
340+
}
341+
325342
/**
326343
* Only encrypted connections to Neo4j instances with certificates signed by a trusted certificate will be accepted.
327344
* The file specified should contain one or more trusted X.509 certificates.
@@ -332,9 +349,14 @@ public File certFile()
332349
* @param certFile the trusted certificate file
333350
* @return an authentication config
334351
*/
335-
public static TrustStrategy trustSignedBy( File certFile )
352+
public static TrustStrategy trustCustomCertificateSignedBy( File certFile )
336353
{
337-
return new TrustStrategy( Strategy.TRUST_SIGNED_CERTIFICATES, certFile );
354+
return new TrustStrategy( Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES, certFile );
355+
}
356+
357+
public static TrustStrategy trustSystemCertifcates()
358+
{
359+
return new TrustStrategy( Strategy.TRUST_SYSTEM_CA_SIGNED_CERTIFICATES );
338360
}
339361

340362
/**
@@ -345,7 +367,7 @@ public static TrustStrategy trustSignedBy( File certFile )
345367
* Each time we reconnect to a known host, we verify that its certificate remains the same, guarding against attackers intercepting our communication.
346368
* <p>
347369
* Note that this approach is vulnerable to man-in-the-middle attacks the very first time you connect to a new Neo4j instance.
348-
* If you do not trust the network you are connecting over, consider using {@link #trustSignedBy(File) signed certificates} instead, or manually adding the
370+
* If you do not trust the network you are connecting over, consider using {@link #trustCustomCertificateSignedBy(File)} signed certificates} instead, or manually adding the
349371
* trusted host line into the specified file.
350372
*
351373
* @param knownHostsFile a file where known certificates are stored.

driver/src/main/java/org/neo4j/driver/v1/GraphDatabase.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.security.GeneralSecurityException;
3131

3232
import static java.lang.String.format;
33+
import static org.neo4j.driver.internal.security.SecurityPlan.insecure;
3334
import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED;
3435
import static org.neo4j.driver.v1.Config.EncryptionLevel.REQUIRED_NON_LOCAL;
3536

@@ -186,21 +187,25 @@ private static SecurityPlan createSecurityPlan( BoltServerAddress address, Confi
186187

187188
if ( requiresEncryption )
188189
{
190+
Logger logger = config.logging().getLog( "session" );
189191
switch ( config.trustStrategy().strategy() )
190192
{
191193
case TRUST_SIGNED_CERTIFICATES:
194+
logger.warn( "Option `TRUST_SIGNED_CERTIFICATE` has been deprecated and will be removed in a future version " +
195+
"of the driver. Please switch to use `TRUST_CUSTOM_CA_SIGNED_CERTIFICATES` instead." );
196+
//intentional fallthrough
197+
case TRUST_CUSTOM_CA_SIGNED_CERTIFICATES:
192198
return SecurityPlan.forSignedCertificates( config.trustStrategy().certFile() );
193199
case TRUST_ON_FIRST_USE:
194200
return SecurityPlan.forTrustOnFirstUse( config.trustStrategy().certFile(),
195-
address, config.logging().getLog( "session" ) );
201+
address, logger );
196202
default:
197203
throw new ClientException( "Unknown TLS authentication strategy: " + config.trustStrategy().strategy().name() );
198204
}
199205
}
200206
else
201207
{
202-
return new SecurityPlan( false );
208+
return insecure();
203209
}
204210
}
205-
206211
}

driver/src/test/java/org/neo4j/driver/internal/ConfigTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ public void shouldChangeToTrustedCert()
6969
{
7070
// Given
7171
File trustedCert = new File( "trusted_cert" );
72-
Config config = Config.build().withTrustStrategy( Config.TrustStrategy.trustSignedBy( trustedCert ) ).toConfig();
72+
Config config = Config.build().withTrustStrategy( Config.TrustStrategy.trustCustomCertificateSignedBy( trustedCert ) ).toConfig();
7373

7474
// When
7575
Config.TrustStrategy authConfig = config.trustStrategy();
7676

7777
// Then
78-
assertEquals( authConfig.strategy(), Config.TrustStrategy.Strategy.TRUST_SIGNED_CERTIFICATES );
78+
assertEquals( authConfig.strategy(), Config.TrustStrategy.Strategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES );
7979
assertEquals( trustedCert.getAbsolutePath(), authConfig.certFile().getAbsolutePath() );
8080
}
8181

@@ -86,7 +86,7 @@ public void shouldConfigureMinIdleTime() throws Throwable
8686
Config config = Config.build().withSessionLivenessCheckTimeout( 1337 ).toConfig();
8787

8888
// then
89-
assertThat( config.idleTimeBeforeConnectionTest(), equalTo( 1337l ) );
89+
assertThat( config.idleTimeBeforeConnectionTest(), equalTo( 1337L ) );
9090
}
9191

9292
public static void deleteDefaultKnownCertFileIfExists()

0 commit comments

Comments
 (0)