1. V1&V2签名
1.1 V1签名
1.2 V2签名
为了保护 APK 内容,APK 包含以下 4 个部分:
- ZIP 条目的内容(从偏移量 0 处开始一直到“APK 签名分块”的起始位置)
- APK 签名分块
- ZIP 中央目录
- ZIP 中央目录结尾
APK 签名方案 v2 负责保护第 1、3、4 部分的完整性,以及第 2 部分包含的“APK 签名方案 v2 分块”中的 signed data
分块的完整性。
第 1、3 和 4 部分的完整性通过其内容的一个或多个摘要来保护,这些摘要存储在 signed data
分块中,而这些分块则通过一个或多个签名来保护。
第 1、3 和 4 部分的摘要采用以下计算方式,类似于两级 Merkle 树。 每个部分都会被拆分成多个大小为 1 MB(220 个字节)的连续块。每个部分的最后一个块可能会短一些。每个块的摘要均通过字节 0xa5
的连接、块的长度(采用小端字节序的 uint32 值,以字节数计)和块的内容进行计算。顶级摘要通过字节 0x5a
的连接、块数(采用小端字节序的 uint32 值)以及块的摘要的连接(按照块在 APK 中显示的顺序)进行计算。摘要以分块方式计算,以便通过并行处理来加快计算速度。
2. apksigner
上面lib目录里面有apksigner.jar,这里路径最好配置到环境变量里。
apksigner对apk签名的各个参数
apksigner sign //执行签名操作
--ks 你的jks路径 //jks签名证书路径
--ks-key-alias 你的alias //生成jks时指定的alias
--ks-pass pass:你的密码 //KeyStore密码
--key-pass pass:你的密码 //签署者的密码,即生成jks时指定alias对应的密码
--out output.apk //输出路径
input.apk //需要签名的APK
apksigner sign –ks aoaoyi.jks –ks-key-alias aoaoyi –ks-pass pass:aoaoyi.com –key-pass pass:aoaoyi.com –out output.apk input.apk签名示例:
apksigner检查apk是否已经签名:
apksigner verify -v --print-certs xxx.apk
参数:
-v, --verbose 显示详情(显示是否使用V1和V2签名)
--print-certs 显示签名证书信息
例如:
apksigner verify -v aoaoyi.apk
Verifies
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Number of signers: 1
3. 批量签名
3.1 我们先来看MultiSigner.py代码
#!/usr/bin/python
#coding:UTF-8
import shutil
import os
"""
1.安装Python27,配置环境变量。
2.配置apksinger的环境变量。
3.把xxx.jks、xxx.apk(支持多个)放入和MultiSigner.py同目录下。
4.双击MultiSigner.py,出现命令行窗体,等待。
5.按任意键结束。
6.查看output目录下已签好的apk文件。
"""
#jks签名证书(放在当前目录中)
jksFile = 'aoaoyi.jks'
#KeyStore密码
storePassword = 'aoaoyi.com'
#生成jks时指定的alias
keyAlias = 'aoaoyi'
#签署者的密码,即生成jks时指定alias对应的密码
keyPassword = 'aoaoyi.com@gmail'
# 获取当前目录中所有的apk源包
src_apks = []
# python3 : os.listdir()即可,这里使用兼容Python2的os.listdir('.')
for file in os.listdir('.'):
if os.path.isfile(file):
extension = os.path.splitext(file)[1][1:]
if extension in 'apk':
src_apks.append(file)
try:
for src_apk in src_apks:
# file name (with extension)
src_apk_file_name = os.path.basename(src_apk)
# 分割文件名与后缀
temp_list = os.path.splitext(src_apk_file_name)
# name without extension
src_apk_name = temp_list[0]
# 后缀名,包含. 例如: ".apk "
src_apk_extension = temp_list[1]
# 创建生成目录
output_dir = 'output/'
# 目录不存在则创建
if not os.path.exists(output_dir):
os.mkdir(output_dir)
#目标文件路径
target_apk = output_dir + src_apk_name + src_apk_extension
#签名后的文件路径
signer_apk = output_dir + src_apk_name + '_signer' + src_apk_extension
#拼装签名命令
signer_str = 'cmd.exe /k apksigner sign --ks ' + jksFile + ' --ks-pass pass:' + storePassword + ' --ks-key-alias ' + keyAlias + ' --key-pass pass:' + keyPassword + ' --out ' + signer_apk + ' ' + src_apk
#执行签名命令
signer_result = os.popen(signer_str)
#输出签名命令执行结果
if(signer_result.read() != ''):
print 'signer_result:\t', 'success'
else:
print 'signer_result:\t', 'fail'
#拼装验证签名命令
verify_str = 'apksigner verify -v --print-certs ' + signer_apk
#执行对签过的apk进行签名验证
verify_result = os.popen(verify_str)
#输出验证签名命令执行结果
print 'verify_result:\t', verify_result.read()
#os.remove(target_apk)
except Exception, e:
print 'Exception:\t', repr(e)
print '请输入任意键退出'.decode('UTF-8').encode('GBK')
#等待输入
raw_input()
3.2. 目录文件
3.2. 运行后,命令展示信息
你把这个脚本甩给测试或运维,你就再也不受他们打扰了。如果考虑到安全的话,你在代码服务器上构建个GO界面直接调用这个脚本,把生成的apk包生成一个下载地址供下载就可以了。这样就节约了自己很多时间。