1cd9f5e17b
Motivation: Subclasses of `OpenSslKeyMaterial` implement `ReferenceCounted`. This means that a new object should have an initial refcount of 1. An `OpenSslPrivateKey.OpenSslPrivateKeyMaterial` object shares its refcount with the enclosing `OpenSslPrivateKey` object. This means the enclosing object's refcount must be incremented by 1 when an instance of `OpenSslPrivateKey.OpenSslPrivateKeyMaterial` is created. Otherwise, when the key material object is `release()`-ed, the refcount on the enclosing object will drop to 0 while it is still in use. Modification: - Increment the refcount in the constructor of `OpenSslPrivateKey.OpenSslPrivateKeyMaterial` - Ensure we also always release the native certificates as well. Result: Refcount is now correct.
155 lines
5.0 KiB
Java
155 lines
5.0 KiB
Java
/*
|
|
* 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.buffer.UnpooledByteBufAllocator;
|
|
import io.netty.internal.tcnative.SSL;
|
|
|
|
import javax.net.ssl.SSLException;
|
|
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;
|
|
}
|
|
|
|
static void validateKeyMaterialSupported(X509Certificate[] keyCertChain, PrivateKey key, String keyPassword)
|
|
throws SSLException {
|
|
validateSupported(keyCertChain);
|
|
validateSupported(key, keyPassword);
|
|
}
|
|
|
|
private static void validateSupported(PrivateKey key, String password) throws SSLException {
|
|
if (key == null) {
|
|
return;
|
|
}
|
|
|
|
long pkeyBio = 0;
|
|
long pkey = 0;
|
|
|
|
try {
|
|
pkeyBio = toBIO(UnpooledByteBufAllocator.DEFAULT, key);
|
|
pkey = SSL.parsePrivateKey(pkeyBio, password);
|
|
} catch (Exception e) {
|
|
throw new SSLException("PrivateKey type not supported " + key.getFormat(), e);
|
|
} finally {
|
|
SSL.freeBIO(pkeyBio);
|
|
if (pkey != 0) {
|
|
SSL.freePrivateKey(pkey);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void validateSupported(X509Certificate[] certificates) throws SSLException {
|
|
if (certificates == null || certificates.length == 0) {
|
|
return;
|
|
}
|
|
|
|
long chainBio = 0;
|
|
long chain = 0;
|
|
PemEncoded encoded = null;
|
|
try {
|
|
encoded = PemX509Certificate.toPEM(UnpooledByteBufAllocator.DEFAULT, true, certificates);
|
|
chainBio = toBIO(UnpooledByteBufAllocator.DEFAULT, encoded.retain());
|
|
chain = SSL.parseX509Chain(chainBio);
|
|
} catch (Exception e) {
|
|
throw new SSLException("Certificate type not supported", e);
|
|
} finally {
|
|
SSL.freeBIO(chainBio);
|
|
if (chain != 0) {
|
|
SSL.freeX509Chain(chain);
|
|
}
|
|
if (encoded != null) {
|
|
encoded.release();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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());
|
|
chain = SSL.parseX509Chain(chainBio);
|
|
|
|
OpenSslKeyMaterial keyMaterial;
|
|
if (key instanceof OpenSslPrivateKey) {
|
|
keyMaterial = ((OpenSslPrivateKey) key).newKeyMaterial(chain, certificates);
|
|
} else {
|
|
pkeyBio = toBIO(allocator, key);
|
|
pkey = key == null ? 0 : SSL.parsePrivateKey(pkeyBio, password);
|
|
keyMaterial = new DefaultOpenSslKeyMaterial(chain, pkey, certificates);
|
|
}
|
|
|
|
// 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.
|
|
}
|
|
}
|