Adding client auth to SslContextBuilder

Motivation:

To simplify the use of client auth, we need to add it to the SslContextBuilder.

Modifications:

Added a ClientAuth enum and plumbed it through the builder, down into the contexts/engines.

Result:

Client auth can be configured when building an SslContext.
This commit is contained in:
nmittler 2015-09-18 09:50:30 -07:00
parent 3ee44a3dbb
commit a1d0207ec5
11 changed files with 140 additions and 65 deletions

View File

@ -0,0 +1,38 @@
/*
* Copyright 2015 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:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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;
/**
* Indicates the state of the {@link javax.net.ssl.SSLEngine} with respect to client authentication.
* This configuration item really only applies when building the server-side {@link SslContext}.
*/
public enum ClientAuth {
/**
* Indicates that the {@link javax.net.ssl.SSLEngine} will not request client authentication.
*/
NONE,
/**
* Indicates that the {@link javax.net.ssl.SSLEngine} will request client authentication.
*/
OPTIONAL,
/**
* Indicates that the {@link javax.net.ssl.SSLEngine} will *require* client authentication.
*/
REQUIRE
}

View File

@ -243,7 +243,7 @@ public final class JdkSslClientContext extends JdkSslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn); super(ciphers, cipherFilter, apn, ClientAuth.NONE);
try { try {
ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory, ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
@ -260,7 +260,7 @@ public final class JdkSslClientContext extends JdkSslContext {
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, toNegotiator(apn, false)); super(ciphers, cipherFilter, toNegotiator(apn, false), ClientAuth.NONE);
ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, sessionCacheSize, sessionTimeout); keyManagerFactory, sessionCacheSize, sessionTimeout);
} }

View File

@ -139,14 +139,12 @@ public abstract class JdkSslContext extends SslContext {
private final String[] cipherSuites; private final String[] cipherSuites;
private final List<String> unmodifiableCipherSuites; private final List<String> unmodifiableCipherSuites;
private final JdkApplicationProtocolNegotiator apn; private final JdkApplicationProtocolNegotiator apn;
private final ClientAuth clientAuth;
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig config, JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
boolean isServer) { ClientAuth clientAuth) {
this(ciphers, cipherFilter, toNegotiator(config, isServer));
}
JdkSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn) {
this.apn = checkNotNull(apn, "apn"); this.apn = checkNotNull(apn, "apn");
this.clientAuth = checkNotNull(clientAuth, "clientAuth");
cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites( cipherSuites = checkNotNull(cipherFilter, "cipherFilter").filterCipherSuites(
ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS); ciphers, DEFAULT_CIPHERS, SUPPORTED_CIPHERS);
unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites)); unmodifiableCipherSuites = Collections.unmodifiableList(Arrays.asList(cipherSuites));
@ -186,23 +184,28 @@ public abstract class JdkSslContext extends SslContext {
@Override @Override
public final SSLEngine newEngine(ByteBufAllocator alloc) { public final SSLEngine newEngine(ByteBufAllocator alloc) {
SSLEngine engine = context().createSSLEngine(); return configureAndWrapEngine(context().createSSLEngine());
engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient());
return wrapEngine(engine);
} }
@Override @Override
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
SSLEngine engine = context().createSSLEngine(peerHost, peerPort); return configureAndWrapEngine(context().createSSLEngine(peerHost, peerPort));
}
private SSLEngine configureAndWrapEngine(SSLEngine engine) {
engine.setEnabledCipherSuites(cipherSuites); engine.setEnabledCipherSuites(cipherSuites);
engine.setEnabledProtocols(PROTOCOLS); engine.setEnabledProtocols(PROTOCOLS);
engine.setUseClientMode(isClient()); engine.setUseClientMode(isClient());
return wrapEngine(engine); if (isServer()) {
switch (clientAuth) {
case OPTIONAL:
engine.setWantClientAuth(true);
break;
case REQUIRE:
engine.setNeedClientAuth(true);
break;
}
} }
private SSLEngine wrapEngine(SSLEngine engine) {
return apn.wrapperFactory().wrapSslEngine(engine, apn, isServer()); return apn.wrapperFactory().wrapSslEngine(engine, apn, isServer());
} }

View File

@ -208,7 +208,7 @@ public final class JdkSslServerContext extends JdkSslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, JdkApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn); super(ciphers, cipherFilter, apn, ClientAuth.NONE);
try { try {
ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory, ctx = newSSLContext(toX509Certificates(trustCertChainFile), trustManagerFactory,
toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword), toX509Certificates(keyCertChainFile), toPrivateKey(keyFile, keyPassword),
@ -224,8 +224,9 @@ public final class JdkSslServerContext extends JdkSslContext {
JdkSslServerContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, JdkSslServerContext(X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword,
KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, KeyManagerFactory keyManagerFactory, Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout) throws SSLException { ApplicationProtocolConfig apn, long sessionCacheSize, long sessionTimeout,
super(ciphers, cipherFilter, toNegotiator(apn, true)); ClientAuth clientAuth) throws SSLException {
super(ciphers, cipherFilter, toNegotiator(apn, true), clientAuth);
ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key, ctx = newSSLContext(trustCertChain, trustManagerFactory, keyCertChain, key,
keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout); keyPassword, keyManagerFactory, sessionCacheSize, sessionTimeout);
} }

View File

@ -171,7 +171,8 @@ public final class OpenSslClientContext extends OpenSslContext {
CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) long sessionCacheSize, long sessionTimeout)
throws SSLException { throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, null); super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, null,
ClientAuth.NONE);
boolean success = false; boolean success = false;
try { try {
if (trustCertChainFile != null && !trustCertChainFile.isFile()) { if (trustCertChainFile != null && !trustCertChainFile.isFile()) {
@ -271,7 +272,8 @@ public final class OpenSslClientContext extends OpenSslContext {
CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) long sessionCacheSize, long sessionTimeout)
throws SSLException { throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain); super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_CLIENT, keyCertChain,
ClientAuth.NONE);
boolean success = false; boolean success = false;
try { try {
if (key == null && keyCertChain != null || key != null && keyCertChain == null) { if (key == null && keyCertChain != null || key != null && keyCertChain == null) {

View File

@ -83,6 +83,7 @@ public abstract class OpenSslContext extends SslContext {
private final OpenSslApplicationProtocolNegotiator apn; private final OpenSslApplicationProtocolNegotiator apn;
private final int mode; private final int mode;
private final Certificate[] keyCertChain; private final Certificate[] keyCertChain;
private final ClientAuth clientAuth;
static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR = static final OpenSslApplicationProtocolNegotiator NONE_PROTOCOL_NEGOTIATOR =
new OpenSslApplicationProtocolNegotiator() { new OpenSslApplicationProtocolNegotiator() {
@ -127,20 +128,24 @@ public abstract class OpenSslContext extends SslContext {
} }
OpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg, OpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apnCfg,
long sessionCacheSize, long sessionTimeout, int mode, Certificate[] keyCertChain) long sessionCacheSize, long sessionTimeout, int mode, Certificate[] keyCertChain,
ClientAuth clientAuth)
throws SSLException { throws SSLException {
this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode, keyCertChain); this(ciphers, cipherFilter, toNegotiator(apnCfg), sessionCacheSize, sessionTimeout, mode, keyCertChain,
clientAuth);
} }
OpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslContext(Iterable<String> ciphers, CipherSuiteFilter cipherFilter,
OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize, OpenSslApplicationProtocolNegotiator apn, long sessionCacheSize,
long sessionTimeout, int mode, Certificate[] keyCertChain) throws SSLException { long sessionTimeout, int mode, Certificate[] keyCertChain,
ClientAuth clientAuth) throws SSLException {
OpenSsl.ensureAvailability(); OpenSsl.ensureAvailability();
if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) { if (mode != SSL.SSL_MODE_SERVER && mode != SSL.SSL_MODE_CLIENT) {
throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT"); throw new IllegalArgumentException("mode most be either SSL.SSL_MODE_SERVER or SSL.SSL_MODE_CLIENT");
} }
this.mode = mode; this.mode = mode;
this.clientAuth = isServer() ? checkNotNull(clientAuth, "clientAuth") : ClientAuth.NONE;
if (mode == SSL.SSL_MODE_SERVER) { if (mode == SSL.SSL_MODE_SERVER) {
rejectRemoteInitiatedRenegotiation = rejectRemoteInitiatedRenegotiation =
@ -291,7 +296,7 @@ public abstract class OpenSslContext extends SslContext {
@Override @Override
public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) { public final SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort) {
final OpenSslEngine engine = new OpenSslEngine(ctx, alloc, isClient(), sessionContext(), apn, engineMap, final OpenSslEngine engine = new OpenSslEngine(ctx, alloc, isClient(), sessionContext(), apn, engineMap,
rejectRemoteInitiatedRenegotiation, peerHost, peerPort, keyCertChain); rejectRemoteInitiatedRenegotiation, peerHost, peerPort, keyCertChain, clientAuth);
engineMap.add(engine); engineMap.add(engine);
return engine; return engine;
} }

View File

@ -110,12 +110,6 @@ public final class OpenSslEngine extends SSLEngine {
static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH; static final int MAX_ENCRYPTION_OVERHEAD_LENGTH = MAX_ENCRYPTED_PACKET_LENGTH - MAX_PLAINTEXT_LENGTH;
enum ClientAuthMode {
NONE,
OPTIONAL,
REQUIRE,
}
private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER; private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER;
private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL"; private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
@ -157,7 +151,7 @@ public final class OpenSslEngine extends SSLEngine {
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
private volatile int destroyed; private volatile int destroyed;
private volatile ClientAuthMode clientAuth = ClientAuthMode.NONE; private volatile ClientAuth clientAuth = ClientAuth.NONE;
private volatile String endPointIdentificationAlgorithm; private volatile String endPointIdentificationAlgorithm;
// Store as object as AlgorithmConstraints only exists since java 7. // Store as object as AlgorithmConstraints only exists since java 7.
@ -189,30 +183,25 @@ public final class OpenSslEngine extends SSLEngine {
@Deprecated @Deprecated
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, public OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
@SuppressWarnings("unused") String fallbackApplicationProtocol) { @SuppressWarnings("unused") String fallbackApplicationProtocol) {
this(sslCtx, alloc, false, null, OpenSslContext.NONE_PROTOCOL_NEGOTIATOR, OpenSslEngineMap.EMPTY, false); this(sslCtx, alloc, false, null, OpenSslContext.NONE_PROTOCOL_NEGOTIATOR, OpenSslEngineMap.EMPTY, false,
ClientAuth.NONE);
} }
/**
* Creates a new instance
*
* @param sslCtx an OpenSSL {@code SSL_CTX} object
* @param alloc the {@link ByteBufAllocator} that will be used by this engine
* @param clientMode {@code true} if this is used for clients, {@code false} otherwise
* @param sessionContext the {@link OpenSslSessionContext} this {@link SSLEngine} belongs to.
*/
OpenSslEngine(long sslCtx, ByteBufAllocator alloc, OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
boolean clientMode, OpenSslSessionContext sessionContext, boolean clientMode, OpenSslSessionContext sessionContext,
OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap, OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap,
boolean rejectRemoteInitiatedRenegation) { boolean rejectRemoteInitiatedRenegation,
ClientAuth clientAuth) {
this(sslCtx, alloc, clientMode, sessionContext, apn, engineMap, rejectRemoteInitiatedRenegation, null, -1, this(sslCtx, alloc, clientMode, sessionContext, apn, engineMap, rejectRemoteInitiatedRenegation, null, -1,
null); null, clientAuth);
} }
OpenSslEngine(long sslCtx, ByteBufAllocator alloc, OpenSslEngine(long sslCtx, ByteBufAllocator alloc,
boolean clientMode, OpenSslSessionContext sessionContext, boolean clientMode, OpenSslSessionContext sessionContext,
OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap, OpenSslApplicationProtocolNegotiator apn, OpenSslEngineMap engineMap,
boolean rejectRemoteInitiatedRenegation, String peerHost, int peerPort, boolean rejectRemoteInitiatedRenegation, String peerHost, int peerPort,
java.security.cert.Certificate[] localCerts) { java.security.cert.Certificate[] localCerts,
ClientAuth clientAuth) {
super(peerHost, peerPort); super(peerHost, peerPort);
OpenSsl.ensureAvailability(); OpenSsl.ensureAvailability();
if (sslCtx == 0) { if (sslCtx == 0) {
@ -221,6 +210,7 @@ public final class OpenSslEngine extends SSLEngine {
this.alloc = checkNotNull(alloc, "alloc"); this.alloc = checkNotNull(alloc, "alloc");
this.apn = checkNotNull(apn, "apn"); this.apn = checkNotNull(apn, "apn");
this.clientAuth = clientMode ? ClientAuth.NONE : checkNotNull(clientAuth, "clientAuth");
ssl = SSL.newSSL(sslCtx, !clientMode); ssl = SSL.newSSL(sslCtx, !clientMode);
session = new OpenSslSession(ssl, sessionContext); session = new OpenSslSession(ssl, sessionContext);
networkBIO = SSL.makeNetworkBIO(ssl); networkBIO = SSL.makeNetworkBIO(ssl);
@ -1194,25 +1184,25 @@ public final class OpenSslEngine extends SSLEngine {
@Override @Override
public void setNeedClientAuth(boolean b) { public void setNeedClientAuth(boolean b) {
setClientAuth(b ? ClientAuthMode.REQUIRE : ClientAuthMode.NONE); setClientAuth(b ? ClientAuth.REQUIRE : ClientAuth.NONE);
} }
@Override @Override
public boolean getNeedClientAuth() { public boolean getNeedClientAuth() {
return clientAuth == ClientAuthMode.REQUIRE; return clientAuth == ClientAuth.REQUIRE;
} }
@Override @Override
public void setWantClientAuth(boolean b) { public void setWantClientAuth(boolean b) {
setClientAuth(b ? ClientAuthMode.OPTIONAL : ClientAuthMode.NONE); setClientAuth(b ? ClientAuth.OPTIONAL : ClientAuth.NONE);
} }
@Override @Override
public boolean getWantClientAuth() { public boolean getWantClientAuth() {
return clientAuth == ClientAuthMode.OPTIONAL; return clientAuth == ClientAuth.OPTIONAL;
} }
private void setClientAuth(ClientAuthMode mode) { private void setClientAuth(ClientAuth mode) {
if (clientMode) { if (clientMode) {
return; return;
} }

View File

@ -317,7 +317,8 @@ public final class OpenSslServerContext extends OpenSslContext {
File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory, File keyCertChainFile, File keyFile, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, OpenSslApplicationProtocolNegotiator apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout) throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, null); super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, null,
ClientAuth.NONE);
OpenSsl.ensureAvailability(); OpenSsl.ensureAvailability();
checkNotNull(keyCertChainFile, "keyCertChainFile"); checkNotNull(keyCertChainFile, "keyCertChainFile");
@ -417,8 +418,9 @@ public final class OpenSslServerContext extends OpenSslContext {
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout, ClientAuth clientAuth) throws SSLException {
super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain); super(ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, SSL.SSL_MODE_SERVER, keyCertChain,
clientAuth);
OpenSsl.ensureAvailability(); OpenSsl.ensureAvailability();
checkNotNull(keyCertChain, "keyCertChainFile"); checkNotNull(keyCertChain, "keyCertChainFile");

View File

@ -377,7 +377,7 @@ public abstract class SslContext {
toX509Certificates(keyCertChainFile), toX509Certificates(keyCertChainFile),
toPrivateKey(keyFile, keyPassword), toPrivateKey(keyFile, keyPassword),
keyPassword, keyManagerFactory, ciphers, cipherFilter, apn, keyPassword, keyManagerFactory, ciphers, cipherFilter, apn,
sessionCacheSize, sessionTimeout); sessionCacheSize, sessionTimeout, ClientAuth.NONE);
} catch (Exception e) { } catch (Exception e) {
if (e instanceof SSLException) { if (e instanceof SSLException) {
throw (SSLException) e; throw (SSLException) e;
@ -391,7 +391,8 @@ public abstract class SslContext {
X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory, X509Certificate[] trustCertChain, TrustManagerFactory trustManagerFactory,
X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory, X509Certificate[] keyCertChain, PrivateKey key, String keyPassword, KeyManagerFactory keyManagerFactory,
Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn, Iterable<String> ciphers, CipherSuiteFilter cipherFilter, ApplicationProtocolConfig apn,
long sessionCacheSize, long sessionTimeout) throws SSLException { long sessionCacheSize, long sessionTimeout,
ClientAuth clientAuth) throws SSLException {
if (provider == null) { if (provider == null) {
provider = defaultServerProvider(); provider = defaultServerProvider();
@ -401,11 +402,13 @@ public abstract class SslContext {
case JDK: case JDK:
return new JdkSslServerContext( return new JdkSslServerContext(
trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth);
case OPENSSL: case OPENSSL:
return new OpenSslServerContext( return new OpenSslServerContext(
trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword, trustCertChain, trustManagerFactory, keyCertChain, key, keyPassword,
keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); keyManagerFactory, ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout,
clientAuth);
default: default:
throw new Error(provider.toString()); throw new Error(provider.toString());
} }

View File

@ -110,6 +110,7 @@ public final class SslContextBuilder {
private ApplicationProtocolConfig apn; private ApplicationProtocolConfig apn;
private long sessionCacheSize; private long sessionCacheSize;
private long sessionTimeout; private long sessionTimeout;
private ClientAuth clientAuth = ClientAuth.NONE;
private SslContextBuilder(boolean forServer) { private SslContextBuilder(boolean forServer) {
this.forServer = forServer; this.forServer = forServer;
@ -298,6 +299,14 @@ public final class SslContextBuilder {
return this; return this;
} }
/**
* Sets the client authentication mode.
*/
public SslContextBuilder clientAuth(ClientAuth clientAuth) {
this.clientAuth = checkNotNull(clientAuth, "clientAuth");
return this;
}
/** /**
* Create new {@code SslContext} instance with configured settings. * Create new {@code SslContext} instance with configured settings.
*/ */
@ -305,7 +314,7 @@ public final class SslContextBuilder {
if (forServer) { if (forServer) {
return SslContext.newServerContextInternal(provider, trustCertChain, return SslContext.newServerContextInternal(provider, trustCertChain,
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,
ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout); ciphers, cipherFilter, apn, sessionCacheSize, sessionTimeout, clientAuth);
} else { } else {
return SslContext.newClientContextInternal(provider, trustCertChain, return SslContext.newClientContextInternal(provider, trustCertChain,
trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory, trustManagerFactory, keyCertChain, key, keyPassword, keyManagerFactory,

View File

@ -15,6 +15,9 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.handler.ssl.util.SelfSignedCertificate; import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.junit.Assume; import org.junit.Assume;
@ -70,20 +73,31 @@ public class SslContextBuilderTest {
private static void testClientContextFromFile(SslProvider provider) throws Exception { private static void testClientContextFromFile(SslProvider provider) throws Exception {
SelfSignedCertificate cert = new SelfSignedCertificate(); SelfSignedCertificate cert = new SelfSignedCertificate();
SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(provider).keyManager( SslContextBuilder builder = SslContextBuilder.forClient()
cert.certificate(), cert.privateKey()).trustManager(cert.certificate()); .sslProvider(provider)
.keyManager(cert.certificate(),
cert.privateKey())
.trustManager(cert.certificate())
.clientAuth(ClientAuth.OPTIONAL);
SslContext context = builder.build(); SslContext context = builder.build();
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
assertFalse(engine.getWantClientAuth());
assertFalse(engine.getNeedClientAuth());
engine.closeInbound(); engine.closeInbound();
engine.closeOutbound(); engine.closeOutbound();
} }
private static void testClientContext(SslProvider provider) throws Exception { private static void testClientContext(SslProvider provider) throws Exception {
SelfSignedCertificate cert = new SelfSignedCertificate(); SelfSignedCertificate cert = new SelfSignedCertificate();
SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(provider).keyManager( SslContextBuilder builder = SslContextBuilder.forClient()
cert.key(), cert.cert()).trustManager(cert.cert()); .sslProvider(provider)
.keyManager(cert.key(), cert.cert())
.trustManager(cert.cert())
.clientAuth(ClientAuth.OPTIONAL);
SslContext context = builder.build(); SslContext context = builder.build();
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
assertFalse(engine.getWantClientAuth());
assertFalse(engine.getNeedClientAuth());
engine.closeInbound(); engine.closeInbound();
engine.closeOutbound(); engine.closeOutbound();
} }
@ -91,9 +105,13 @@ public class SslContextBuilderTest {
private static void testServerContextFromFile(SslProvider provider) throws Exception { private static void testServerContextFromFile(SslProvider provider) throws Exception {
SelfSignedCertificate cert = new SelfSignedCertificate(); SelfSignedCertificate cert = new SelfSignedCertificate();
SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey()) SslContextBuilder builder = SslContextBuilder.forServer(cert.certificate(), cert.privateKey())
.sslProvider(provider).trustManager(cert.certificate()); .sslProvider(provider)
.trustManager(cert.certificate())
.clientAuth(ClientAuth.OPTIONAL);
SslContext context = builder.build(); SslContext context = builder.build();
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
assertTrue(engine.getWantClientAuth());
assertFalse(engine.getNeedClientAuth());
engine.closeInbound(); engine.closeInbound();
engine.closeOutbound(); engine.closeOutbound();
} }
@ -101,9 +119,13 @@ public class SslContextBuilderTest {
private static void testServerContext(SslProvider provider) throws Exception { private static void testServerContext(SslProvider provider) throws Exception {
SelfSignedCertificate cert = new SelfSignedCertificate(); SelfSignedCertificate cert = new SelfSignedCertificate();
SslContextBuilder builder = SslContextBuilder.forServer(cert.key(), cert.cert()) SslContextBuilder builder = SslContextBuilder.forServer(cert.key(), cert.cert())
.sslProvider(provider).trustManager(cert.cert()); .sslProvider(provider)
.trustManager(cert.cert())
.clientAuth(ClientAuth.REQUIRE);
SslContext context = builder.build(); SslContext context = builder.build();
SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT); SSLEngine engine = context.newEngine(UnpooledByteBufAllocator.DEFAULT);
assertFalse(engine.getWantClientAuth());
assertTrue(engine.getNeedClientAuth());
engine.closeInbound(); engine.closeInbound();
engine.closeOutbound(); engine.closeOutbound();
} }