Add supported for X509ExtendedTrustManager when using OpenSslEngine

Motivation:

For some use cases X509ExtendedTrustManager is needed as it allows to also access the SslEngine during validation.

Modifications:

Add support for X509ExtendedTrustManager on java >= 7

Result:

It's now possible to use X509ExtendedTrustManager with OpenSslEngine
This commit is contained in:
Norman Maurer 2015-03-13 16:12:37 +01:00
parent bd2cbe6a35
commit b6bf683a3e
6 changed files with 434 additions and 332 deletions

View File

@ -17,15 +17,13 @@ package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.CertificateVerifier;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import java.io.File;
@ -40,8 +38,8 @@ import java.security.cert.X509Certificate;
* 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;
private final OpenSslEngineMap engineMap;
/**
* Creates a new instance.
@ -132,19 +130,26 @@ public final class OpenSslClientContext extends OpenSslContext {
initTrustManagerFactory(certChainFile, trustManagerFactory);
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
SSLContext.setCertVerifyCallback(ctx, new CertificateVerifier() {
@Override
public boolean verify(long ssl, byte[][] chain, String auth) {
X509Certificate[] peerCerts = certificates(chain);
try {
manager.checkServerTrusted(peerCerts, auth);
return true;
} catch (Exception e) {
logger.debug("verification of certificate failed", e);
engineMap = newEngineMap(manager);
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
final X509ExtendedTrustManager extendedManager = (X509ExtendedTrustManager) manager;
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(long ssl, X509Certificate[] peerCerts, String auth) throws Exception {
OpenSslEngine engine = engineMap.remove(ssl);
extendedManager.checkServerTrusted(peerCerts, auth, engine);
}
return false;
}
});
});
} else {
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(long ssl, X509Certificate[] peerCerts, String auth) throws Exception {
manager.checkServerTrusted(peerCerts, auth);
}
});
}
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
@ -185,6 +190,11 @@ public final class OpenSslClientContext extends OpenSslContext {
return sessionContext;
}
@Override
OpenSslEngineMap engineMap() {
return engineMap;
}
// No cache is currently supported for client side mode.
private static final class OpenSslClientSessionContext extends OpenSslSessionContext {
private OpenSslClientSessionContext(long context) {

View File

@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.CertificateVerifier;
import org.apache.tomcat.jni.Pool;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
@ -26,11 +27,13 @@ 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.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
@ -226,13 +229,20 @@ public abstract class OpenSslContext extends SslContext {
@Override
public final SSLEngine newEngine(ByteBufAllocator alloc) {
List<String> protos = applicationProtocolNegotiator().protocols();
OpenSslEngineMap engineMap = engineMap();
final OpenSslEngine engine;
if (protos.isEmpty()) {
return new OpenSslEngine(ctx, alloc, null, isClient(), sessionContext());
engine = new OpenSslEngine(ctx, alloc, null, isClient(), sessionContext(), engineMap);
} else {
return new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient(), sessionContext());
engine = new OpenSslEngine(ctx, alloc, protos.get(protos.size() - 1), isClient(),
sessionContext(), engineMap);
}
engineMap.add(engine);
return engine;
}
abstract OpenSslEngineMap engineMap();
/**
* Returns the {@code SSL_CTX} object of this context.
*/
@ -290,7 +300,7 @@ public abstract class OpenSslContext extends SslContext {
}
protected static X509TrustManager chooseTrustManager(TrustManager[] managers) {
for (TrustManager m: managers) {
for (TrustManager m : managers) {
if (m instanceof X509TrustManager) {
return (X509TrustManager) m;
}
@ -333,4 +343,44 @@ public abstract class OpenSslContext extends SslContext {
.append(config.protocol()).append(" protocol").toString());
}
}
static OpenSslEngineMap newEngineMap(X509TrustManager trustManager) {
if (useExtendedTrustManager(trustManager)) {
return new DefaultOpenSslEngineMap();
}
return OpenSslEngineMap.EMPTY;
}
static boolean useExtendedTrustManager(X509TrustManager trustManager) {
return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
}
abstract static class AbstractCertificateVerifier implements CertificateVerifier {
@Override
public final boolean verify(long ssl, byte[][] chain, String auth) {
X509Certificate[] peerCerts = certificates(chain);
try {
verify(ssl, peerCerts, auth);
return true;
} catch (Exception e) {
logger.debug("verification of certificate failed", e);
}
return false;
}
abstract void verify(long ssl, X509Certificate[] peerCerts, String auth) throws Exception;
}
private static final class DefaultOpenSslEngineMap implements OpenSslEngineMap {
private final Map<Long, OpenSslEngine> engines = PlatformDependent.newConcurrentHashMap();
@Override
public OpenSslEngine remove(long ssl) {
return engines.remove(ssl);
}
@Override
public void add(OpenSslEngine engine) {
engines.put(engine.ssl(), engine);
}
}
}

View File

@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -47,7 +48,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
import static javax.net.ssl.SSLEngineResult.Status.*;
@ -75,12 +75,6 @@ public final class OpenSslEngine extends SSLEngine {
destroyedUpdater = AtomicIntegerFieldUpdater.newUpdater(OpenSslEngine.class, "destroyed");
}
DESTROYED_UPDATER = destroyedUpdater;
AtomicReferenceFieldUpdater<OpenSslEngine, SSLSession> sessionUpdater =
PlatformDependent.newAtomicReferenceFieldUpdater(OpenSslEngine.class, "session");
if (sessionUpdater == null) {
sessionUpdater = AtomicReferenceFieldUpdater.newUpdater(OpenSslEngine.class, SSLSession.class, "session");
}
SESSION_UPDATER = sessionUpdater;
}
private static final int MAX_PLAINTEXT_LENGTH = 16 * 1024; // 2^14
@ -117,7 +111,6 @@ public final class OpenSslEngine extends SSLEngine {
}
private static final AtomicIntegerFieldUpdater<OpenSslEngine> DESTROYED_UPDATER;
private static final AtomicReferenceFieldUpdater<OpenSslEngine, SSLSession> SESSION_UPDATER;
private static final String INVALID_CIPHER = "SSL_NULL_WITH_NULL_NULL";
@ -154,9 +147,9 @@ public final class OpenSslEngine extends SSLEngine {
private final ByteBufAllocator alloc;
private final String fallbackApplicationProtocol;
private final OpenSslSessionContext sessionContext;
private final OpenSslEngineMap engineMap;
@SuppressWarnings("unused")
private volatile SSLSession session;
private final SSLSession session = new OpenSslSession();
/**
* Creates a new instance
@ -166,7 +159,7 @@ public final class OpenSslEngine extends SSLEngine {
*/
@Deprecated
public OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol) {
this(sslCtx, alloc, fallbackApplicationProtocol, false, null);
this(sslCtx, alloc, fallbackApplicationProtocol, false, null, OpenSslEngineMap.EMPTY);
}
/**
@ -178,21 +171,33 @@ public final class OpenSslEngine extends SSLEngine {
* @param sessionContext the {@link OpenSslSessionContext} this {@link SSLEngine} belongs to.
*/
OpenSslEngine(long sslCtx, ByteBufAllocator alloc, String fallbackApplicationProtocol,
boolean clientMode, OpenSslSessionContext sessionContext) {
boolean clientMode, OpenSslSessionContext sessionContext, OpenSslEngineMap engineMap) {
OpenSsl.ensureAvailability();
if (sslCtx == 0) {
throw new NullPointerException("sslContext");
}
if (alloc == null) {
throw new NullPointerException("alloc");
throw new NullPointerException("sslCtx");
}
this.alloc = alloc;
this.alloc = ObjectUtil.checkNotNull(alloc, "alloc");
ssl = SSL.newSSL(sslCtx, !clientMode);
networkBIO = SSL.makeNetworkBIO(ssl);
this.fallbackApplicationProtocol = fallbackApplicationProtocol;
this.clientMode = clientMode;
this.sessionContext = sessionContext;
this.engineMap = engineMap;
}
@Override
public SSLSession getHandshakeSession() {
if (accepted > 0) {
// handshake started we are able to return the session.
return session;
}
// As stated by the javadocs of getHandshakeSession() we should return null if the handshake not started yet.
return null;
}
long ssl() {
return ssl;
}
/**
@ -200,6 +205,7 @@ public final class OpenSslEngine extends SSLEngine {
*/
public synchronized void shutdown() {
if (DESTROYED_UPDATER.compareAndSet(this, 0, 1)) {
engineMap.remove(ssl);
SSL.freeSSL(ssl);
SSL.freeBIO(networkBIO);
ssl = networkBIO = 0;
@ -733,9 +739,7 @@ public final class OpenSslEngine extends SSLEngine {
@Override
public void setEnabledCipherSuites(String[] cipherSuites) {
if (cipherSuites == null) {
throw new NullPointerException("cipherSuites");
}
ObjectUtil.checkNotNull(cipherSuites, "cipherSuites");
final StringBuilder buf = new StringBuilder();
for (String c: cipherSuites) {
@ -850,283 +854,8 @@ public final class OpenSslEngine extends SSLEngine {
}
}
private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
byte[][] chain = SSL.getPeerCertChain(ssl);
byte[] clientCert;
if (!clientMode) {
// if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
// We use SSL_get_peer_certificate to get it in this case and add it to our array later.
//
// See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
clientCert = SSL.getPeerCertificate(ssl);
} else {
clientCert = null;
}
if (chain == null && clientCert == null) {
throw new SSLPeerUnverifiedException("peer not verified");
}
int len = 0;
if (chain != null) {
len += chain.length;
}
int i = 0;
Certificate[] peerCerts;
if (clientCert != null) {
len++;
peerCerts = new Certificate[len];
peerCerts[i++] = new OpenSslX509Certificate(clientCert);
} else {
peerCerts = new Certificate[len];
}
if (chain != null) {
int a = 0;
for (; i < peerCerts.length; i++) {
peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
}
}
return peerCerts;
}
@Override
public SSLSession getSession() {
// A other methods on SSLEngine are thread-safe we also need to make this thread-safe...
SSLSession session = this.session;
if (session == null) {
session = new SSLSession() {
// SSLSession implementation seems to not need to be thread-safe so no need for volatile etc.
private X509Certificate[] x509PeerCerts;
// lazy init for memory reasons
private Map<String, Object> values;
@Override
public byte[] getId() {
// We don't cache that to keep memory usage to a minimum.
byte[] id = SSL.getSessionId(ssl);
if (id == null) {
// The id should never be null, if it was null then the SESSION itself was not valid.
throw new IllegalStateException("SSL session ID not available");
}
return id;
}
@Override
public SSLSessionContext getSessionContext() {
return sessionContext;
}
@Override
public long getCreationTime() {
// We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
return SSL.getTime(ssl) * 1000L;
}
@Override
public long getLastAccessedTime() {
// TODO: Add proper implementation
return getCreationTime();
}
@Override
public void invalidate() {
// NOOP
}
@Override
public boolean isValid() {
return false;
}
@Override
public void putValue(String name, Object value) {
if (name == null) {
throw new NullPointerException("name");
}
if (value == null) {
throw new NullPointerException("value");
}
Map<String, Object> values = this.values;
if (values == null) {
// Use size of 2 to keep the memory overhead small
values = this.values = new HashMap<String, Object>(2);
}
Object old = values.put(name, value);
if (value instanceof SSLSessionBindingListener) {
((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
}
notifyUnbound(old, name);
}
@Override
public Object getValue(String name) {
if (name == null) {
throw new NullPointerException("name");
}
if (values == null) {
return null;
}
return values.get(name);
}
@Override
public void removeValue(String name) {
if (name == null) {
throw new NullPointerException("name");
}
Map<String, Object> values = this.values;
if (values == null) {
return;
}
Object old = values.remove(name);
notifyUnbound(old, name);
}
@Override
public String[] getValueNames() {
Map<String, Object> values = this.values;
if (values == null || values.isEmpty()) {
return EmptyArrays.EMPTY_STRINGS;
}
return values.keySet().toArray(new String[values.size()]);
}
private void notifyUnbound(Object value, String name) {
if (value instanceof SSLSessionBindingListener) {
((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
}
}
@Override
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
// these are lazy created to reduce memory overhead
Certificate[] c = peerCerts;
if (c == null) {
if (SSL.isInInit(ssl) != 0) {
throw new SSLPeerUnverifiedException("peer not verified");
}
c = peerCerts = initPeerCertChain();
}
return c;
}
@Override
public Certificate[] getLocalCertificates() {
// TODO: Find out how to get these
return EMPTY_CERTIFICATES;
}
@Override
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
// these are lazy created to reduce memory overhead
X509Certificate[] c = x509PeerCerts;
if (c == null) {
if (SSL.isInInit(ssl) != 0) {
throw new SSLPeerUnverifiedException("peer not verified");
}
byte[][] chain = SSL.getPeerCertChain(ssl);
if (chain == null) {
throw new SSLPeerUnverifiedException("peer not verified");
}
X509Certificate[] peerCerts = new X509Certificate[chain.length];
for (int i = 0; i < peerCerts.length; i++) {
try {
peerCerts[i] = X509Certificate.getInstance(chain[i]);
} catch (CertificateException e) {
throw new IllegalStateException(e);
}
}
c = x509PeerCerts = peerCerts;
}
return c;
}
@Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
Certificate[] peer = getPeerCertificates();
if (peer == null || peer.length == 0) {
return null;
}
return principal(peer);
}
@Override
public Principal getLocalPrincipal() {
Certificate[] local = getLocalCertificates();
if (local == null || local.length == 0) {
return null;
}
return principal(local);
}
private Principal principal(Certificate[] certs) {
return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
}
@Override
public String getCipherSuite() {
if (!handshakeFinished) {
return INVALID_CIPHER;
}
if (cipher == null) {
String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
if (c != null) {
cipher = c;
}
}
return cipher;
}
@Override
public String getProtocol() {
String applicationProtocol = OpenSslEngine.this.applicationProtocol;
if (applicationProtocol == null) {
applicationProtocol = SSL.getNextProtoNegotiated(ssl);
if (applicationProtocol == null) {
applicationProtocol = fallbackApplicationProtocol;
}
if (applicationProtocol != null) {
OpenSslEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
} else {
OpenSslEngine.this.applicationProtocol = applicationProtocol = "";
}
}
String version = SSL.getVersion(ssl);
if (applicationProtocol.isEmpty()) {
return version;
} else {
return version + ':' + applicationProtocol;
}
}
@Override
public String getPeerHost() {
return null;
}
@Override
public int getPeerPort() {
return 0;
}
@Override
public int getPacketBufferSize() {
return MAX_ENCRYPTED_PACKET_LENGTH;
}
@Override
public int getApplicationBufferSize() {
return MAX_PLAINTEXT_LENGTH;
}
};
if (!SESSION_UPDATER.compareAndSet(this, null, session)) {
// Was lazy created in the meantime so get the current reference.
session = this.session;
}
}
return session;
}
@ -1349,4 +1078,265 @@ public final class OpenSslEngine extends SSLEngine {
// Call shutdown as the user may have created the OpenSslEngine and not used it at all.
shutdown();
}
private final class OpenSslSession implements SSLSession {
// SSLSession implementation seems to not need to be thread-safe so no need for volatile etc.
private X509Certificate[] x509PeerCerts;
// lazy init for memory reasons
private Map<String, Object> values;
@Override
public byte[] getId() {
// We don't cache that to keep memory usage to a minimum.
byte[] id = SSL.getSessionId(ssl);
if (id == null) {
// The id should never be null, if it was null then the SESSION itself was not valid.
throw new IllegalStateException("SSL session ID not available");
}
return id;
}
@Override
public SSLSessionContext getSessionContext() {
return sessionContext;
}
@Override
public long getCreationTime() {
// We need ot multiple by 1000 as openssl uses seconds and we need milli-seconds.
return SSL.getTime(ssl) * 1000L;
}
@Override
public long getLastAccessedTime() {
// TODO: Add proper implementation
return getCreationTime();
}
@Override
public void invalidate() {
// NOOP
}
@Override
public boolean isValid() {
return false;
}
@Override
public void putValue(String name, Object value) {
ObjectUtil.checkNotNull(name, "name");
ObjectUtil.checkNotNull(value, "value");
Map<String, Object> values = this.values;
if (values == null) {
// Use size of 2 to keep the memory overhead small
values = this.values = new HashMap<String, Object>(2);
}
Object old = values.put(name, value);
if (value instanceof SSLSessionBindingListener) {
((SSLSessionBindingListener) value).valueBound(new SSLSessionBindingEvent(this, name));
}
notifyUnbound(old, name);
}
@Override
public Object getValue(String name) {
ObjectUtil.checkNotNull(name, "name");
if (values == null) {
return null;
}
return values.get(name);
}
@Override
public void removeValue(String name) {
ObjectUtil.checkNotNull(name, "name");
Map<String, Object> values = this.values;
if (values == null) {
return;
}
Object old = values.remove(name);
notifyUnbound(old, name);
}
@Override
public String[] getValueNames() {
Map<String, Object> values = this.values;
if (values == null || values.isEmpty()) {
return EmptyArrays.EMPTY_STRINGS;
}
return values.keySet().toArray(new String[values.size()]);
}
private void notifyUnbound(Object value, String name) {
if (value instanceof SSLSessionBindingListener) {
((SSLSessionBindingListener) value).valueUnbound(new SSLSessionBindingEvent(this, name));
}
}
@Override
public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException {
// these are lazy created to reduce memory overhead
Certificate[] c = peerCerts;
if (c == null) {
if (SSL.isInInit(ssl) != 0) {
throw new SSLPeerUnverifiedException("peer not verified");
}
c = peerCerts = initPeerCertChain();
}
return c;
}
private Certificate[] initPeerCertChain() throws SSLPeerUnverifiedException {
byte[][] chain = SSL.getPeerCertChain(ssl);
byte[] clientCert;
if (!clientMode) {
// if used on the server side SSL_get_peer_cert_chain(...) will not include the remote peer certificate.
// We use SSL_get_peer_certificate to get it in this case and add it to our array later.
//
// See https://www.openssl.org/docs/ssl/SSL_get_peer_cert_chain.html
clientCert = SSL.getPeerCertificate(ssl);
} else {
clientCert = null;
}
if (chain == null && clientCert == null) {
throw new SSLPeerUnverifiedException("peer not verified");
}
int len = 0;
if (chain != null) {
len += chain.length;
}
int i = 0;
Certificate[] peerCerts;
if (clientCert != null) {
len++;
peerCerts = new Certificate[len];
peerCerts[i++] = new OpenSslX509Certificate(clientCert);
} else {
peerCerts = new Certificate[len];
}
if (chain != null) {
int a = 0;
for (; i < peerCerts.length; i++) {
peerCerts[i] = new OpenSslX509Certificate(chain[a++]);
}
}
return peerCerts;
}
@Override
public Certificate[] getLocalCertificates() {
// TODO: Find out how to get these
return EMPTY_CERTIFICATES;
}
@Override
public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException {
// these are lazy created to reduce memory overhead
X509Certificate[] c = x509PeerCerts;
if (c == null) {
if (SSL.isInInit(ssl) != 0) {
throw new SSLPeerUnverifiedException("peer not verified");
}
byte[][] chain = SSL.getPeerCertChain(ssl);
if (chain == null) {
throw new SSLPeerUnverifiedException("peer not verified");
}
X509Certificate[] peerCerts = new X509Certificate[chain.length];
for (int i = 0; i < peerCerts.length; i++) {
try {
peerCerts[i] = X509Certificate.getInstance(chain[i]);
} catch (CertificateException e) {
throw new IllegalStateException(e);
}
}
c = x509PeerCerts = peerCerts;
}
return c;
}
@Override
public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
Certificate[] peer = getPeerCertificates();
if (peer == null || peer.length == 0) {
return null;
}
return principal(peer);
}
@Override
public Principal getLocalPrincipal() {
Certificate[] local = getLocalCertificates();
if (local == null || local.length == 0) {
return null;
}
return principal(local);
}
private Principal principal(Certificate[] certs) {
return ((java.security.cert.X509Certificate) certs[0]).getIssuerX500Principal();
}
@Override
public String getCipherSuite() {
if (!handshakeFinished) {
return INVALID_CIPHER;
}
if (cipher == null) {
String c = toJavaCipherSuite(SSL.getCipherForSSL(ssl));
if (c != null) {
cipher = c;
}
}
return cipher;
}
@Override
public String getProtocol() {
String applicationProtocol = OpenSslEngine.this.applicationProtocol;
if (applicationProtocol == null) {
applicationProtocol = SSL.getNextProtoNegotiated(ssl);
if (applicationProtocol == null) {
applicationProtocol = fallbackApplicationProtocol;
}
if (applicationProtocol != null) {
OpenSslEngine.this.applicationProtocol = applicationProtocol.replace(':', '_');
} else {
OpenSslEngine.this.applicationProtocol = applicationProtocol = "";
}
}
String version = SSL.getVersion(ssl);
if (applicationProtocol.isEmpty()) {
return version;
} else {
return version + ':' + applicationProtocol;
}
}
@Override
public String getPeerHost() {
return null;
}
@Override
public int getPeerPort() {
return 0;
}
@Override
public int getPacketBufferSize() {
return MAX_ENCRYPTED_PACKET_LENGTH;
}
@Override
public int getApplicationBufferSize() {
return MAX_PLAINTEXT_LENGTH;
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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;
interface OpenSslEngineMap {
OpenSslEngineMap EMPTY = new OpenSslEngineMap() {
@Override
public OpenSslEngine remove(long ssl) {
return null;
}
@Override
public void add(OpenSslEngine engine) {
// NOOP
}
};
/**
* Remove the {@link OpenSslEngine} with the given {@code ssl} address and
* return it.
*/
OpenSslEngine remove(long ssl);
/**
* Add a {@link OpenSslEngine} to this {@link OpenSslEngineMap}.
*/
void add(OpenSslEngine engine);
}

View File

@ -17,14 +17,12 @@ package io.netty.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import org.apache.tomcat.jni.CertificateVerifier;
import org.apache.tomcat.jni.SSL;
import org.apache.tomcat.jni.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.security.KeyFactory;
@ -44,9 +42,8 @@ import static io.netty.util.internal.ObjectUtil.*;
* A server-side {@link SslContext} which uses OpenSSL's SSL/TLS implementation.
*/
public final class OpenSslServerContext extends OpenSslContext {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(OpenSslServerContext.class);
private final OpenSslServerSessionContext sessionContext;
private final OpenSslEngineMap engineMap;
/**
* Creates a new instance.
@ -230,19 +227,26 @@ public final class OpenSslServerContext extends OpenSslContext {
}
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
SSLContext.setCertVerifyCallback(ctx, new CertificateVerifier() {
@Override
public boolean verify(long ssl, byte[][] chain, String auth) {
X509Certificate[] peerCerts = certificates(chain);
try {
manager.checkClientTrusted(peerCerts, auth);
return true;
} catch (Exception e) {
logger.debug("verification of certificate failed", e);
engineMap = newEngineMap(manager);
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
final X509ExtendedTrustManager extendedManager = (X509ExtendedTrustManager) manager;
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(long ssl, X509Certificate[] peerCerts, String auth) throws Exception {
OpenSslEngine engine = engineMap.remove(ssl);
extendedManager.checkClientTrusted(peerCerts, auth, engine);
}
return false;
}
});
});
} else {
SSLContext.setCertVerifyCallback(ctx, new AbstractCertificateVerifier() {
@Override
void verify(long ssl, X509Certificate[] peerCerts, String auth) throws Exception {
manager.checkClientTrusted(peerCerts, auth);
}
});
}
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
@ -260,4 +264,9 @@ public final class OpenSslServerContext extends OpenSslContext {
public OpenSslServerSessionContext sessionContext() {
return sessionContext;
}
@Override
OpenSslEngineMap engineMap() {
return engineMap;
}
}

View File

@ -963,8 +963,9 @@
<ignore>sun.security.x509.X509CertInfo</ignore>
<ignore>sun.security.x509.X509CertImpl</ignore>
<!-- SSLSession implelementation -->
<!-- SSLSession implementation -->
<ignore>javax.net.ssl.SSLEngine</ignore>
<ignore>javax.net.ssl.X509ExtendedTrustManager</ignore>
</ignores>
</configuration>
<executions>