Android 4.2 JellyBean apk 签名方法

http://blog.csdn.net/virus026/article/details/8979021

 

通过阅读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 包装成一个宏方法。

  1. define sign-package  
  2. $(hide) mv $@ $@.unsigned  
  3. $(hide) java -jar $(SIGNAPK_JAR) \  
  4.     $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) $@.unsigned $@.signed  
  5. $(hide) mv $@.signed $@  
  6. endef  
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 

  1. LOCAL_PATH:= $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4. LOCAL_MODULE :BugReporter  
  5.   
  6. LOCAL_MODULE_TAGS :optional  
  7. LOCAL_SRC_FILES := $(LOCAL_MODULE).apk  
  8. LOCAL_MODULE_CLASS :APPS  
  9.   
  10. LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)  
  11. LOCAL_CERTIFICATE :platform  
  12.   
  13. include $(BUILD_PREBUILT)  
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文件时,

  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. LOCAL_JAVA_LIBRARIES :bouncycastle telephony-common  
  5. LOCAL_STATIC_JAVA_LIBRARIES :guava android-support-v4 jsr305  
  6.   
  7. LOCAL_MODULE_TAGS :optional  
  8.   
  9. LOCAL_SRC_FILES := $(call all-java-files-under, src)  
  10.   
  11. LOCAL_PACKAGE_NAME :Settings  
  12. LOCAL_CERTIFICATE :platform  
  13.   
  14. LOCAL_PROGUARD_FLAG_FILES :proguard.flags  
  15.   
  16. include $(BUILD_PACKAGE)  
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的路径是:

  1. # If this is not an absolute certificate, assign it to a generic one.  
  2.  ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./)  
  3.      LOCAL_CERTIFICATE := $(dir $(DEFAULT_SYSTEM_DEV_CERTIFICATE))$(LOCAL_CERTIFICATE)  
  4.  endif  
 # 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为例,


  1. ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE    
  2. DEFAULT_SYSTEM_DEV_CERTIFICATE := $(PRODUCT_DEFAULT_DEV_CERTIFICATE)  
  3. else    
  4. DEFAULT_SYSTEM_DEV_CERTIFICATE :build/target/product/security/testkey  
  5. 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


在config.xml中,如果定义了PRODUCT_DEFAULT_DEV_CERTIFICATE, DEFAULT_SYSTEM_DEV_CERTIFICATE就等于PRODUCT_DEFAULT_DEV_CERTIFICATE;

否则的话,就是"build/target/product/security/testkey".


product_config.xml

  1. PRODUCT_DEFAULT_DEV_CERTIFICATE := \  
  2.     $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEFAULT_DEV_CERTIFICATE))  
  3. ifdef PRODUCT_DEFAULT_DEV_CERTIFICATE  
  4. ifneq (1,$(words $(PRODUCT_DEFAULT_DEV_CERTIFICATE)))  
  5.     $(error PRODUCT_DEFAULT_DEV_CERTIFICATE='$(PRODUCT_DEFAULT_DEV_CERTIFICATE)', \  
  6.       only 1 certificate is allowed.)  
  7. endif  
  8. endif   
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脚本可以给





 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值