[转]Android 根证书管理与证书验证(2)
转自 https://sq.163yun.com/blog/article/188782427227049984
OpenSSLSocketImpl 的 verifyCertificateChain() 从 sslParameters 获得 X509TrustManager,然后在 Platform.checkServerTrusted() (com.android.org.conscrypt.Platform,位于 external/conscrypt/src/compat/java/org/conscrypt/Platform.java)中执行服务端证书合法有效性的检查:
public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
String authType, OpenSSLSocketImpl socket) throws CertificateException {
if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
&& !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
socket.getHandshakeSession().getPeerHost())) {
tm.checkServerTrusted(chain, authType);
}
}
Platform.checkServerTrusted() 通过执行 X509TrustManager 的 checkServerTrusted() 方法执行证书有合法性检查。
X509TrustManager 来自于 OpenSSLSocketImpl 的 sslParameters,那 sslParameters 又来自于哪里呢?OpenSSLSocketImpl 的 sslParameters 由对象的创建者传入:
public class OpenSSLSocketImpl
extends javax.net.ssl.SSLSocket
implements NativeCrypto.SSLHandshakeCallbacks, SSLParametersImpl.AliasChooser,
SSLParametersImpl.PSKCallbacks {
. . . . . .
private final SSLParametersImpl sslParameters;
. . . . . .
protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
this.socket = this;
this.peerHostname = null;
this.peerPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
}
protected OpenSSLSocketImpl(String hostname, int port, SSLParametersImpl sslParameters)
throws IOException {
super(hostname, port);
this.socket = this;
this.peerHostname = hostname;
this.peerPort = port;
this.autoClose = false;
this.sslParameters = sslParameters;
}
protected OpenSSLSocketImpl(InetAddress address, int port, SSLParametersImpl sslParameters)
throws IOException {
super(address, port);
this.socket = this;
this.peerHostname = null;
this.peerPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
}
protected OpenSSLSocketImpl(String hostname, int port,
InetAddress clientAddress, int clientPort,
SSLParametersImpl sslParameters) throws IOException {
super(hostname, port, clientAddress, clientPort);
this.socket = this;
this.peerHostname = hostname;
this.peerPort = port;
this.autoClose = false;
this.sslParameters = sslParameters;
}
protected OpenSSLSocketImpl(InetAddress address, int port,
InetAddress clientAddress, int clientPort,
SSLParametersImpl sslParameters) throws IOException {
super(address, port, clientAddress, clientPort);
this.socket = this;
this.peerHostname = null;
this.peerPort = -1;
this.autoClose = false;
this.sslParameters = sslParameters;
}
/**
* Create an SSL socket that wraps another socket. Invoked by
* OpenSSLSocketImplWrapper constructor.
*/
protected OpenSSLSocketImpl(Socket socket, String hostname, int port,
boolean autoClose, SSLParametersImpl sslParameters) throws IOException {
this.socket = socket;
this.peerHostname = hostname;
this.peerPort = port;
this.autoClose = autoClose;
this.sslParameters = sslParameters;
// this.timeout is not set intentionally.
// OpenSSLSocketImplWrapper.getSoTimeout will delegate timeout
// to wrapped socket
}
也就是说,OpenSSLSocketImpl 的 sslParameters 来自于 javax.net.ssl.SSLSocketFactory,即 OpenSSLSocketFactoryImpl。OpenSSLSocketFactoryImpl 定义(位于 external/conscrypt/src/main/java/org/conscrypt/OpenSSLSocketFactoryImpl.java)如下:
package org.conscrypt;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
public class OpenSSLSocketFactoryImpl extends javax.net.ssl.SSLSocketFactory {
private final SSLParametersImpl sslParameters;
private final IOException instantiationException;
public OpenSSLSocketFactoryImpl() {
SSLParametersImpl sslParametersLocal = null;
IOException instantiationExceptionLocal = null;
try {
sslParametersLocal = SSLParametersImpl.getDefault();
} catch (KeyManagementException e) {
instantiationExceptionLocal = new IOException("Delayed instantiation exception:");
instantiationExceptionLocal.initCause(e);
}
this.sslParameters = sslParametersLocal;
this.instantiationException = instantiationExceptionLocal;
}
public OpenSSLSocketFactoryImpl(SSLParametersImpl sslParameters) {
this.sslParameters = sslParameters;
this.instantiationException = null;
}
@Override
public String[] getDefaultCipherSuites() {
return sslParameters.getEnabledCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return NativeCrypto.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
if (instantiationException != null) {
throw instantiationException;
}
return new OpenSSLSocketImpl((SSLParametersImpl) sslParameters.clone());
}
@Override
public Socket createSocket(String hostname, int port) throws IOException, UnknownHostException {
return new OpenSSLSocketImpl(hostname, port, (SSLParametersImpl) sslParameters.clone());
}
@Override
public Socket createSocket(String hostname, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return new OpenSSLSocketImpl(hostname,
port,
localHost,
localPort,
(SSLParametersImpl) sslParameters.clone());
}
@Override
public Socket createSocket(InetAddress address, int port) throws IOException {
return new OpenSSLSocketImpl(address, port, (SSLParametersImpl) sslParameters.clone());
}
@Override
public Socket createSocket(InetAddress address,
int port,
InetAddress localAddress,
int localPort)
throws IOException {
return new OpenSSLSocketImpl(address,
port,
localAddress,
localPort,
(SSLParametersImpl) sslParameters.clone());
}
@Override
public Socket createSocket(Socket s, String hostname, int port, boolean autoClose)
throws IOException {
return new OpenSSLSocketImplWrapper(s,
hostname,
port,
autoClose,
(SSLParametersImpl) sslParameters.clone());
}
}
OpenSSLSocketImpl 最主要的职责,即是将 SSL/TLS 参数 SSLParametersImpl 与 SSLSocket 粘起来。主要来看默认情况下 SSLParametersImpl 的 X509TrustManager 是什么(位于external/conscrypt/src/main/java/org/conscrypt/SSLParametersImpl.java ):
/**
* Initializes the parameters. Naturally this constructor is used
* in SSLContextImpl.engineInit method which directly passes its
* parameters. In other words this constructor holds all
* the functionality provided by SSLContext.init method.
* See {@link javax.net.ssl.SSLContext#init(KeyManager[],TrustManager[],
* SecureRandom)} for more information
*/
protected SSLParametersImpl(KeyManager[] kms, TrustManager[] tms,
SecureRandom sr, ClientSessionContext clientSessionContext,
ServerSessionContext serverSessionContext, String[] protocols)
throws KeyManagementException {
this.serverSessionContext = serverSessionContext;
this.clientSessionContext = clientSessionContext;
// initialize key managers
if (kms == null) {
x509KeyManager = getDefaultX509KeyManager();
// There's no default PSK key manager
pskKeyManager = null;
} else {
x509KeyManager = findFirstX509KeyManager(kms);
pskKeyManager = findFirstPSKKeyManager(kms);
}
// initialize x509TrustManager
if (tms == null) {
x509TrustManager = getDefaultX509TrustManager();
} else {
x509TrustManager = findFirstX509TrustManager(tms);
}
// initialize secure random
// We simply use the SecureRandom passed in by the caller. If it's
// null, we don't replace it by a new instance. The native code below
// then directly accesses /dev/urandom. Not the most elegant solution,
// but faster than going through the SecureRandom object.
secureRandom = sr;
// initialize the list of cipher suites and protocols enabled by default
enabledProtocols = NativeCrypto.checkEnabledProtocols(
protocols == null ? NativeCrypto.DEFAULT_PROTOCOLS : protocols).clone();
boolean x509CipherSuitesNeeded = (x509KeyManager != null) || (x509TrustManager != null);
boolean pskCipherSuitesNeeded = pskKeyManager != null;
enabledCipherSuites = getDefaultCipherSuites(
x509CipherSuitesNeeded, pskCipherSuitesNeeded);
}
protected static SSLParametersImpl getDefault() throws KeyManagementException {
SSLParametersImpl result = defaultParameters;
if (result == null) {
// single-check idiom
defaultParameters = result = new SSLParametersImpl(null,
null,
null,
new ClientSessionContext(),
new ServerSessionContext(),
null);
}
return (SSLParametersImpl) result.clone();
}
. . . . . .
/**
* @return X.509 trust manager or {@code null} for none.
/
protected X509TrustManager getX509TrustManager() {
return x509TrustManager;
}
. . . . . .
/*
* Gets the default X.509 trust manager.
* <p>
* TODO: Move this to a published API under dalvik.system.
*/
public static X509TrustManager getDefaultX509TrustManager()
throws KeyManagementException {
X509TrustManager result = defaultX509TrustManager;
if (result == null) {
// single-check idiom
defaultX509TrustManager = result = createDefaultX509TrustManager();
}
return result;
}
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 = 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);
} catch (KeyStoreException e) {
throw new KeyManagementException(e);
}
}
将 createDefaultX509TrustManager() 的代码复制到我们的应用程序中,就像下面这样:
private X509TrustManager systemDefaultTrustManager() {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
} catch (GeneralSecurityException e) {
throw new AssertionError(); // The system has no TLS. Just give up.
}
}
在应用程序执行时打断点,借助于 Android Studio 确认系统默认的 X509TrustManager 是什么,不难确认,它是 android.security.net.config.RootTrustManager。android.security.net.config.RootTrustManager 的 checkServerTrusted() 定义(位于 frameworks/base/core/java/android/security/net/config/RootTrustManager.java)如下:
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)
throws CertificateException {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
SSLSession session = sslSocket.getHandshakeSession();
if (session == null) {
throw new CertificateException("Not in handshake; no session available");
}
String host = session.getPeerHost();
NetworkSecurityConfig config = mConfig.getConfigForHostname(host);
config.getTrustManager().checkServerTrusted(certs, authType, socket);
} else {
// Not an SSLSocket, use the hostname unaware checkServerTrusted.
checkServerTrusted(certs, authType);
}
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
throws CertificateException {
SSLSession session = engine.getHandshakeSession();
if (session == null) {
throw new CertificateException("Not in handshake; no session available");
}
String host = session.getPeerHost();
NetworkSecurityConfig config = mConfig.getConfigForHostname(host);
config.getTrustManager().checkServerTrusted(certs, authType, engine);
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
if (mConfig.hasPerDomainConfigs()) {
throw new CertificateException(
"Domain specific configurations require that hostname aware"
+ " checkServerTrusted(X509Certificate[], String, String) is used");
}
NetworkSecurityConfig config = mConfig.getConfigForHostname("");
config.getTrustManager().checkServerTrusted(certs, authType);
}
/**
* Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
* This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
* modify without modifying those callers.
*/
public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
String hostname) throws CertificateException {
if (hostname == null && mConfig.hasPerDomainConfigs()) {
throw new CertificateException(
"Domain specific configurations require that the hostname be provided");
}
NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
}
NetworkSecurityConfig 的 getTrustManager() 定义(位于 frameworks/base/core/java/android/security/net/config/NetworkSecurityConfig.java)如下:
public NetworkSecurityTrustManager getTrustManager() {
synchronized(mTrustManagerLock) {
if (mTrustManager == null) {
mTrustManager = new NetworkSecurityTrustManager(this);
}
return mTrustManager;
}
}
NetworkSecurityConfig 将管根证书库的组件 SystemCertificateSource 、 UserCertificateSource 和执行证书合法性验证的 NetworkSecurityTrustManager 粘起来:
public static final Builder getDefaultBuilder(int targetSdkVersion) {
Builder builder = new Builder()
.setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
.setHstsEnforced(DEFAULT_HSTS_ENFORCED)
// System certificate store, does not bypass static pins.
.addCertificatesEntryRef(
new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
// Applications targeting N and above must opt in into trusting the user added certificate
// store.
if (targetSdkVersion <= Build.VERSION_CODES.M) {
// User certificate store, does not bypass static pins.
builder.addCertificatesEntryRef(
new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
}
return builder;
}
同时 NetworkSecurityConfig 还提供了一些根据特定条件查找根证书的操作:
public Set<TrustAnchor> getTrustAnchors() {
synchronized (mAnchorsLock) {
if (mAnchors != null) {
return mAnchors;
}
// Merge trust anchors based on the X509Certificate.
// If we see the same certificate in two TrustAnchors, one with overridesPins and one
// without, the one with overridesPins wins.
// Because mCertificatesEntryRefs is sorted with all overridesPins anchors coming first
// this can be simplified to just using the first occurrence of a certificate.
Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
Set<TrustAnchor> anchors = ref.getTrustAnchors();
for (TrustAnchor anchor : anchors) {
X509Certificate cert = anchor.certificate;
if (!anchorMap.containsKey(cert)) {
anchorMap.put(cert, anchor);
}
}
}
ArraySet<TrustAnchor> anchors = new ArraySet<TrustAnchor>(anchorMap.size());
anchors.addAll(anchorMap.values());
mAnchors = anchors;
return mAnchors;
}
}
. . . . . .
public NetworkSecurityTrustManager getTrustManager() {
synchronized(mTrustManagerLock) {
if (mTrustManager == null) {
mTrustManager = new NetworkSecurityTrustManager(this);
}
return mTrustManager;
}
}
/** @hide */
public TrustAnchor findTrustAnchorBySubjectAndPublicKey(X509Certificate cert) {
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
TrustAnchor anchor = ref.findBySubjectAndPublicKey(cert);
if (anchor != null) {
return anchor;
}
}
return null;
}
/** @hide */
public TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate cert) {
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
TrustAnchor anchor = ref.findByIssuerAndSignature(cert);
if (anchor != null) {
return anchor;
}
}
return null;
}
/** @hide */
public Set<X509Certificate> findAllCertificatesByIssuerAndSignature(X509Certificate cert) {
Set<X509Certificate> certs = new ArraySet<X509Certificate>();
for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
certs.addAll(ref.findAllCertificatesByIssuerAndSignature(cert));
}
return certs;
}