更改已经签名的app中的内容

转载请说明出处http://blog.csdn.net/andywuchuanlong

记得上次在南昌中兴的一个项目中遇到过一个这种需求:一个app能够给多个渠道商去运营,渠道商推广出去能够获得对应的推广金额。

那么这种情况下就必需要使得这个app能够唯一的标志一个渠道商。

那个时候我们在这个项目中的解决方式是:让用户在app中手动填入渠道商的工号。我如今想想这种方式也是醉了,真不知道那个时候项目经理是怎么想的,居然会给出这种方案。

这次的项目中又遇到了这个问题:需求是这个app可以给多个渠道商去推广。渠道商可以获得推广金额。这次我提出的解决方式是:先把打包后的app解压,然后在assets文件夹中写入渠道商的唯一标识id。然后压缩app,压缩完成又一次签名app,之后就大工告成。用户在第一次进入app的时候。会把assets中的id读出来,提交到server,就完美的攻克了这个用户是此渠道商的推广所获得的用户。

首先第一步:把app解压。删除META-INF目录中的CERT.RSA和CERT.SF两个文件

第二步:读取解压后的assets文件夹中的id.txt文件,写入渠道商的id

[java] view plaincopy
  1. File file = new File("d:/app/assets/id.txt");  
  2.         OutputStream outputStream = new FileOutputStream(file);  
  3.         outputStream.write(user.getId().toString().getBytes());  
  4.         outputStream.flush();  
  5.         outputStream.close();  
第三步:压缩写入渠道商id后的全部app文件

[java] view plaincopy
  1. ZipCompressor zc = new  ZipCompressor("d:/play.apk");    
  2.         zc.compressExe("d:/app/");  
详细的压缩代码例如以下:

[java] view plaincopy
  1. package com.xyc.signSystem.utils;  
  2. import java.io.BufferedInputStream;  
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileOutputStream;  
  6. import java.util.zip.CRC32;  
  7. import java.util.zip.CheckedOutputStream;  
  8.   
  9. import org.apache.tools.zip.ZipEntry;  
  10. import org.apache.tools.zip.ZipOutputStream;  
  11.   
  12. /** 
  13.  * @ClassName: ZipCompressor 
  14.  * @author :andywuchuanlong   QQ:312037487 
  15.  * @Description: 压缩文件的通用工具类-採用org.apache.tools.zip.ZipOutputStream实现。较复杂。 
  16.  * 
  17.  */  
  18. public class ZipCompressor {  
  19.     static final int BUFFER = 8192;  
  20.     private File zipFile;  
  21.   
  22.     /** 
  23.      * 压缩文件构造函数 
  24.      *  
  25.      * @param pathName 
  26.      *            压缩的文件存放文件夹 
  27.      */  
  28.     public ZipCompressor(String pathName) {  
  29.         zipFile = new File(pathName);  
  30.     }  
  31.   
  32.     /** 
  33.      * 运行压缩操作 
  34.      *  
  35.      * @param srcPathName 
  36.      *            被压缩的文件/目录 
  37.      */  
  38.     public void compressExe(String srcPathName) {  
  39.         File file = new File(srcPathName);  
  40.         if (!file.exists()) {  
  41.             throw new RuntimeException(srcPathName + "不存在!");  
  42.         }  
  43.         try {  
  44.             FileOutputStream fileOutputStream = new FileOutputStream(zipFile);  
  45.             CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,  
  46.                     new CRC32());  
  47.             ZipOutputStream out = new ZipOutputStream(cos);  
  48.             String basedir = "";  
  49.             compressByType(file, out, basedir);  
  50.             out.close();  
  51.         } catch (Exception e) {  
  52.             e.printStackTrace();  
  53.             throw new RuntimeException(e);  
  54.         }  
  55.     }  
  56.   
  57.     /** 
  58.      * 推断是文件夹还是文件。依据类型(文件/文件夹)运行不同的压缩方法 
  59.      *  
  60.      * @param file 
  61.      * @param out 
  62.      * @param basedir 
  63.      */  
  64.     private void compressByType(File file, ZipOutputStream out, String basedir) {  
  65.         if (basedir.equals("play/")) {  
  66.             basedir = "";  
  67.         }  
  68.         /* 推断是文件夹还是文件 */  
  69.         if (file.isDirectory()) {  
  70.             this.compressDirectory(file, out, basedir);  
  71.         } else {  
  72.             this.compressFile(file, out, basedir);  
  73.         }  
  74.     }  
  75.   
  76.     boolean isFirst = true;  
  77.   
  78.     /** 
  79.      * 压缩一个文件夹 
  80.      *  
  81.      * @param dir 
  82.      * @param out 
  83.      * @param basedir 
  84.      */  
  85.     private void compressDirectory(File dir, ZipOutputStream out, String basedir) {  
  86.         if (!dir.exists()) {  
  87.             return;  
  88.         }  
  89.         if (basedir.equals("play/")) {  
  90.             basedir = "";  
  91.         }  
  92.         File[] files = dir.listFiles();  
  93.         for (int i = 0; i < files.length; i++) {  
  94.             /* 递归 */  
  95.             compressByType(files[i], out, basedir + dir.getName() + "/");  
  96.         }  
  97.     }  
  98.   
  99.     /** 
  100.      * 压缩一个文件 
  101.      *  
  102.      * @param file 
  103.      * @param out 
  104.      * @param basedir 
  105.      */  
  106.     private void compressFile(File file, ZipOutputStream out, String basedir) {  
  107.         if (!file.exists()) {  
  108.             isFirst = false;  
  109.             return;  
  110.         }  
  111.         if (basedir.equals("play/")) {  
  112.             basedir = "";  
  113.         }  
  114.         try {  
  115.             BufferedInputStream bis = new BufferedInputStream(  
  116.                     new FileInputStream(file));  
  117.             ZipEntry entry = new ZipEntry(basedir + file.getName());  
  118.             out.putNextEntry(entry);  
  119.             int count;  
  120.             byte data[] = new byte[BUFFER];  
  121.             while ((count = bis.read(data, 0, BUFFER)) != -1) {  
  122.                 out.write(data, 0, count);  
  123.             }  
  124.             bis.close();  
  125.         } catch (Exception e) {  
  126.             throw new RuntimeException(e);  
  127.         }  
  128.     }  
  129. }  

第四步:压缩完成之后。此时的包是没有签名过的,所以还须要签名。签名能够使用jarsigner工具,首先我们要寻找到java的安装文件夹

[java] view plaincopy
  1. <span style="white-space:pre">    </span>public  String getJavaPath() {  
  2.         String javaPath = (String) System.getenv("Path");  
  3.         String paths[]= javaPath.split(";");  
  4.         String myPath = null;  
  5.         for(String path:paths){  
  6.             if (path.contains("Java")&&!path.contains("jre")  
  7.                     &&path.contains("bin") ){  
  8.                 myPath = path;  
  9.                 break;  
  10.             }  
  11.         }  
  12.         return myPath+"\";  
  13.     }  

签名:

[java] view plaincopy
  1. <span style="white-space:pre">    </span>String javaPath = getJavaPath();  
  2.     Runtime rt = Runtime.getRuntime();  
  3.     String cmd = javaPath  
  4.             + "jarsigner -verbose"  
  5.             + " -keystore "+ keystorePath  
  6.             + " -storepass player"// password  
  7.             + " -signedjar "+signedApkPath // 签名后的apk存放位置  
  8.             + " -digestalg SHA1  -sigalg  MD5withRSA "  
  9.             + unsignedApkPath//未签名的apk  
  10.             + " player";// 别名  
  11.     Process child = rt.exec(cmd);  

OK,签名成功。

原文地址:https://www.cnblogs.com/yfceshi/p/6852208.html