Android 根证书管理与证书验证(3)

达芬奇密码2018-08-17 13:44

真正执行证书合法性验证的还不是 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 握手过程粘起来。OpenSSLSocketFactoryImplOpenSSLSocketImplSSLParametersImpl 粘起来。SSLParametersImplOpenSSLSocketImplRootTrustManager 粘起来。

NetworkSecurityConfigRootTrustManagerNetworkSecurityTrustManager 粘起来。NetworkSecurityConfigNetworkSecurityTrustManagerTrustedCertificateStoreAdapterTrustManagerImpl 和管理系统根证书库的 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;二是如何定制或者影响证书合法性的验证过程。

TrustManager 的查找

Java 加密体系架构(JCA)是一个非常灵活的架构,它的整体结构如下图:

Java 应用程序通过接口层访问加密服务,接口层的组成包括 JAAS(Java Authentication Authorization Service,Java验证和授权API)、JSSE(Java Secure Socket Extension,Java 安全 套接字扩展)、JGSS(Java Generic Security Service )和 CertPath等。具体的组件如我们前面看到的 CertificateFactoryTrustManagerFactorySSLSocketFactory 等。

JCA 还定义了一组加密服务 Provider 接口,如 javax.net.ssl.SSLContextSpijavax.net.ssl.TrustManagerFactorySpi 等。加密服务的实现者实现这些接口,并通过 java.security.Security 提供的接口注册进 JCA 框架。

对于 Android 系统来说,TrustManagerFactory 加密服务的注册是在 ActivityThreadhandleBindApplication() 中做的,相关代码(位于 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;
        }
    }
}

RootTrustManagerFactorySpiTrustManager 来自于 ApplicationConfigApplicationConfigTrustManager 相关的代码(位于 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;
        }
    }

ApplicationConfigTrustManagerRootTrustManager

再来看 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



相关阅读:Android 根证书管理与证书验证(1)

Android 根证书管理与证书验证(2)

Android 根证书管理与证书验证(3)

Android 根证书管理与证书验证(4)


网易云新用户大礼包:https://www.163yun.com/gift

本文来自网易实践者社区,经作者韩鹏飞授权发布。