Android开发签名知识梳理总结

作者:??九心???? 时间:2023-03-15 03:52:02 

前言

最近帮测试做了一点关于签名的需求,今天就和各位同学简单聊一聊关于签名的那些事儿。

如果问到 Android 为什么需要签名?大家都可能想到官网的解释:

Android 系统要求所有 APK 必须先使用证书进行数字签名,然后才能安装到设备上进行更新。

这是一个比较模糊的解释,简单来说,有了签名,就可以让 App 和开发者绑定。

毕竟,应用那么多,别的开发者也有可能盗用你的代码,这个时候,包名和你相同,代码和你相同,怎么区分你的 App 和这些人的 App 不是同一个呢?

这个时候数字签名就派上用场了。

一、签名基础

想要彻底了解签名知识,我们得了解以下知识:

  • 消息摘要

  • 数字签名

  • 加密

  • 数字证书

这一系列的知识各位可能在学习网络的时候或多或少的接触过。

我们简单的学习一下这些知识:

1. 消息摘要

消息摘要常常被被称为数字摘要或者数字指纹,定义如下:

在原来的数据基础上,经过一个单向的 Hash 计算,得到一个固定的 Hash 值,这就是消息摘要。

常见的摘要算法都有 MD5、SHA-1 和 SHA-256,特点如下:

  • 长度固定,与内容长度无关:比如 MD5 是 128 位、SHA-1 是 160 位、SHA-256 是 256 位。

  • 看似随机,其实不随机:同内容两次摘要得出的结果一致

  • 单向:只能从原数据得出摘要,不能从消息摘要得出原来的数据

  • 优秀的摘要算法很难 Hash 碰撞

基于此,消息摘要常常会被用来检查内容的完整性。

比如我们下载起点读书,消息摘要的用法如下:

  • 计算摘要:App 会针对自己的文件信息计算出一个数字摘要比如 123**...**123

  • 下载App

  • 验证摘要:对下载的 App 再次计算摘要,比如得出的也是 123**...**123,和之前的数字摘要一对比,这就代表我从服务器下载的内容是完整的,可以正常使用

当然,上面值涉及了摘要部分,其他过程,我们后面分析。

2. 加密算法

什么是加密?

百科是这么解释的:

将明文信息改变为难以读取的密文内容,使之不可读的过程。只有拥有解密方法的对象,经由解密过程,才能将密文还原为正常可读的内容。

所以啊,加密方法得到的密文是可以转变为明文的,像信息摘要算法比如 MD5 得出来的结果是不可逆的,所以面试官问你们什么事加密算法的时候,你可不能把 MD5 说进去!

加密算法分为两大类,对称加密非对称加密

2.1 对称加密

对称加密在加密和解密的时候使用的同一把钥匙:

Android开发签名知识梳理总结

2.2 非对称加密

非对称加密是使用公钥/私钥中的公钥来加密明文,然后使用对应的私钥来解密密文的过程:

Android开发签名知识梳理总结

简单对比一下对称加密和非对称机密:

 非对称加密对称加密
速度
效率
安全性
常见算法RSA\DHAES\DES\IDEA

2.3 使用场景

学过网络的同学应该都了解,在 Https 的传输过程中,客户端和服务端使用非对称加密生成对称加密的密钥,然后用对称加密传输网络中的数据。

比如我上大学那会儿,每个月的月尾我和我妈的对话是这样的:

Android开发签名知识梳理总结

网络环境是开放的,万一这时,有一个黑客监听了我和我妈的对话,过程就变成了这样:

Android开发签名知识梳理总结

在我发卡号的时候,黑客将我的卡号改成了它的卡号,于是我的生活费变成了他的生活费。

为了避免这种情况,于是我和我妈约定好了,每次发送前,使用对称加密对消息进行加密,接受消息的时候使用密钥解密,过程就变成了这样:

Android开发签名知识梳理总结

中间人再也不能获取到消息了,看似一点问题都没有,但是我和老妈之间如何确定密钥呢?

密钥总要在互联网之间进行传输的,有传输就有被中间人截获的风险,一旦被截获,钱可就没了!

为了解决对称加密钥匙传输的问题,我和老妈用上了非对称加密,像这样:

Android开发签名知识梳理总结

即使这样,还是有问题存在:

  • 怎么才能确认我获得的公钥来自老妈?

  • 如何确定消息确实来自老妈?

解决这两个问题也很简单,一是数字签名,二就是数字证书。

3. 数字签名

数字签名的作用是为了消息的完整性。

在非对称加密的体系下,消息的发送过程是这样的,还是上面的例子:

Android开发签名知识梳理总结

数字签名的过程是这样的:

  • 我发送消息前,利用 Hash 算法针对数据得出一个摘要

  • 我使用老妈的公钥对摘要内容进行加密,连同对称加密的数据一起发送过去

  • 老妈接收到消息后,先利用对称密钥对内容解密,再进行 Hash 计算得出摘要

  • 老妈使用私钥将摘要内容解密,和再次计算得出的摘要作对比,一致就代表消息无误

上面的这种场景其实有点不妥,数字签名一般用在证书上,协商好对称密钥以后一般不会进行消息完整性校验了,不过大伙只要了解数字签名要来校验消息完整性就好。

截止现在,还有最后一个问题,我无法确认获取的公钥确实来自老妈。

4. 数字证书

证书的作用很简单,证明公钥的身份。

就像在现实中,大家都是怎么证明自己的身份的?

没错,是身份证。你有没有发现,每张身份证,会有三种信息:

  • 自身的信息

  • 置办身份证的派出所

  • 有效期

对应的数字证书也有很多内容:

  • CA:证书的颁发机构

  • 证书的有效期

  • 公钥

  • 证书的授予对象

CA 将这些内容利用 CA 的私钥进行签名,用户使用 CA 的公钥验签,从而证明公钥的身份。

常见的证书分为两种:

  • 签名证书:由 CA 机构颁发,绝大部分网站都采用的这种方式

  • 自签名证书:由服务器自己颁发给自己

重回之前的例子,老妈只需要将自己的签名证书发给我,我就可以获取她的公钥,之后就可以正常的通信。

二、Android签名机制

在 Android 中,也需要使用数字证书做数字签名,数字证书中公钥对应的私钥由开发者持有。

在 Android Studio 中,最终会生成一个 .jks 的文件,早期 Eclipse 是 .keystore,它们都是用作证书和私钥的二进制文件。

App 如果使用了一种私钥签名,另外一个私钥签名的文件将无法安装或覆盖老的版本,这样做是为了防止已经安装的 App 被恶意的第三方覆盖。

1. Android签名机制的异同点

Android 中数字签名的生成和普通的数字签名并没有很大的区别。

但是进行数字签名的证书可以采用自签名证书,即不需要权威证书颁发机构(CA)来做背书,因为它的作用是用来标识应用程序的开发者,下载的用户并不需要这个证书来下载该 App。

2. Debug和Relase的签名

当我们在IDE中运行或调试项目时,AS 会自动使用 Android SDK 工具生成的调试证书为我们的应用签名,路径为 $HOME/.android/debug.keystore,但是应用商店可不接受使用调试证书发布的应用签名。

打包Release时,我们一般会在 app 模块中的 build.gradle 进行配置:

android {
   ...
   signingConfigs {
       release {
           storeFile file("release.keystore")
           storePassword "******"
           keyAlias "******"
           keyPassword "******"
       }
   }
}

这些都是我们生成 .jks 或者 .keystore 需要生成的参数。

三、签名方案

目前 Android 支持以下四种应用签名方案:

  • v1方案:基于 JAR 签名

  • v2方案:Android 7.0 引入,改动大

  • v3方案:Android 9.0 引入,基于 v2 的升级

  • v4方案:Android 11.0 引入,用来支持 ADB 增量 APK 安装

1 v1方案

v1 是一个老生常谈的签名了,签名过程也很简单。

我们如果选中一个任意签名后的 apk 进行解压,会找到一个 META-INF 文件,这个文件里一般会有以 MF、SF 和 RSA 结尾的文件,如图:

Android开发签名知识梳理总结

这些文件在 v1 签名流程中是这样的:

Android开发签名知识梳理总结

验证过程在 Apk 安装的过程中:

Android开发签名知识梳理总结

整个过程清晰明了,但 v1 有两个问题:

第一个问题是签名校验慢,要针对 Apk 中所有的文件进行校验,这会拖累老设备的安装时间。

第二个问题是仅针对 ZIP 条目校验,META-INF 文件不会计入校验过程。这样会导致即使我 Apk 已经签过名,工程师也可以移动条目顺序并重新压缩,也可以修改 META-INF 文件下的内容,带来一些安全隐患,早期的多渠道打包就是在这里做的文章。

2. v2方案

v2 是 Android 签名方案的一大步,它解决了 v1 遗留的签名校验慢和完整性的问题。

我们先来看一下 v2 的组成部分:

Android开发签名知识梳理总结

v1 的组成部分其实就和 Before signing 那一块儿一样,v2 多了红色区域,我们称之为APK签名分块。

Android开发签名知识梳理总结

从保护的内容来看,v1 仅保护内容1,v2 保护的区域有 1、3、4 和 2 的 signed data 区域,signed data 是 1、3 和 4 得出来的摘要等信息。

四、签名过程

就一个 App 而言,它可能有一个或者多个签名者,对于每个签名者而言,都会进行签名过程。

v2 没有对每个文件都进行计算,而是针对的所有字节。它将 1、3 和 4 区域都拆分成了大小为 1MB 的连续块,

计算方式如下:

  • 每个小块都按:字节 0xa5 + 块字节长度 + 块内容 进行计算

  • 每个1、3 和 4 块都按:字节 0xa5 + 块数 + 小块摘要 进行计算

最后,将这些一个或者多个签名者的摘要、证书等信息都打包到 Apk 中。

Android开发签名知识梳理总结

验签过程:

v2 方案的 APK 验证过程是这样的:

  • 找到APK签名分块区域

  • 每找到一个签名者,都会验证:签名算法、信息摘要、证书和公钥

  • 所有的签名者都验证通过了,APK 验证才会通过

1. v3方案

v3 方案建立在 v2 的基础上,目标是解决在更新过程中更改签名密钥的问题。

所以 APK 签名分块中 添加了两部分内容:

  • Proof-of-rotation: 一个存在替换的所有旧签名证书的链表,根节点是最旧的证书

  • SDK 版本支持

v3 和 v2 的签名过程和验证过程几乎一致,就不写出来了。

2. v4方案

如果同学们经常玩一些主机游戏,可以发现,在 PS5 或者 Swtich 上,一些游戏即使没有安装完成,我们也可以打开游戏玩一些基本功能,比如我以前常玩的 NBA 2k 系列。

Android 11 中谷歌也新增了 ADB增量APK安装 功能,比如一个 APK 有 2GB,我下载完 50 MB 以后,就可以使用一些基本功能,剩余的文件通过后台流式传输,不过 Android 11 中的这个功能是面向 ADB 的。

虽然这个功能很赞,但是对签名方案带来了一些挑战,之前的方案都是基于所有文件进行校验的,于是推出 Android * 签名方案 v4。

v4 基于 APK 所有的字节计算出 Merkle Hash 树,并将 Merkle 树的根 Hash、盐值作为签名数据进行包完整性校验,v4 签名必须单独存在 .idsig 文件中,不会存在于 APK 文件中,所以 apk 文件中仍然需要 v2 或者 v3 签名。

3. 向下兼容的签名方案

Android 中的签名方案是自上而下兼容的,如图:

Android开发签名知识梳理总结

对于 Android 11 来说,验证过程是这样的:

  • 是否支持 v4,v4 验证完了再验证 v3 或者 v2

  • v4 不通过,验证 v3

  • v3 不通过,验证 v2

  • v2 不通过,验证 v1

  • v1 不通过,安装失败

对于 Android 9 来说,就得从 v3 方案开始验证的。

来源:https://juejin.cn/post/7111116047960244254

标签:Android,开发,签名
0
投稿

猜你喜欢

  • Windows下使用Graalvm将Springboot应用编译成exe大大提高启动和运行效率(推荐)

    2023-09-27 02:05:02
  • 基于C#实现语音识别功能详解

    2023-07-12 18:55:24
  • Android自定义ListView实现下拉刷新上拉加载更多

    2021-08-20 21:17:04
  • springBoot+dubbo+zookeeper实现分布式开发应用的项目实践

    2021-11-22 02:03:25
  • Android6.0获取动态权限代码示例

    2022-07-22 10:35:15
  • Spring Boot 定义系统启动任务的多种方式

    2023-11-24 13:25:33
  • 解决IDEA maven 项目修改代码不生效,mvn clean、install后才生效

    2022-06-19 12:17:06
  • Java中的 stop the world是什么呢

    2022-09-11 21:32:42
  • Android的OkHttp包中的HTTP拦截器Interceptor用法示例

    2022-01-31 06:31:25
  • Java 多线程并发AbstractQueuedSynchronizer详情

    2022-12-22 10:54:35
  • c#实现一个超实用的证件照换底色小工具(附源码)

    2023-05-20 08:32:17
  • 100-200之间所有素数求和程序代码(二个版本)

    2022-07-03 18:53:58
  • C#中string用法实例详解

    2022-06-11 03:22:17
  • SpringBoot如何获取Kafka的Topic列表

    2023-11-26 16:01:52
  • Mybatis步骤分解实现一个增删改查程序

    2021-09-16 01:12:43
  • 一文读懂Spring Bean的生命周期

    2022-11-01 04:01:20
  • c#中多线程间的同步示例详解

    2022-07-05 00:24:56
  • Java实现LeetCode(1.两数之和)

    2021-06-03 02:11:19
  • idea手动刷新git分支的详细教程

    2022-04-05 11:53:43
  • 基于java math API 的详细解释说明

    2021-10-04 06:51:44
  • asp之家 软件编程 m.aspxhome.com