Skip to content

Commit ebf0e41

Browse files
Add support for specifying SecureRandom in SSLContext initialization (#14058)
Motivation: Enhance security by supporting specific secure randomness source in SSLContext initialization Modification: Support building SecureRandom in `SslContextBuilder`. Allow passing SecureRandom as a parameter when creating an instance of `JdkSslServerContext` through its constructor. Result: Enhance security Fixes #14026 --------- Co-authored-by: Norman Maurer <[email protected]>
1 parent 243de91 commit ebf0e41

File tree

5 files changed

+145
-23
lines changed

5 files changed

+145
-23
lines changed

handler/src/main/java/io/netty/handler/ssl/JdkSslClientContext.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import javax.net.ssl.TrustManagerFactory;
2828
import java.io.File;
2929
import java.security.PrivateKey;
30+
import java.security.SecureRandom;
3031
import java.security.cert.X509Certificate;
3132

3233
/**
@@ -175,7 +176,7 @@ public JdkSslClientContext(
175176
long sessionCacheSize, long sessionTimeout) throws SSLException {
176177
super(newSSLContext(provider, toX509CertificatesInternal(trustCertCollectionFile),
177178
trustManagerFactory, null, null,
178-
null, null, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), true,
179+
null, null, sessionCacheSize, sessionTimeout, null, KeyStore.getDefaultType()), true,
179180
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
180181
}
181182

@@ -258,7 +259,8 @@ public JdkSslClientContext(File trustCertCollectionFile, TrustManagerFactory tru
258259
super(newSSLContext(null, toX509CertificatesInternal(
259260
trustCertCollectionFile), trustManagerFactory,
260261
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
261-
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), true,
262+
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout,
263+
null, KeyStore.getDefaultType()), true,
262264
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
263265
}
264266

@@ -267,11 +269,11 @@ public JdkSslClientContext(File trustCertCollectionFile, TrustManagerFactory tru
267269
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
268270
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
269271
ApplicationProtocolConfig apn, String[] protocols, long sessionCacheSize, long sessionTimeout,
270-
String keyStoreType)
272+
SecureRandom secureRandom, String keyStoreType)
271273
throws SSLException {
272274
super(newSSLContext(sslContextProvider, trustCertCollection, trustManagerFactory,
273275
keyCertChain, key, keyPassword, keyManagerFactory, sessionCacheSize,
274-
sessionTimeout, keyStoreType),
276+
sessionTimeout, secureRandom, keyStoreType),
275277
true, ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE, protocols, false);
276278
}
277279

@@ -280,7 +282,7 @@ private static SSLContext newSSLContext(Provider sslContextProvider,
280282
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
281283
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
282284
long sessionCacheSize, long sessionTimeout,
283-
String keyStore) throws SSLException {
285+
SecureRandom secureRandom, String keyStore) throws SSLException {
284286
try {
285287
if (trustCertCollection != null) {
286288
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory, keyStore);
@@ -293,7 +295,7 @@ private static SSLContext newSSLContext(Provider sslContextProvider,
293295
: SSLContext.getInstance(PROTOCOL, sslContextProvider);
294296
ctx.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(),
295297
trustManagerFactory == null ? null : trustManagerFactory.getTrustManagers(),
296-
null);
298+
secureRandom);
297299

298300
SSLSessionContext sessCtx = ctx.getClientSessionContext();
299301
if (sessionCacheSize > 0) {

handler/src/main/java/io/netty/handler/ssl/JdkSslServerContext.java

+12-9
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import javax.net.ssl.X509ExtendedTrustManager;
4141
import java.io.File;
4242
import java.security.PrivateKey;
43+
import java.security.SecureRandom;
4344
import java.security.UnrecoverableKeyException;
4445
import java.security.cert.CertificateException;
4546
import java.security.cert.X509Certificate;
@@ -207,7 +208,7 @@ public JdkSslServerContext(
207208
long sessionCacheSize, long sessionTimeout, String keyStore) throws SSLException {
208209
super(newSSLContext(provider, null, null,
209210
toX509CertificatesInternal(certChainFile), toPrivateKeyInternal(keyFile, keyPassword),
210-
keyPassword, null, sessionCacheSize, sessionTimeout, keyStore), false,
211+
keyPassword, null, sessionCacheSize, sessionTimeout, null, keyStore), false,
211212
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
212213
}
213214

@@ -247,7 +248,7 @@ public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory tru
247248
long sessionCacheSize, long sessionTimeout) throws SSLException {
248249
super(newSSLContext(null, toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory,
249250
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
250-
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, null), false,
251+
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, null, null), false,
251252
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
252253
}
253254

@@ -288,7 +289,8 @@ public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory tru
288289
long sessionCacheSize, long sessionTimeout) throws SSLException {
289290
super(newSSLContext(null, toX509CertificatesInternal(trustCertCollectionFile), trustManagerFactory,
290291
toX509CertificatesInternal(keyCertChainFile), toPrivateKeyInternal(keyFile, keyPassword),
291-
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, KeyStore.getDefaultType()), false,
292+
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout,
293+
null, KeyStore.getDefaultType()), false,
292294
ciphers, cipherFilter, apn, ClientAuth.NONE, null, false);
293295
}
294296

@@ -298,16 +300,17 @@ public JdkSslServerContext(File trustCertCollectionFile, TrustManagerFactory tru
298300
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
299301
ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout,
300302
ClientAuth clientAuth, String[] protocols, boolean startTls,
301-
String keyStore) throws SSLException {
303+
SecureRandom secureRandom, String keyStore) throws SSLException {
302304
super(newSSLContext(provider, trustCertCollection, trustManagerFactory, keyCertChain, key,
303-
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, keyStore), false,
305+
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout, secureRandom, keyStore), false,
304306
ciphers, cipherFilter, toNegotiator(apn, true), clientAuth, protocols, startTls);
305307
}
306308

307309
private static SSLContext newSSLContext(Provider sslContextProvider, X509Certificate[] trustCertCollection,
308-
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
309-
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
310-
long sessionCacheSize, long sessionTimeout, String keyStore)
310+
TrustManagerFactory trustManagerFactory, X509Certificate[] keyCertChain,
311+
PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
312+
long sessionCacheSize, long sessionTimeout,
313+
SecureRandom secureRandom, String keyStore)
311314
throws SSLException {
312315
if (key == null && keyManagerFactory == null) {
313316
throw new NullPointerException("key, keyManagerFactory");
@@ -333,7 +336,7 @@ private static SSLContext newSSLContext(Provider sslContextProvider, X509Certifi
333336
: SSLContext.getInstance(PROTOCOL, sslContextProvider);
334337
ctx.init(keyManagerFactory.getKeyManagers(),
335338
wrapTrustManagerIfNeeded(trustManagerFactory.getTrustManagers()),
336-
null);
339+
secureRandom);
337340

338341
SSLSessionContext sessCtx = ctx.getServerSessionContext();
339342
if (sessionCacheSize > 0) {

handler/src/main/java/io/netty/handler/ssl/SslContext.java

+9-6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import java.security.KeyStoreException;
5959
import java.security.NoSuchAlgorithmException;
6060
import java.security.PrivateKey;
61+
import java.security.SecureRandom;
6162
import java.security.UnrecoverableKeyException;
6263
import java.security.cert.CertificateException;
6364
import java.security.cert.CertificateFactory;
@@ -441,7 +442,7 @@ trustManagerFactory, toX509Certificates(keyCertChainFile),
441442
toPrivateKey(keyFile, keyPassword),
442443
keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
443444
sessionCacheSize, sessionTimeout, ClientAuth.NONE, null,
444-
false, false, keyStore);
445+
false, false, null, keyStore);
445446
} catch (Exception e) {
446447
if (e instanceof SSLException) {
447448
throw (SSLException) e;
@@ -457,7 +458,8 @@ static SslContext newServerContextInternal(
457458
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
458459
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
459460
long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth, String[] protocols, boolean startTls,
460-
boolean enableOcsp, String keyStoreType, Map.Entry<SslContextOption<?>, Object>... ctxOptions)
461+
boolean enableOcsp, SecureRandom secureRandom, String keyStoreType,
462+
Map.Entry<SslContextOption<?>, Object>... ctxOptions)
461463
throws SSLException {
462464

463465
if (provider == null) {
@@ -472,7 +474,7 @@ static SslContext newServerContextInternal(
472474
return new JdkSslServerContext(sslContextProvider,
473475
trustCertCollection, trustManagerFactory, keyCertChain, key, keyPassword,
474476
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
475-
clientAuth, protocols, startTls, keyStoreType);
477+
clientAuth, protocols, startTls, secureRandom, keyStoreType);
476478
case OPENSSL:
477479
verifyNullSslContextProvider(provider, sslContextProvider);
478480
return new OpenSslServerContext(
@@ -801,7 +803,7 @@ public static SslContext newClientContext(
801803
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
802804
keyPassword, keyManagerFactory, ciphers, cipherFilter,
803805
apn, null, sessionCacheSize, sessionTimeout, false,
804-
KeyStore.getDefaultType());
806+
null, KeyStore.getDefaultType());
805807
} catch (Exception e) {
806808
if (e instanceof SSLException) {
807809
throw (SSLException) e;
@@ -816,7 +818,8 @@ static SslContext newClientContextInternal(
816818
X509Certificate[] trustCert, TrustManagerFactory trustManagerFactory,
817819
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
818820
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, String[] protocols,
819-
long sessionCacheSize, long sessionTimeout, boolean enableOcsp, String keyStoreType,
821+
long sessionCacheSize, long sessionTimeout, boolean enableOcsp,
822+
SecureRandom secureRandom, String keyStoreType,
820823
Map.Entry<SslContextOption<?>, Object>... options) throws SSLException {
821824
if (provider == null) {
822825
provider = defaultClientProvider();
@@ -829,7 +832,7 @@ static SslContext newClientContextInternal(
829832
return new JdkSslClientContext(sslContextProvider,
830833
trustCert, trustManagerFactory, keyCertChain, key, keyPassword,
831834
keyManagerFactory, ciphers, cipherFilter, apn, protocols, sessionCacheSize,
832-
sessionTimeout, keyStoreType);
835+
sessionTimeout, secureRandom, keyStoreType);
833836
case OPENSSL:
834837
verifyNullSslContextProvider(provider, sslContextProvider);
835838
OpenSsl.ensureAvailability();

handler/src/main/java/io/netty/handler/ssl/SslContextBuilder.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.security.KeyStore;
3232
import java.security.PrivateKey;
3333
import java.security.Provider;
34+
import java.security.SecureRandom;
3435
import java.security.cert.X509Certificate;
3536
import java.util.ArrayList;
3637
import java.util.HashMap;
@@ -206,6 +207,7 @@ public static SslContextBuilder forServer(KeyManager keyManager) {
206207
private String[] protocols;
207208
private boolean startTls;
208209
private boolean enableOcsp;
210+
private SecureRandom secureRandom;
209211
private String keyStoreType = KeyStore.getDefaultType();
210212
private final Map<SslContextOption<?>, Object> options = new HashMap<SslContextOption<?>, Object>();
211213

@@ -600,6 +602,21 @@ public SslContextBuilder enableOcsp(boolean enableOcsp) {
600602
return this;
601603
}
602604

605+
/**
606+
* Specify a non-default source of randomness for the {@link JdkSslContext}
607+
* <p>
608+
* In general, the best practice is to leave this unspecified, or to assign a new random source using the
609+
* default {@code new SecureRandom()} constructor.
610+
* Only assign this something when you have a good reason to.
611+
*
612+
* @param secureRandom the source of randomness for {@link JdkSslContext}
613+
*
614+
*/
615+
public SslContextBuilder secureRandom(SecureRandom secureRandom) {
616+
this.secureRandom = secureRandom;
617+
return this;
618+
}
619+
603620
/**
604621
* Create new {@code SslContext} instance with configured settings.
605622
* <p>If {@link #sslProvider(SslProvider)} is set to {@link SslProvider#OPENSSL_REFCNT} then the caller is
@@ -610,11 +627,12 @@ public SslContext build() throws SSLException {
610627
return SslContext.newServerContextInternal(provider, sslContextProvider, trustCertCollection,
611628
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
612629
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth, protocols, startTls,
613-
enableOcsp, keyStoreType, toArray(options.entrySet(), EMPTY_ENTRIES));
630+
enableOcsp, secureRandom, keyStoreType, toArray(options.entrySet(), EMPTY_ENTRIES));
614631
} else {
615632
return SslContext.newClientContextInternal(provider, sslContextProvider, trustCertCollection,
616633
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
617-
ciphers, cipherFilter, apn, protocols, sessionCacheSize, sessionTimeout, enableOcsp, keyStoreType,
634+
ciphers, cipherFilter, apn, protocols, sessionCacheSize,
635+
sessionTimeout, enableOcsp, secureRandom, keyStoreType,
618636
toArray(options.entrySet(), EMPTY_ENTRIES));
619637
}
620638
}

handler/src/test/java/io/netty/handler/ssl/SslContextBuilderTest.java

+96
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.net.Socket;
3232
import java.security.Principal;
3333
import java.security.PrivateKey;
34+
import java.security.SecureRandom;
3435
import java.security.cert.CertificateException;
3536
import java.security.cert.X509Certificate;
3637
import java.util.Collections;
@@ -244,6 +245,16 @@ public void testInvalidCipherOpenSSL() throws Exception {
244245
}
245246
}
246247

248+
@Test
249+
public void testServerContextWithSecureRandom() throws Exception {
250+
testServerContextWithSecureRandom(SslProvider.JDK, new SpySecureRandom());
251+
}
252+
253+
@Test
254+
public void testClientContextWithSecureRandom() throws Exception {
255+
testClientContextWithSecureRandom(SslProvider.JDK, new SpySecureRandom());
256+
}
257+
247258
private static void testKeyStoreType(SslProvider provider) throws Exception {
248259
SelfSignedCertificate cert = new SelfSignedCertificate();
249260
SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey())
@@ -326,6 +337,41 @@ private static void testServerContext(SslProvider provider) throws Exception {
326337
engine.closeOutbound();
327338
}
328339

340+
private static void testServerContextWithSecureRandom(SslProvider provider,
341+
SpySecureRandom secureRandom) throws Exception {
342+
SelfSignedCertificate cert = new SelfSignedCertificate();
343+
SslContextBuilder builder = SslContextBuilder.forServer(cert.key(), cert.cert())
344+
.sslProvider(provider)
345+
.secureRandom(secureRandom)
346+
.trustManager(cert.cert())
347+
.clientAuth(ClientAuth.REQUIRE);
348+
SslContext context = builder.build();
349+
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
350+
assertFalse(engine.getWantClientAuth());
351+
assertTrue(engine.getNeedClientAuth());
352+
assertTrue(secureRandom.getCount() > 0);
353+
engine.closeInbound();
354+
engine.closeOutbound();
355+
}
356+
357+
private static void testClientContextWithSecureRandom(SslProvider provider,
358+
SpySecureRandom secureRandom) throws Exception {
359+
SelfSignedCertificate cert = new SelfSignedCertificate();
360+
SslContextBuilder builder = SslContextBuilder.forClient()
361+
.sslProvider(provider)
362+
.secureRandom(secureRandom)
363+
.keyManager(cert.key(), cert.cert())
364+
.trustManager(cert.cert())
365+
.clientAuth(ClientAuth.OPTIONAL);
366+
SslContext context = builder.build();
367+
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
368+
assertFalse(engine.getWantClientAuth());
369+
assertFalse(engine.getNeedClientAuth());
370+
assertTrue(secureRandom.getCount() > 0);
371+
engine.closeInbound();
372+
engine.closeOutbound();
373+
}
374+
329375
private static void testContextFromManagers(SslProvider provider) throws Exception {
330376
final SelfSignedCertificate cert = new SelfSignedCertificate();
331377
KeyManager customKeyManager = new X509ExtendedKeyManager() {
@@ -425,4 +471,54 @@ public X509Certificate[] getAcceptedIssuers() {
425471
server_engine.closeInbound();
426472
server_engine.closeOutbound();
427473
}
474+
475+
private static final class SpySecureRandom extends SecureRandom {
476+
private int count;
477+
478+
@Override
479+
public int nextInt() {
480+
count++;
481+
return super.nextInt();
482+
}
483+
484+
@Override
485+
public int nextInt(int bound) {
486+
count++;
487+
return super.nextInt(bound);
488+
}
489+
490+
@Override
491+
public long nextLong() {
492+
count++;
493+
return super.nextLong();
494+
}
495+
496+
@Override
497+
public boolean nextBoolean() {
498+
count++;
499+
return super.nextBoolean();
500+
}
501+
502+
@Override
503+
public float nextFloat() {
504+
count++;
505+
return super.nextFloat();
506+
}
507+
508+
@Override
509+
public double nextDouble() {
510+
count++;
511+
return super.nextDouble();
512+
}
513+
514+
@Override
515+
public double nextGaussian() {
516+
count++;
517+
return super.nextGaussian();
518+
}
519+
520+
public int getCount() {
521+
return count;
522+
}
523+
}
428524
}

0 commit comments

Comments
 (0)