应用签名是一种数字签名,主要用来验证应用的完整性以及发布者身份认证。
在Android应用发布之前非常重要的一环就是应用签名,Android系统要求所有APK在安装到设备上或更新之前都必须使用证书进行数字签名,Android系统会验证应用的签名。
版本更新时,新版本APK必须使用相同密钥签名,否则无法覆盖安装;如果签名变更,也无法更新应用,必须卸载重新安装。可见签名对Android应用发布的重要性。
在Android中,签名一般包含三个信息,密钥库、秘钥和证书,在应用首次发布时,我们可以借助AndroidStudio工具来生成它们。
选择Build>GenerateSignedBundle/APK开始生成密钥库和秘钥。
选择签名类型,这里选择APK,然后点击Next按钮。
点击Createnew按钮来创建新的秘钥库文件,如果已有秘钥库文件,选择对应路径即可。
在表单中填写如下两部分信息。
填写完所有信息之后,点击OK按钮即可创建密钥文件。
密钥库和秘钥创建成功之后,会自动回到上一页并填充我们刚才填好的签名信息。
然后点击Next按钮,选择需要签名的构建变体。
一般选择release,也可以多选。
点击Create按钮即开始构建并签名了。
打包构建完成后,签名好的APK文件在app/变体/目录下:
上面可视化的签名打包方式,一般适用于临时构建,或应用渠道较少的场景。
好处是操作方便,不易泄露秘钥的密码等关键信息。
不足的是不适用自动化构建(CI/CD)场景,以及多渠道构建场景下,可能会出现渠道选漏的情况。
如果在工作中,渠道包是自动化构建的,那么就需要在build.gradle文件中进行签名配置了。
签名配置步骤如下:
第一步,在项目的根目录下创建一个名为keystore.properties的文件,并在其中包含以下信息:
storePassword=myStorePasswordkeyPassword=myKeyPasswordkeyAlias=myKeyAliasstoreFile=myStoreFileLocation第二步,在build.gradle文件中,按如下方式加载keystore.properties文件(必须在android代码块前面):
importjava.util.Propertiesimportjava.io.FileInputStreamvalkeystorePropertiesFile=rootProject.file("keystore.properties")valkeystoreProperties=Properties()keystoreProperties.load(FileInputStream(keystorePropertiesFile))android{...}...第三步,在signingConfigs{}中配置存储在keystoreProperties文件中的签名信息:
android{signingConfigs{create("config"){keyAlias=keystoreProperties["keyAlias"]asStringkeyPassword=keystoreProperties["keyPassword"]asStringstoreFile=file(keystoreProperties["storeFile"]asString)storePassword=keystoreProperties["storePassword"]asString}}...}第四步,在构建类型buildTypes中引用signingConfigs{}中的签名配置:
buildTypes{release{signingConfig=signingConfigs.getByName("release")//...}}最后点击Sync同步即可。
这样就可以使用命令./gradlewassembleRelease来进行批量签名打包了。
在本地开发阶段,AndroidStudio会自动生成Debug签名,无需手动配置,但是Debug签名无法用于应用发布。
v1签名(Jar签名):传统的JAR签名机制,验证速度慢,但兼容早期版本(7.0以前)。v1签名不保护APK的某些部分,例如ZIP元数据。
v2签名(APK签名方案):Android7.0引入,基于Merkle哈希树,比v1签名更安全,验证速度更快,支持Android7.0及以上。
v3签名(APK签名方案):Android9.0引入,继承了v2签名的性能优势和安全性,并对整个APK文件进行签名检查,还增加了秘钥轮换能力,就是允许对应用的签名密钥进行更新,而不会影响应用的安装和更新。
v3.1签名(APK签名方案):Fix版本,Android13.0引入,修复了v3方案中秘钥轮换的已知问题。
v4签名(APK签名方案):Android11.0引入,主要是在验证方面,提升了APK增量更新和安装速度。
美团基于v2签名方案的多渠道打包方案(walle)原理是:打包过程中在META-INF目录下添加空文件用来作为渠道的唯一标识。
在v3签名方案中,基于Merkle哈希树对整个APK文件进行签名检查,确保APK文件的完整性,对APK进行的任何修改都会使APK签名作废,从而导致安装失败。而META-INF作为APK的一部分(签名块),自然也是会被签名检查,且不能修改的。
所以这也是walle为什么会在Android9.0(v3)以后失效的原因。
那这几个签名方案怎么选呢?不妨先看看市面上大型APP它们是怎么选的?
v1、v2的兼容性和安全性不错,一般是必选的,v3、v4有新特性,性能较好,可以结合自己的项目按需配置(v4需要与v2或v3结合使用)。
确定了签名方案之后,就是怎么让签名方案生效了,这就需要在build.gradle文件中添加签名方案配置了。
示例配置如下:
//配置签名方案buildTypes{release{//...//启用v1签名v1SigningEnabledtrue//启用v2签名v2SigningEnabledtrue//启用v3签名v3SigningEnabledtrue//启用v4签名v4SigningEnabledtrue}}新的签名格式向后兼容,比如使用v3签名方案的APK可以在5.0的设备上安装,前提是包含v1签名方案。
以v4签名为例,验证流程如下:
在v2、v3、v4签名验证流程中,最大的区别在于,v2、v3的签名规则验证不通过会直接拒接安装,v4则是继续往下走。
查看一个已经签名的APK文件使用了哪种签名方案,通常需要一些工具来解析和分析APK的签名信息。
我们可以使用AndroidSDK提供的apksigner工具,apksigner主要用于签名和验证APK文件,通过apksigner可以查看APK的签名信息。
在使用apksigner之前,需要先确认环境变量是否配置正确。
以macOS系统为例:
找到~/.bash_profile或~/.bashrc文件,进入编辑。
在文件末尾添加以下内容:
exportPATH=$PATH:~/Library/Android/sdk/build-tools/33.0.1AndroidSDK路径和版本号替换成你自己的。
然后执行命令刷新文件是环境变量配置立即生效:
source~/.bashrc//orsource~/.zshrc5.5.2、apksigner使用配置好环境之后,执行以下命令:
apksignerverify--verbose--print-certs/Users/yechao/AndroidStudioProjects/GradleX/app/release/app-release.apkapk地址换成你自己的。
输出如下图:
下面来介绍几种常用的提取签名信息的方式。
signingReport是Gradle插件提供的一个能力,用于生成签名报告。
在Gradle面板中找到signingReport任务,app>Tasks>android>signingReport。
在执行signingReport任务前,确认在build.gradle文件中已经正确配置了signingConfigs。
双击执行signingReport任务,在构建日志中会同时输出debug和release签名信息。
如下图:
keytool是JavaSDK自带的一个工具,可以用来查看密钥库文件(例如.jks或.keystore)中的签名信息,包括SHA-1指纹。
keytool支持两种文件方式查看签名信息,分别是keystore和apk。
命令如下:
keytool-list-v-keystoreyour-keystore-file.jks输出:
keytool-printcert-jarfileyour-apk-file.apk输出:
如果签名文件丢失或者损坏,唯一的解决办法就是生成新的签名文件,但是会导致应用版本更新问题,需要用户卸载重装,沉没成本较大。
一些安全建议:
本章我们了解了Android应用签名的重要性、具体的实现方法,和签名方案的介绍及方案选择,还有签名验证流程,最后是一些关于签名安全的建议。