Commit 8d63ff13 authored by Alex Klyubin's avatar Alex Klyubin
Browse files

Support TLS/SSL without X509TrustManager or X509KeyManager.

This makes TLS/SSL primitives operate as expected when no
X509TrustManager or X509KeyManager is provided. Instead of blowing up
with KeyManagementException or NullPointerException (or similar) when
X509TrustManager or X509KeyManager is not provided, this CL makes
SSLContext.init accept such setup, and makes SSLSocket and SSLEngine
reject certificate chains, select no private keys/aliases, and accept
no certificate issuers.

Bug: 13563574
Change-Id: I8de58377a09025258357dd4da9f6cb1b6f2dab80
parent 2a5460fc
......@@ -377,23 +377,25 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
String alias = null;
String[] certTypes = certificateRequest.getTypesAsString();
X500Principal[] issuers = certificateRequest.certificate_authorities;
X509KeyManager km = parameters.getKeyManager();
if (km instanceof X509ExtendedKeyManager) {
X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
alias = ekm.chooseEngineClientAlias(certTypes, issuers, this.engineOwner);
if (alias != null) {
certs = ekm.getCertificateChain(alias);
}
} else {
alias = km.chooseClientAlias(certTypes, issuers, null);
if (alias != null) {
certs = km.getCertificateChain(alias);
X509KeyManager km = parameters.getX509KeyManager();
if (km != null) {
if (km instanceof X509ExtendedKeyManager) {
X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
alias = ekm.chooseEngineClientAlias(certTypes, issuers, this.engineOwner);
if (alias != null) {
certs = ekm.getCertificateChain(alias);
}
} else {
alias = km.chooseClientAlias(certTypes, issuers, null);
if (alias != null) {
certs = km.getCertificateChain(alias);
}
}
clientKey = km.getPrivateKey(alias);
}
session.localCertificates = certs;
clientCert = new CertificateMessage(certs);
clientKey = km.getPrivateKey(alias);
send(clientCert);
}
// Client key exchange
......@@ -530,7 +532,10 @@ public class ClientHandshakeImpl extends HandshakeProtocol {
}
String hostname = engineOwner.getPeerHost();
try {
X509TrustManager x509tm = parameters.getTrustManager();
X509TrustManager x509tm = parameters.getX509TrustManager();
if (x509tm == null) {
throw new CertificateException("No X.509 TrustManager");
}
if (x509tm instanceof TrustManagerImpl) {
TrustManagerImpl tm = (TrustManagerImpl) x509tm;
tm.checkServerTrusted(serverCert.certs, authType, hostname);
......
......@@ -41,6 +41,7 @@ import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLProtocolException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;
import libcore.io.ErrnoException;
......@@ -407,13 +408,15 @@ public class OpenSSLSocketImpl
keyTypes.add(keyType);
}
}
for (String keyType : keyTypes) {
try {
setCertificate(sslParameters.getKeyManager().chooseServerAlias(keyType,
null,
this));
} catch (CertificateEncodingException e) {
throw new IOException(e);
X509KeyManager x509KeyManager = sslParameters.getX509KeyManager();
if (x509KeyManager != null) {
for (String keyType : keyTypes) {
try {
setCertificate(
x509KeyManager.chooseServerAlias(keyType, null, this));
} catch (CertificateEncodingException e) {
throw new IOException(e);
}
}
}
}
......@@ -441,16 +444,18 @@ public class OpenSSLSocketImpl
}
if (certRequested) {
X509TrustManager trustManager = sslParameters.getTrustManager();
X509Certificate[] issuers = trustManager.getAcceptedIssuers();
if (issuers != null && issuers.length != 0) {
byte[][] issuersBytes;
try {
issuersBytes = encodeIssuerX509Principals(issuers);
} catch (CertificateEncodingException e) {
throw new IOException("Problem encoding principals", e);
X509TrustManager x509TrustManager = sslParameters.getX509TrustManager();
if (x509TrustManager != null) {
X509Certificate[] issuers = x509TrustManager.getAcceptedIssuers();
if (issuers != null && issuers.length != 0) {
byte[][] issuersBytes;
try {
issuersBytes = encodeIssuerX509Principals(issuers);
} catch (CertificateEncodingException e) {
throw new IOException("Problem encoding principals", e);
}
NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
}
NativeCrypto.SSL_set_client_CA_list(sslNativePointer, issuersBytes);
}
}
}
......@@ -635,11 +640,16 @@ public class OpenSSLSocketImpl
if (alias == null) {
return;
}
PrivateKey privateKey = sslParameters.getKeyManager().getPrivateKey(alias);
X509KeyManager keyManager = sslParameters.getX509KeyManager();
if (keyManager == null) {
return;
}
PrivateKey privateKey = keyManager.getPrivateKey(alias);
if (privateKey == null) {
return;
}
X509Certificate[] certificates = sslParameters.getKeyManager().getCertificateChain(alias);
X509Certificate[] certificates =
keyManager.getCertificateChain(alias);
if (certificates == null) {
return;
}
......@@ -693,7 +703,10 @@ public class OpenSSLSocketImpl
issuers[i] = new X500Principal(asn1DerEncodedPrincipals[i]);
}
}
setCertificate(sslParameters.getKeyManager().chooseClientAlias(keyTypes, issuers, this));
X509KeyManager keyManager = sslParameters.getX509KeyManager();
String alias =
(keyManager != null) ? keyManager.chooseClientAlias(keyTypes, issuers, this) : null;
setCertificate(alias);
}
@Override
......@@ -766,6 +779,10 @@ public class OpenSSLSocketImpl
public void verifyCertificateChain(long[] certRefs, String authMethod)
throws CertificateException {
try {
X509TrustManager x509tm = sslParameters.getX509TrustManager();
if (x509tm == null) {
throw new CertificateException("No X.509 TrustManager");
}
if (certRefs == null || certRefs.length == 0) {
throw new SSLException("Peer sent no certificate");
}
......@@ -775,7 +792,6 @@ public class OpenSSLSocketImpl
}
boolean client = sslParameters.getUseClientMode();
if (client) {
X509TrustManager x509tm = sslParameters.getTrustManager();
if (x509tm instanceof TrustManagerImpl) {
TrustManagerImpl tm = (TrustManagerImpl) x509tm;
tm.checkServerTrusted(peerCertChain, authMethod, getPeerHostName());
......@@ -784,8 +800,7 @@ public class OpenSSLSocketImpl
}
} else {
String authType = peerCertChain[0].getPublicKey().getAlgorithm();
sslParameters.getTrustManager().checkClientTrusted(peerCertChain,
authType);
x509tm.checkClientTrusted(peerCertChain, authType);
}
} catch (CertificateException e) {
......
......@@ -41,10 +41,10 @@ import javax.net.ssl.X509TrustManager;
*/
public class SSLParametersImpl implements Cloneable {
// default source of authentication keys
private static volatile X509KeyManager defaultKeyManager;
// default source of authentication trust decisions
private static volatile X509TrustManager defaultTrustManager;
// default source of X.509 certificate based authentication keys
private static volatile X509KeyManager defaultX509KeyManager;
// default source of X.509 certificate based authentication trust decisions
private static volatile X509TrustManager defaultX509TrustManager;
// default source of random numbers
private static volatile SecureRandom defaultSecureRandom;
// default SSL parameters
......@@ -56,10 +56,10 @@ public class SSLParametersImpl implements Cloneable {
// server session context contains the set of reusable
// server-side SSL sessions
private final ServerSessionContext serverSessionContext;
// source of authentication keys
private X509KeyManager keyManager;
// source of authentication trust decisions
private X509TrustManager trustManager;
// source of X.509 certificate based authentication keys or null if not provided
private final X509KeyManager x509KeyManager;
// source of X.509 certificate based authentication trust decisions or null if not provided
private final X509TrustManager x509TrustManager;
// source of random numbers
private SecureRandom secureRandom;
......@@ -106,18 +106,18 @@ public class SSLParametersImpl implements Cloneable {
// if the arrays of length 0 are specified. This implementation
// behave as for null arrays (i.e. use installed security providers)
// initialize keyManager
// initialize x509KeyManager
if ((kms == null) || (kms.length == 0)) {
keyManager = getDefaultKeyManager();
x509KeyManager = getDefaultX509KeyManager();
} else {
keyManager = findX509KeyManager(kms);
x509KeyManager = findFirstX509KeyManager(kms);
}
// initialize trustManager
// initialize x509TrustManager
if ((tms == null) || (tms.length == 0)) {
trustManager = getDefaultTrustManager();
x509TrustManager = getDefaultX509TrustManager();
} else {
trustManager = findX509TrustManager(tms);
x509TrustManager = findFirstX509TrustManager(tms);
}
// initialize secure random
// BEGIN android-removed
......@@ -167,17 +167,17 @@ public class SSLParametersImpl implements Cloneable {
}
/**
* @return key manager
* @return X.509 key manager or {@code null} for none.
*/
protected X509KeyManager getKeyManager() {
return keyManager;
protected X509KeyManager getX509KeyManager() {
return x509KeyManager;
}
/**
* @return trust manager
* @return X.509 trust manager or {@code null} for none.
*/
protected X509TrustManager getTrustManager() {
return trustManager;
protected X509TrustManager getX509TrustManager() {
return x509TrustManager;
}
/**
......@@ -348,21 +348,26 @@ public class SSLParametersImpl implements Cloneable {
}
}
private static X509KeyManager getDefaultKeyManager() throws KeyManagementException {
X509KeyManager result = defaultKeyManager;
private static X509KeyManager getDefaultX509KeyManager() throws KeyManagementException {
X509KeyManager result = defaultX509KeyManager;
if (result == null) {
// single-check idiom
defaultKeyManager = result = createDefaultKeyManager();
defaultX509KeyManager = result = createDefaultX509KeyManager();
}
return result;
}
private static X509KeyManager createDefaultKeyManager() throws KeyManagementException {
private static X509KeyManager createDefaultX509KeyManager() throws KeyManagementException {
try {
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
kmf.init(null, null);
KeyManager[] kms = kmf.getKeyManagers();
return findX509KeyManager(kms);
X509KeyManager result = findFirstX509KeyManager(kms);
if (result == null) {
throw new KeyManagementException("No X509KeyManager among default KeyManagers: "
+ Arrays.toString(kms));
}
return result;
} catch (NoSuchAlgorithmException e) {
throw new KeyManagementException(e);
} catch (KeyStoreException e) {
......@@ -371,13 +376,19 @@ public class SSLParametersImpl implements Cloneable {
throw new KeyManagementException(e);
}
}
private static X509KeyManager findX509KeyManager(KeyManager[] kms) throws KeyManagementException {
/**
* Finds the first {@link X509KeyManager} element in the provided array.
*
* @return the first {@code X509KeyManager} or {@code null} if not found.
*/
private static X509KeyManager findFirstX509KeyManager(KeyManager[] kms) {
for (KeyManager km : kms) {
if (km instanceof X509KeyManager) {
return (X509KeyManager)km;
}
}
throw new KeyManagementException("Failed to find an X509KeyManager in " + Arrays.toString(kms));
return null;
}
/**
......@@ -385,21 +396,26 @@ public class SSLParametersImpl implements Cloneable {
*
* TODO: Move this to a published API under dalvik.system.
*/
public static X509TrustManager getDefaultTrustManager() throws KeyManagementException {
X509TrustManager result = defaultTrustManager;
private static X509TrustManager getDefaultX509TrustManager() throws KeyManagementException {
X509TrustManager result = defaultX509TrustManager;
if (result == null) {
// single-check idiom
defaultTrustManager = result = createDefaultTrustManager();
defaultX509TrustManager = result = createDefaultX509TrustManager();
}
return result;
}
private static X509TrustManager createDefaultTrustManager() throws KeyManagementException {
private static X509TrustManager createDefaultX509TrustManager() throws KeyManagementException {
try {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init((KeyStore) null);
TrustManager[] tms = tmf.getTrustManagers();
X509TrustManager trustManager = findX509TrustManager(tms);
X509TrustManager trustManager = findFirstX509TrustManager(tms);
if (trustManager == null) {
throw new KeyManagementException(
"No X509TrustManager in among default TrustManagers: "
+ Arrays.toString(tms));
}
return trustManager;
} catch (NoSuchAlgorithmException e) {
throw new KeyManagementException(e);
......@@ -407,12 +423,18 @@ public class SSLParametersImpl implements Cloneable {
throw new KeyManagementException(e);
}
}
private static X509TrustManager findX509TrustManager(TrustManager[] tms) throws KeyManagementException {
/**
* Finds the first {@link X509TrustManager} element in the provided array.
*
* @return the first {@code X509TrustManager} or {@code null} if not found.
*/
private static X509TrustManager findFirstX509TrustManager(TrustManager[] tms) {
for (TrustManager tm : tms) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager)tm;
}
}
throw new KeyManagementException("Failed to find an X509TrustManager in " + Arrays.toString(tms));
return null;
}
}
......@@ -28,6 +28,7 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPublicKey;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.interfaces.DHPublicKey;
......@@ -142,8 +143,11 @@ public class ServerHandshakeImpl extends HandshakeProtocol {
} else {
String authType = clientCert.getAuthType();
try {
parameters.getTrustManager().checkClientTrusted(
clientCert.certs, authType);
X509TrustManager tm = parameters.getX509TrustManager();
if (tm == null) {
new CertificateException("No X.509 TrustManager");
}
tm.checkClientTrusted(clientCert.certs, authType);
} catch (CertificateException e) {
fatalAlert(AlertProtocol.BAD_CERTIFICATE,
"Untrusted Client Certificate ", e);
......@@ -431,18 +435,20 @@ public class ServerHandshakeImpl extends HandshakeProtocol {
}
// obtain certificates from key manager
String alias = null;
X509KeyManager km = parameters.getKeyManager();
if (km instanceof X509ExtendedKeyManager) {
X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
alias = ekm.chooseEngineServerAlias(certType, null,
this.engineOwner);
if (alias != null) {
certs = ekm.getCertificateChain(alias);
}
} else {
alias = km.chooseServerAlias(certType, null, null);
if (alias != null) {
certs = km.getCertificateChain(alias);
X509KeyManager km = parameters.getX509KeyManager();
if (km != null) {
if (km instanceof X509ExtendedKeyManager) {
X509ExtendedKeyManager ekm = (X509ExtendedKeyManager)km;
alias = ekm.chooseEngineServerAlias(certType, null,
this.engineOwner);
if (alias != null) {
certs = ekm.getCertificateChain(alias);
}
} else {
alias = km.chooseServerAlias(certType, null, null);
if (alias != null) {
certs = km.getCertificateChain(alias);
}
}
}
......@@ -452,7 +458,9 @@ public class ServerHandshakeImpl extends HandshakeProtocol {
}
session.localCertificates = certs;
serverCert = new CertificateMessage(certs);
privKey = km.getPrivateKey(alias);
if (km != null) {
privKey = km.getPrivateKey(alias);
}
send(serverCert);
}
......@@ -544,8 +552,12 @@ public class ServerHandshakeImpl extends HandshakeProtocol {
|| parameters.getNeedClientAuth()) {
X509Certificate[] accepted;
try {
X509TrustManager tm = parameters.getTrustManager();
accepted = tm.getAcceptedIssuers();
X509TrustManager tm = parameters.getX509TrustManager();
if (tm == null) {
accepted = new X509Certificate[0];
} else {
accepted = tm.getAcceptedIssuers();
}
} catch (ClassCastException e) {
// don't send certificateRequest
break certRequest;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment