美文网首页
[转]Android 根证书管理与证书验证(3)

[转]Android 根证书管理与证书验证(3)

作者: 原野大神 | 来源:发表于2020-10-28 09:50 被阅读0次

    转自 https://sq.163yun.com/blog/article/188782804680855552

    真正执行证书合法性验证的还不是 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 层证书验证的过程如下图所示:

    image

    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)是一个非常灵活的架构,它的整体结构如下图:

    image

    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 根证书管理与证书验证(3)

          本文链接:https://www.haomeiwen.com/subject/fgxfvktx.html