在Android应用程序打包成Apk的过程中,为防止生成的Apk被篡改,需要进行签名,生成一个签名包。在Apk的META-INF目录下,有三个文件CERT.SF,MANIFEST.MF和CERT.RSA,它们的作用是对Apk内的所有文件进行编解码验证,确保用户安装的是官方发布的Apk包。
Apk的签名文件一、MANIFEST.MF文件
Name: AndroidManifest.xml
SHA-256-Digest: vXdzhLVz8GptiwYOXfa8L3DNtsl/TIpygI+PZL0n/X0=
Name: META-INF/android.arch.core_runtime.version
SHA-256-Digest: zFL2eISLgUNzdXtGA4O/YZYOSUPCA3Na3eCjULPlCYk=
Name: res/anim/abc_fade_out.xml
SHA-256-Digest: dyKjxQwRIZ2twlcCaD3N+XtCHcKd806BO6cxWo36qh0=
...
文件内容是摘要,Apk中的所有文件,非文件夹,逐个进行sha-256,再base64计算。其中Name字段是Apk中的文件路径,SHA-256-Digest字段是每个文件的sha-256值再进行base64的结果。
通过命令将每个文件生成它的sha-256值,不同的平台都有相关命令。在Mac电脑上命令如下。
openssl dgst -sha256 文件路径
解压Apk,获取到AndroidManifest.xml文件,执行上面命令。
AndroidManifest文件的sha-256值bd777384b573f06a6d8b060e5df6bc2f70cdb6c97f4c8a72808f8f64bd27fd7d
这串字符就是获取到的该文件sha-256值,它是16进制的,先转换成字节数组,再转换base64值,下面是字节数组+base64转换方法。
public static String toByteArray(String str) {
//String str = "bd777384b573f06a6d8b060e5df6bc2f70cdb6c97f4c8a72808f8f64bd27fd7d";
byte[] src = new byte[str.length() / 2];
int k = 0;
for (int i = 0; i < src.length; i++) {
byte high = (byte) (Character.digit(str.charAt(k), 16) & 0xff);
byte low = (byte) (Character.digit(str.charAt(k + 1), 16) & 0xff);
src[i] = (byte) (high << 4 | low);
k += 2;
}
String des = "";
try {
des = android.util.Base64.encodeToString(src, android.util.Base64.DEFAULT);
} catch (Exception e) {
}
return des;
}
经过base64转换,最终的字符串。
屏幕快照 .png经过对比MANIFEST.MF文件,发现和Name字段为AndroidManifest.xml的SHA-256-Digest值一致。
注意,不要直接将sha-256值拿到网上去进行base64编码,会当成字符串进行编码,而不是16进制,编码值是错误的,将16进制转换字节数组进行编码,才能得到正确的值。
综上,打包时,Apk中除了文件夹外的所有文件都生成sha-256值,base64编码,保存在MANIFEST.MF文件中。
经过测试,同一套代码,当改变build工具buildToolsVersion版本,在27以下版本和28版本时,AndroidManifest.xml摘要在MANIFEST.MF文件中的值是不同的,下面是buildToolsVersion28版本的摘要值。
Name: AndroidManifest.xml
SHA-256-Digest: oW/hNRRbsn6AF3U690UPEe7Z9RD4uiFtPl3QbQSeMDM=
但是,一些静态图片文件的摘要值相同,经查看,是因为使用28版本build工具会在AndroidManifest文件多插入几行内容,导致文件改变了,sha-256值自然会改变。
buildToolsVersion是28.0.3版本 buildToolsVersion是27.0.2版本二、CERT.SF文件
Signature-Version: 1.0
Created-By: 1.0 (Android)
SHA-256-Digest-Manifest: KgN+jZmO5ZWFqV08U+s3kRBaboJNXt1VdLLF/HMKcBM=
X-Android-APK-Signed: 2
Name: AndroidManifest.xml
SHA-256-Digest: 3Ydc2Q6uqYweLyGPrhcsI1fWoOmiBsN9O7Udq3hQP6U=
Name: META-INF/android.arch.core_runtime.version
SHA-256-Digest: I65bgli5vdqHKel7MD74YlSuuyCR/5NDrXr2kf5FigA=
该文件也是摘要签名。对比MANIFEST.MF文件,多了SHA-256-Digest-Manifest这项,该项摘要是对MANIFEST.MF文件提取sha-256值,base64编码,和上面的流程一样。
其他项和MANIFEST.MF文件有相同的Name字段,它是对MANIFEST.MF文件里的每一项再次进行一次sha-256和base64编码。
注意,这里的每一项,是指将Name和SHA-256-Digest的字段和值整体提取出来,保存在文件中,对该文件进行sha-256和base64编码。
新建一个文件,名字随便取,将整个AndroidManifest.xml摘要项复制出来,蓝色区域,贴到新文件中,相同命令,获取新文件sha-256值,再次base64。
注意,最好直接复制,因为还有\r\n换行符。
经过base64转换,最终的字符串。
经过对比CERT.SF文件,发现和Name字段为AndroidManifest.xml的SHA-256-Digest值一致。
综上,将MANIFEST.MF文件和文件中的每一项摘要再进行一次摘要提取+base64编码,保存在CERT.SF文件。
每项包括Name和SHA-256-Digest的字段和值,MANIFEST.MF文件的摘要项是SHA-256-Digest-Manifest。
三、CERT.RSA文件
经过以上摘要和base64编码,如果我们改了官方发布包Apk的某一个文件,二次打包Apk,没修改MANIFEST.MF文件,就会导致安装失败。
当然,也可以改MANIFEST.MF文件中的对应项,重新提取摘要。同样,也需要改CERT.SF文件,这里也会验证,该对应文件项和MANIFEST.MF项。
最后,有一个CERT.RSA验证。CERT.RSA是对CERT.SF文件进行签名的文件,需要openssl命令查看,内容包括公钥,加密算法,用开发者私钥对CERT.SF文件加密后的值。
当修改了MANIFEST.MF文件和CERT.SF文件,如果没有私钥,就无法生成正确的CERT.RSA文件。
如果不修改CERT.RSA文件,在安装时,公钥解密的CERT.SF文件内容肯定与你改过的不符,安装失败。
查看打包Apk使用的签名证书,在Mac电脑,进入工程主目录,keystore证书在该目录下保存,keytool命令。
keytool -list -v -keystore keystore -storepass 123456
第二个keystore是证书名称,123456是密码,获得证书内容。
证书内容查看CERT.RSA证书内容,和打包签名的证书指纹内容相同,是签名公钥。
证书内容任重而道远
网友评论