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:
parent
4a8d3a274c
commit
0337ecdcc8
@ -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());
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -15,11 +15,10 @@
|
||||
*/
|
||||
package io.netty.handler.ssl;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.internal.tcnative.CertificateRequestedCallback;
|
||||
import io.netty.internal.tcnative.SSL;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.security.PrivateKey;
|
||||
@ -29,14 +28,12 @@ import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
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
|
||||
* {@link X509Certificate}s.
|
||||
*/
|
||||
class OpenSslKeyMaterialManager {
|
||||
final class OpenSslKeyMaterialManager {
|
||||
|
||||
// Code in this class is inspired by code of conscrypts:
|
||||
// - https://android.googlesource.com/platform/external/
|
||||
@ -62,15 +59,13 @@ class OpenSslKeyMaterialManager {
|
||||
KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA);
|
||||
}
|
||||
|
||||
private final X509KeyManager keyManager;
|
||||
private final String password;
|
||||
private final OpenSslKeyMaterialProvider provider;
|
||||
|
||||
OpenSslKeyMaterialManager(X509KeyManager keyManager, String password) {
|
||||
this.keyManager = keyManager;
|
||||
this.password = password;
|
||||
OpenSslKeyMaterialManager(OpenSslKeyMaterialProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException {
|
||||
void setKeyMaterialServerSide(ReferenceCountedOpenSslEngine engine) throws SSLException {
|
||||
long ssl = engine.sslPointer();
|
||||
String[] authMethods = SSL.authenticationMethods(ssl);
|
||||
Set<String> aliases = new HashSet<String>(authMethods.length);
|
||||
@ -79,101 +74,62 @@ class OpenSslKeyMaterialManager {
|
||||
if (type != null) {
|
||||
String alias = chooseServerAlias(engine, type);
|
||||
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,
|
||||
X500Principal[] issuer) throws SSLException {
|
||||
void setKeyMaterialClientSide(ReferenceCountedOpenSslEngine engine, long certOut, long keyOut, String[] keyTypes,
|
||||
X500Principal[] issuer) throws SSLException {
|
||||
String alias = chooseClientAlias(engine, keyTypes, issuer);
|
||||
long keyBio = 0;
|
||||
long keyCertChainBio = 0;
|
||||
long pkey = 0;
|
||||
long certChain = 0;
|
||||
|
||||
OpenSslKeyMaterial keyMaterial = null;
|
||||
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 null;
|
||||
}
|
||||
|
||||
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();
|
||||
keyMaterial = provider.chooseKeyMaterial(engine.alloc, alias);
|
||||
if (keyMaterial != null) {
|
||||
SSL.setKeyMaterialClientSide(engine.sslPointer(), certOut, keyOut,
|
||||
keyMaterial.certificateChainAddress(), keyMaterial.privateKeyAddress());
|
||||
}
|
||||
} catch (SSLException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new SSLException(e);
|
||||
} finally {
|
||||
freeBio(keyBio);
|
||||
freeBio(keyCertChainBio);
|
||||
freeBio(keyCertChainBio2);
|
||||
if (keyMaterial != null) {
|
||||
keyMaterial.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String chooseClientAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine,
|
||||
private String chooseClientAlias(ReferenceCountedOpenSslEngine engine,
|
||||
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) {
|
||||
return keyManager.chooseServerAlias(type, null, null);
|
||||
private String chooseServerAlias(ReferenceCountedOpenSslEngine engine, String type) {
|
||||
X509KeyManager manager = provider.keyManager();
|
||||
if (manager instanceof X509ExtendedKeyManager) {
|
||||
return ((X509ExtendedKeyManager) manager).chooseEngineServerAlias(type, null, engine);
|
||||
}
|
||||
return manager.chooseServerAlias(type, null, null);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
}
|
||||
}
|
@ -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.
|
||||
*/
|
||||
public final class OpenSslServerSessionContext extends OpenSslSessionContext {
|
||||
OpenSslServerSessionContext(ReferenceCountedOpenSslContext context) {
|
||||
super(context);
|
||||
OpenSslServerSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
|
||||
super(context, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,14 +34,21 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
|
||||
private static final Enumeration<byte[]> EMPTY = new EmptyEnumeration();
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
// segfault when the user calls any of the methods here that try to pass the pointer down to the native
|
||||
// level.
|
||||
OpenSslSessionContext(ReferenceCountedOpenSslContext context) {
|
||||
OpenSslSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
|
||||
this.context = context;
|
||||
this.provider = provider;
|
||||
stats = new OpenSslSessionStats(context);
|
||||
}
|
||||
|
||||
@ -123,6 +130,12 @@ public abstract class OpenSslSessionContext implements SSLSessionContext {
|
||||
return stats;
|
||||
}
|
||||
|
||||
final void destroy() {
|
||||
if (provider != null) {
|
||||
provider.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class EmptyEnumeration implements Enumeration<byte[]> {
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
|
@ -31,9 +31,7 @@ import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
@ -90,71 +88,82 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
|
||||
throw new IllegalArgumentException(
|
||||
"Either both keyCertChain and key needs to be null or none of them");
|
||||
}
|
||||
OpenSslKeyMaterialProvider keyMaterialProvider = null;
|
||||
try {
|
||||
if (!OpenSsl.useKeyManagerFactory()) {
|
||||
if (keyManagerFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"KeyManagerFactory not supported");
|
||||
}
|
||||
if (keyCertChain != null/* && key != null*/) {
|
||||
setKeyMaterial(ctx, keyCertChain, key, keyPassword);
|
||||
}
|
||||
} else {
|
||||
// javadocs state that keyManagerFactory has precedent over keyCertChain
|
||||
if (keyManagerFactory == null && keyCertChain != null) {
|
||||
keyManagerFactory = buildKeyManagerFactory(
|
||||
keyCertChain, key, keyPassword, keyManagerFactory);
|
||||
}
|
||||
try {
|
||||
if (!OpenSsl.useKeyManagerFactory()) {
|
||||
if (keyManagerFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"KeyManagerFactory not supported");
|
||||
}
|
||||
if (keyCertChain != null/* && key != null*/) {
|
||||
setKeyMaterial(ctx, keyCertChain, key, keyPassword);
|
||||
}
|
||||
} else {
|
||||
// javadocs state that keyManagerFactory has precedent over keyCertChain
|
||||
if (keyManagerFactory == null && keyCertChain != null) {
|
||||
keyMaterialProvider = new OpenSslCachingKeyMaterialProvider(
|
||||
chooseX509KeyManager(buildKeyManagerFactory(keyCertChain, key, keyPassword, null)
|
||||
.getKeyManagers()), keyPassword);
|
||||
} else if (keyManagerFactory != null) {
|
||||
keyMaterialProvider = providerFor(keyManagerFactory, keyPassword);
|
||||
}
|
||||
|
||||
if (keyManagerFactory != null) {
|
||||
X509KeyManager keyManager = chooseX509KeyManager(keyManagerFactory.getKeyManagers());
|
||||
OpenSslKeyMaterialManager materialManager = useExtendedKeyManager(keyManager) ?
|
||||
new OpenSslExtendedKeyMaterialManager(
|
||||
(X509ExtendedKeyManager) keyManager, keyPassword) :
|
||||
new OpenSslKeyMaterialManager(keyManager, keyPassword);
|
||||
SSLContext.setCertRequestedCallback(ctx, new OpenSslCertificateRequestedCallback(
|
||||
engineMap, materialManager));
|
||||
if (keyMaterialProvider != null) {
|
||||
OpenSslKeyMaterialManager materialManager = new OpenSslKeyMaterialManager(keyMaterialProvider);
|
||||
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.
|
||||
static final class OpenSslClientSessionContext extends OpenSslSessionContext {
|
||||
OpenSslClientSessionContext(ReferenceCountedOpenSslContext context) {
|
||||
super(context);
|
||||
OpenSslClientSessionContext(ReferenceCountedOpenSslContext context, OpenSslKeyMaterialProvider provider) {
|
||||
super(context, provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -232,7 +241,8 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
|
||||
}
|
||||
|
||||
@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);
|
||||
try {
|
||||
final Set<String> keyTypesSet = supportedClientKeyTypes(keyTypeBytes);
|
||||
@ -246,13 +256,12 @@ public final class ReferenceCountedOpenSslClientContext extends ReferenceCounted
|
||||
issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
|
||||
}
|
||||
}
|
||||
return keyManagerHolder.keyMaterial(engine, keyTypes, issuers);
|
||||
keyManagerHolder.setKeyMaterialClientSide(engine, certOut, keyOut, keyTypes, issuers);
|
||||
} catch (Throwable cause) {
|
||||
logger.debug("request of key failed", cause);
|
||||
SSLHandshakeException e = new SSLHandshakeException("General OpenSslEngine problem");
|
||||
e.initCause(cause);
|
||||
engine.handshakeException = e;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,11 +49,11 @@ import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
@ -486,6 +486,7 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
||||
|
||||
SSLContext.free(ctx);
|
||||
ctx = 0;
|
||||
sessionContext().destroy();
|
||||
}
|
||||
} finally {
|
||||
writerLock.unlock();
|
||||
@ -566,10 +567,6 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
||||
return PlatformDependent.javaVersion() >= 7 && trustManager instanceof X509ExtendedTrustManager;
|
||||
}
|
||||
|
||||
static boolean useExtendedKeyManager(X509KeyManager keyManager) {
|
||||
return PlatformDependent.javaVersion() >= 7 && keyManager instanceof X509ExtendedKeyManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int refCnt() {
|
||||
return refCnt.refCnt();
|
||||
@ -819,4 +816,19 @@ public abstract class ReferenceCountedOpenSslContext extends SslContext implemen
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1567,7 +1567,7 @@ public class ReferenceCountedOpenSslEngine extends SSLEngine implements Referenc
|
||||
|
||||
if (!certificateSet && keyMaterialManager != null) {
|
||||
certificateSet = true;
|
||||
keyMaterialManager.setKeyMaterial(this);
|
||||
keyMaterialManager.setKeyMaterialServerSide(this);
|
||||
}
|
||||
|
||||
int code = SSL.doHandshake(ssl);
|
||||
|
@ -29,9 +29,7 @@ import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
@ -107,87 +105,97 @@ public final class ReferenceCountedOpenSslServerContext extends ReferenceCounted
|
||||
String keyPassword, KeyManagerFactory keyManagerFactory)
|
||||
throws SSLException {
|
||||
ServerContext result = new ServerContext();
|
||||
OpenSslKeyMaterialProvider keyMaterialProvider = null;
|
||||
try {
|
||||
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
|
||||
if (!OpenSsl.useKeyManagerFactory()) {
|
||||
if (keyManagerFactory != null) {
|
||||
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);
|
||||
try {
|
||||
SSLContext.setVerify(ctx, SSL.SSL_CVERIFY_NONE, VERIFY_DEPTH);
|
||||
if (!OpenSsl.useKeyManagerFactory()) {
|
||||
if (keyManagerFactory != null) {
|
||||
throw new IllegalArgumentException(
|
||||
"KeyManagerFactory not supported");
|
||||
}
|
||||
} finally {
|
||||
freeBio(bio);
|
||||
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) {
|
||||
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) {
|
||||
// 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
|
||||
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 matcher.
|
||||
SSLContext.setSniHostnameMatcher(ctx, new OpenSslSniHostnameMatcher(engineMap));
|
||||
}
|
||||
} catch (SSLException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new SSLException("unable to setup trustmanager", e);
|
||||
}
|
||||
// a global reference to the callbacks.
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/5372
|
||||
|
||||
result.sessionContext = new OpenSslServerSessionContext(thiz);
|
||||
result.sessionContext.setSessionIdContext(ID);
|
||||
return result;
|
||||
// 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 {
|
||||
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 {
|
||||
|
@ -121,6 +121,9 @@ public final class SslContextBuilder {
|
||||
/**
|
||||
* 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
|
||||
* @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
|
||||
* {@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)}.
|
||||
*
|
||||
* If you use {@link SslProvider#OPENSSL} or {@link SslProvider#OPENSSL_REFCNT} consider using
|
||||
* {@link OpenSslCachingX509KeyManagerFactory}.
|
||||
*/
|
||||
public SslContextBuilder keyManager(KeyManagerFactory keyManagerFactory) {
|
||||
if (forServer) {
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
2
pom.xml
2
pom.xml
@ -221,7 +221,7 @@
|
||||
<!-- Fedora-"like" systems. This is currently only used for the netty-tcnative dependency -->
|
||||
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
|
||||
<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>
|
||||
<conscrypt.groupId>org.conscrypt</conscrypt.groupId>
|
||||
<conscrypt.artifactId>conscrypt-openjdk-uber</conscrypt.artifactId>
|
||||
|
Loading…
Reference in New Issue
Block a user