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.X509KeyManager;
import javax.security.auth.x500.X500Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.HashMap;
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;
class OpenSslKeyMaterialManager {
static final String KEY_TYPE_RSA = "RSA";
static final String KEY_TYPE_DH_RSA = "DH_RSA";
static final String KEY_TYPE_EC = "EC";
static final String KEY_TYPE_EC_EC = "EC_EC";
static final String KEY_TYPE_EC_RSA = "EC_RSA";
private static final Map<String, String> KEY_TYPES = new HashMap<String, String>();
static {
KEY_TYPES.put("RSA", KEY_TYPE_RSA);
KEY_TYPES.put("DHE_RSA", KEY_TYPE_RSA);
KEY_TYPES.put("ECDHE_RSA", KEY_TYPE_RSA);
KEY_TYPES.put("ECDHE_ECDSA", KEY_TYPE_EC);
KEY_TYPES.put("ECDH_RSA", KEY_TYPE_EC_RSA);
KEY_TYPES.put("ECDH_ECDSA", KEY_TYPE_EC_EC);
KEY_TYPES.put("DH_RSA", KEY_TYPE_DH_RSA);
}
private final X509KeyManager keyManager;
private final String password;
OpenSslKeyMaterialManager(X509KeyManager keyManager, String password) {
this.keyManager = keyManager;
this.password = password;
}
void setKeyMaterial(ReferenceCountedOpenSslEngine engine) throws SSLException {
long ssl = engine.sslPointer();
String[] authMethods = SSL.authenticationMethods(ssl);
Set<String> aliases = new HashSet<String>(authMethods.length);
for (String authMethod : authMethods) {
String type = KEY_TYPES.get(authMethod);
if (type != null) {
String alias = chooseServerAlias(engine, type);
if (alias != null && aliases.add(alias)) {
setKeyMaterial(ssl, alias);
}
}
}
}
CertificateRequestedCallback.KeyMaterial keyMaterial(ReferenceCountedOpenSslEngine engine, String[] keyTypes,
X500Principal[] issuer) throws SSLException {
String alias = chooseClientAlias(engine, keyTypes, issuer);
long keyBio = 0;
long keyCertChainBio = 0;
long pkey = 0;
long certChain = 0;
try {
X509Certificate[] certificates = keyManager.getCertificateChain(alias);
if (certificates == null || certificates.length == 0) {
return null;
}
PrivateKey key = keyManager.getPrivateKey(alias);
keyCertChainBio = toBIO(certificates);
certChain = SSL.parseX509Chain(keyCertChainBio);
if (key != null) {
keyBio = toBIO(key);
pkey = SSL.parsePrivateKey(keyBio, password);
}
CertificateRequestedCallback.KeyMaterial material = new CertificateRequestedCallback.KeyMaterial(
certChain, pkey);
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) throws SSLException {
long keyBio = 0;
long keyCertChainBio = 0;
long keyCertChainBio2 = 0;
try {
X509Certificate[] certificates = keyManager.getCertificateChain(alias);
if (certificates == null || certificates.length == 0) {
return;
}
PrivateKey key = keyManager.getPrivateKey(alias);
PemEncoded encoded = PemX509Certificate.toPEM(ByteBufAllocator.DEFAULT, true, certificates);
try {
keyCertChainBio = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
keyCertChainBio2 = toBIO(ByteBufAllocator.DEFAULT, encoded.retain());
if (key != null) {
keyBio = toBIO(key);
}
SSL.setCertificateBio(ssl, keyCertChainBio, keyBio, password);
SSL.setCertificateChainBio(ssl, keyCertChainBio2, true);
} finally {
encoded.release();
}
} catch (SSLException e) {
throw e;
} catch (Exception e) {
throw new SSLException(e);
} finally {
freeBio(keyBio);
freeBio(keyCertChainBio);
freeBio(keyCertChainBio2);
}
}
protected String chooseClientAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine,
String[] keyTypes, X500Principal[] issuer) {
return keyManager.chooseClientAlias(keyTypes, issuer, null);
}
protected String chooseServerAlias(@SuppressWarnings("unused") ReferenceCountedOpenSslEngine engine, String type) {
return keyManager.chooseServerAlias(type, null, null);
}
}