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 19f6b420a9
commit b080fde03b
6 changed files with 184 additions and 28 deletions

View File

@ -16,7 +16,6 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufInputStream;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; 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.SSL;
import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
@ -37,13 +35,13 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.List;
/** /**
* A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation. * A client-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
*/ */
public final class OpenSslClientContext extends OpenSslContext { public final class OpenSslClientContext extends OpenSslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslClientContext.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslClientContext.class);
private final OpenSslSessionContext sessionContext;
/** /**
* Creates a new instance. * Creates a new instance.
@ -151,6 +149,7 @@ public final class OpenSslClientContext extends OpenSslContext {
throw new SSLException("unable to setup trustmanager", e); throw new SSLException("unable to setup trustmanager", e);
} }
} }
sessionContext = new OpenSslClientSessionContext(ctx);
success = true; success = true;
} finally { } finally {
if (!success) { if (!success) {
@ -182,12 +181,38 @@ public final class OpenSslClientContext extends OpenSslContext {
} }
@Override @Override
public SSLEngine newEngine(ByteBufAllocator alloc) { public OpenSslSessionContext sessionContext() {
List<String> protos = applicationProtocolNegotiator().protocols(); return sessionContext;
if (protos.isEmpty()) { }
return new OpenSslEngine(ctx, alloc, null, isClient());
} else { // No cache is currently supported for client side mode.
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient()); 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 */ /** The OpenSSL SSL_CTX object */
protected final long ctx; protected final long ctx;
private final int mode; private final int mode;
private final OpenSslSessionStats stats;
static { static {
List<String> ciphers = new ArrayList<String>(); List<String> ciphers = new ArrayList<String>();
@ -191,8 +190,6 @@ public abstract class OpenSslContext extends SslContext {
destroyPools(); destroyPools();
} }
} }
stats = new OpenSslSessionStats(ctx);
} }
@Override @Override
@ -225,6 +222,19 @@ public abstract class OpenSslContext extends SslContext {
throw new UnsupportedOperationException(); 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. * 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. * Returns the stats of this context.
* @deprecated use {@link #sessionContext#stats()}
*/ */
@Deprecated
public final OpenSslSessionStats stats() { public final OpenSslSessionStats stats() {
return stats; return sessionContext().stats();
} }
@Override @Override
@ -254,14 +266,16 @@ public abstract class OpenSslContext extends SslContext {
/** /**
* Sets the SSL session ticket keys of this context. * Sets the SSL session ticket keys of this context.
* @deprecated use {@link OpenSslSessionContext#setTicketKeys(byte[])}
*/ */
@Deprecated
public final void setTicketKeys(byte[] keys) { public final void setTicketKeys(byte[] keys) {
if (keys == null) { sessionContext().setTicketKeys(keys);
throw new NullPointerException("keys");
}
SSLContext.setSessionTicketKeys(ctx, keys);
} }
@Override
public abstract OpenSslSessionContext sessionContext();
protected final void destroyPools() { protected final void destroyPools() {
// Guard against multiple destroyPools() calls triggered by construction exception and finalize() later // Guard against multiple destroyPools() calls triggered by construction exception and finalize() later
if (aprPool != 0 && DESTROY_UPDATER.compareAndSet(this, 0, 1)) { 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 boolean clientMode;
private final ByteBufAllocator alloc; private final ByteBufAllocator alloc;
private final String fallbackApplicationProtocol; private final String fallbackApplicationProtocol;
private final OpenSslSessionContext sessionContext;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private volatile SSLSession session; private volatile SSLSession session;
@ -141,7 +142,7 @@ public final class OpenSslEngine extends SSLEngine {
*/ */
@Deprecated @Deprecated
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) { 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 sslCtx an OpenSSL {@code SSL_CTX} object
* @param alloc the {@link ByteBufAllocator} that will be used by this engine * @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 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, OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol,
boolean clientMode) { boolean clientMode, OpenSslSessionContext sessionContext) {
OpenSsl.ensureAvailability(); OpenSsl.ensureAvailability();
if (sslCtx == 0) { if (sslCtx == 0) {
throw new NullPointerException("sslContext"); throw new NullPointerException("sslContext");
@ -166,6 +168,7 @@ public final class OpenSslEngine extends SSLEngine {
networkBIO = SSL.makeNetworkBIO(ssl); networkBIO = SSL.makeNetworkBIO(ssl);
this.fallbackApplicationProtocol = fallbackApplicationProtocol; this.fallbackApplicationProtocol = fallbackApplicationProtocol;
this.clientMode = clientMode; this.clientMode = clientMode;
this.sessionContext = sessionContext;
} }
/** /**
@ -717,7 +720,7 @@ public final class OpenSslEngine extends SSLEngine {
@Override @Override
public SSLSessionContext getSessionContext() { public SSLSessionContext getSessionContext() {
return null; return sessionContext;
} }
@Override @Override

View File

@ -16,7 +16,6 @@
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufInputStream;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; 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.SSL;
import org.apache.tomcat.jni.SSLContext; import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
@ -48,6 +46,8 @@ import static io.netty.util.internal.ObjectUtil.*;
public final class OpenSslServerContext extends OpenSslContext { public final class OpenSslServerContext extends OpenSslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslServerContext.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslServerContext.class);
private final OpenSslSessionContext sessionContext;
/** /**
* Creates a new instance. * Creates a new instance.
* *
@ -247,6 +247,7 @@ public final class OpenSslServerContext extends OpenSslContext {
throw new SSLException("unable to setup trustmanager", e); throw new SSLException("unable to setup trustmanager", e);
} }
} }
sessionContext = new OpenSslServerSessionContext(ctx);
success = true; success = true;
} finally { } finally {
if (!success) { if (!success) {
@ -256,12 +257,39 @@ public final class OpenSslServerContext extends OpenSslContext {
} }
@Override @Override
public SSLEngine newEngine(ByteBufAllocator alloc) { public OpenSslSessionContext sessionContext() {
List<String> protos = applicationProtocolNegotiator().protocols(); return sessionContext;
if (protos.isEmpty()) { }
return new OpenSslEngine(ctx, alloc, null, isClient());
} else { private static final class OpenSslServerSessionContext extends OpenSslSessionContext {
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient()); 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

@ -31,6 +31,7 @@ import javax.crypto.spec.PBEKeySpec;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import java.io.File; import java.io.File;
@ -541,6 +542,11 @@ public abstract class SslContext {
*/ */
public abstract SSLEngine newEngine(ByteBufAllocator alloc, String peerHost, int peerPort); 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}. * Creates a new {@link SslHandler}.
* *