Android 根证书管理与证书验证

大部分的安卓应用都免不了与后端服务器进行通信。在通信过程中,主要面临两方面的风险:1、中间人攻击。当通信使用 HTTP 等明文协议,通信内容可被嗅探甚至篡改。2、通信内容被攻击者分析。使用加密的协议,虽然避免了中间人攻击,但是攻击者仍然可以通过各种方式获取请求内容,然后针对性的进行漏洞挖掘。

一、背景知识

1、SSL/TLS 简介

为了应对中间人攻击,通常会采用安全套接字层 (SSL,现在技术上称为传输层安全协议 (TLS))来对通信进行加密传输。

当 SSL 与 HTTP 协议相结合就形成了 HTTPS,当 SSL 与 POP3 相结合就形成了 POP3S。

SSL/TLS 的握手过程如下图(了解下就好):

2、数字证书

证书通常是指 X.509 格式证书,是由Netscape 当初设计 SSL 协议的时候定义的。其是服务端公钥的载体,同时还包含很多其他信息,如签发者信息、以及证书申请者相关的信息(国家、组织名称、常用名称等等)、加密算法、签名算法、签名指纹等。下图展示了 baidu.com 的证书部分信息。

在证书校验时候,签发者(CA)信息很重要。那么,为什么一个证书还需要有签发者呢?我们知道任何组织或个人都可以生成自己的数字证书,并设置证书中的任何信息。当客户端与服务端通信的时候,客户端就很难确认当前证书是否真的是服务端组织的真实证书。这个时候,一个比较可行的方案就是有个客户端信任的第三方,这个第三方为服务端证书进行签名,来证明证书的真实性。以 Android 系统为例,其预置了知名的权威签名机构公钥证书,可以判断服务端证书是否为这些机构签发。如果不是知名的权威机构签发,则认为证书是非法的,不会建立 SSL/TLS 通信。

除了签发者(CA)信息之外,证书中的常用名称(Common Name)也是比较重要的信息,常被用来校验HTTPS通信的域名是否正确。

3.自签名证书

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

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

二 .Android 的根证书管理

在 AOSP 源码库中,CA 根证书主要存放在 system/ca-certificates 目录下,而在 Android 系统中,则存放在 /system/etc/security/cacerts 目录下

它们都是 PEM 格式的 X.509 证书。Android 系统通过 SystemCertificateSource、DirectoryCertificateSource 和 CertificateSource 等类管理系统根证书库。CertificateSource定义(位于frameworks/base/core/java/android/security/net/config/CertificateSource.java)了可以对根证书库执行的操作,主要是对根证书的获取和查找:

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


 @Override
    public Set<X509Certificate> getCertificates() {
        // TODO: loading all of these is wasteful, we should instead use a keystore style API.
        synchronized (mLock) {
            if (mCertificates != null) {
                return mCertificates;
            }

            Set<X509Certificate> certs = new ArraySet<X509Certificate>();
            if (mDir.isDirectory()) {
                for (String caFile : mDir.list()) {
                    if (isCertMarkedAsRemoved(caFile)) {
                        continue;
                    }
                    X509Certificate cert = readCertificate(caFile);
                    if (cert != null) {
                        certs.add(cert);
                    }
                }
            }
            mCertificates = certs;
            return mCertificates;
        }
    }

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

三.OKHTTP中的SSL,双向验证

    public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
            if (sslSocketFactory == null)
                throw new NullPointerException("sslSocketFactory == null");
            this.sslSocketFactory = sslSocketFactory;
            this.certificateChainCleaner = Platform.get().buildCertificateChainCleaner
                    (sslSocketFactory);
            return this;
        }
    //如果没有设置sslSocketFactory,就会使用默认的systemDefaultSslSocketFactory
    if (builder.sslSocketFactory != null || !isTLS) {
            this.sslSocketFactory = builder.sslSocketFactory;
            this.certificateChainCleaner = builder.certificateChainCleaner;
        } else {
            X509TrustManager trustManager = Util.platformTrustManager();
            this.sslSocketFactory = newSslSocketFactory(trustManager);
            this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
        }

默认的SslSocketFactory

  private static SSLSocketFactory newSslSocketFactory(X509TrustManager trustManager) {
    try {
      SSLContext sslContext = Platform.get().getSSLContext();
      sslContext.init(null, new TrustManager[] { trustManager }, null);
      return sslContext.getSocketFactory();
    } catch (GeneralSecurityException e) {
      throw new AssertionError("No System TLS", e); // The system has no TLS. Just give up.
    }
  }

在newSslSocketFactory方法中
1.调用SSLContext初始化获取SSLContext对象
2.调用init方法
3.获取SocketFactory

在SSL中有两种类型的校验模式:
单向校验:服务器向客户端发送证书进行校验
双向校验:除了服务器向客户端发送证书以外,客户端需要发送证书到服务器进行校验
sslContext.init(null, new TrustManager[]{trustManager}, null);
init函数中第一个传NULL,默认表示Okhttp只支持单向校验。
TrustManager[]{trustManager} 参数表示信任证书

X509TrustManager

 public static X509TrustManager platformTrustManager() {
    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
          TrustManagerFactory.getDefaultAlgorithm());
      trustManagerFactory.init((KeyStore) null);
      TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
      if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
        throw new IllegalStateException("Unexpected default trust managers:"
            + Arrays.toString(trustManagers));
      }
      return (X509TrustManager) trustManagers[0];
    } catch (GeneralSecurityException e) {
      throw new AssertionError("No System TLS", e); // The system has no TLS. Just give up.
    }
  }

getTrustManagers

public TrustManager[] engineGetTrustManagers() {
        if (keyStore == null) {
            throw new IllegalStateException(
                    "TrustManagerFactory is not initialized");
        }
        return new TrustManager[] { new TrustManagerImpl(keyStore) };
    }

keyStore:

 public void engineInit(KeyStore ks) throws KeyStoreException {
        if (ks != null) {
            keyStore = ks;
        } else {
            keyStore = Platform.getDefaultCertKeyStore();  //获取默认的系统根证书
        }
    }

系统根证书查看

代码如下:

        try {
        KeyStore keyStore = KeyStore.getInstance("AndroidCAStore");
            keyStore.load(null, null);
            Enumeration<String> aliases = keyStore.aliases();
            while (aliases.hasMoreElements()){
                String alias = aliases.nextElement();
                Certificate certificate = keyStore.getCertificate(alias);
                Log.i(TAG, "alias: "+alias);
                Log.i(TAG, "certificate: "+certificate);
            }
        } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) {
        }

打印结果:

2024-06-26 18:58:10.416 6813-6813/com.jarvis.permission I/LX-MainActivity: alias: system:3c899c73.0
2024-06-26 18:58:10.418 6813-6813/com.jarvis.permission I/LX-MainActivity: certificate: Certificate:
        Data:
//版本号
            Version: 3 (0x2)
//序列号
            Serial Number:
                21:2a:56:0c:ae:da:0c:ab:40:45:bf:2b:a2:2d:3a:ea
//证书签名算法
        Signature Algorithm: ecdsa-with-SHA384
//证书发行者
            Issuer: C=CH, O=WISeKey, OU=OISTE Foundation Endorsed, CN=OISTE WISeKey Global Root GC CA
//证书有效时间
            Validity
                Not Before: May  9 09:48:34 2017 GMT
                Not After : May  9 09:58:33 2042 GMT
//证书主体名称
            Subject: C=CH, O=WISeKey, OU=OISTE Foundation Endorsed, CN=OISTE WISeKey Global Root GC CA
//证书公钥信息
            Subject Public Key Info:
//公钥算法
                Public Key Algorithm: id-ecPublicKey
//公钥
                    Public-Key: (384 bit)
                    00000000  04 4c e9 50 c0 c6 0f 72  18 bc d8 f1 ba b3 89 e2  |.L.P...r........|
                    00000010  79 4a a3 16 a7 6b 54 24  db 51 ff ea f4 09 24 c3  |yJ...kT$.Q....$.|
                    00000020  0b 22 9f cb 6a 27 82 81  0d d2 c0 af 31 e4 74 82  |."..j'......1.t.|
                    00000030  6e ca 25 d9 8c 75 9d f1  db d0 9a a2 4b 21 7e 16  |n.%..u......K!~.|
                    00000040  a7 63 90 d2 39 d4 b1 87  78 5f 18 96 0f 50 1b 35  |.c..9...x_...P.5|
                    00000050  37 0f 6a c6 dc d9 13 4d  a4 8e 90 37 e6 bd 5b 31  |7.j....M...7..[1|
                    00000060  91   
                    //扩展|.|
            X509v3 extensions:
                X509v3 Key Usage: critical
                    Certificate Sign, CRL Sign
                X509v3 Basic Constraints: critical
                    CA:TRUE
                X509v3 Subject Key Identifier: 
                    48:87:14:AC:E3:C3:9E:90:60:3A:D7:CA:89:EE:D3:AD:8C:B4:50:66
                1.3.6.1.4.1.311.21.1: 
                    ...
                      //签名算法
        Signature Algorithm: ecdsa-with-SHA384
             30:65:02:30:26:c7:69:5b:dc:d5:e7:b2:e7:c8:0c:8c:8c:c3:
             dd:79:8c:1b:63:d5:c9:52:94:4e:4d:82:4a:73:1e:b2:80:84:
             a9:25:c0:4c:5a:6d:49:29:60:78:13:e2:7e:48:eb:64:02:31:
             00:db:34:20:32:08:ff:9a:49:02:b6:88:de:14:af:5d:6c:99:
             71:8d:1a:3f:8b:d7:e0:a2:36:86:1c:07:82:3a:76:53:fd:c2:
             a2:ed:ef:7b:b0:80:4f:58:0f:4b:53:39:bd

如果目标 URL 服务器下发的证书不在已信任的证书列表里,或者该证书是自签名的,不是由权威机构颁发,那么会出异常,如何处理?

四.自签名证书处理:

方式一:修改 X509TrustManager 所用的根证书库

private static TrustManager[] prepareTrustManager(InputStream certificates) {
        if (certificates == null) {
            return null;
        }
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certificates);
            for (Certificate certificate : certs) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificate);
            }
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.
                    getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);

            return trustManagerFactory.getTrustManagers();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

方式二:自定义TrustManager

通过自定义TrustManager自己实现对服务器证书的校验

@SuppressLint("CustomX509TrustManager")
    private static class MyTrustManager implements X509TrustManager {
        private static final String TAG = MyTrustManager.class.getSimpleName();
        private final X509TrustManager defaultTrustManager;
        private final X509TrustManager localTrustManager;

        public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
            TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            var4.init((KeyStore) null);
            defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
            this.localTrustManager = localTrustManager;
        }


        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) {
            Log.i(TAG, "checkClientTrusted,authType=" + authType);
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            try {
                defaultTrustManager.checkServerTrusted(chain, authType);
            } catch (CertificateException ce) {
                localTrustManager.checkServerTrusted(chain, authType);
            }
        }


        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/749394.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PFA铲子聚四氟乙烯物料特氟龙铲粉料铲耐酸碱无污染塑料

PFA铲子:又称四氟铲子、聚四氟乙烯物料铲、特氟龙铲子; 常用尺寸型号 全长x宽x高(mm)165x57x31mm&#xff0c;一体成型&#xff0c;产品坚固&#xff0c;可以直接与食品接触。常用于制药厂&#xff0c;实验室等转移物料&#xff0c;铲取药品化学物品等&#xff0c;可以直接接触…

LangChain4j之HelloWorld

什么是LangChain4j 它是Java版本的LangChain&#xff0c;随着大模型的不断发展&#xff0c;如何在程序中更好的利用大模型的能力来提高编程效率是一种趋势&#xff0c;LangChain是这么自己介绍自己的&#xff1a; LangChain gives developers a framework to construct LLM‑p…

【Redis一】Redis配置与优化

目录 一.关系型数据库与非关系型数据库 1.关系型数据库 2.非关系型数据库 3.二者区别 4.非关系型数据库产生背景 5.NoSQL与SQL数据记录对比 关系型数据库 非关系型数据库 二.Redis相关概述 1.简介 2.五大数据类型 3.优缺点 3.1.优点 3.2.缺点 4.使用场景 5.采用…

阿里巴巴向国际用户开放人工智能模型平台ModelScope(魔搭社区)

阿里巴巴对 Hugging Face 和 Amazon Bedrock 的回应包含 5,000 多个中国专业模型&#xff0c;以及 1,500 个工具包和数据集 阿里云已将其人工智能模型存储库ModelScope&#xff08;魔搭社区&#xff09;的访问权限扩展至全球英语用户&#xff0c;意在吸引更多国际企业和开发者…

床旁交互,全视通打造以患者为中心的智慧病房

随着我国医疗建设的发展&#xff0c;医疗服务体系的不断建立健全&#xff0c;新形势下人们的医疗需求发生变化&#xff0c;医疗服务理念正逐步从传统的“以疾病为中心”向“以患者为中心”转变。 基于医院临床实际应用场景&#xff0c;在兼具实用性的前提下&#xff0c;建设了床…

yolov5驾驶员不规范行为检测

1 项目介绍 1.1 摘要 随着汽车工业的迅速发展和交通拥堵的加剧&#xff0c;驾驶员在行车过程中的不规范行为成为了导致交通事故频发的重要因素之一。为了减少交通事故的发生&#xff0c;保障道路安全&#xff0c;提高驾驶员的行车安全意识&#xff0c;本研究致力于实现驾驶员…

德国威步的技术演进之路(上):从软件保护到用户体验提升

德国威步自1989年成立以来一直专注于数字安全技术的研究和发展&#xff0c;在软件保护和数字授权领域树立了行业标杆&#xff0c;并在云端许可管理和物联网安全技术方面不断创新。德国威步的成就彰显了其对安全、创新和可持续发展的坚定追求。 德国威步将“完美保护、完美授权…

webpack+webpack server入门

​ 1.webpack介绍 webpack是一个模块加载器兼打包工具。它是以 commonJS 的形式来书写脚本的&#xff0c;但对 AMD/CMD 的支持也很全面&#xff0c;方便旧项目进行代码迁移。支持对react热插拔。 2.安装&#xff08;使用淘宝镜像&#xff09; 全局安装 cnpm install webpa…

Redis-数据类型-Set(不允许重复)

文章目录 1、查看redis是否启动2、通过客户端连接redis3、切换到2数据库4、给key指定的set集合中存入数据&#xff0c;set会自动去重5、返回可以指定的set集合中所有的元素6、返回集合中元素的数量(set cardinality)7、检查当前指定member是否是集合中的元素8、从集合中删除元素…

数学类-课程资料推荐-中科大教师首页

http://staff.ustc.edu.cn/~rui/cn/rui-course.html 数学分析讲义&#xff08;第一册&#xff09; (ustc.edu.cn)

BIO、NIO编程深入理解与直接内存、零拷贝

网路编程基本常识 一. Socket 什么是Socket Socket是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。它提供了应用层进程利用网络协议交换数据的机制&#xff0c;是应用程序与网络协议栈进行交互的接口。 说白了&#xff0c;Socket就是把TCP/IP协议族进行封装…

数通云网架构师涨薪班毕业都有哪些工作企业和岗位?

数通云网架构师涨薪班课程学完后&#xff0c;学员具备全行业全场景交付数通项目的能力&#xff0c;胜任企业网&#xff0c;广域网&#xff0c;数据中心网络等各种网络项目的交付能力&#xff0c;技术能力一项能够匹配年薪达30w-40w以上网络工程师岗位。 与誉天进行人才培养&…

Wordpress图像编辑插件-palleon v3.8.1中文版语言包

Palleon是一个强大的WordPress图像编辑器&#xff0c;可以与您的WordPress网站无缝集成&#xff0c;让您快速高效地工作。它拥有为你的WordPress网站创建令人惊叹的图像所需的一切。 Palleon让您完全控制图像&#xff0c;允许您逐个像素进行更改。您可以轻松地裁剪、调整图像大…

Android jetpack Room的简单使用

文章目录 项目添加ksp插件添加 room 引用开始使用room1. 创建bean2. 创建 dao类3. 创建database类 数据库升级复制数据库到指定路径参考文献 项目添加ksp插件 注意&#xff0c;因为ksp插件 是跟项目中使用的kotlin的版本要保持一致的&#xff0c;否则会报错的 首先我们去 https…

高德.js2.0绘制多条折线(轨迹)及清除所有折线

2.0版本的地图,需要绘制多条折线的时候,就需要循环生成,因此也需要循环清除 for (let j 0; j < combinedArray.length; j) {const item combinedArray[j];this.polyline new AMap.Polyline({map: this.map,path: item,showDir: true,strokeColor: "#28F", //线…

免费Logo在线生成:必试的6款工具

logo对企业来说非常重要。一个好的logo免费设计在线生成器往往会给企业带来无形的利润。因此&#xff0c;许多企业非常重视自己公司的logo。作为一名设计师&#xff0c;如果能找到一个好的logo免费设计在线生成器&#xff0c;势必会给实际的logo设计带来事半功倍的效果。本文精…

软件测试面试题:Web View如何测试?

Web View介绍 Web View&#xff08;网页视图&#xff09;是一种用于在应用程序中显示网页内容的组件或控件。提供了一种将网页内容嵌入到应用程序中的方式&#xff0c;使用户能够在应用程序中浏览和交互网页。 Web View通常用于开发移动应用程序&#xff0c;特别是混合应用程…

Docker构建多平台镜像

docker的多架构镜像构建 目前很多服务器都是基于arm架构的&#xff0c;而现在大多数的docker镜像都是基于x86架构的。一种情况就是同样的代码编译成业务包做成镜像需要部署在不同架构的服务器上&#xff0c;这个时候我们就可以使用docker的多平台构建了。 以下操作是在centos7.…

Github 2024-06-21 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-06-21统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目3Python项目3Java项目2非开发语言项目2JavaScript项目1Rust项目1Dart项目1HTML项目1Vue项目1C++项目1TensorFlow: 机器学习的开源…

软件自动化测试有哪些流程?可替代手工测试吗?

随着科技的不断发展&#xff0c;软件在我们生活中的地位越来越重要。然而&#xff0c;在软件开发过程中&#xff0c;必然会出现各种各样的问题和bug&#xff0c;为了提高软件的质量和稳定性&#xff0c;保证用户的使用体验&#xff0c;软件自动化测试应运而生。 那么&#xff…