Allow to enable session cache when using OpenSsl

Motivation:

At the moment it is not possible to make use of the session cache when OpenSsl is used. This should be possible when server mode is used.

Modifications:

- Add OpenSslSessionContext (implements SSLSessionContext) which exposes all the methods to modify the session cache.
- Add various extra methods to OpenSslSessionContext for extra functionality
- Return OpenSslSessionContext when OpenSslEngine.getSession().getContext() is called.
- Add sessionContext() to SslContext
- Move OpenSsl specific session operations to OpenSslSessionContext and mark the old methods @deprecated

Result:

It's now possible to use session cache with OpenSsl
This commit is contained in:
Norman Maurer 2014-11-03 15:34:21 +01:00 committed by Norman Maurer
parent 61a5e60513
commit 8282b12b6a
6 changed files with 184 additions and 28 deletions

View File

@ -16,7 +16,6 @@
package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -24,7 +23,6 @@ import org.apache.tomcat.jni.CertificateVerifier;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
@ -37,13 +35,13 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
/**
* A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
*/
public final class OpenSslClientContext extends OpenSslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslClientContext.class);
private final OpenSslSessionContext sessionContext;
/**
* Creates a new instance.
@ -151,6 +149,7 @@ public final class OpenSslClientContext extends OpenSslContext {
throw new SSLException("unable to setup trustmanager", e);
}
}
sessionContext = new OpenSslClientSessionContext(ctx);
success = true;
} finally {
if (!success) {
@ -182,12 +181,38 @@ public final class OpenSslClientContext extends OpenSslContext {
}
@Override
public SSLEngine newEngine(ByteBufAllocator alloc) {
List<String> protos = applicationProtocolNegotiator().protocols();
if (protos.isEmpty()) {
return new OpenSslEngine(ctx, alloc, null, isClient());
} else {
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient());
public OpenSslSessionContext sessionContext() {
return sessionContext;
}
// No cache is currently supported for client side mode.
private static final class OpenSslClientSessionContext extends OpenSslSessionContext {
private OpenSslClientSessionContext(long context) {
super(context);
}
@Override
public void setSessionTimeout(int seconds) {
if (seconds < 0) {
throw new IllegalArgumentException();
}
}
@Override
public int getSessionTimeout() {
return 0;
}
@Override
public void setSessionCacheSize(int size) {
if (size < 0) {
throw new IllegalArgumentException();
}
}
@Override
public int getSessionCacheSize() {
return 0;
}
}
}

View File

@ -55,7 +55,6 @@ public abstract class OpenSslContext extends SslContext {
/** The OpenSSL SSL_CTX object */
protected final long ctx;
private final int mode;
private final OpenSslSessionStats stats;
static {
List<String> ciphers = new ArrayList<String>();
@ -191,8 +190,6 @@ public abstract class OpenSslContext extends SslContext {
destroyPools();
}
}
stats = new OpenSslSessionStats(ctx);
}
@Override
@ -225,6 +222,19 @@ public abstract class OpenSslContext extends SslContext {
throw new UnsupportedOperationException();
}
/**
* Returns a new server-side {@link javax.net.ssl.SSLEngine} with the current configuration.
*/
@Override
public final SSLEngine newEngine(ByteBufAllocator alloc) {
List<String> protos = applicationProtocolNegotiator().protocols();
if (protos.isEmpty()) {
return new OpenSslEngine(ctx, alloc, null, isClient(), sessionContext());
} else {
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient(), sessionContext());
}
}
/**
* Returns the {@code SSL_CTX} object of this context.
*/
@ -234,9 +244,11 @@ public abstract class OpenSslContext extends SslContext {
/**
* Returns the stats of this context.
* @deprecated use {@link #sessionContext#stats()}
*/
@Deprecated
public final OpenSslSessionStats stats() {
return stats;
return sessionContext().stats();
}
@Override
@ -254,14 +266,16 @@ public abstract class OpenSslContext extends SslContext {
/**
* Sets the SSL session ticket keys of this context.
* @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])}
*/
@Deprecated
public final void setTicketKeys(byte[] keys) {
if (keys == null) {
throw new NullPointerException("keys");
}
SSLContext.setSessionTicketKeys(ctx, keys);
sessionContext().setTicketKeys(keys);
}
@Override
public abstract OpenSslSessionContext sessionContext();
protected final void destroyPools() {
// Guard against multiple destroyPools() calls triggered by construction exception and finalize() later
if (aprPool != 0 && DESTROY_UPDATER.compareAndSet(this, 0, 1)) {

View File

@ -129,6 +129,7 @@ public final class OpenSslEngine extends SSLEngine {
private final boolean clientMode;
private final ByteBufAllocator alloc;
private final String fallbackApplicationProtocol;
private final OpenSslSessionContext sessionContext;
@SuppressWarnings("unused")
private volatile SSLSession session;
@ -141,7 +142,7 @@ public final class OpenSslEngine extends SSLEngine {
*/
@Deprecated
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
this(sslCtx, alloc, fallbackApplicationProtocol, false);
this(sslCtx, alloc, fallbackApplicationProtocol, false, null);
}
/**
@ -150,9 +151,10 @@ public final class OpenSslEngine extends SSLEngine {
* @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, String fallbackApplicationProtocol,
boolean clientMode) {
boolean clientMode, OpenSslSessionContext sessionContext) {
OpenSsl.ensureAvailability();
if (sslCtx == 0) {
throw new NullPointerException("sslContext");
@ -166,6 +168,7 @@ public final class OpenSslEngine extends SSLEngine {
networkBIO = SSL.makeNetworkBIO(ssl);
this.fallbackApplicationProtocol = fallbackApplicationProtocol;
this.clientMode = clientMode;
this.sessionContext = sessionContext;
}
/**
@ -717,7 +720,7 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public SSLSessionContext getSessionContext() {
return null;
return sessionContext;
}
@Override

View File

@ -16,7 +16,6 @@
package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -24,7 +23,6 @@ import org.apache.tomcat.jni.CertificateVerifier;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
@ -48,6 +46,8 @@ import static io.netty.util.internal.ObjectUtil.*;
public final class OpenSslServerContext extends OpenSslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslServerContext.class);
private final OpenSslSessionContext sessionContext;
/**
* Creates a new instance.
*
@ -275,6 +275,7 @@ public final class OpenSslServerContext extends OpenSslContext {
throw new SSLException("unable to setup trustmanager", e);
}
}
sessionContext = new OpenSslServerSessionContext(ctx);
success = true;
} finally {
if (!success) {
@ -284,12 +285,39 @@ public final class OpenSslServerContext extends OpenSslContext {
}
@Override
public SSLEngine newEngine(ByteBufAllocator alloc) {
List<String> protos = applicationProtocolNegotiator().protocols();
if (protos.isEmpty()) {
return new OpenSslEngine(ctx, alloc, null, isClient());
} else {
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient());
public OpenSslSessionContext sessionContext() {
return sessionContext;
}
private static final class OpenSslServerSessionContext extends OpenSslSessionContext {
private OpenSslServerSessionContext(long context) {
super(context);
}
@Override
public void setSessionTimeout(int seconds) {
if (seconds < 0) {
throw new IllegalArgumentException();
}
SSLContext.setSessionCacheTimeout(context, seconds);
}
@Override
public int getSessionTimeout() {
return (int) SSLContext.getSessionCacheTimeout(context);
}
@Override
public void setSessionCacheSize(int size) {
if (size < 0) {
throw new IllegalArgumentException();
}
SSLContext.setSessionCacheSize(context, size);
}
@Override
public int getSessionCacheSize() {
return (int) SSLContext.getSessionCacheSize(context);
}
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2014 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;
import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSessionContext;
import java.util.Enumeration;
import java.util.NoSuchElementException;
/**
* OpenSSL specific {@link SSLSessionContext} implementation.
*/
public abstract class OpenSslSessionContext implements SSLSessionContext {
private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
private final OpenSslSessionStats stats;
final long context;
OpenSslSessionContext(long context) {
this.context = context;
stats = new OpenSslSessionStats(context);
}
@Override
public SSLSession getSession(byte[] bytes) {
if (bytes == null) {
throw new NullPointerException("bytes");
}
return null;
}
@Override
public Enumeration<byte[]> getIds() {
return EMPTY;
}
/**
* Sets the SSL session ticket keys of this context.
*/
public void setTicketKeys(byte[] keys) {
if (keys == null) {
throw new NullPointerException("keys");
}
SSLContext.setSessionTicketKeys(context, keys);
}
/**
* Returns the stats of this context.
*/
public OpenSslSessionStats stats() {
return stats;
}
private static final class EmptyEnumeration implements Enumeration<byte[]> {
@Override
public boolean hasMoreElements() {
return false;
}
@Override
public byte[] nextElement() {
throw new NoSuchElementException();
}
}
}

View File

@ -34,6 +34,7 @@ import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
@ -731,6 +732,11 @@ public abstract class SslContext {
*/
public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort);
/**
* Returns the {@link SSLSessionContext} object held by this context.
*/
public abstract SSLSessionContext sessionContext();
/**
* Creates a new {@link SslHandler}.
*