安装证书相关流程及keystore存储机制

安装证书相关流程

@[证书|KeyStore]

通过上层一系列跳转到该函数

0x01 : 函数入口

startInstallActivity(String mimeType, Uri uri)

参数: uri 为证书路径,mimeType 根据 uri 获取文件 mimeType 说明: 将文件读入到流(可以直接读取网络文件),传给下面函数

startInstallActivity(String mimeType, byte[] value)

参数: mimeType 为证书类型,value 为证书字节流

说明: 判断证书类型并构造 intent 传给 CertInstaller

证书类型:

  • application/x-pkcs12
  • application/x-x509-ca-cert
  • application/x-x509-user-cert
  • application/x-x509-server-cert
  • application/x-pem-file
  • application/pkix-cert

以上证书类型区别主要在于:

  1. pkcs12 含有私钥,同时可以有公钥,有口令保护
  2. x509 公钥证书,只有公钥
    Intent intent = new Intent(this, CertInstaller.class);
    if ("application/x-pkcs12".equals(mimeType)) {
        intent.putExtra(KeyChain.EXTRA_PKCS12, value);
        startActivityForResult(intent, REQUEST_INSTALL);
    } else if ("application/x-x509-ca-cert".equals(mimeType)
            || "application/x-x509-user-cert".equals(mimeType)
            || "application/x-x509-server-cert".equals(mimeType)
            || "application/x-pem-file".equals(mimeType)
            || "application/pkix-cert".equals(mimeType)) {
        intent.putExtra(KeyChain.EXTRA_CERTIFICATE, value);
        startActivityForResult(intent, REQUEST_INSTALL);
    } else {
        Log.e(TAG, "Failed to read certificate");
        Toast.makeText(this, R.string.invalid_cert, Toast.LENGTH_SHORT).show();
        finish();
    }


CertInstaller

  1. CertInstaller 接收到 REQUEST_INSTALL 后开始进行各种初始化,通过 createCredentialHelper 对象对传入的证书字节流开始解析,我们查看了CredentialHelper 对象结构,可以看到安装证书时需要用到的基础数据结构有:

    struct { 
    
        HashMap<String, byte[]> mBundle;      // 证书其他条目保持到改哈希表
        String                  mName;        // 证书名称
        int                     mUid;         // 安装UID(默认值 -1)
        PrivateKey              mUserKey;     // 用户私匙
        X509Certificate         mUserCert;    // CA 用户证书
        List<X509Certificate>   mCaCerts;     // CA 证书
        byte[]                  mWapiAsCert;  // WAPI 证书
        byte[]                  mWapiUserCert;// WAPI 用户证书
    } CredentialHelper;
    
  2. 通过 parseCert 函数进一步解析证书内容

    parseCert(getData(KeyChain.EXTRA_CERTIFICATE));

    说明: 通过 CertificateFactory 类来解析证书内容

  3. 通过 CredentialHelper 预加载证书数据后,做一些常规验证,比如:判断证书内容是否全部处理完、判断证书是否含有 pkcs12 密钥(另外流程)、判断状态,其中状态为当前用户 KeyStore 的状态:

    public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
    

    如果不是UNLOCKED状态则弹出锁屏解锁界面。

  4. 解锁通过后开始执行 installOthers(),做两件事情:

    1. 查找私匙并设置它 // mCredentials.setPrivateKey(privatekey);
    2. 弹出命名的对话框 // nameCredential();

0x02 : 开始安装

  1. 点击命名对话框的确定后,开始带上前面解析的数据并构建 intent 发给 com.android.settings.CredentialStorage

    Intent createSystemInstallIntent() {
        Intent intent = new Intent("com.android.credentials.INSTALL");
        // To prevent the private key from being sniffed, we explicitly spell
        // out the intent receiver class.
        intent.setClassName("com.android.settings", "com.android.settings.CredentialStorage");
        intent.putExtra(Credentials.EXTRA_INSTALL_AS_UID, mUid);
        try {
            if (mUserKey != null) {
                intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_NAME,
                        Credentials.USER_PRIVATE_KEY + mName);
                intent.putExtra(Credentials.EXTRA_USER_PRIVATE_KEY_DATA,
                        mUserKey.getEncoded());
            }
            if (mUserCert != null) {
                intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_NAME,
                        Credentials.USER_CERTIFICATE + mName);
                intent.putExtra(Credentials.EXTRA_USER_CERTIFICATE_DATA,
                        Credentials.convertToPem(mUserCert));
            }
            if (!mCaCerts.isEmpty()) {
                intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_NAME,
                        Credentials.CA_CERTIFICATE + mName);
                X509Certificate[] caCerts
                        = mCaCerts.toArray(new X509Certificate[mCaCerts.size()]);
                intent.putExtra(Credentials.EXTRA_CA_CERTIFICATES_DATA,
                        Credentials.convertToPem(caCerts));
            }
    //Broadcom, WAPI
            if (mWapiAsCert != null) {
            /*
                intent.putExtra(Credentials.WAPI_AS_CERTIFICATE + mName,
                    mWapiAsCert);
           */
                intent.putExtra(Credentials.EXTRA_WAPI_AS_CERTIFICATES_NAME,
            Credentials.WAPI_AS_CERTIFICATE + mName);
        intent.putExtra(Credentials.EXTRA_WAPI_AS_CERTIFICATES_DATA,
                        mWapiAsCert);
            }
            if (mWapiUserCert != null) {
        /*    
                intent.putExtra(Credentials.WAPI_USER_CERTIFICATE + mName,
                    mWapiUserCert);
          */
                intent.putExtra(Credentials.EXTRA_WAPI_USER_CERTIFICATES_NAME,
            Credentials.WAPI_USER_CERTIFICATE + mName);
        intent.putExtra(Credentials.EXTRA_WAPI_USER_CERTIFICATES_DATA,
                        mWapiUserCert);
    
            }
    //Broadcom, WAPI
            return intent;
        } catch (IOException e) {
            throw new AssertionError(e);
        } catch (CertificateEncodingException e) {
            throw new AssertionError(e);
        }
    }
    
  2. CredentialStorage 开始处理数据

    1. handleUnlockOrInstall 再次检查状态,mKeyStore.state() 不是 UNLOCKED 再弹出解锁界面
    2. installIfAvailable 准备安装了:
      1. 取出前面的 uid
      2. 构造 flags 值 int flags = (uid == Process.WIFI_UID) ? KeyStore.FLAG_NONE : KeyStore.FLAG_ENCRYPTED;
      3. 取出证书内容 byte[] caListData = bundle.getByteArray(Credentials.EXTRA_WAPI_AS_CERTIFICATES_DATA);
    3. 通过 KeyStore 服务来安装 mKeyStore.put(caListName, caListData, uid, flags)

    小结:到此为止上面代码为上层处理证书内容和准备数据的全部流程。

0x03 : Binder 机制与 KeyStore.cpp 通信

  1. 通过 IKeystoreService 与 KeyStore.cpp 通信

    int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int targetUid, int32_t flags);

    该函数为 KeyStore.cpp 里的 insert 函数, mKeyStore.put 调用的就是该函数:

    1. name 为证书名称(固定值)
    2. item 为证书 EXTRA_WAPI_AS_CERTIFICATES_DATA 段内容
    3. itemLength 其实是 uid

    准备好参数后,检查是否有 P_INSERT 权限并 KeyStore 获取状态, 准备写入数据,KeyStore 里的元数据是 Blob,把上面的参数构造到 KeyBlob 对象里,然后调用 writeBlob 方法,写入数据

    最后,writeBlob 方法通过AES加密算法把东西加密后写到文件里去了,以上就是Android在存储私有数据的流程,读取则反之。

0x04 : 那么问题来了

  1. 为什么安装证书的时候要设置锁屏密码呢?哪是因为 AES 在加密的时候会用到一个 Key 这个 Key 一般就是我们的锁屏密码,那么回到一开始的问题上:“安装证书的时候能否绕过锁屏密码呢?” 目前答案是否定的,如果直接修改代码绕过去后,对手机的信息安全会有造成较大风险。哪这个问题怎么解决呢?要么等 android 加入指纹解锁验证等相关机制,要么我们自己开发一套和 android 并存的密保机制。比如,安装证书不存入 KeyStore 存入我们自己的加密文件,在使用证书的时候从我们自己的加密文件里取证书,该机制和现有的指纹存储类似。风险可控。

PS:支付宝钱包已经加入指纹支付相关的功能了,我们是不是也要加把劲了。

Fiddler是一个强大的HTTP代理工具,它主要用于网络调试和安全测试。然而,Fiddler本身并未提供专门针对手机端浏览器的证书安装功能。通常,在移动设备上安装自签名证书是为了绕过HTTPS的安全验证机制,允许应用程序能够访问通过该证书加密的数据。 ### 安装手机端证书的一般步骤: #### 对于Android设备: 1. **获取证书**:首先,你需要有需要安装到手机的证书文件。这个文件通常是`.crt` 或 `.pem` 格式。 2. **导入证书**:打开 Android 设备上的“设置”应用,然后进入“关于设备”,点击“版本号”7次,这将开启开发者选项。 - 返回主菜单,找到并打开“开发者选项”。 - 在开发者选项中,查找“USB调试”或类似名称的设置,并启用它。 - 将您的 Android 设备连接到电脑,并在弹出的提示中选择“仅传输媒体”或其他不需要认证的方式。 - 使用 USB 数据线连接手机,然后运行 adb 命令行工具(通常位于 Android SDK 的 tools 文件夹内)。执行 `adb devices` 确认设备已成功连接。 - 使用以下命令之一导入证书到信任库(通常推荐使用 `keytool` 命令): ``` keytool -importcert -file <路径到.crt文件> -alias <别名> -keystore /path/to/truststore.keystore -storepass android -noprompt ``` 这里的 `<路径到.crt文件>` 是你要导入的证书文件的完整路径,`<别名>` 可以是你指定的一个字符串作为证书的别名, `/path/to/truststore.keystore` 是你的信任存储库的位置,通常默认为 `/system/product/etc/security/cacerts` 或 `/data/system/certificates/trustanchors/`。 3. **重启设备**:完成证书安装后,建议重启设备以让系统更新安全配置。 #### 对于iOS设备(iPhone/iPad): 苹果设备的证书管理更为复杂,通常涉及到Apple Developer Account 和相应的证书颁发流程。对于开发者而言,可能需要通过Xcode进行证书及配置文件的管理和分发给特定的应用程序。非开发者用户可能难以直接操作,除非他们有开发者账号权限和相应的知识去配置和管理证书。 ### 注意事项: - **安全风险**:直接向手机安装自签名证书可能会引入安全隐患,因为它会信任未知来源的证书,使得数据在网络上传输时不再受到标准的安全保护。 - **适配性**:并非所有应用程序都支持直接从手机端安装自签名证书的方式访问其服务器。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值