给Android的APK程序签名和重新签名的方法

签名工具的使用
Android源码编译出来的signapk.jar既可给apk签名,也可给rom签名的。使用格式:

java –jar signapk.jar [-w] publickey.x509[.pem] privatekey.pk8 input.jar output.jar
  • -w 是指对ROM签名时需使用的参数
  • publickey.x509[.pem] 是公钥文件
  • privatekey.pk8 是指 私钥文件
  • input.jar 要签名的apk或者rom
  • output.jar 签名后生成的apk或者rom

signapk.java

1) main函数
main函数会生成公钥对象和私钥对象,并调用addDigestsToManifest函数生成清单对象Manifest后,再调用signFile签名。

 public static void main(String[] args) {
 //...
 boolean signWholeFile = false;
 int argstart = 0;
 /*如果对ROM签名需传递-w参数*/
 if (args[0].equals("-w")) {
  signWholeFile = true;
  argstart = 1;
 }
 // ...
 try {
  File publicKeyFile = new File(args[argstart+0]);
  X509Certificate publicKey = readPublicKey(publicKeyFile);
  PrivateKey privateKey = readPrivateKey(new File(args[argstart+1]));
  inputJar = new JarFile(new File(args[argstart+2]), false);
  outputFile = new FileOutputStream(args[argstart+3]);
  /*对ROM签名,读者可自行分析,和Apk饿签名类似,但是它会添加otacert文件*/
  if (signWholeFile) {
   SignApk.signWholeFile(inputJar, publicKeyFile, publicKey,
    privateKey, outputFile);
  }
  else {
   JarOutputStream outputJar = new JarOutputStream(outputFile);
   outputJar.setLevel(9);
   /*addDigestsToManifest会生成Manifest对象,然后调用signFile进行签名*/
   signFile(addDigestsToManifest(inputJar), inputJar,
   publicKeyFile, publicKey, privateKey, outputJar);
   outputJar.close();
  }
 } catch (Exception e) {
  e.printStackTrace();
  System.exit(1);
 } finally {
  //...
 }
}

2) addDigestsToManifest
首先我们得理解Manifest文件的结构,Manifest文件里用空行分割成多个段,每个段由多个属性组成,第一个段的属性集合称为主属性集合,其它段称为普通属性集合,普通属性集合一般会有Name属性,作为该属性集合所在段的名字。Android的manifeset文件会为zip的所有文件各自建立一个段,这个段的Name属性的值就是该文件的path+文件名,另外还有一个SHA1-Digest的属性,该属性的值是对文件的sha1摘要用base64编码得到的字符串。
Manifest示例:

Manifest-Version: 1.0
Created-By: 1.6.0-rc (Sun Microsystems Inc.)

Name: res/drawable-hdpi/user_logout.png
SHA1-Digest: zkQSZbt3Tqc9myEVuxc1dzMDPCs=

Name: res/drawable-hdpi/contacts_cancel_btn_pressed.png
SHA1-Digest: mSVZvKpvKpmgUJ9oXDJaTWzhdic=

Name: res/drawable/main_head_backgroud.png
SHA1-Digest: fe1yzADfDGZvr0cyIdNpGf/ySio=

Manifest-Version属性和Created-By所在的段就是主属性集合,其它属性集合就是普通属性集合,这些普通属性集合都有Name属性,作为该段的名字。
addDigestsToManifest源代码:

private static Manifest addDigestsToManifest(JarFile jar)
   throws IOException, GeneralSecurityException {
 Manifest input = jar.getManifest();
 Manifest output = new Manifest();
 Attributes main = output.getMainAttributes();
 if (input != null) {
  main.putAll(input.getMainAttributes());
 } else {
  main.putValue("Manifest-Version", "1.0");
  main.putValue("Created-By", "1.0 (Android SignApk)");
 }
 MessageDigest md = MessageDigest.getInstance("SHA1");
 byte[] buffer = new byte[4096];
 int num;
 // We sort the input entries by name, and add them to the
 // output manifest in sorted order. We expect that the output
 // map will be deterministic.
 TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();

 for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
  JarEntry entry = e.nextElement();
  byName.put(entry.getName(), entry);
 }

 for (JarEntry entry: byName.values()) {
  String name = entry.getName();
  if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
   !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) &&
   !name.equals(OTACERT_NAME) &&
   (stripPattern == null ||
    !stripPattern.matcher(name).matches())) {
   InputStream data = jar.getInputStream(entry);
   /*计算sha1*/
   while ((num = data.read(buffer)) > 0) {
    md.update(buffer, 0, num);
   }
   Attributes attr = null;
   if (input != null) attr = input.getAttributes(name);
   attr = attr != null ? new Attributes(attr) : new Attributes();
   /*base64编码sha1值得到SHA1-Digest属性的值*/
   attr.putValue("SHA1-Digest",
       new String(Base64.encode(md.digest()), "ASCII"));
   output.getEntries().put(name, attr);
  }
 }
 return output;
}

3) signFile
先将inputjar的所有文件拷贝至outputjar,然后生成Manifest.MF,CERT.SF和CERT.RSA

public static void signFile(Manifest manifest, JarFile inputJar,
File publicKeyFile, X509Certificate publicKey, PrivateKey privateKey,
 JarOutputStream outputJar) throws Exception {
 // Assume the certificate is valid for at least an hour.
 long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
 JarEntry je;
 // 拷贝文件
 copyFiles(manifest, inputJar, outputJar, timestamp);
 // 生成MANIFEST.MF
 je = new JarEntry(JarFile.MANIFEST_NAME);
 je.setTime(timestamp);
 outputJar.putNextEntry(je);
 manifest.write(outputJar);
 // 调用writeSignatureFile 生成CERT.SF
 je = new JarEntry(CERT_SF_NAME);
 je.setTime(timestamp);
 outputJar.putNextEntry(je);
 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 writeSignatureFile(manifest, baos);
 byte[] signedData = baos.toByteArray();
 outputJar.write(signedData);
 // 非常关键的一步 生成 CERT.RSA
 je = new JarEntry(CERT_RSA_NAME);
 je.setTime(timestamp);
 outputJar.putNextEntry(je);
 writeSignatureBlock(new CMSProcessableByteArray(signedData),
      publicKey, privateKey, outputJar);
}

4) writeSignatureFile
生成CERT.SF,其实是对MANIFEST.MF的各个段再次计算Sha1摘要得到CERT.SF。

private static void writeSignatureFile(Manifest manifest, OutputStream out)
  throws IOException, GeneralSecurityException {
 Manifest sf = new Manifest();
 Attributes main = sf.getMainAttributes();
 //添加属性
 main.putValue("Signature-Version", "1.0");
 main.putValue("Created-By", "1.0 (Android SignApk)");
 MessageDigest md = MessageDigest.getInstance("SHA1");
 PrintStream print = new PrintStream(
   new DigestOutputStream(new ByteArrayOutputStream(), md),
   true, "UTF-8");
 // 添加Manifest.mf的sha1摘要
 manifest.write(print);
 print.flush();
 main.putValue("SHA1-Digest-Manifest",
     new String(Base64.encode(md.digest()), "ASCII"));
 //对MANIFEST.MF的各个段计算sha1摘要
 Map<String, Attributes> entries = manifest.getEntries();
 for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
  // Digest of the manifest stanza for this entry.
  print.print("Name: " + entry.getKey() + "\r\n");
  for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
   print.print(att.getKey() + ": " + att.getValue() + "\r\n");
  }
  print.print("\r\n");
  print.flush();

  Attributes sfAttr = new Attributes();
  sfAttr.putValue("SHA1-Digest",
      new String(Base64.encode(md.digest()), "ASCII"));
  sf.getEntries().put(entry.getKey(), sfAttr);
 }
 CountOutputStream cout = new CountOutputStream(out);
 sf.write(cout);
 // A bug in the java.util.jar implementation of Android platforms
 // up to version 1.6 will cause a spurious IOException to be thrown
 // if the length of the signature file is a multiple of 1024 bytes.
 // As a workaround, add an extra CRLF in this case.
 if ((cout.size() % 1024) == 0) {
  cout.write('\r');
  cout.write('\n');
 }
}

5) writeSignatureBlock
采用SHA1withRSA算法对CERT.SF计算摘要并加密得到数字签名,使用的私钥是privateKey,然后将数字签名和公钥一起存入CERT.RSA。这里使用了开源库bouncycastle来签名。

private static void writeSignatureBlock(
 CMSTypedData data, X509Certificate publicKey, PrivateKey privateKey,
 OutputStream out)
 throws IOException,
   CertificateEncodingException,
   OperatorCreationException,
   CMSException {
 ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>(1);
 certList.add(publicKey);
 JcaCertStore certs = new JcaCertStore(certList);
 CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
 //签名算法是SHA1withRSA
 ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA")
  .setProvider(sBouncyCastleProvider)
  .build(privateKey);
 gen.addSignerInfoGenerator(
  new JcaSignerInfoGeneratorBuilder(
   new JcaDigestCalculatorProviderBuilder()
   .setProvider(sBouncyCastleProvider)
   .build())
  .setDirectSignature(true)
  .build(sha1Signer, publicKey));
 gen.addCertificates(certs);
 CMSSignedData sigData = gen.generate(data, false);

 ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
 DEROutputStream dos = new DEROutputStream(out);
 dos.writeObject(asn1.readObject());
}

采用命令行重新签名APK
重新签名apk,其实也有最简单的方法,即下载一个重新签名的工具re-sign.jar,将apk拖进此工具的窗口就生成了重新签名的apk了。下面我就来讲讲复杂的重新签名的方式:采用命令行方法。
一、配置环境,需安装jdk,sdk
二、在已成功安装jdk的目录中找到jarsigner.exe文件,本机的目录如下:C:\Program Files\Java\jdk1.8.0_20\bin
三、去除准备重新签名的apk本身的签名(fantongyo.apk)
将apk以Winrar方式打开,删除META-INF文件夹即可,并将此Apk文件拷贝至C:\Program Files\Java\jdk1.8.0_20\bin目录中
Apk压缩包内容解析:
1.META-INF目录:存放签名后的CERT和MANIFEST文件,用于识别软件的签名及版本信息
2.rest目录:存放各种Android原始资源,包括:动画anim、图片drawable、布局layout、菜单、xml等等
3.AndroidManifest.xml编码后的Android项目描述文件,包括了Android项目的名称、版限、程序组件描述等等
4.Classes.dex编译后Class被dx程序转换成Dalvik虚拟机的可执行字节码文件
5.Resources.arsc所有文本资源的编译产物,里面包含了各Location对应的字符串资源
四、重新签名Apk文件
方法一:通过命令重新生成AndroidApk包签名证书后再重新签名Apk文件
1.在cmd中切换到jdk的bin目录中:cd C:\Program Files\Java\jdk1.8.0_20\bin 回车
2.再输入以下的命令:

Keytool -genkey -alias fantongyo.keystore -keyalg RSA -validity 20000 -keystore fantongyo.keystore
/*解释:keytool工具是Java JDK自带的证书工具
-genkey参数表示:要生成一个证书(版权、身份识别的安全证书)
-alias参数表示:证书有别名,-alias fantongyo.keystore表示证书别名为:fantongyo
-keyalg RSA表示加密类型,RSA表示需要加密,以防止别人盗取
-validity 20000表示有效时间20000天( K3
-keystore fantongyo.keystore表示要生成的证书名称为fantongyo
*/

输入完回车后屏幕显示:
输入keystore密码:[密码不回显](一般建议使用20位,最好记下来后面还要用)
再次输入新密码:[密码不回显]( o' ^$ _( F( K& I0
您的名字与姓氏是什么?
[Unknown]:fantongyo
您的组织单位名称是什么?
[Unknown]:fantong
您的组织名称是什么?
[Unknown]:life
您所在的城市或区域名称是什么?) L# V' |. E0 f; {
[Unknown]:shenzhen
您所在的州或省份名称是什么?
[Unknown]:guangdong
该单位的两字母国家代码是什么
[Unknown]:CN
CN=fantongyo, U=fantong, O=fantong team, L=shenzhen, ST=guangdong, C=CN正确吗?
[否]:Y
输入< mine.keystore>的主密码
(如果和keystore密码相同,按回车):
查看C:\Program Files\Java\jdk1.8.0_20\bin目录下,生成了一个签名用的证书文件 fantongyo.keystore
3.重新签名Apk文件
在cmd中输入:jarsigner –verbose –keystore fantongyo.keystore –signedjar fantongyo_signed.apk fantongyo.apk fantongyo.keystore
/*解释:* ^, {& k1 Z. M* P/ M+ K5 n5 hjarsigner是Java的签名工具# K8 ~% s# Y. @6 P
-verbose参数表示:显示出签名详细信息
-keystore表示使用当前目录中的fantongyo.keystore签名证书文件。
-signedjar fantongyo_signed.apk表示签名后生成的APK名称,% v! a7 e2 v4 W# ]; Gfantongyo.apk表示未签名 的APK Android软件,fantongyo.keystore表示别名
*/
输入完回车后屏幕显示:
jar已签名。

在C:\Program Files\Java\jdk1.8.0_20\bin目录下已重新生成fantongyo_signed.apk文件

方法二、以android自带的debug.keystore重新签名Apk文件
1.打开eclipse,菜单栏Window—>Preferences—>Android—>Build—>Default debug keystore目录(我的编辑器显示:C:\Users\Administrator\.android\debug.keystore)
2.将debug.keystore文件拷贝至C:\Program Files\Java\jdk1.8.0_20\bin目录下
3.在cmd中切换到jdk的bin目录中:cd C:\Program Files\Java\jdk1.8.0_20\bin 回车
4.再输入以下的命令:

代码如下:

jarsigner -digestalg SHA1 -sigalg MD5withRSA -keystore debug.keystore -storepass android -keypass android fantongyo.apk androiddebugkey

回车
5.在sdk中找到zipalign文件,我电脑的目录为:E:\SoftWare\adt-bundle-windows-x86-20140702\sdk\build-tools\android-4.4W
在cmd中切换到sdk的存放zipalign.exe文件的目录中:

cd E:\SoftWare\adt-bundle-windows-x86-20140702\sdk\build-tools\android-4.4W

6.再输入:zipalign 4 fantongyo.apk fantongyo_signed.apk即可(fantongyo_signed.apk是   重新签名后的apk文件)

(0)

相关推荐

  • Android签名机制介绍:生成keystore、签名、查看签名信息等方法

    Android独有的安全机制,除了权限机制外,另外一个就是签名机制了.签名机制主要用在以下两个主要场合起到其作用:升级App和权限检查. 升级App 用户在升级一款已经安装过的App时,如果程序的修改来自于同一来源,则允许升级安装,否则会提示签名不一致无法安装的提示. 权限检查 我曾在Android Permission权限机制的具体使用一文中提过,对于申请权限的  protection level 为 signature 或者 signatureOrSystem 的,会检查权限申请者和权限声明

  • Android APK使用Debug签名重新打包 Eclipse更改默认Debug签名

    对已有的apk文件进行重新打包,前面 Android签名机制:生成keystore.签名.查看签名信息 已经介绍了.本文介绍另外两种需求. 使用默认的Debug签名打包 如果直接使用Eclipse开发新程序,默认就会被打上Debug的签名,这个是地球人皆知,但是如果你是想Repack别人并且想使用Debug签名该怎么办? 首先需要找到你本机的默认签名的,一般位于C:\Users\用户名\.android\debug.keystore下,拷出来,然后打包,打包命令: 复制代码 代码如下: jars

  • android应用签名详细步骤

    1.准备工作apk的签名工作可以通过两种方式来完成: 1)通过ADT提供的图形化界面完成apk签名:右键项目--> Android Tools -->Export Signed Application Package... --> 打开图形界面2)完全通过shell/dos命令来完成apk签名 我比较喜欢第2)种方式,所以下面将讲解如何通过命令的方式完成apk签名. 给apk签名一共要用到3个工具,或者说3个命令,分别是:keytool. jarsigner. zipalign,下面是对

  • 解决在eclipse中将android项目生成apk并且给apk签名的实现方法详解

    生成apk最懒惰的方法是:只要你运行过android项目,到工作目录的bin文件夹下就能找到与项目同名的apk文件,这种apk默认是已经使用debug用户签名的.如果想要自己给apk签名:1.签名的意义为了保证每个应用程序开发商合法ID,防止部分开放商可能通过使用相同的Package Name来混淆替换已经安装的程序,我们需要对我们发布的APK文件进行唯一签名,保证我们每次发布的版本的一致性(如自动更新不会因为版本不一致而无法安装).2.签名的步骤a.创建keyb.使用步骤a中产生的key对ap

  • Android获取apk程序签名信息代码示例

    复制代码 代码如下: 这是获取apk包的签名信息private String getSign(Context context) {  PackageManager pm = context.getPackageManager();  List<PackageInfo> apps = pm.getInstalledPackages(PackageManager.GET_SIGNATURES);  Iterator<PackageInfo> iter = apps.iterator()

  • Android 应用签名的两种方法

    Android 应用签名的两种方法 一.使用pem签名 (一) apk签名命令 java –jar signapk.jar [-w] publickey.x509[.pem] privatekey.pk8 Hello.apk Hello_signed.apk -w 是指对ROM签名时需使用的参数 publickey.x509[.pem] 签名公钥文件,例如platform.x509.pem,在build\target\product\security下 privatekey.pk8 签名私钥文件

  • 给Android的APK程序签名和重新签名的方法

    签名工具的使用 Android源码编译出来的signapk.jar既可给apk签名,也可给rom签名的.使用格式: java –jar signapk.jar [-w] publickey.x509[.pem] privatekey.pk8 input.jar output.jar -w 是指对ROM签名时需使用的参数 publickey.x509[.pem] 是公钥文件 privatekey.pk8 是指 私钥文件 input.jar 要签名的apk或者rom output.jar 签名后生成

  • Android adb.exe程序启动不起来 具体解决方法

    经常遇到 Please ensure that adb is correctly located at 'D:\java\sdk\platform-tools\adb.exe' and can be executed. 遇到问题描述: 运行android程序控制台输出 [2012-07-18 16:18:26 - ] The connection to adb is down, and a severe error has occured.    [2012-07-18 16:18:26 - ]

  • Android 实现银联刷卡机消费后手动签名的功能(示例代码)

    几天前去物管交物业费,物管工作人员说小区引进高新产品,使用银行卡消费后,不需要拿笔在银联机上签名,直接用手指触摸实现消费签名,当时心想,果然是高科技,机子外形如下左图,签名如下右图. 仔细一看,其实就是一个触摸屏,用户在上面直接手动签名,实现这个功能其实并不复杂,我们自定义一个控件,继承view,使用 Canvas的drawLine,drawPoint这两个方法来实现这个功能. 首先自定义控件 MDrawLineView package com.view; import android.cont

  • Android的APK应用签名机制以及读取签名的方法

    发布过Android应用的朋友们应该都知道,Android APK的发布是需要签名的.签名机制在Android应用和框架中有着十分重要的作用.例如,Android系统禁止更新安装签名不一致的APK:如果应用需要使用system权限,必须保证APK签名与Framework签名一致,等等. 什么是签名 首先我们得知道什么是摘要,摘要是指采用单向Hash函数对数据进行计算生成的固定长度的Hash值,摘要算法有Md5,Sha1等,Md5生成的Hash值是128位的数字,即16个字节,用十六进制表示是32

  • Android Apk去掉签名以及重新签名的方法

    Android Apk去掉签名以及重新签名的方法 Android开发中很重要的一部就是用自己的密钥给Apk文件签名,不经过签名的Apk文件一般是无法安装的,就算装了最后也是失败. 网上流传的"勾选允许安装未知来源的应用"其实跟签不签名没啥关系,说白了就是允许安装不从电子市场上下载的应用而已. 近几日需要修改一个Apk中JNI调用的.so文件,苦于没有apk源代码,只好研究了一下签名相关的问题.当然有很多第三方工具可以做到,但其实JDK中已经提供了强大的签名工具jarsigner. 1.

  • Android获取apk签名指纹的md5值(防止重新被打包)的实现方法

    本文实例讲述了Android获取apk签名指纹的md5值以防止重新被打包的实现方法.分享给大家供大家参考,具体如下: 做个记录(这里只是Java层的签名校验,java层容易被破解,我建议apk加固下) 获取md5值来进行Apk签名校验, 可以防止apk重新被打包. 下面我说说怎么获取apk签名的md5值(有三种方法) 1.用代码获取签名指纹的md5值 /** * MD5加密 * @param byteStr 需要加密的内容 * @return 返回 byteStr的md5值 */ public

  • Android中APK签名工具之jarsigner和apksigner详解

    一.工具介绍 jarsigner是JDK提供的针对jar包签名的通用工具, 位于JDK/bin/jarsigner.exe apksigner是Google官方提供的针对Android apk签名及验证的专用工具, 位于Android SDK/build-tools/SDK版本/apksigner.bat 不管是apk包,还是jar包,本质都是zip格式的压缩包,所以它们的签名过程都差不多(仅限V1签名), 以上两个工具都可以对Android apk包进行签名. 1.V1和V2签名的区别 在An

  • Android Studio将程序打包成APK的步骤详解

    第一步:先点击Build选择GenerateSigned APK 第二步:如果之前有编译成APK的话,就直接选择Choose existing已经存在的key:如果没有编译成APK那就选择Create new创建一个新的key的存放路径,然后填上密码,其中First and Last Name填一下,其他的无所谓.如图 尽量保证图中所指的两处密码相同,这样可以避免混淆,然后点击ok.下图的红圈之内填的是存储key的文件名. 做完上述的操作,会返回下图,然后点击next 接下来,一定要点击下图标记

  • 实时获取股票数据的android app应用程序源码分享

    最近学习Android应用开发,不知道写一个什么样的程序来练练手,正好最近股票很火,就一个App来实时获取股票数据,取名为Mystock.使用开发工具Android Studio,需要从Android官网下载,下载地址:http://developer.android.com/sdk/index.html.不幸的是Android是Google公司的,任何和Google公司相关的在国内都无法直接访问,只能通过VPN访问. 下图为Android Studio打开一个工程的截图: 下面按步介绍Myst

随机推荐