gpt4 book ai didi

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

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章给Android的APK程序签名和重新签名的方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

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

?
1
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签名.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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示例:

?
1
2
3
4
5
6
7
8
9
10
11
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源代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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 。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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来签名.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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了。下面我就来讲讲复杂的重新签名的方式:采用命令行方法。 1、配置环境,需安装jdk,sdk 2、在已成功安装jdk的目录中找到jarsigner.exe文件,本机的目录如下:C:\Program Files\Java\jdk1.8.0_20\bin 3、去除准备重新签名的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对应的字符串资源 4、重新签名Apk文件 方法一:通过命令重新生成AndroidApk包签名证书后再重新签名Apk文件 1.在cmd中切换到jdk的bin目录中:cd C:\Program Files\Java\jdk1.8.0_20\bin 回车 2.再输入以下的命令:

?
1
2
3
4
5
6
7
8
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已签名.

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

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

方法2、以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文件的目录中:

  。

?
1
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文件) 。

最后此篇关于给Android的APK程序签名和重新签名的方法的文章就讲到这里了,如果你想了解更多关于给Android的APK程序签名和重新签名的方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com