Android 4.2 JellyBean apk 签名方法。
2013-05-28 11:15
369 查看
通过阅读signapk源码,我们可以理清签名APK包的整个过程。
1、 生成MANIFEST.MF文件:
程序遍历apk包中的所有文件(entry),对非文件夹非签名文件的文件,逐个生成SHA1的数字签名信息,再用Base64进行编码。具体代码见这个方法:
private static Manifest addDigestsToManifest(JarFile jar)
关键代码如下:
1 for (JarEntry entry: byName.values()) {
2 String name = entry.getName();
3 if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
4 !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
5 (stripPattern == null ||!stripPattern.matcher(name).matches())) {
6 InputStream data = jar.getInputStream(entry);
7 while ((num = data.read(buffer)) > 0) {
8 md.update(buffer, 0, num);
9 }
10 Attributes attr = null;
11 if (input != null) attr = input.getAttributes(name);
12 attr = attr != null ? new Attributes(attr) : new Attributes();
13 attr.putValue("SHA1-Digest", base64.encode(md.digest()));
14 output.getEntries().put(name, attr);
15 }
16 }
之后将生成的签名写入MANIFEST.MF文件。关键代码如下:
1 Manifest manifest = addDigestsToManifest(inputJar);
2 je = new JarEntry(JarFile.MANIFEST_NAME);
3 je.setTime(timestamp);
4 outputJar.putNextEntry(je);
5 manifest.write(outputJar);
这里简单介绍下SHA1数字签名。简单地说,它就是一种安全哈希算法,类似于MD5算法。它把任意长度的输入,通过散列算法变成固定长度的输出(这里我们称作“摘要信息”)。你不能仅通过这个摘要信息复原原来的信息。另外,它保证不同信息的摘要信息彼此不同。因此,如果你改变了apk包中的文件,那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是程序就不能成功安装。
用SHA1将apk包中的非签名文件算出哈希值。
2、 生成CERT.SF文件:
对前一步生成的Manifest,使用SHA1-RSA算法,用私钥进行签名。关键代码如下:
1 Signature signature = Signature.getInstance("SHA1withRSA");
2 signature.initSign(privateKey);
3 je = new JarEntry(CERT_SF_NAME);
4 je.setTime(timestamp);
5 outputJar.putNextEntry(je);
6 writeSignatureFile(manifest,
7 new SignatureOutputStream(outputJar, signature));
RSA是一种非对称加密算法。用私钥通过RSA算法对摘要信息进行加密。在安装时只能使用公钥才能解密它。解密之后,将它与未加密的摘要信息进行对比,如果相符,则表明内容没有被异常修改。
用RSA算法和私钥将哈希值加密。只由发布者才有真正的私钥。公钥是public的。
3、 生成CERT.RSA文件:
生成MANIFEST.MF没有使用密钥信息,生成CERT.SF文件使用了私钥文件。那么我们可以很容易猜测到,CERT.RSA文件的生成肯定和公钥相关。
客户将CERT.RSA中记录的公钥解密得出哈希值,和没有加密的哈希值比较判断apk包是否被篡改。
CERT.RSA文件中保存了公钥、所采用的加密算法等信息。核心代码如下:
1 je = new JarEntry(CERT_RSA_NAME);
2 je.setTime(timestamp);
3 outputJar.putNextEntry(je);
4 writeSignatureBlock(signature, publicKey, outputJar);
其中writeSignatureBlock的代码如下:
1 private static void writeSignatureBlock(
2 Signature signature, X509Certificate publicKey, OutputStream out)
3 throws IOException, GeneralSecurityException {
4 SignerInfo signerInfo = new SignerInfo(
5 new X500Name(publicKey.getIssuerX500Principal().getName()),
6 publicKey.getSerialNumber(),
7 AlgorithmId.get("SHA1"),
8 AlgorithmId.get("RSA"),
9 signature.sign());
10
11 PKCS7 pkcs7 = new PKCS7(
12 new AlgorithmId[] { AlgorithmId.get("SHA1") },
13 new ContentInfo(ContentInfo.DATA_OID, null),
14 new X509Certificate[] { publicKey },
15 new SignerInfo[] { signerInfo });
16
17 pkcs7.encodeSignedData(out);
18 }
好了,分析完APK包的签名流程,我们可以清楚地意识到:
1、 Android签名机制其实是对APK包完整性和发布机构唯一性的一种校验机制。
2、 Android签名机制不能阻止APK包被修改,但修改后的再签名无法与原先的签名保持一致。(拥有私钥的情况除外)。
3、 APK包加密的公钥就打包在APK包内,且不同的私钥对应不同的公钥。换句话言之,不同的私钥签名的APK公钥也必不相同。所以我们可以根据公钥的对比,来判断私钥是否一致。
编译时签名
Android 只会对apk文件做签名。1. 在definitions.mk中, Android将build/tools/signapk/signapk.jar 包装成一个宏方法。
define sign-package $(hide) mv $@ $@.unsigned $(hide) java -jar $(SIGNAPK_JAR) \ $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) $@.unsigned $@.signed $(hide) mv $@.signed $@ endef
config.mk 定义了SIGNAPK_JAR := $(HOST_OUT_JAVA_LIBRARIES)/signapk$(COMMON_JAVA_PACKAGE_SUFFIX)
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)
config.mk:70:BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
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
LOCAL_CERTIFICATE在我们自己定义的Android.mk中可以是: platform, media, shared or test。 例子中是platform, 那我们就以platform为例,
ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE DEFAULT_SYSTEM_DEV_CERTIFICATE := $(PRODUCT_DEFAULT_DEV_CERTIFICATE) else DEFAULT_SYSTEM_DEV_CERTIFICATE := build/target/product/security/testkey endif
在config.xml中,如果定义了PRODUCT_DEFAULT_DEV_CERTIFICATE, DEFAULT_SYSTEM_DEV_CERTIFICATE就等于PRODUCT_DEFAULT_DEV_CERTIFICATE;
否则的话,就是"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
给PRODUCT_DEFAULT_DEV_CERTIFICATE赋值。可以在这里在编译时更改默认的key的位置。
给apk压缩包签名
在build/tools/releasetools/sign_target_files_apks脚本可以给相关文章推荐
- Android 4.2 JellyBean apk 签名方法
- Android APK破解、反编译、打包签名基本思路与方法
- android apk签名方法
- Android 使用jarsigner给apk签名的方法详细介绍
- android apk 应用 重新签名 方法 .
- Android apk签名方法
- Android apk签名方法介绍
- Android apk签名方法
- android实现给未签名的apk签名方法
- 新版Cordova(>4.0)编译Android APK打上签名方法
- 给Android的APK程序签名和重新签名的方法
- 解决在eclipse中将android项目生成apk并且给apk签名的实现方法详解
- Android apk签名的两种方法
- androidstudio签名打包之后apk安装不上,adb报错Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES]的原因及解决方法
- Android查看apk签名信息方法
- Robotium_android apk签名打包 原理 用处 方法
- 〖Android〗JDK7签名apk出现INSTALL_PARSE_FAILED_NO_CERTIFICATES的解决方法
- android获取APK文件,及应用内部签名信息方法
- android给apk空包签名的方法