Allow to cache keymaterial when using OpenSSL

Motiviation:

During profiling it showed that a lot of time during the handshake is spent by parsing the key / chain over and over again. We should cache these parsed structures if possible to reduce the overhead during handshake.

Modification:

- Use new APIs provided by https://github.com/netty/netty-tcnative/pull/360.
- Introduce OpensslStaticX509KeyManagerFactory which allows to wrap another KeyManagerFactory and caches the key material provided by it.

Result:

In benchmarks handshake times have improved by 30 %.
This commit is contained in:
Norman Maurer 2018-06-19 18:46:13 +02:00
parent 4a8d3a274c
commit 0337ecdcc8
16 changed files with 751 additions and 275 deletions

View File

@ -0,0 +1,68 @@
/*
* Copyright 2018 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 io.netty.buffer.ByteBufAllocator;
import javax.net.ssl.X509KeyManager;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* {@link OpenSslKeyMaterialProvider} that will cache the {@link OpenSslKeyMaterial} to reduce the overhead
* of parsing the chain and the key for generation of the material.
*/
final class OpenSslCachingKeyMaterialProvider extends OpenSslKeyMaterialProvider {
private final ConcurrentMap<String, OpenSslKeyMaterial> cache = new ConcurrentHashMap<String, OpenSslKeyMaterial>();
OpenSslCachingKeyMaterialProvider(X509KeyManager keyManager, String password) {
super(keyManager, password);
}
@Override
OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
OpenSslKeyMaterial material = cache.get(alias);
if (material == null) {
material = super.chooseKeyMaterial(allocator, alias);
if (material == null) {
// No keymaterial should be used.
return null;
}
OpenSslKeyMaterial old = cache.putIfAbsent(alias, material);
if (old != null) {
material.release();
material = old;
}
}
// We need to call retain() as we want to always have at least a refCnt() of 1 before destroy() was called.
return material.retain();
}
@Override
void destroy() {
// Remove and release all entries.
do {
Iterator<OpenSslKeyMaterial> iterator = cache.values().iterator();
while (iterator.hasNext()) {
iterator.next().release();
iterator.remove();
}
} while (!cache.isEmpty());
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright 2018 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 javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.X509KeyManager;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.X509Certificate;
/**
* Wraps another {@link KeyManagerFactory} and caches its chains / certs for an alias for better performance when using
* {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT}.
*
* Because of the caching its important that the wrapped {@link KeyManagerFactory}s {@link X509KeyManager}s always
* return the same {@link X509Certificate} chain and {@link PrivateKey} for the same alias.
*/
public final class OpenSslCachingX509KeyManagerFactory extends KeyManagerFactory {
public OpenSslCachingX509KeyManagerFactory(final KeyManagerFactory factory) {
super(new KeyManagerFactorySpi() {
@Override
protected void engineInit(KeyStore keyStore, char[] chars)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
factory.init(keyStore, chars);
}
@Override
protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
throws InvalidAlgorithmParameterException {
factory.init(managerFactoryParameters);
}
@Override
protected KeyManager[] engineGetKeyManagers() {
return factory.getKeyManagers();
}
}, factory.getProvider(), factory.getAlgorithm());
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2016 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 javax.net.ssl.X509ExtendedKeyManager;
import javax.security.auth.x500.X500Principal;
final class OpenSslExtendedKeyMaterialManager extends OpenSslKeyMaterialManager {
private final X509ExtendedKeyManager keyManager;
OpenSslExtendedKeyMaterialManager(X509ExtendedKeyManager keyManager, String password) {
super(keyManager, password);
this.keyManager = keyManager;
}
@Override
protected String chooseClientAlias(ReferenceCountedOpenSslEngine engine, String[] keyTypes,
X500Principal[] issuer) {
return keyManager.chooseEngineClientAlias(keyTypes, issuer, engine);
}
@Override
protected String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) {
return keyManager.chooseEngineServerAlias(type, null, engine);
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2018 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 io.netty.internal.tcnative.SSL;
import io.netty.util.AbstractReferenceCounted;
import io.netty.util.IllegalReferenceCountException;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.ResourceLeakDetectorFactory;
import io.netty.util.ResourceLeakTracker;
/**
* Holds references to the native key-material that is used by OpenSSL.
*/
final class OpenSslKeyMaterial extends AbstractReferenceCounted {
private static final ResourceLeakDetector<OpenSslKeyMaterial> leakDetector =
ResourceLeakDetectorFactory.instance().newResourceLeakDetector(OpenSslKeyMaterial.class);
private final ResourceLeakTracker<OpenSslKeyMaterial> leak;
private long chain;
private long privateKey;
OpenSslKeyMaterial(long chain, long privateKey) {
this.chain = chain;
this.privateKey = privateKey;
leak = leakDetector.track(this);
}
/**
* Returns the pointer to the {@code STACK_OF(X509)} which holds the certificate chain.
*/
public long certificateChainAddress() {
if (refCnt() <= 0) {
throw new IllegalReferenceCountException();
}
return chain;
}
/**
* Returns the pointer to the {@code EVP_PKEY}.
*/
public long privateKeyAddress() {
if (refCnt() <= 0) {
throw new IllegalReferenceCountException();
}
return privateKey;
}
@Override
protected void deallocate() {
SSL.freeX509Chain(chain);
chain = 0;
SSL.freePrivateKey(privateKey);
privateKey = 0;
if (leak != null) {
boolean closed = leak.close(this);
assert closed;
}
}
@Override
public OpenSslKeyMaterial retain() {
if (leak != null) {
leak.record();
}
super.retain();
return this;
}
@Override
public OpenSslKeyMaterial retain(int increment) {
if (leak != null) {
leak.record();
}
super.retain(increment);
return this;
}
@Override
public OpenSslKeyMaterial touch() {
if (leak != null) {
leak.record();
}
super.touch();
return this;
}
@Override
public OpenSslKeyMaterial touch(Object hint) {
if (leak != null) {
leak.record(hint);
}
return this;
}
@Override
public boolean release() {
if (leak != null) {
leak.record();
}
return super.release();
}
@Override
public boolean release(int decrement) {
if (leak != null) {
leak.record();
}
return super.release(decrement);
}
}

View File

@ -15,11 +15,10 @@
*/ */
package io.netty.handler.ssl; package io.netty.handler.ssl;
import io.netty.buffer.ByteBufAllocator;
import io.netty.internal.tcnative.CertificateRequestedCallback;
import io.netty.internal.tcnative.SSL; import io.netty.internal.tcnative.SSL;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509KeyManager;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -29,14 +28,12 @@ import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.freeBio;
import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO;
/** /**
* Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and * Manages key material for {@link OpenSslEngine}s and so set the right {@link PrivateKey}s and
* {@link X509Certificate}s. * {@link X509Certificate}s.
*/ */
class OpenSslKeyMaterialManager { final class OpenSslKeyMaterialManager {
// Code in this class is inspired by code of conscrypts: // Code in this class is inspired by code of conscrypts:
// - https://android.googlesource.com/platform/external/ // - https://android.googlesource.com/platform/external/
@ -62,15 +59,13 @@ class OpenSslKeyMaterialManager {
KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA); KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA);
} }
private final X509KeyManager keyManager; private final OpenSslKeyMaterialProvider provider;
private final String password;
OpenSslKeyMaterialManager(X509KeyManager keyManager, String password) { OpenSslKeyMaterialManager(OpenSslKeyMaterialProvider provider) {
this.keyManager = keyManager; this.provider = provider;
this.password = password;
} }
void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException { void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLException {
long ssl = engine.sslPointer(); long ssl = engine.sslPointer();
String[] authMethods = SSL.authenticationMethods(ssl); String[] authMethods = SSL.authenticationMethods(ssl);
Set<String> aliases = new HashSet<String>(authMethods.length); Set<String> aliases = new HashSet<String>(authMethods.length);
@ -79,101 +74,62 @@ class OpenSslKeyMaterialManager {
if (type != null) { if (type != null) {
String alias = chooseServerAlias(engine, type); String alias = chooseServerAlias(engine, type);
if (alias != null && aliases.add(alias)) { if (alias != null && aliases.add(alias)) {
setKeyMaterial(ssl, alias, engine.alloc); OpenSslKeyMaterial keyMaterial = null;
try {
keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias);
if (keyMaterial != null) {
SSL.setKeyMaterialServerSide(
ssl, keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress());
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException(e);
} finally {
if (keyMaterial != null) {
keyMaterial.release();
}
}
} }
} }
} }
} }
CertificateRequestedCallback.KeyMaterial keyMaterial(ReferenceCountedOpenSslEngine engine, String[] keyTypes, void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, long certOut, long keyOut, String[] keyTypes,
X500Principal[] issuer) throws SSLException { X500Principal[] issuer) throws SSLException {
String alias = chooseClientAlias(engine, keyTypes, issuer); String alias = chooseClientAlias(engine, keyTypes, issuer);
long keyBio = 0; OpenSslKeyMaterial keyMaterial = null;
long keyCertChainBio = 0;
long pkey = 0;
long certChain = 0;
try { try {
// TODO: Should we cache these and so not need to do a memory copy all the time ? keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias);
X509Certificate[] certificates = keyManager.getCertificateChain(alias); if (keyMaterial != null) {
if (certificates == null || certificates.length == 0) { SSL.setKeyMaterialClientSide(engine.sslPointer(), certOut, keyOut,
return null; keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress());
}
PrivateKey key = keyManager.getPrivateKey(alias);
keyCertChainBio = toBIO(engine.alloc, certificates);
certChain = SSL.parseX509Chain(keyCertChainBio);
if (key != null) {
keyBio = toBIO(engine.alloc, key);
pkey = SSL.parsePrivateKey(keyBio, password);
}
CertificateRequestedCallback.KeyMaterial material = new CertificateRequestedCallback.KeyMaterial(
certChain, pkey);
// Reset to 0 so we do not free these. This is needed as the client certificate callback takes ownership
// of both the key and the certificate if they are returned from this method, and thus must not
// be freed here.
certChain = pkey = 0;
return material;
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException(e);
} finally {
freeBio(keyBio);
freeBio(keyCertChainBio);
SSL.freePrivateKey(pkey);
SSL.freeX509Chain(certChain);
}
}
private void setKeyMaterial(long ssl, String alias, ByteBufAllocator allocator) throws SSLException {
long keyBio = 0;
long keyCertChainBio = 0;
long keyCertChainBio2 = 0;
try {
// TODO: Should we cache these and so not need to do a memory copy all the time ?
X509Certificate[] certificates = keyManager.getCertificateChain(alias);
if (certificates == null || certificates.length == 0) {
return;
}
PrivateKey key = keyManager.getPrivateKey(alias);
// Only encode one time
PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates);
try {
keyCertChainBio = toBIO(allocator, encoded.retain());
keyCertChainBio2 = toBIO(allocator, encoded.retain());
if (key != null) {
keyBio = toBIO(allocator, key);
}
SSL.setCertificateBio(ssl, keyCertChainBio, keyBio, password);
// We may have more then one cert in the chain so add all of them now.
SSL.setCertificateChainBio(ssl, keyCertChainBio2, true);
} finally {
encoded.release();
} }
} catch (SSLException e) { } catch (SSLException e) {
throw e; throw e;
} catch (Exception e) { } catch (Exception e) {
throw new SSLException(e); throw new SSLException(e);
} finally { } finally {
freeBio(keyBio); if (keyMaterial != null) {
freeBio(keyCertChainBio); keyMaterial.release();
freeBio(keyCertChainBio2); }
} }
} }
protected String chooseClientAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine, private String chooseClientAlias(ReferenceCountedOpenSslEngine engine,
String[] keyTypes, X500Principal[] issuer) { String[] keyTypes, X500Principal[] issuer) {
return keyManager.chooseClientAlias(keyTypes, issuer, null); X509KeyManager manager = provider.keyManager();
if (manager instanceof X509ExtendedKeyManager) {
return ((X509ExtendedKeyManager) manager).chooseEngineClientAlias(keyTypes, issuer, engine);
}
return manager.chooseClientAlias(keyTypes, issuer, null);
} }
protected String chooseServerAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine, String type) { private String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) {
return keyManager.chooseServerAlias(type, null, null); X509KeyManager manager = provider.keyManager();
if (manager instanceof X509ExtendedKeyManager) {
return ((X509ExtendedKeyManager) manager).chooseEngineServerAlias(type, null, engine);
}
return manager.chooseServerAlias(type, null, null);
} }
} }

View File

@ -0,0 +1,94 @@
/*
* Copyright 2018 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 io.netty.buffer.ByteBufAllocator;
import io.netty.internal.tcnative.SSL;
import javax.net.ssl.X509KeyManager;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import static io.netty.handler.ssl.ReferenceCountedOpenSslContext.toBIO;
/**
* Provides {@link OpenSslKeyMaterial} for a given alias.
*/
class OpenSslKeyMaterialProvider {
private final X509KeyManager keyManager;
private final String password;
OpenSslKeyMaterialProvider(X509KeyManager keyManager, String password) {
this.keyManager = keyManager;
this.password = password;
}
/**
* Returns the underlying {@link X509KeyManager} that is used.
*/
X509KeyManager keyManager() {
return keyManager;
}
/**
* Returns the {@link OpenSslKeyMaterial} or {@code null} (if none) that should be used during the handshake by
* OpenSSL.
*/
OpenSslKeyMaterial chooseKeyMaterial(ByteBufAllocator allocator, String alias) throws Exception {
X509Certificate[] certificates = keyManager.getCertificateChain(alias);
if (certificates == null || certificates.length == 0) {
return null;
}
PrivateKey key = keyManager.getPrivateKey(alias);
PemEncoded encoded = PemX509Certificate.toPEM(allocator, true, certificates);
long chainBio = 0;
long pkeyBio = 0;
long chain = 0;
long pkey = 0;
try {
chainBio = toBIO(allocator, encoded.retain());
pkeyBio = toBIO(allocator, key);
chain = SSL.parseX509Chain(chainBio);
pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password);
OpenSslKeyMaterial keyMaterial = new OpenSslKeyMaterial(chain, pkey);
// See the chain and pkey to 0 so we will not release it as the ownership was
// transferred to OpenSslKeyMaterial.
chain = 0;
pkey = 0;
return keyMaterial;
} finally {
SSL.freeBIO(chainBio);
SSL.freeBIO(pkeyBio);
if (chain != 0) {
SSL.freeX509Chain(chain);
}
if (pkey != 0) {
SSL.freePrivateKey(pkey);
}
encoded.release();
}
}
/**
* Will be invoked once the provider should be destroyed.
*/
void destroy() {
// NOOP.
}
}

View File

@ -25,8 +25,8 @@ import java.util.concurrent.locks.Lock;
* {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side. * {@link OpenSslSessionContext} implementation which offers extra methods which are only useful for the server-side.
*/ */
public final class OpenSslServerSessionContext extends OpenSslSessionContext { public final class OpenSslServerSessionContext extends OpenSslSessionContext {
OpenSslServerSessionContext(ReferenceCountedOpenSslContext context) { OpenSslServerSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
super(context); super(context, provider);
} }
@Override @Override

View File

@ -34,14 +34,21 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration(); private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
private final OpenSslSessionStats stats; private final OpenSslSessionStats stats;
// The OpenSslKeyMaterialProvider is not really used by the OpenSslSessionContext but only be stored here
// to make it easier to destroy it later because the ReferenceCountedOpenSslContext will hold a reference
// to OpenSslSessionContext.
private final OpenSslKeyMaterialProvider provider;
final ReferenceCountedOpenSslContext context; final ReferenceCountedOpenSslContext context;
// IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent // IMPORTANT: We take the OpenSslContext and not just the long (which points the native instance) to prevent
// the GC to collect OpenSslContext as this would also free the pointer and so could result in a // the GC to collect OpenSslContext as this would also free the pointer and so could result in a
// segfault when the user calls any of the methods here that try to pass the pointer down to the native // segfault when the user calls any of the methods here that try to pass the pointer down to the native
// level. // level.
OpenSslSessionContext(ReferenceCountedOpenSslContext context) { OpenSslSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
this.context = context; this.context = context;
this.provider = provider;
stats = new OpenSslSessionStats(context); stats = new OpenSslSessionStats(context);
} }
@ -123,6 +130,12 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
return stats; return stats;
} }
final void destroy() {
if (provider != null) {
provider.destroy();
}
}
private static final class EmptyEnumeration implements Enumeration<byte[]> { private static final class EmptyEnumeration implements Enumeration<byte[]> {
@Override @Override
public boolean hasMoreElements() { public boolean hasMoreElements() {

View File

@ -31,9 +31,7 @@ import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal; import javax.security.auth.x500.X500Principal;
@ -90,71 +88,82 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Either both keyCertChain and key needs to be null or none of them"); "Either both keyCertChain and key needs to be null or none of them");
} }
OpenSslKeyMaterialProvider keyMaterialProvider = null;
try { try {
if (!OpenSsl.useKeyManagerFactory()) { try {
if (keyManagerFactory != null) { if (!OpenSsl.useKeyManagerFactory()) {
throw new IllegalArgumentException( if (keyManagerFactory != null) {
"KeyManagerFactory not supported"); throw new IllegalArgumentException(
} "KeyManagerFactory not supported");
if (keyCertChain != null/* && key != null*/) { }
setKeyMaterial(ctx, keyCertChain, key, keyPassword); if (keyCertChain != null/* && key != null*/) {
} setKeyMaterial(ctx, keyCertChain, key, keyPassword);
} else { }
// javadocs state that keyManagerFactory has precedent over keyCertChain } else {
if (keyManagerFactory == null && keyCertChain != null) { // javadocs state that keyManagerFactory has precedent over keyCertChain
keyManagerFactory = buildKeyManagerFactory( if (keyManagerFactory == null && keyCertChain != null) {
keyCertChain, key, keyPassword, keyManagerFactory); keyMaterialProvider = new OpenSslCachingKeyMaterialProvider(
} chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null)
.getKeyManagers()), keyPassword);
} else if (keyManagerFactory != null) {
keyMaterialProvider = providerFor(keyManagerFactory, keyPassword);
}
if (keyManagerFactory != null) { if (keyMaterialProvider != null) {
X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers()); OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider);
OpenSslKeyMaterialManager materialManager = useExtendedKeyManager(keyManager) ? SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback(
new OpenSslExtendedKeyMaterialManager( engineMap, materialManager));
(X509ExtendedKeyManager) keyManager, keyPassword) : }
new OpenSslKeyMaterialManager(keyManager, keyPassword);
SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback(
engineMap, materialManager));
} }
} catch (Exception e) {
throw new SSLException("failed to set certificate and key", e);
}
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
} else if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
}
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
// IMPORTANT: The callbacks set for verification must be static to prevent memory leak as
// otherwise the context can never be collected. This is because the JNI code holds
// a global reference to the callbacks.
//
// See https://github.com/netty/netty/issues/5372
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
SSLContext.setCertVerifyCallback(ctx,
new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager));
} else {
SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager));
}
} catch (Exception e) {
if (keyMaterialProvider != null) {
keyMaterialProvider.destroy();
}
throw new SSLException("unable to setup trustmanager", e);
}
OpenSslClientSessionContext context = new OpenSslClientSessionContext(thiz, keyMaterialProvider);
keyMaterialProvider = null;
return context;
} finally {
if (keyMaterialProvider != null) {
keyMaterialProvider.destroy();
} }
} catch (Exception e) {
throw new SSLException("failed to set certificate and key", e);
} }
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
} else if (trustManagerFactory == null) {
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
}
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
// IMPORTANT: The callbacks set for verification must be static to prevent memory leak as
// otherwise the context can never be collected. This is because the JNI code holds
// a global reference to the callbacks.
//
// See https://github.com/netty/netty/issues/5372
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
SSLContext.setCertVerifyCallback(ctx,
new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager));
} else {
SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager));
}
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
return new OpenSslClientSessionContext(thiz);
} }
// No cache is currently supported for client side mode. // No cache is currently supported for client side mode.
static final class OpenSslClientSessionContext extends OpenSslSessionContext { static final class OpenSslClientSessionContext extends OpenSslSessionContext {
OpenSslClientSessionContext(ReferenceCountedOpenSslContext context) { OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
super(context); super(context, provider);
} }
@Override @Override
@ -232,7 +241,8 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
} }
@Override @Override
public KeyMaterial requested(long ssl, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) { public void requested(
long ssl, long certOut, long keyOut, byte[] keyTypeBytes, byte[][] asn1DerEncodedPrincipals) {
final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl); final ReferenceCountedOpenSslEngine engine = engineMap.get(ssl);
try { try {
final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes); final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes);
@ -246,13 +256,12 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]); issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
} }
} }
return keyManagerHolder.keyMaterial(engine, keyTypes, issuers); keyManagerHolder.setKeyMaterialClientSide(engine, certOut, keyOut, keyTypes, issuers);
} catch (Throwable cause) { } catch (Throwable cause) {
logger.debug("request of key failed", cause); logger.debug("request of key failed", cause);
SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem"); SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
e.initCause(cause); e.initCause(cause);
engine.handshakeException = e; engine.handshakeException = e;
return null;
} }
} }

View File

@ -49,11 +49,11 @@ import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager; import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
@ -486,6 +486,7 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
SSLContext.free(ctx); SSLContext.free(ctx);
ctx = 0; ctx = 0;
sessionContext().destroy();
} }
} finally { } finally {
writerLock.unlock(); writerLock.unlock();
@ -566,10 +567,6 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager; return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
} }
static boolean useExtendedKeyManager(X509KeyManager keyManager) {
return PlatformDependent.javaVersion() >= 7 && keyManager instanceof X509ExtendedKeyManager;
}
@Override @Override
public final int refCnt() { public final int refCnt() {
return refCnt.refCnt(); return refCnt.refCnt();
@ -819,4 +816,19 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
buffer.release(); buffer.release();
} }
} }
/**
* Returns the {@link OpenSslKeyMaterialProvider} that should be used for OpenSSL. Depending on the given
* {@link KeyManagerFactory} this may cache the {@link OpenSslKeyMaterial} for better performance if it can
* ensure that the same material is always returned for the same alias.
*/
static OpenSslKeyMaterialProvider providerFor(KeyManagerFactory factory, String password) {
X509KeyManager keyManager = chooseX509KeyManager(factory.getKeyManagers());
if (factory instanceof OpenSslCachingX509KeyManagerFactory) {
// The user explicit used OpenSslCachingX509KeyManagerFactory which signals us that its fine to cache.
return new OpenSslCachingKeyMaterialProvider(keyManager, password);
}
// We can not be sure if the material may change at runtime so we will not cache it.
return new OpenSslKeyMaterialProvider(keyManager, password);
}
} }

View File

@ -1567,7 +1567,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
if (!certificateSet && keyMaterialManager != null) { if (!certificateSet && keyMaterialManager != null) {
certificateSet = true; certificateSet = true;
keyMaterialManager.setKeyMaterial(this); keyMaterialManager.setKeyMaterialServerSide(this);
} }
int code = SSL.doHandshake(ssl); int code = SSL.doHandshake(ssl);

View File

@ -29,9 +29,7 @@ import java.security.cert.X509Certificate;
import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509ExtendedTrustManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
import static io.netty.util.internal.ObjectUtil.checkNotNull; import static io.netty.util.internal.ObjectUtil.checkNotNull;
@ -107,87 +105,97 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
String keyPassword, KeyManagerFactory keyManagerFactory) String keyPassword, KeyManagerFactory keyManagerFactory)
throws SSLException { throws SSLException {
ServerContext result = new ServerContext(); ServerContext result = new ServerContext();
OpenSslKeyMaterialProvider keyMaterialProvider = null;
try { try {
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH); try {
if (!OpenSsl.useKeyManagerFactory()) { SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
if (keyManagerFactory != null) { if (!OpenSsl.useKeyManagerFactory()) {
throw new IllegalArgumentException( if (keyManagerFactory != null) {
"KeyManagerFactory not supported"); throw new IllegalArgumentException(
} "KeyManagerFactory not supported");
checkNotNull(keyCertChain, "keyCertChain");
setKeyMaterial(ctx, keyCertChain, key, keyPassword);
} else {
// javadocs state that keyManagerFactory has precedent over keyCertChain, and we must have a
// keyManagerFactory for the server so build one if it is not specified.
if (keyManagerFactory == null) {
keyManagerFactory = buildKeyManagerFactory(
keyCertChain, key, keyPassword, keyManagerFactory);
}
X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers());
result.keyMaterialManager = useExtendedKeyManager(keyManager) ?
new OpenSslExtendedKeyMaterialManager(
(X509ExtendedKeyManager) keyManager, keyPassword) :
new OpenSslKeyMaterialManager(keyManager, keyPassword);
}
} catch (Exception e) {
throw new SSLException("failed to set certificate and key", e);
}
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
} else if (trustManagerFactory == null) {
// Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
}
final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
// IMPORTANT: The callbacks set for verification must be static to prevent memory leak as
// otherwise the context can never be collected. This is because the JNI code holds
// a global reference to the callbacks.
//
// See https://github.com/netty/netty/issues/5372
// Use this to prevent an error when running on java < 7
if (useExtendedTrustManager(manager)) {
SSLContext.setCertVerifyCallback(ctx,
new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager));
} else {
SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager));
}
X509Certificate[] issuers = manager.getAcceptedIssuers();
if (issuers != null && issuers.length > 0) {
long bio = 0;
try {
bio = toBIO(ByteBufAllocator.DEFAULT, issuers);
if (!SSLContext.setCACertificateBio(ctx, bio)) {
throw new SSLException("unable to setup accepted issuers for trustmanager " + manager);
} }
} finally { checkNotNull(keyCertChain, "keyCertChain");
freeBio(bio);
setKeyMaterial(ctx, keyCertChain, key, keyPassword);
} else {
// javadocs state that keyManagerFactory has precedent over keyCertChain, and we must have a
// keyManagerFactory for the server so build one if it is not specified.
if (keyManagerFactory == null) {
keyMaterialProvider = new OpenSslCachingKeyMaterialProvider(
chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null)
.getKeyManagers()), keyPassword);
} else {
keyMaterialProvider = providerFor(keyManagerFactory, keyPassword);
}
result.keyMaterialManager = new OpenSslKeyMaterialManager(keyMaterialProvider);
} }
} catch (Exception e) {
throw new SSLException("failed to set certificate and key", e);
} }
try {
if (trustCertCollection != null) {
trustManagerFactory = buildTrustManagerFactory(trustCertCollection, trustManagerFactory);
} else if (trustManagerFactory == null) {
// Mimic the way SSLContext.getInstance(KeyManager[], null, null) works
trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
}
if (PlatformDependent.javaVersion() >= 8) { final X509TrustManager manager = chooseTrustManager(trustManagerFactory.getTrustManagers());
// Only do on Java8+ as SNIMatcher is not supported in earlier releases.
// IMPORTANT: The callbacks set for hostname matching must be static to prevent memory leak as // IMPORTANT: The callbacks set for verification must be static to prevent memory leak as
// otherwise the context can never be collected. This is because the JNI code holds // otherwise the context can never be collected. This is because the JNI code holds
// a global reference to the matcher. // a global reference to the callbacks.
SSLContext.setSniHostnameMatcher(ctx, new OpenSslSniHostnameMatcher(engineMap)); //
} // See https://github.com/netty/netty/issues/5372
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
result.sessionContext = new OpenSslServerSessionContext(thiz); // Use this to prevent an error when running on java < 7
result.sessionContext.setSessionIdContext(ID); if (useExtendedTrustManager(manager)) {
return result; SSLContext.setCertVerifyCallback(ctx,
new ExtendedTrustManagerVerifyCallback(engineMap, (X509ExtendedTrustManager) manager));
} else {
SSLContext.setCertVerifyCallback(ctx, new TrustManagerVerifyCallback(engineMap, manager));
}
X509Certificate[] issuers = manager.getAcceptedIssuers();
if (issuers != null && issuers.length > 0) {
long bio = 0;
try {
bio = toBIO(ByteBufAllocator.DEFAULT, issuers);
if (!SSLContext.setCACertificateBio(ctx, bio)) {
throw new SSLException("unable to setup accepted issuers for trustmanager " + manager);
}
} finally {
freeBio(bio);
}
}
if (PlatformDependent.javaVersion() >= 8) {
// Only do on Java8+ as SNIMatcher is not supported in earlier releases.
// IMPORTANT: The callbacks set for hostname matching must be static to prevent memory leak as
// otherwise the context can never be collected. This is because the JNI code holds
// a global reference to the matcher.
SSLContext.setSniHostnameMatcher(ctx, new OpenSslSniHostnameMatcher(engineMap));
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException("unable to setup trustmanager", e);
}
result.sessionContext = new OpenSslServerSessionContext(thiz, keyMaterialProvider);
result.sessionContext.setSessionIdContext(ID);
keyMaterialProvider = null;
return result;
} finally {
if (keyMaterialProvider != null) {
keyMaterialProvider.destroy();
}
}
} }
private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier { private static final class TrustManagerVerifyCallback extends AbstractCertificateVerifier {

View File

@ -121,6 +121,9 @@ public final class SslContextBuilder {
/** /**
* Creates a builder for new server-side {@link SslContext}. * Creates a builder for new server-side {@link SslContext}.
* *
* If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using
* {@link OpenSslCachingX509KeyManagerFactory}.
*
* @param keyManagerFactory non-{@code null} factory for server's private key * @param keyManagerFactory non-{@code null} factory for server's private key
* @see #keyManager(KeyManagerFactory) * @see #keyManager(KeyManagerFactory)
*/ */
@ -335,6 +338,9 @@ public final class SslContextBuilder {
* if the used openssl version is 1.0.1+. You can check if your openssl version supports using a * if the used openssl version is 1.0.1+. You can check if your openssl version supports using a
* {@link KeyManagerFactory} by calling {@link OpenSsl#supportsKeyManagerFactory()}. If this is not the case * {@link KeyManagerFactory} by calling {@link OpenSsl#supportsKeyManagerFactory()}. If this is not the case
* you must use {@link #keyManager(File, File)} or {@link #keyManager(File, File, String)}. * you must use {@link #keyManager(File, File)} or {@link #keyManager(File, File, String)}.
*
* If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using
* {@link OpenSslCachingX509KeyManagerFactory}.
*/ */
public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) { public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) {
if (forServer) { if (forServer) {

View File

@ -0,0 +1,77 @@
/*
* Copyright 2018 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 io.netty.buffer.UnpooledByteBufAllocator;
import org.junit.Assert;
import org.junit.Test;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.X509KeyManager;
import static org.junit.Assert.*;
public class OpenSslCachingKeyMaterialProviderTest extends OpenSslKeyMaterialProviderTest {
@Override
protected KeyManagerFactory newKeyManagerFactory() throws Exception {
return new OpenSslCachingX509KeyManagerFactory(super.newKeyManagerFactory());
}
@Override
protected OpenSslKeyMaterialProvider newMaterialProvider(X509KeyManager manager, String password) {
return new OpenSslCachingKeyMaterialProvider(manager, password);
}
@Override
protected void assertRelease(OpenSslKeyMaterial material) {
Assert.assertFalse(material.release());
}
@Override
protected Class<? extends OpenSslKeyMaterialProvider> providerClass() {
return OpenSslCachingKeyMaterialProvider.class;
}
@Test
public void testMaterialCached() throws Exception {
X509KeyManager manager = ReferenceCountedOpenSslContext.chooseX509KeyManager(
newKeyManagerFactory().getKeyManagers());
OpenSslKeyMaterialProvider provider = newMaterialProvider(manager, PASSWORD);
OpenSslKeyMaterial material = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS);
assertNotNull(material);
assertNotEquals(0, material.certificateChainAddress());
assertNotEquals(0, material.privateKeyAddress());
assertEquals(2, material.refCnt());
OpenSslKeyMaterial material2 = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS);
assertNotNull(material2);
assertEquals(material.certificateChainAddress(), material2.certificateChainAddress());
assertEquals(material.privateKeyAddress(), material2.privateKeyAddress());
assertEquals(3, material.refCnt());
assertEquals(3, material2.refCnt());
assertFalse(material.release());
assertFalse(material2.release());
// After this the material should have been released.
provider.destroy();
assertEquals(0, material.refCnt());
assertEquals(0, material2.refCnt());
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2018 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 io.netty.buffer.UnpooledByteBufAllocator;
import org.junit.BeforeClass;
import org.junit.Test;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.X509KeyManager;
import java.security.KeyStore;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
public class OpenSslKeyMaterialProviderTest {
static final String PASSWORD = "example";
static final String EXISTING_ALIAS = "1";
private static final String NON_EXISTING_ALIAS = "nonexisting";
@BeforeClass
public static void checkOpenSsl() {
assumeTrue(OpenSsl.isAvailable());
}
protected KeyManagerFactory newKeyManagerFactory() throws Exception {
char[] password = PASSWORD.toCharArray();
final KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(getClass().getResourceAsStream("mutual_auth_server.p12"), password);
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, password);
return kmf;
}
protected OpenSslKeyMaterialProvider newMaterialProvider(X509KeyManager manager, String password) {
return new OpenSslKeyMaterialProvider(manager, password);
}
protected Class<? extends OpenSslKeyMaterialProvider> providerClass() {
return OpenSslKeyMaterialProvider.class;
}
protected void assertRelease(OpenSslKeyMaterial material) {
assertTrue(material.release());
}
@Test
public void testChooseKeyMaterial() throws Exception {
X509KeyManager manager = ReferenceCountedOpenSslContext.chooseX509KeyManager(
newKeyManagerFactory().getKeyManagers());
OpenSslKeyMaterialProvider provider = newMaterialProvider(manager, PASSWORD);
OpenSslKeyMaterial nonExistingMaterial = provider.chooseKeyMaterial(
UnpooledByteBufAllocator.DEFAULT, NON_EXISTING_ALIAS);
assertNull(nonExistingMaterial);
OpenSslKeyMaterial material = provider.chooseKeyMaterial(UnpooledByteBufAllocator.DEFAULT, EXISTING_ALIAS);
assertNotNull(material);
assertNotEquals(0, material.certificateChainAddress());
assertNotEquals(0, material.privateKeyAddress());
assertRelease(material);
provider.destroy();
}
@Test
public void testChooseTheCorrectProvider() throws Exception {
OpenSslKeyMaterialProvider provider = ReferenceCountedOpenSslContext.providerFor(
newKeyManagerFactory(), PASSWORD);
assertEquals(providerClass(), provider.getClass());
provider.destroy();
}
}

View File

@ -221,7 +221,7 @@
<!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency --> <!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency -->
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes> <os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
<tcnative.artifactId>netty-tcnative</tcnative.artifactId> <tcnative.artifactId>netty-tcnative</tcnative.artifactId>
<tcnative.version>2.0.10.Final</tcnative.version> <tcnative.version>2.0.11.Final</tcnative.version>
<tcnative.classifier>${os.detected.classifier}</tcnative.classifier> <tcnative.classifier>${os.detected.classifier}</tcnative.classifier>
<conscrypt.groupId>org.conscrypt</conscrypt.groupId> <conscrypt.groupId>org.conscrypt</conscrypt.groupId>
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId> <conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>