0%

Android 证书安装与使用的调用分析

最近有个需求需要定制一个证书安装接口提供给业务应用:实现应用能够通过接口安装证书并且无需设置密码以及鉴权放行。

一般情况在 Android 系统中安装 CA 证书有两种方法:

  • 通过设置应用通过设置应用安装需要用户手动操作,并且安装过程需要满足用户设置了锁屏密码。
  • 通过 MDM 接口
    还有一种方式是应用调用 MDM 接口,通过 installCaCert 系统接口安装证书;这种方式可以实现用户不感知,适合编程的方式。

程序调用接口是最好的选择,先来把原有的证书安装使用逻辑撸一下。

本文分析路径基于 AOSPAndroid 14 正式版源码。

证书安装调用分析

从 SDK 出发

应用调用功能接口都是 Android SDK 提供的,直接从SDK作为切入点吧。

DevicePolicyManager

安装证书的入口是 DevicePolicyManager 类作为入口,接口如下:

public class DevicePolicyManager {
    ...
    public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
        ...
        return mService.installCaCert(admin, mContext.getPackageName(), certBuffer);
        ...
    }
    ...
}

这个接口的逻辑很简单,直接将证书发送到 mService,这个 mService 是一个名为 IDevicePolicyManageraidl 接口,IDevicePolicyManagerDevicePolicyManagerFramework 中的定义的接口。在构造 DevicePolicyManagerSystemServiceRegistry 会从 ServiceManager 取得 IDevicePolicyManager 的实例作为构造参数传入。等同于下一步直接进入 Framework

进入 Framework 层面

IDevicePolicyManager

这个 IDevicePolicyManager 只是一个 aidl 接口文件,模板代码没得什么具体逻辑,直接去看它的实现类。

interface IDevicePolicyManager {
    ...
    boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer);
    ...
}

DevicePolicyManagerService

于是发现了 DevicePolicyManagerService,它继承 IDevicePolicyManager.Stub。是 IDevicePolicyManager 接口的实现类。

public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    @Override
    public boolean installCaCert(ComponentName admin, String callerPackage, byte[] certBuffer) {
        ...
        final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
        Preconditions.checkCallAuthorization(canManageCaCerts(caller));
        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_CA_CERT);

        final String alias = mInjector.binderWithCleanCallingIdentity(() -> {
            String installedAlias = mCertificateMonitor.installCaCert(
                    caller.getUserHandle(), certBuffer);
            ...
            return installedAlias;
        });
        ...
        synchronized (getLockObject()) {
            getUserData(caller.getUserId()).mOwnerInstalledCaCerts.add(alias);
            saveSettingsLocked(caller.getUserId());
        }
        return true;
    }
    ...
}

流程细节:

  1. getCallerIdentity 校验 admincallerPackage 是否匹配,返回包装信息。
  2. canManageCaCerts 检查 caller 是否激活 MDM,是否是 ProfileOwner,是否具有 MANAGE_CA_CERTIFICATES 权限。
  3. checkCanExecuteOrThrowUnsafe 检查设备状态,是否设置了安全密钥(锁屏等)。
  4. binderWithCleanCallingIdentity 语法糖,用于设置 CallingIdentity

进入实现方法先是检查各种权限和安全状态,然后安装证书,最后存盘。

CertificateMonitor

CertificateMonitorDevicePolicyManagerService 的一个功能封装类,用于安装证书,监听系统证书变化事件,维护系统证书状态。

public class CertificateMonitor {
      public CertificateMonitor(final DevicePolicyManagerService service,
            final DevicePolicyManagerService.Injector injector, final Handler handler) {
        ...
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_USER_STARTED);
        filter.addAction(Intent.ACTION_USER_UNLOCKED);
        filter.addAction(KeyChain.ACTION_TRUST_STORE_CHANGED);
        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
        mInjector.mContext.registerReceiverAsUser(
                mRootCaReceiver, UserHandle.ALL, filter, null, mHandler);
    }
    ...
    public String installCaCert(final UserHandle userHandle, byte[] certBuffer) {
        // Convert certificate data from X509 format to PEM.
        byte[] pemCert;
        try {
            X509Certificate cert = parseCert(certBuffer);
            pemCert = Credentials.convertToPem(cert);
        } catch (CertificateException | IOException ce) {
            Slogf.e(LOG_TAG, "Problem converting cert", ce);
            return null;
        }

        try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) {
            return keyChainConnection.getService().installCaCertificate(pemCert);
        } catch (RemoteException e) {
            Slogf.e(LOG_TAG, "installCaCertsToKeyChain(): ", e);
        } catch (InterruptedException e1) {
            Slogf.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1);
            Thread.currentThread().interrupt();
        }
        return null;
    }
    ...
}

可以看到 CertificateMonitor 构造函数中注册了用户解锁、可信存储变化等事件广播,和它的具体功能匹配。

DevicePolicyManagerService.Injector

mInjectorDevicePolicyManagerService 的一个内部类,用于给类似 CertificateMonitor 这样的功能子类注入各种实例。

public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    ...
    static class Injector {
        ...
        KeyChainConnection keyChainBindAsUser(UserHandle user) throws InterruptedException {
            return KeyChain.bindAsUser(mContext, user);
        }
        ...
    }
    ...
}

调来调去原来是这个 KeyChain 在处理证书安装的具体逻辑,bindAsUser 看这个方法名和入参大概可以看出每个用户都是可以安装不同的证书的。

KeyChain.bindAsUser

这个方法用来取得名为 KeyChainConnection 的包装类。

public final class KeyChain {
    public static KeyChainConnection bindAsUser(@NonNull Context context, @Nullable Handler handler,
            UserHandle user) throws InterruptedException {

        if (context == null) {
            throw new NullPointerException("context == null");
        }
        if (handler == null) {
            ensureNotOnMainThread(context);
        }
        if (!UserManager.get(context).isUserUnlocked(user)) {
            throw new IllegalStateException("User must be unlocked");
        }

        final CountDownLatch countDownLatch = new CountDownLatch(1);
        final AtomicReference<IKeyChainService> keyChainService = new AtomicReference<>();
        ServiceConnection keyChainServiceConnection = new ServiceConnection() {
            volatile boolean mConnectedAtLeastOnce = false;
            @Override public void onServiceConnected(ComponentName name, IBinder service) {
                if (!mConnectedAtLeastOnce) {
                    mConnectedAtLeastOnce = true;
                    keyChainService.set(
                            IKeyChainService.Stub.asInterface(Binder.allowBlocking(service)));
                    countDownLatch.countDown();
                }
            }
            @Override public void onBindingDied(ComponentName name) {
                if (!mConnectedAtLeastOnce) {
                    mConnectedAtLeastOnce = true;
                    countDownLatch.countDown();
                }
            }
            @Override public void onServiceDisconnected(ComponentName name) {}
        };
        Intent intent = new Intent(IKeyChainService.class.getName());
        ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
        if (comp == null) {
            throw new AssertionError("could not resolve KeyChainService");
        }
        intent.setComponent(comp);
        final boolean bindSucceed;
        if (handler != null) {
            bindSucceed = context.bindServiceAsUser(
                    intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, handler, user);
        } else {
            bindSucceed = context.bindServiceAsUser(
                    intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user);
        }
        if (!bindSucceed) {
            context.unbindService(keyChainServiceConnection);
            throw new AssertionError("could not bind to KeyChainService");
        }
        countDownLatch.await();
        IKeyChainService service = keyChainService.get();
        if (service != null) {
            return new KeyChainConnection(context, keyChainServiceConnection, service);
        } else {
            context.unbindService(keyChainServiceConnection);
            throw new AssertionError("KeyChainService died while binding");
        }
    }
}

首先判断了用户解锁状态,这也是必然的。操作证书是相对用户的行为,修改的都是用户的私有数据,不解锁的话操作系统不能操作这些数据。

从这可以看出安装证书的实现还在一个 ACTIONIKeyChainService.class.getName()Service

KeyChain.KeyChainConnection

上一步骤中通过绑定 ACTIONIKeyChainService.class.getName()Service 得到 IKeyChainService 接口的实例。然后作为构造参数传入 KeyChainConnection 类。

public final class KeyChain {
    ...
    public static class KeyChainConnection implements Closeable {
        private final Context mContext;
        private final ServiceConnection mServiceConnection;
        private final IKeyChainService mService;
        protected KeyChainConnection(Context context,
                                     ServiceConnection serviceConnection,
                                     IKeyChainService service) {
            this.mContext = context;
            this.mServiceConnection = serviceConnection;
            this.mService = service;
        }
        @Override public void close() {
            mContext.unbindService(mServiceConnection);
        }

        /** returns the service binder. */
        public IKeyChainService getService() {
            return mService;
        }
    }
    ...
}

这个类就一个纯粹的包装类,存储 IKeyChainService 和提供 unbindService 的方法。综合起来还得回头看看 IKeyChainService.installCaCertificate() 是怎么处理的。

IKeyChainService

IKeyChainService 又是一个 aidl 接口文件,继续跟踪实现它的服务。

interface IKeyChainService {
    ...
    String installCaCertificate(in byte[] caCertificate);
    ...
}

深入 KeyChain 应用

属实没想到,Android 竟然把证书安装逻辑外放到一个应用里面,上一步找 IKeyChainService 时找了好一阵,属实意外。

KeyChainService

这是 Android 中一个系统预置的应用 KeyChain 的一个 Service,它实现了 IKeyChainService 的接口。也就是说安装证书的逻辑已经到它的管辖范围了。

public class KeyChainService extends IntentService {
    ...
    private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
        ...
    }
    ...
    @Override public IBinder onBind(Intent intent) {
        if (IKeyChainService.class.getName().equals(intent.getAction())) {
            return mIKeyChainService;
        }
        return null;
    }
}

这个类本身是一个 IntentService,但是它重写了 onBind 方法并且将内部成员变量 mIKeyChainService 返回了回去。

这个 mIKeyChainService 就是 IKeyChainService 的实现。

AndroidManifest.xml 中将这个 KeyChainServiceACTION 定义为 android.security.IKeyChainService。和 Framework 中匹配。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.keychain"
          android:sharedUserId="android.uid.system">
    ...
    <application>
        <service android:name="com.android.keychain.KeyChainService"
            android:exported="true">
            <intent-filter>
                <action android:name="android.security.IKeyChainService"/>
            </intent-filter>
        </service>
        ...
    </application>
</manifest>

这个应用是系统应用。这波 低耦合 实在是高!

再看 installCaCertificate 的具体实现:

public class KeyChainService extends IntentService {
    ...
    private final IKeyChainService.Stub mIKeyChainService = new IKeyChainService.Stub() {
        ...
        @Override
        public String installCaCertificate(byte[] caCertificate) {
            final CallerIdentity caller = getCaller();
            Preconditions.checkCallAuthorization(isSystemUid(caller) || isCertInstaller(caller),
                    MSG_NOT_SYSTEM_OR_CERT_INSTALLER);
            final String alias;
            String subject = null;
            final boolean isSecurityLoggingEnabled = mInjector.isSecurityLoggingEnabled();
            final X509Certificate cert;
            try {
                cert = parseCertificate(caCertificate);
                ...
                synchronized (mTrustedCertificateStore) {
                    mTrustedCertificateStore.installCertificate(cert);
                    alias = mTrustedCertificateStore.getCertificateAlias(cert);
                }
            } catch (IOException | CertificateException e) {
                ...
                throw new IllegalStateException(e);
            }
            ...
            broadcastLegacyStorageChange();
            broadcastTrustStoreChange();
            return alias;
        }
    }
    ...
    private void broadcastTrustStoreChange() {
        Intent intent = new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED);
        sendBroadcastAsUser(intent, UserHandle.of(UserHandle.myUserId()));
    }
    ...
}

大约是这么个流程:

  1. checkCallAuthorization 校验调用者身份和权限。
  2. parseCertificate 解析 caCertificate 中的内容为 X509Certificate
  3. 调用 TrustedCertificateStore.installCertificate() 安装证书。
  4. 广播可信存储变化事件。

正常情况下安装完一个用户证书,通知栏会弹出一个警告。就是因为这里触发的 KeyChain.ACTION_TRUST_STORE_CHANGED 事件广播被 CertificateMonitor 监听后,CertificateMonitor 判断新安装的证书有没有被用户 approveCaCert 的结果。

TrustedCertificateStore

继续看这个 TrustedCertificateStore,它的主要作用是管理系统内的证书文件。

TrustedCertificateStore 完整包名为 com.android.org.conscrypt.TrustedCertificateStore,它是 Android 引用 Bouncy Castle 并本地化后的库类。

它管理的证书存放于 /data/misc/user/0/cacerts-added//data/misc/user/0/cacerts-removed//system/etc/security/cacerts/ 目录,其中前两者是用户安装和删除(用户删除系统证书也在这里记录)的证书数据目录,后者是系统证书目录(只读)。路径里面的的 0 是用户ID,不同的用户路径不同。

用户路径在 ActivityThread 初始化时被设定:

public final class ActivityThread extends ClientTransactionHandler {
    public static void main(String[] args) {
        ...
        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);
        ...
    }
}

这也是为什么 DevicePolicyManager.uninstallCaCert() 需要先获得 alias 再传给 Framework:因为 uninstallCaCert 调用发生在用户应用层面,此时的上下文信息是用户而不是系统。

继续看 TrustedCertificateStore.installCertificate()

public class TrustedCertificateStore implements ConscryptCertStore {
    ...
    public void installCertificate(X509Certificate cert) throws IOException, CertificateException {
        if (cert == null) {
            throw new NullPointerException("cert == null");
        }
        File system = getCertificateFile(systemDir, cert);
        if (system.exists()) {
            File deleted = getCertificateFile(deletedDir, cert);
            if (deleted.exists()) {
                // we have a system cert that was marked deleted.
                // remove the deleted marker to expose the original
                if (!deleted.delete()) {
                    throw new IOException("Could not remove " + deleted);
                }
                return;
            }
            // otherwise we just have a dup of an existing system cert.
            // return taking no further action.
            return;
        }
        File user = getCertificateFile(addedDir, cert);
        if (user.exists()) {
            // we have an already installed user cert, bail.
            return;
        }
        // install the user cert
        writeCertificate(user, cert);
    }
    ...
    private void writeCertificate(File file, X509Certificate cert)
            throws IOException, CertificateException {
        File dir = file.getParentFile();
        dir.mkdirs();
        dir.setReadable(true, false);
        dir.setExecutable(true, false);
        OutputStream os = null;
        try {
            os = new FileOutputStream(file);
            os.write(cert.getEncoded());
        } finally {
            IoUtils.closeQuietly(os);
        }
        file.setReadable(true, false);
    }
    ...
}

流程如下:

  1. 检查是否是系统已经存在的证书,或者被用户删除的系统证书,防止重复安装。
  2. 判断是否重复安装。
  3. 写入证书要存储路径。

至此,安装流程到此结束!

整个流程虽然在经过了一个 KeyChina 的应用绕来绕去,但整体并不复杂。最终还是这个 TrustedCertificateStore 干了所有的事情。

证书使用调用分析

KeyStore

应用使用系统证书一般从 KeyStore 开始,来看一段经典代码:

public class InsecureEapNetworkHandler {
    ...
    private boolean isCertInTrustStore(X509Certificate rootCaCert) {
        try {
            // Get the Android trust store.
            KeyStore keystore = KeyStore.getInstance("AndroidCAStore");
            keystore.load(null);

            Enumeration<String> aliases = keystore.aliases();
            while (aliases.hasMoreElements()) {
                String alias = aliases.nextElement();
                X509Certificate trusted = (X509Certificate) keystore.getCertificate(alias);
                if (trusted.getSubjectDN().equals(rootCaCert.getSubjectDN())) {
                    // Check that the supplied cert was actually signed by the key we trust.
                    rootCaCert.verify(trusted.getPublicKey());
                    return true;
                }
            }
        } catch (Exception e) {
            // Fall through
            Log.e(TAG, e.getMessage());
        }
        // The certificate is not in the trust store.
        return false;
    }
}

这是 Android 系统中 WiFi 服务进行 EAP 校验的代码片段,这个方法的作用是校验 rootCaCert 是否是系统信任的证书。

典型的使用系统证书的场景:通过 KeyStore.getInstance("AndroidCAStore") 获得了系统的 KeyStore,其中存储了系统预置和用户安装的所有证书,通过 alias 来遍历设备证书。

像平时使用的 HTTPSFTPS 等协议访问证书的方式都差不多,只不过有的地方包装成了 TrustManagerFactory.init()

KeyStore.getInstance

继续再看一下 KeyStore 是如何创建实例的:

public class KeyStore {
    ...
    public static KeyStore getInstance(String type)
        throws KeyStoreException
    {
        try {
            Object[] objs = Security.getImpl(type, "KeyStore", (String)null);
            return new KeyStore((KeyStoreSpi)objs[0], (Provider)objs[1], type);
        } catch (NoSuchAlgorithmException nsae) {
            throw new KeyStoreException(type + " not found", nsae);
        } catch (NoSuchProviderException nspe) {
            throw new KeyStoreException(type + " not found", nspe);
        }
    }
    ...
    protected KeyStore(KeyStoreSpi keyStoreSpi, Provider provider, String type)
    {
        this.keyStoreSpi = keyStoreSpi;
        this.provider = provider;
        this.type = type;
    }
    ...
    public final Certificate getCertificate(String alias)
        throws KeyStoreException
    {
        if (!initialized) {
            throw new KeyStoreException("Uninitialized keystore");
        }
        return keyStoreSpi.engineGetCertificate(alias);
    }
}

构造 KeyStore 时通过 Security 获取到的一个叫做 KeyStoreSpi 的类,调用 KeyStore 的方法时也被中转到 KeyStoreSpi 中。

这个 KeyStoreSpi 是一个 Java 本身就存在的类,其原理是使用一种叫做 SPI(Service Provider Interface) 的服务发现机制。Android 的证书系统沿用了 Java 的设计,使得 Android 在不修改 Java 框架的情况下注入某些功能。

Security.getImpl(SPI)

现在看得到是从 Security 类中获得了一个叫 KeyStoreSpi 的类,那继续跟踪看看:

public final class Security {
    ...
    private static Class<?> getSpiClass(String type) {
        Class<?> clazz = spiMap.get(type);
        if (clazz != null) {
            return clazz;
        }
        try {
            clazz = Class.forName("java.security." + type + "Spi");
            spiMap.put(type, clazz);
            return clazz;
        } catch (ClassNotFoundException e) {
            throw new AssertionError("Spi class not found", e);
        }
    }
    ...
    static Object[] getImpl(String algorithm, String type, String provider)
            throws NoSuchAlgorithmException, NoSuchProviderException {
        if (provider == null) {
            return GetInstance.getInstance
                (type, getSpiClass(type), algorithm).toArray();
        } else {
            return GetInstance.getInstance
                (type, getSpiClass(type), algorithm, provider).toArray();
        }
    }
    ...
}

结合这两个方法的实现可以看出来,调用了 GetInstance.getInstance 获取结果,参数分别为 AndroidCAStoreClass<java.security.KeyStoreSpi>null

GetInstance.getInstance

GetInstance 这个类看名字就能猜到它的作用,撸一撸代码:

public class GetInstance {
    ...
    public static final class Instance {
        // public final fields, access directly without accessors
        public final Provider provider;
        public final Object impl;
        private Instance(Provider provider, Object impl) {
            this.provider = provider;
            this.impl = impl;
        }
        ...
        public Object[] toArray() {
            return new Object[] {impl, provider};
        }
    }
    ...
    public static Instance getInstance(String type, Class<?> clazz,
            String algorithm) throws NoSuchAlgorithmException {
        ...
        ProviderList list = Providers.getProviderList();
        Service firstService = list.getService(type, algorithm);
        ...
        NoSuchAlgorithmException failure;
        try {
            return getInstance(firstService, clazz);
        } catch (NoSuchAlgorithmException e) {
            failure = e;
        }
        ...
        throw failure;
    }
    ...
    public static Instance getInstance(Service s, Class<?> clazz)
            throws NoSuchAlgorithmException {
        Object instance = s.newInstance(null);
        checkSuperClass(s, instance.getClass(), clazz);
        return new Instance(s.getProvider(), instance);
    }
    ...
}

继续绕弯子,从 Providers.getProviderList() 获取了一个 ProviderList,然后调用 ProviderList.getService() 获取到一个 Service。最后构造出一个 GetInstance.Instance 对象。

这里注意 GetInstance.Instance 它重写了 toArray() 方法,可以实现由 GetInstance.Instance 对象直接转换为 Object[] 的操作。和上一步看到的源码能够对应上。

Providers.getProviderList

Providers 从名字看出来应该是能够读取注册过的所有的 Provider

public class Providers {
    ...
    static {
        // set providerList to empty list first in case initialization somehow
        // triggers a getInstance() call (although that should not happen)
        providerList = ProviderList.EMPTY;
        providerList = ProviderList.fromSecurityProperties();
        ...
    }
    ...
    public static ProviderList getProviderList() {
        ProviderList list = getThreadProviderList();
        if (list == null) {
            list = getSystemProviderList();
        }
        return list;
    }
    ...
    private static ProviderList getSystemProviderList() {
        return providerList;
    }
    ...
}

Providers 的静态代码块中调用了 ProviderList.fromSecurityProperties() 获取 providerList

从方法名字看这个 providerList 来自于某种属性。

ProviderList.fromSecurityProperties

这个类是关键点之一,它表明了大名鼎鼎的 SPI 是如何关联上的:

public final class ProviderList {
    ...
    static ProviderList fromSecurityProperties() {
        // doPrivileged() because of Security.getProperty()
        return AccessController.doPrivileged(
                        new PrivilegedAction<ProviderList>() {
            public ProviderList run() {
                return new ProviderList();
            }
        });
    }
    ...
    private ProviderList() {
        List<ProviderConfig> configList = new ArrayList<>();
        for (int i = 1; true; i++) {
            String entry = Security.getProperty("security.provider." + i);
            ...
                config = new ProviderConfig(entry);
            ...

            // Get rid of duplicate providers.
            if (configList.contains(config) == false) {
                configList.add(config);
            }
        }
        configs = configList.toArray(PC0);
        ...
    }
    ...
    Provider getProvider(int index) {
        Provider p = configs[index].getProvider();
        return (p != null) ? p : EMPTY_PROVIDER;
    }
    ...
    public Service getService(String type, String name) {
        for (int i = 0; i < configs.length; i++) {
            Provider p = getProvider(i);
            Service s = p.getService(type, name);
            if (s != null) {
                return s;
            }
        }
        return null;
    }
    ...
}

这里面有一个关键点,就是 Security.getProperty("security.provider." + i) 这句代码,表明是从系统属性中取得 SPI 的注入 Provider 对象的信息。

但是要得到 Provider 实例还需要经过 ProviderConfigService,继续跟下去。

ProviderConfig

起初看这个类名字以为只是某个存储一些配置信息的类,但具体看过之后发现原来就是它真正意义上构造了 Provider 的实例:

final class ProviderConfig {
    ...
    ProviderConfig(String className) {
        this(className, "");
    }
    ...
    ProviderConfig(String className, String argument) {
        if (className.equals(P11_SOL_NAME) && argument.equals(P11_SOL_ARG)) {
            checkSunPKCS11Solaris();
        }
        this.className = className;
        this.argument = expand(argument);
    }
    ...
    synchronized Provider getProvider() {
        // volatile variable load
        Provider p = provider;
        ...
            p = doLoadProvider();
        ...
        provider = p;
        return p;
    }
    ...
    private Provider doLoadProvider() {
        return AccessController.doPrivileged(new PrivilegedAction<Provider>() {
            public Provider run() {
                ...
                    return initProvider(className, Object.class.getClassLoader());
                ...
            }
        });
    }
    ...
    private Provider initProvider(String className, ClassLoader cl) throws Exception {
        Class<?> provClass;
        ...
            provClass = cl.loadClass(className);
        ...
        Object obj;
        if (hasArgument() == false) {
            obj = provClass.newInstance();
        } else {
            Constructor<?> cons = provClass.getConstructor(CL_STRING);
            obj = cons.newInstance(argument);
        }
        ...
            return (Provider)obj;
        ...
    }
    ...
}

可以看到原来上一步解析系统属性的值就是 Provider 的类名,然后在初始化的时候就被构造了出来。

Provider

上面的步骤得到了关键实例 Provider,还缺少 Provider.getService 的细节:

public abstract class Provider extends Properties {
    @Override
    public synchronized Object put(Object key, Object value) {
        ...
        return implPut(key, value);
    }
    ...
    private Object implPut(Object key, Object value) {
        ...
            legacyStrings.put((String)key, (String)value);
        ...
    }
    ...
    private void ensureLegacyParsed() {
        ...
        for (Map.Entry<String,String> entry : legacyStrings.entrySet()) {
            parseLegacyPut(entry.getKey(), entry.getValue());
        }
        ...
    }
    ...
    private void parseLegacyPut(String name, String value) {
        ...
            String[] typeAndAlg = getTypeAndAlgorithm(name);
            ...
            int i = typeAndAlg[1].indexOf(' ');
            if (i == -1) {
                // e.g. put("MessageDigest.SHA-1", "sun.security.provider.SHA");
                String type = getEngineName(typeAndAlg[0]);
                String stdAlg = typeAndAlg[1].intern();
                String className = value;
                ServiceKey key = new ServiceKey(type, stdAlg, true);
                Service s = legacyMap.get(key);
                if (s == null) {
                    s = new Service(this);
                    s.type = type;
                    s.algorithm = stdAlg;
                    legacyMap.put(key, s);
                }
                s.className = className;
            } else { // attribute
                ...
            }
        }
    }
    ...
    public synchronized Service getService(String type, String algorithm) {
        ...
        ensureLegacyParsed();
        return (legacyMap != null) ? legacyMap.get(key) : null;
    }
    ...
    public static class Service {
        public Service(Provider provider, String type, String algorithm,
                String className, List<String> aliases,
                Map<String,String> attributes) {
            if ((provider == null) || (type == null) ||
                    (algorithm == null) || (className == null)) {
                throw new NullPointerException();
            }
            this.provider = provider;
            this.type = getEngineName(type);
            this.algorithm = algorithm;
            this.className = className;
            if (aliases == null) {
                this.aliases = Collections.<String>emptyList();
            } else {
                this.aliases = new ArrayList<String>(aliases);
            }
            if (attributes == null) {
                this.attributes = Collections.<UString,String>emptyMap();
            } else {
                this.attributes = new HashMap<UString,String>();
                for (Map.Entry<String,String> entry : attributes.entrySet()) {
                    this.attributes.put(new UString(entry.getKey()), entry.getValue());
                }
            }
        }
        ...
        public Object newInstance(Object constructorParameter)
                throws NoSuchAlgorithmException {
            ...
                if (cap.constructorParameterClassName == null) {
                    if (constructorParameter != null) {
                        throw new InvalidParameterException
                            ("constructorParameter not used with " + type
                            + " engines");
                    }
                    Class<?> clazz = getImplClass();
                    Class<?>[] empty = {};
                    Constructor<?> con = clazz.getConstructor(empty);
                    return con.newInstance();
                } else {
                    ...
                }
            ...
        }
        ...
        private Class<?> getImplClass() throws NoSuchAlgorithmException {
            ...
                    ClassLoader cl = provider.getClass().getClassLoader();
                    ...
                        clazz = cl.loadClass(className);
                    ...
                return clazz;
            ...
        }
        ...
    }
}

Provider 有个内部类 Service,和 SPI 的概念也是对应的关系。SPI 提供注入 Provider 的方式,Provider 提供了各种满足功能的 Service

但走到这里似乎走不动了,这个代码可以看到 Service 能够被创建,然后 Service 中也有被 GetInstance.getInstance 调用的 Service.newInstance() 方法实现,唯独没有看到 Service.newInstance() 的类名。

这里就需要换个思路了,看看 Provider 是谁,里面是否有玄机?

Security.initializeStatic

Android 引用的 java.security.Security 类中,有一代码块增加了 security.provider.x 的属性,这些属性是虚拟机注入 java.security.Provider 的源头:

public final class Security {
    ...
    private static void initializeStatic() {
        ...
        props.put("security.provider.4", "com.android.org.conscrypt.JSSEProvider");
    }
    ...
}

Security 中有注入一个 com.android.org.conscrypt.JSSEProvider,它就是 KeyStoreSpi 的宿主。

JSSEProvider

再看 JSSEProvider 类,这个类继承于 java.security.Provider,在构造函数中调用 Provider.put() 像系统注入了 KeyStore.AndroidCAStore

public final class JSSEProvider extends Provider {

    private static final long serialVersionUID = 3075686092260669675L;

    public JSSEProvider() {
        super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
        ...
        put("KeyStore.AndroidCAStore", TrustedCertificateKeyStoreSpi.class.getName());
    }
}

看见了一个眼熟的关键字 KeyStore.AndroidCAStore,这正是之前获取 KeyStore 所对应的类型。

也就是说使用 KeyStore.getInstance("AndroidCAStore") 获取 KeyStore 必然会调用到 TrustedCertificateKeyStoreSpi 类中。

TrustedCertificateKeyStoreSpi

最后这个 TrustedCertificateKeyStoreSpi 继承于 java.security.KeyStoreSpi,并且持有一个老熟人 TrustedCertificateStore,也就是安装证书时分析过的用于管理系统证书的类:

public final class TrustedCertificateKeyStoreSpi extends KeyStoreSpi {
    private final TrustedCertificateStore store = new TrustedCertificateStore();
    @Override
    public Certificate engineGetCertificate(String alias) {
        return store.getCertificate(alias);
    }
    @Override
    public Date engineGetCreationDate(String alias) {
        return store.getCreationDate(alias);
    }
    @Override
    public Enumeration<String> engineAliases() {
        return Collections.enumeration(store.aliases());
    }
    @Override
    public boolean engineContainsAlias(String alias) {
        return store.containsAlias(alias);
    }
    @Override
    public int engineSize() {
         return store.aliases().size();
    }
    @Override
    public boolean engineIsCertificateEntry(String alias) {
        return engineContainsAlias(alias);
    }
    @Override
    public String engineGetCertificateAlias(Certificate c) {
        return store.getCertificateAlias(c);
    }
 }

至此,整个流程就串起来了。

Android 系统中,证书存取继承了 JavaSPI 特性:

  1. 先通过 SPI 注入 ProviderJSSEProvider
  2. 然后在 JSSEProvider 中注册了名为 AndroidCAStoreKeyStoreSpiTrustedCertificateKeyStoreSpi
  3. 最后在 TrustedCertificateKeyStoreSpi 中实现了 KeyStore 需要用到的部分方法完成闭环。

而在 TrustedCertificateKeyStoreSpi 内部持有一个 TrustedCertificateStore,由它消化了所有的调用。

不得不说,SPI 确实灵活,不过刚开始接触倒是容易让人找不着北。

AndroidKeyStore

这篇笔记记录了 Android 是如何处理系统证书,和它对应的是名为 AndroidKeyStore 的私钥管理系统。

由于私钥的保密性,相比开放的证书管理系统,密钥管理系统可谓高深莫测。私钥信息在 Android 的设计中是需要和 TEE 或者 SE 绑定的,也就是说操作私钥信息会涉及到和硬件打交道。

最直观的感受:用于管理私钥的接口 API android.security.KeyStore 一上来就是一个 IPC 调用,并且实现其接口 IKeystoreService 服务端 keystore::KeyStoreService 是实现在 Native 中的,其私密性可见一斑。

后续有机会咱们就来挖一挖这个神秘莫测的 AndroidKeyStore

  • 本文作者: 6x
  • 本文链接: https://6xyun.cn/article/205
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-ND 许可协议。转载请注明出处!