关于应用程序根证书

概述:

互联网上传输的任何数据都可以轻而易举的被发送者和接收者之外的第三方所获得。如果敏感的或隐私的数据以明文方式发送,将会造成信息的泄露。例如,网上银行的各种操作,电子商务交易的数据等是不允许交易的第三方获悉的。因此我们需要把数据加密后传输,通常我们称传输加密数据的链接叫做安全链接。

安全链接和安全的数据传输面临三个难题:1. 如何确认交易双方不是仿冒的。2. 怎样对数据进行加密。3. 如何保证传输的数据没有被篡改。证书就是用来在建立链接时确认交易双方身份的一个良好解决办法。
如果手机中没有预置证书,或在弹出无证书或证书错误的提示时仍然手动链接,潜在的危险是终端用户的隐私信息被被钓鱼网站或第三方攻击者获取。

可以使用数字证书标识设备,以实现多种目的,包括 VPN 或 Wi-Fi 网络访问,以及“电子邮件”或 Chrome 浏览器等应用对服务器的身份验证。

Android 支持以 .crt 或 .cer 扩展名的文件形式保存的 DER 编码 X.509 证书。如果您的证书文件带有 .der 或其他扩展名,则必须将其更改为 .crt 或 .cer,否则无法安装。
Android 也支持以 .p12 或 .pfx 扩展名的 PKCS#12 密钥库文件形式保存的 X.509 证书。如果您的密钥库文件带有其他扩展名,则必须将其更改为 .p12 或 .pfx,否则无法安装。通过 PKCS#12 密钥库安装证书时Android 会同时安装所有随附的私有密钥或证书授权中心的证书。

Android根证书管理

设置路径:
setting=>Security&Location=>Encryption&credentials=>Trusted credentials
手机目录:
system/etc/security
aosp源码:
system/ca-certificates

ca

在 Android 系统的 Java 应用程序中,证书验证通常由不同层面的多个组件完成。第一步的证书合法性验证,主要由 Java 标准库的 javax.net.ssl.SSLSocket 在 startHandshake() 方法中完成,后面两个步骤由更上层的组件完成

其中 cacerts_google 目录下的根证书,主要用于 system/update_engine、external/libbrillo 和 system/core/crash_reporter 等模块,cacerts 目录下的根证书则用于所有的应用。cacerts 目录下的根证书,即 Android 系统的根证书库

管理系统根证书库

他们是 PEM 格式的 X.509 证书。Android 系统通过 SystemCertificateSource、DirectoryCertificateSource 和 CertificateSource 等类管理系统根证书库。

CertificateSource

frameworks/base/core/java/android/security/net/config/CertificateSource.java

主要是对根证书的获取和查找:

package android.security.net.config;

import java.security.cert.X509Certificate;
import java.util.Set;

/** @hide */
public interface CertificateSource {
    Set<X509Certificate> getCertificates();
    X509Certificate findBySubjectAndPublicKey(X509Certificate cert);
    X509Certificate findByIssuerAndSignature(X509Certificate cert);
    Set<X509Certificate> findAllByIssuerAndSignature(X509Certificate cert);
    void handleTrustStorageUpdate();
}

DirectoryCertificateSource 类则基于文件系统上分开存放的根证书文件的形式保存的根证书库,提供证书的创建、获取和查找操作

frameworks/base/core/java/android/security/net/config/DirectoryCertificateSource.java

其中获取根证书库的 getCertificates() 操作在第一次被调用时,遍历文件系统,并加载系统所有的根证书文件,并缓存起来,以备后面访问。根证书的查找操作,主要依据证书文件的文件名进行,证书文件被要求以 [SubjectName 的哈希值].[Index] 的形式命名。

SystemCertificateSource
frameworks/base/core/java/android/security/net/config/SystemCertificateSource.java)了系统根证书库的路径,以及无效一个根证书的机制:

public final class SystemCertificateSource extends DirectoryCertificateSource {
    private static class NoPreloadHolder {
        private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
    }

    private final File mUserRemovedCaDir;

    private SystemCertificateSource() {
        super(new File(System.getenv("ANDROID_ROOT") + "/etc/security/cacerts"));
        File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        mUserRemovedCaDir = new File(configDir, "cacerts-removed");
    }

    public static SystemCertificateSource getInstance() {
        return NoPreloadHolder.INSTANCE;
    }

    @Override
    protected boolean isCertMarkedAsRemoved(String caFile) {
        return new File(mUserRemovedCaDir, caFile).exists();
    }
}

Android 系统的根证书位于 /system/etc/security/cacerts/ 目录下。用户可以通过将特定根证书复制到用户配置目录的 cacerts-removed 目录下来无效一个根证书。

Android framework 还提供了另外一个用于加载并访问用户根证书库的组件 UserCertificateSource,这个类的定义(位于 frameworks/base/core/java/android/security/net/config/UserCertificateSource.java)如下:

public final class UserCertificateSource extends DirectoryCertificateSource {
    private static class NoPreloadHolder {
        private static final UserCertificateSource INSTANCE = new UserCertificateSource();
    }

    private UserCertificateSource() {
        super(new File(
                Environment.getUserConfigDirectory(UserHandle.myUserId()), "cacerts-added"));
    }

    public static UserCertificateSource getInstance() {
        return NoPreloadHolder.INSTANCE;
    }

    @Override
    protected boolean isCertMarkedAsRemoved(String caFile) {
        return false;
    }
}

证书链合法性验证

证书的合法性由 Java 标准库的 javax.net.ssl.SSLSocket 在 startHandshake() 方法中完成。对于 Android 系统而言,SSLSocket 基于 OpenSSL 库实现,这一实现由 external/conscrypt 模块提供,SSLSocket 的实现为 OpenSSLSocketImpl 类(位于external/conscrypt/src/main/java/org/conscrypt/OpenSSLSocketImpl.java)。

OpenSSLSocketImpl.startHandshake() 中的 SSL/TLS 握手是一个极为精巧的过程,我们略过详细的握手过程,主要关注证书验证的部分。

    /**
     * Returns the sslSessionNativePointer of the negotiated session. If this is
     * a server negotiation, supplying the {@code alpnProtocols} will enable
     * ALPN negotiation.
     */
    public static native long SSL_do_handshake(long sslNativePointer,
                                               FileDescriptor fd,
                                               SSLHandshakeCallbacks shc,
                                               int timeoutMillis,
                                               boolean client_mode,
                                               byte[] npnProtocols,
                                               byte[] alpnProtocols)
        throws SSLException, SocketTimeoutException, CertificateException;

NativeCrypto 类内部定义了一组将会在本地层由与 SSL 握手相关的 OpenSSL C/C++ 代码调用的回调 SSLHandshakeCallbacks,在上面的 SSL_do_handshake() 方法中,这组回调作为参数传入本地层

其中 verifyCertificateChain() 回调用于服务端证书的验证。Android 系统通过这一回调,将根证书库的管理模块和底层 OpenSSL 的 SSL/TLS 握手及身份验证连接起来。

SSLHandshakeCallbacks 回调由 OpenSSLSocketImpl 实现

私有 CA 签名证书的应用

自签名证书是无需别的证书为其签名来证明其合法性的证书,根证书都是自签名证书。私有 CA 签名证书则是指,为域名证书签名的 CA,其合法有效性没有得到广泛的认可,该 CA 的根证书没有被内置到系统中。

在实际的开发过程中,有时为了节省昂贵的购买证书的费用,而想要自己给自己的服务器的域名签发域名证书,这即是私有 CA 签名的证书。为了能够使用这种证书,需要在客户端预埋根证书,并对客户端证书合法性验证的过程进行干预,通过我们预埋的根证书为服务端的证书做合法性验证,而不依赖系统的根证书库。

自定义 javax.net.ssl.SSLSocket 的代价太高,通常不会通过自定义 javax.net.ssl.SSLSocket 来修改服务端证书的合法性验证过程。以此为基础,从上面的分析中不难看出,要想定制 OpenSSLSocketImpl 的证书验证过程,则必然要改变 SSLParametersImpl,要改变 OpenSSLSocketImpl 的 SSLParametersImpl,则必然需要修改 SSLSocketFactory。修改 SSLSocketFactory 常常是一个不错的方法。

安装证书

安装证书,请按以下步骤操作:

1 将证书或密钥库从计算机复制到设备的内存设备的根目录下(也就是说,不要复制到某个文件夹中)。
2 依次转到设置 > 个人 > 安全 > 凭据存储 > 从存储设备安装。
3 触摸证书或密钥库的文件名即可安装。系统只显示您尚未安装的证书。
4 根据系统提示输入密钥库的密码,然后触摸确定。
5 为该证书输入一个名称,然后触摸确定。

通常,系统会同时安装客户端证书随附的 CA 证书。您也可以通过相同步骤安装单独的 CA 证书。
对于“电子邮件”和“浏览器”等支持证书的应用,可直接从应用内部安装证书

使用 CA 证书

依次触摸设置 > 个人 > 安全 > 凭据存储 > 受信任的凭据。“受信任的凭据”屏幕有以下两个标签:
系统显示永久安装在手机 ROM 中的证书授权中心 (CA) 的证书。
用户显示您自行安装的所有 CA 证书,例如安装客户端证书过程中安装的 CA 证书。
要查看 CA 证书的详情,请触摸其名称。相应详情会显示在滚动屏幕中。

QS

为什么手机设置->安全设置->证书管理中有可以查看证书,而浏览器->设置->安全设置->信任证书中也可以查看证书?这他们查看到的证书有什么区别?分别有什么作用?

手机证书管理器:
不支持WTLS格式的证书。
最多支持30个证书,如果要预置多余30个证书,需要打patch解决。
支持通过文件系统安全证书。方法:打开文件管理器,选中需要安装的证书。点击左键,选择安装。
存储的证书有两大类用途:验证网络上服务器的身份;验证java midlet的来源是否合法。
用于验证网络上服务器身份的证书与浏览器证书管理器中预置的证书是相同的。用于验证java应用来源的证书是通过数组custpack_java_root_ca来预置的。
用于安全链接的证书仅被OPEN SSL、CIC LIB这两个SSL库用到。
浏览器证书管理器:
支持X.509证书和WTLS证书。
所支持的证书个数没有限制。
不支持终端用户自行安装证书,但支持通过网络下载安装证书。所下载的文件的content type必须为application/vnd.wap.hashed-certificate, application/vnd.wap.signed-certificate。
使用浏览器访问安全网站或通过WPS上网时都会用到这个管理器中的证书。

怎样去掉访问安全网站时弹出的pop框?
方案一 :往手机中添加相应证书
如果往手机中添加的证书能够验证server传过来的证书,在登录这个网站时手机就能自动认证这个网站而不会有提示对话框。这需要由贵司找对应的网站或者提供证书文件的第三方公司申请对应的证书文件。将证书通过代码添加到手机中。由于全世界的安全网站很多,手机中不可能能认证所有安全网站。所以添加证书只能保证访问某些网站不出现证书错误,在访问另外一些安全网站还是可能出现这个提示对话框给用户。
该方案仅能保证去除”No trusted certificate, continue?”的提示。对于其他提示不起作用。
方案二:在证书验证阶段忽略掉这些提示
因为出现这个对话框不会影响用户访问,手机端默认用户需要访问的安全网站是用户信任的安全网站,因此去掉这个提示对话框。去掉对话框也不会影响手机通过安全连接方式与server交互数据。如果选择这个解决方案,需要由我们提供patch给贵司,在拿到这个patch后贵司就可以通过宏:BRA_CFG_SEC_CONN_WITHOUT_CERT_DLG 来选择需要这两个对话框还是去掉这两个对话框(默认为0表示去除,1表示保留)。但去掉对话框的话可能不符合国外某些运营商的规范(在访问不受信任的站点时提示用户)。贵司如果有这个patch的话也可以自行打开这两个对话框。
目前这个patch可以去掉"No trusted certificate, continue?”, "Error in certificate. continue?”以及"Unknown server, continue?”的提示。
对于"Remove existing old trust certificate?”的提示,不能通过patch的方式去除。贵司可以通过internet service->settings->security settings->trusted certificates查看证书及其过期时间,找到过期证书后从代码中将其移除。
目前已确认过期的证书是verisign_rsa_ca,请在custpack_root_ca这个数组中删除
{verisign_rsa_ca, sizeof(verisign_rsa_ca), CERT_FORMAT_X509, CERT_DOMAIN_OPERATOR, CERT_READ_ONLY_ON}。
其他我司release code时预置的证书最早在2018年才会过期。
对于09BW10.12及其之后的版本,可在函数mmi_brw_sec_confirm_dialog_ind中的code body开始时加上如下代码:
/don’t display certificate related popups. if you need remove other popup about certificate, please add string id as a case/
switch (localBuff_p->title_id)
{
case SEC_STR_ID_CERT16: //no certificate
case SEC_STR_ID_CERT19: //error in certificate
case SEC_STR_ID_CERT18: //unknown server
{
wps_show_confirm_dialog_res_struct myMsgPtr;
myMsgPtr = (wps_show_confirm_dialog_res_struct
) OslConstructDataPtr(sizeof(wps_show_confirm_dialog_res_struct));
myMsgPtr->request_id = localBuff_p->request_id;
myMsgPtr->result = WPS_DLG_OK;
mmi_brw_event_hdlr_send_ilm_to_wps(MSG_ID_WPS_SEC_SHOW_CONFIRM_DIALOG_RES, myMsgPtr, NULL);
return;
}
default:
break;
}

方案三:修改提示对话框的内容
因为提示No和error信息终端用户可能会认为手机发生错误而影响正常访问,通常会误认为是手机没有授权访问这个网站。因此贵司可以考虑参考对比机修改提示给用户提示的内容(修改字符串资源SEC_STR_ID_CERT7,SEC_STR_ID_CERT16,SEC_STR_ID_CERT18,SEC_STR_ID_CERT19),例如Nokia N72在遇到server传回的证书无法验证的情况下会提示用户:“是否确认继续浏览这个安全网站?”,在访问该手机能认证的安全网站时不会给用户提示上述信息

如何往手机中添加证书

mtk平台只支持WTLS (16进制数据以0X01开头) 或者 X.509(16进制数据以0X30开头)格式并且是DER 编码方式的证书

拿到证书文件后把它的证书转成十六进制的数据(可以通过16进制编辑器如Ultraedit完成),可以参考custpack_certs.c里已有证书添加16进制数据,参考数组custpack_root_ca将证书加载进这个数组。同时更新custpack_certs.h里NUMBER_OF_CERTIFICATES为现有证书个数,如果增加较多证书就需要修改最多证书个数CUSTPACK_CFG_MAX_CERTS_NUM(默认30),详细信息可以参考DMS上文档:How to preinstall CA.ppt,08A及较早的工程证书数据写在数组wap_root_ca_default中。

需要注意的是在添加某网站对应的证书后download新的bin文件后必须format FAT才能通过菜单Wap->Settings->Trusted certificates查看已经添加的证书。另外请注意要验证添加的证书是否有效必须将手机设置为正确的时间。

如果手头上的证书不是DER编码的,则请先在电脑上打开此证书,然后在对话框的"详细信息"->"复制到文件"向导中将证书保存为DER编码。

参考文章:
Android 根证书管理与证书验证

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章