通过阅读signapk源码,我们可以理清签名APK包的整个过程。
1、 生成MANIFEST.MF文件:
程序遍历apk包中的所有文件(entry),对非文件夹非签名文件的文件,逐个生成SHA1的数字签名信息,再用Base64进行编码。具体代码见这个方法:
关键代码如下:
之后将生成的签名写入MANIFEST.MF文件。关键代码如下:
这里简单介绍下SHA1数字签名。简单地说,它就是一种安全哈希算法,类似于MD5算法。它把任意长度的输入,通过散列算法变成固定长度的输出(这里我们称作“摘要信息”)。你不能仅通过这个摘要信息复原原来的信息。另外,它保证不同信息的摘要信息彼此不同。因此,如果你改变了apk包中的文件,那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是程序就不能成功安装。
用SHA1将apk包中的非签名文件算出哈希值。
2、 生成CERT.SF文件:
对前一步生成的Manifest,使用SHA1-RSA算法,用私钥进行签名。关键代码如下:
RSA是一种非对称加密算法。用私钥通过RSA算法对摘要信息进行加密。在安装时只能使用公钥才能解密它。解密之后,将它与未加密的摘要信息进行对比,如果相符,则表明内容没有被异常修改。
用RSA算法和私钥将哈希值加密。只由发布者才有真正的私钥。公钥是public的。
3、 生成CERT.RSA文件:
生成MANIFEST.MF没有使用密钥信息,生成CERT.SF文件使用了私钥文件。那么我们可以很容易猜测到,CERT.RSA文件的生成肯定和公钥相关。
客户将CERT.RSA中记录的公钥解密得出哈希值,和没有加密的哈希值比较判断apk包是否被篡改。
CERT.RSA文件中保存了公钥、所采用的加密算法等信息。核心代码如下:
其中writeSignatureBlock的代码如下:
好了,分析完APK包的签名流程,我们可以清楚地意识到:
1、 Android签名机制其实是对APK包完整性和发布机构唯一性的一种校验机制。
2、 Android签名机制不能阻止APK包被修改,但修改后的再签名无法与原先的签名保持一致。(拥有私钥的情况除外)。
3、 APK包加密的公钥就打包在APK包内,且不同的私钥对应不同的公钥。换句话言之,不同的私钥签名的APK公钥也必不相同。所以我们可以根据公钥的对比,来判断私钥是否一致。
1. 在definitions.mk中, Android将build/tools/signapk/signapk.jar 包装成一个宏方法。
define sign-package $(hide) mv $@ [email protected] $(hide) java -jar $(SIGNAPK_JAR) \ $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) [email protected] [email protected] $(hide) mv [email protected] $@ endef
PRIVATE_CERTIFICATE 和 PRIVATE_PRIVATE_KEY由调用方法赋值。
2. 在android build/core下面只由prebuild.mk 和 package.mk 调用了singn-package宏,
A) 当系统包含预先编译过的.apk文件时,Andoid.mk
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := BugReporter LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(LOCAL_MODULE).apk LOCAL_MODULE_CLASS := APPS LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_CERTIFICATE := platform include $(BUILD_PREBUILT)
In config.mk BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
B) 当系统包含带源代码的.apk文件时,
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_JAVA_LIBRARIES := bouncycastle telephony-common LOCAL_STATIC_JAVA_LIBRARIES := guava android-support-v4 jsr305 LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := Settings LOCAL_CERTIFICATE := platform LOCAL_PROGUARD_FLAG_FILES := proguard.flags include $(BUILD_PACKAGE)
3. 那么prebuilt.mk和package.mk是如何给sign-package的PRIVATE_CERTIFICATE 和 PRIVATE_PRIVATE_KEY变量赋值的呢。
对于prebuit apk来说,如果LOCAL_CERTIFICATE是presigned或是空的话,不指定LOCAL_CERTIFICATE路径
对于源代码的apk来说,如果 LOCAL_CERTIFICATE是EXTERNAL的话,不指定LOCAL_CERTIFICATE路径
否则的话LOCAL_CERTIFICATE的路径是:
# If this is not an absolute certificate, assign it to a generic one. ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./) LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE) endif
ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE DEFAULT_SYSTEM_DEV_CERTIFICATE := $(PRODUCT_DEFAULT_DEV_CERTIFICATE) else DEFAULT_SYSTEM_DEV_CERTIFICATE := build/target/product/security/testkey endif
否则的话,就是"build/target/product/security/testkey".
product_config.xml
PRODUCT_DEFAULT_DEV_CERTIFICATE := \ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEFAULT_DEV_CERTIFICATE)) ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE ifneq (1,$(words $(PRODUCT_DEFAULT_DEV_CERTIFICATE))) $(error PRODUCT_DEFAULT_DEV_CERTIFICATE='$(PRODUCT_DEFAULT_DEV_CERTIFICATE)', \ only 1 certificate is allowed.) endif endif