真正执行证书合法性验证的还不是 NetworkSecurityTrustManager
,而是 TrustManagerImpl
(位于 external/conscrypt/src/platform/java/org/conscrypt/TrustManagerImpl.java
),由 NetworkSecurityTrustManager
的定义(位于frameworks/base/core/java/android/security/net/config/NetworkSecurityTrustManager.java
)不难看出这一点:
public NetworkSecurityTrustManager(NetworkSecurityConfig config) {
if (config == null) {
throw new NullPointerException("config must not be null");
}
mNetworkSecurityConfig = config;
try {
TrustedCertificateStoreAdapter certStore = new TrustedCertificateStoreAdapter(config);
// Provide an empty KeyStore since TrustManagerImpl doesn't support null KeyStores.
// TrustManagerImpl will use certStore to lookup certificates.
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null);
mDelegate = new TrustManagerImpl(store, null, certStore);
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e);
}
}
. . . . . .
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType)
throws CertificateException {
checkServerTrusted(certs, authType, (String) null);
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType, Socket socket)
throws CertificateException {
List<X509Certificate> trustedChain =
mDelegate.getTrustedChainForServer(certs, authType, socket);
checkPins(trustedChain);
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType, SSLEngine engine)
throws CertificateException {
List<X509Certificate> trustedChain =
mDelegate.getTrustedChainForServer(certs, authType, engine);
checkPins(trustedChain);
}
/**
* 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 host) throws CertificateException {
List<X509Certificate> trustedChain = mDelegate.checkServerTrusted(certs, authType, host);
checkPins(trustedChain);
return trustedChain;
}
private void checkPins(List<X509Certificate> chain) throws CertificateException {
PinSet pinSet = mNetworkSecurityConfig.getPins();
if (pinSet.pins.isEmpty()
|| System.currentTimeMillis() > pinSet.expirationTime
|| !isPinningEnforced(chain)) {
return;
}
Set<String> pinAlgorithms = pinSet.getPinAlgorithms();
Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>(
pinAlgorithms.size());
for (int i = chain.size() - 1; i >= 0 ; i--) {
X509Certificate cert = chain.get(i);
byte[] encodedSPKI = cert.getPublicKey().getEncoded();
for (String algorithm : pinAlgorithms) {
MessageDigest md = digestMap.get(algorithm);
if (md == null) {
try {
md = MessageDigest.getInstance(algorithm);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
digestMap.put(algorithm, md);
}
if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) {
return;
}
}
}
// TODO: Throw a subclass of CertificateException which indicates a pinning failure.
throw new CertificateException("Pin verification failed");
}
public class TrustedCertificateStoreAdapter extends TrustedCertificateStore {
private final NetworkSecurityConfig mConfig;
public TrustedCertificateStoreAdapter(NetworkSecurityConfig config) {
mConfig = config;
}
@Override
public X509Certificate findIssuer(X509Certificate cert) {
TrustAnchor anchor = mConfig.findTrustAnchorByIssuerAndSignature(cert);
if (anchor == null) {
return null;
}
return anchor.certificate;
}
@Override
public Set<X509Certificate> findAllIssuers(X509Certificate cert) {
return mConfig.findAllCertificatesByIssuerAndSignature(cert);
}
@Override
public X509Certificate getTrustAnchor(X509Certificate cert) {
TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
if (anchor == null) {
return null;
}
return anchor.certificate;
}
@Override
public boolean isUserAddedCertificate(X509Certificate cert) {
// isUserAddedCertificate is used only for pinning overrides, so use overridesPins here.
TrustAnchor anchor = mConfig.findTrustAnchorBySubjectAndPublicKey(cert);
if (anchor == null) {
return false;
}
return anchor.overridesPins;
}
不难看出 Android 中 Java 层证书验证的过程如下图所示:
OpenSSLSocketImpl.startHandshake()
和 NativeCrypto.SSL_do_handshake()
执行完整的 SSL/TLS 握手过程。证书合法性验证作为 SSL/TLS 握手的一个重要步骤,通过本地层调用的 Java 层的回调方法 SSLHandshakeCallbacks.verifyCertificateChain()
完成,OpenSSLSocketImpl
实现这一回调。OpenSSLSocketImpl.verifyCertificateChain()
、Platform.checkServerTrusted()
、RootTrustManager.checkServerTrusted()
和NetworkSecurityTrustManager.checkServerTrusted()
用于将真正的根据系统根证书库执行证书合法性验证的 TrustManagerImpl
和 SSL/TLS 握手过程粘起来。OpenSSLSocketFactoryImpl
将 OpenSSLSocketImpl
和 SSLParametersImpl
粘起来。SSLParametersImpl
将 OpenSSLSocketImpl
和 RootTrustManager
粘起来。
NetworkSecurityConfig
将 RootTrustManager
和 NetworkSecurityTrustManager
粘起来。NetworkSecurityConfig
、NetworkSecurityTrustManager
和 TrustedCertificateStoreAdapter
将 TrustManagerImpl
和管理系统根证书库的 SystemCertificateSource
粘起来。
TrustManagerImpl
是证书合法性验证的核心,它会查找系统根证书库,并对服务端证书的合法性做验证。
这个过程的调用栈如下:
com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted()
android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted()
android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted()
android.security.net.config.RootTrustManager.checkServerTrusted()
com.android.org.conscrypt.Platform.checkServerTrusted()
com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain()
com.android.org.conscrypt.NativeCrypto.SSL_do_handshake()
com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake()
com.android.okhttp.Connection.connectTls()
还有两个问题,一是 SSLParametersImpl
是如何找到的 RootTrustManager
;二是如何定制或者影响证书合法性的验证过程。
Java 加密体系架构(JCA)是一个非常灵活的架构,它的整体结构如下图:
Java 应用程序通过接口层访问加密服务,接口层的组成包括 JAAS(Java Authentication Authorization Service,Java验证和授权API)、JSSE(Java Secure Socket Extension,Java 安全 套接字扩展)、JGSS(Java Generic Security Service )和 CertPath等。具体的组件如我们前面看到的 CertificateFactory
、TrustManagerFactory
和 SSLSocketFactory
等。
JCA 还定义了一组加密服务 Provider 接口,如 javax.net.ssl.SSLContextSpi
和 javax.net.ssl.TrustManagerFactorySpi
等。加密服务的实现者实现这些接口,并通过 java.security.Security
提供的接口注册进 JCA 框架。
对于 Android 系统来说,TrustManagerFactory
加密服务的注册是在 ActivityThread
的 handleBindApplication()
中做的,相关代码(位于 frameworks/base/core/java/android/app/ActivityThread.java
)如下:
// Install the Network Security Config Provider. This must happen before the application
// code is loaded to prevent issues with instances of TLS objects being created before
// the provider is installed.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "NetworkSecurityConfigProvider.install");
NetworkSecurityConfigProvider.install(appContext);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
NetworkSecurityConfigProvider
类的定义(位于 frameworks/base/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
)如下:
package android.security.net.config;
import android.content.Context;
import java.security.Security;
import java.security.Provider;
/** @hide */
public final class NetworkSecurityConfigProvider extends Provider {
private static final String PREFIX =
NetworkSecurityConfigProvider.class.getPackage().getName() + ".";
public NetworkSecurityConfigProvider() {
// TODO: More clever name than this
super("AndroidNSSP", 1.0, "Android Network Security Policy Provider");
put("TrustManagerFactory.PKIX", PREFIX + "RootTrustManagerFactorySpi");
put("Alg.Alias.TrustManagerFactory.X509", "PKIX");
}
public static void install(Context context) {
ApplicationConfig config = new ApplicationConfig(new ManifestConfigSource(context));
ApplicationConfig.setDefaultInstance(config);
int pos = Security.insertProviderAt(new NetworkSecurityConfigProvider(), 1);
if (pos != 1) {
throw new RuntimeException("Failed to install provider as highest priority provider."
+ " Provider was installed at position " + pos);
}
libcore.net.NetworkSecurityPolicy.setInstance(new ConfigNetworkSecurityPolicy(config));
}
}
在 NetworkSecurityConfigProvider.install()
方法中,通过 Security.insertProviderAt()
将 NetworkSecurityConfigProvider
注册进 JCA 框架中。从 NetworkSecurityConfigProvider
的构造函数可以看到,它将 android.security.net.config.RootTrustManagerFactorySpi
带进 JCA 框架。
android.security.net.config.RootTrustManagerFactorySpi
的定义(位于 frameworks/base/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
)如下:
package android.security.net.config;
import android.util.Pair;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.util.Set;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.TrustManagerFactorySpi;
import com.android.internal.annotations.VisibleForTesting;
/** @hide */
public class RootTrustManagerFactorySpi extends TrustManagerFactorySpi {
private ApplicationConfig mApplicationConfig;
private NetworkSecurityConfig mConfig;
@Override
public void engineInit(ManagerFactoryParameters spec)
throws InvalidAlgorithmParameterException {
if (!(spec instanceof ApplicationConfigParameters)) {
throw new InvalidAlgorithmParameterException("Unsupported spec: " + spec + ". Only "
+ ApplicationConfigParameters.class.getName() + " supported");
}
mApplicationConfig = ((ApplicationConfigParameters) spec).config;
}
@Override
public void engineInit(KeyStore ks) throws KeyStoreException {
if (ks != null) {
mApplicationConfig = new ApplicationConfig(new KeyStoreConfigSource(ks));
} else {
mApplicationConfig = ApplicationConfig.getDefaultInstance();
}
}
@Override
public TrustManager[] engineGetTrustManagers() {
if (mApplicationConfig == null) {
throw new IllegalStateException("TrustManagerFactory not initialized");
}
return new TrustManager[] { mApplicationConfig.getTrustManager() };
}
@VisibleForTesting
public static final class ApplicationConfigParameters implements ManagerFactoryParameters {
public final ApplicationConfig config;
public ApplicationConfigParameters(ApplicationConfig config) {
this.config = config;
}
}
}
RootTrustManagerFactorySpi
的 TrustManager
来自于 ApplicationConfig
,ApplicationConfig
中 TrustManager
相关的代码(位于 frameworks/base/core/java/android/security/net/config/ApplicationConfig.java
)如下:
public final class ApplicationConfig {
private static ApplicationConfig sInstance;
private static Object sLock = new Object();
private Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
private NetworkSecurityConfig mDefaultConfig;
private X509TrustManager mTrustManager;
. . . . . .
/**
* Returns the {@link X509TrustManager} that implements the checking of trust anchors and
* certificate pinning based on this configuration.
*/
public X509TrustManager getTrustManager() {
ensureInitialized();
return mTrustManager;
}
. . . . . .
private void ensureInitialized() {
synchronized(mLock) {
if (mInitialized) {
return;
}
mConfigs = mConfigSource.getPerDomainConfigs();
mDefaultConfig = mConfigSource.getDefaultConfig();
mConfigSource = null;
mTrustManager = new RootTrustManager(this);
mInitialized = true;
}
}
ApplicationConfig
的 TrustManager
是 RootTrustManager
。
再来看 JCA 接口层的 javax.net.ssl.TrustManagerFactory
的定义:
public class TrustManagerFactory {
// The provider
private Provider provider;
// The provider implementation (delegate)
private TrustManagerFactorySpi factorySpi;
// The name of the trust management algorithm.
private String algorithm;
. . . . . .
public final static String getDefaultAlgorithm() {
String type;
type = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return Security.getProperty(
"ssl.TrustManagerFactory.algorithm");
}
});
if (type == null) {
type = "SunX509";
}
return type;
}
. . . . . .
/**
* Creates a TrustManagerFactory object.
*
* @param factorySpi the delegate
* @param provider the provider
* @param algorithm the algorithm
*/
protected TrustManagerFactory(TrustManagerFactorySpi factorySpi,
Provider provider, String algorithm) {
this.factorySpi = factorySpi;
this.provider = provider;
this.algorithm = algorithm;
}
. . . . . .
public static final TrustManagerFactory getInstance(String algorithm)
throws NoSuchAlgorithmException {
GetInstance.Instance instance = GetInstance.getInstance
("TrustManagerFactory", TrustManagerFactorySpi.class,
algorithm);
return new TrustManagerFactory((TrustManagerFactorySpi)instance.impl,
instance.provider, algorithm);
}
. . . . . .
public final void init(KeyStore ks) throws KeyStoreException {
factorySpi.engineInit(ks);
}
/**
* Initializes this factory with a source of provider-specific
* trust material.
* <P>
* In some cases, initialization parameters other than a keystore
* may be needed by a provider. Users of that particular provider
* are expected to pass an implementation of the appropriate
* <CODE>ManagerFactoryParameters</CODE> as defined by the
* provider. The provider can then call the specified methods in
* the <CODE>ManagerFactoryParameters</CODE> implementation to obtain the
* needed information.
*
* @param spec an implementation of a provider-specific parameter
* specification
* @throws InvalidAlgorithmParameterException if an error is
* encountered
*/
public final void init(ManagerFactoryParameters spec) throws
InvalidAlgorithmParameterException {
factorySpi.engineInit(spec);
}
/**
* Returns one trust manager for each type of trust material.
*
* @throws IllegalStateException if the factory is not initialized.
*
* @return the trust managers
*/
public final TrustManager[] getTrustManagers() {
return factorySpi.engineGetTrustManagers();
}
TrustManagerFactory
通过 JCA 框架提供的 sun.security.jca.GetInstance
找到注册的 javax.net.ssl.TrustManagerFactorySpi
。应用程序通过 javax.net.ssl.TrustManagerFactory
-> android.security.net.config.RootTrustManagerFactorySpi
-> android.security.net.config.ApplicationConfig
得到 android.security.net.config.RootTrustManager
,即 X509TrustManager
。
网易云新用户大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者韩鹏飞授权发布。