更改已经签名的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
- File file = new File("d:/app/assets/id.txt");
- OutputStream outputStream = new FileOutputStream(file);
- outputStream.write(user.getId().toString().getBytes());
- outputStream.flush();
- outputStream.close();
- ZipCompressor zc = new ZipCompressor("d:/play.apk");
- zc.compressExe("d:/app/");
- package com.xyc.signSystem.utils;
- import java.io.BufferedInputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.util.zip.CRC32;
- import java.util.zip.CheckedOutputStream;
- import org.apache.tools.zip.ZipEntry;
- import org.apache.tools.zip.ZipOutputStream;
- /**
- * @ClassName: ZipCompressor
- * @author :andywuchuanlong QQ:312037487
- * @Description: 压缩文件的通用工具类-採用org.apache.tools.zip.ZipOutputStream实现。较复杂。
- *
- */
- public class ZipCompressor {
- static final int BUFFER = 8192;
- private File zipFile;
- /**
- * 压缩文件构造函数
- *
- * @param pathName
- * 压缩的文件存放文件夹
- */
- public ZipCompressor(String pathName) {
- zipFile = new File(pathName);
- }
- /**
- * 运行压缩操作
- *
- * @param srcPathName
- * 被压缩的文件/目录
- */
- public void compressExe(String srcPathName) {
- File file = new File(srcPathName);
- if (!file.exists()) {
- throw new RuntimeException(srcPathName + "不存在!");
- }
- try {
- FileOutputStream fileOutputStream = new FileOutputStream(zipFile);
- CheckedOutputStream cos = new CheckedOutputStream(fileOutputStream,
- new CRC32());
- ZipOutputStream out = new ZipOutputStream(cos);
- String basedir = "";
- compressByType(file, out, basedir);
- out.close();
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException(e);
- }
- }
- /**
- * 推断是文件夹还是文件。依据类型(文件/文件夹)运行不同的压缩方法
- *
- * @param file
- * @param out
- * @param basedir
- */
- private void compressByType(File file, ZipOutputStream out, String basedir) {
- if (basedir.equals("play/")) {
- basedir = "";
- }
- /* 推断是文件夹还是文件 */
- if (file.isDirectory()) {
- this.compressDirectory(file, out, basedir);
- } else {
- this.compressFile(file, out, basedir);
- }
- }
- boolean isFirst = true;
- /**
- * 压缩一个文件夹
- *
- * @param dir
- * @param out
- * @param basedir
- */
- private void compressDirectory(File dir, ZipOutputStream out, String basedir) {
- if (!dir.exists()) {
- return;
- }
- if (basedir.equals("play/")) {
- basedir = "";
- }
- File[] files = dir.listFiles();
- for (int i = 0; i < files.length; i++) {
- /* 递归 */
- compressByType(files[i], out, basedir + dir.getName() + "/");
- }
- }
- /**
- * 压缩一个文件
- *
- * @param file
- * @param out
- * @param basedir
- */
- private void compressFile(File file, ZipOutputStream out, String basedir) {
- if (!file.exists()) {
- isFirst = false;
- return;
- }
- if (basedir.equals("play/")) {
- basedir = "";
- }
- try {
- BufferedInputStream bis = new BufferedInputStream(
- new FileInputStream(file));
- ZipEntry entry = new ZipEntry(basedir + file.getName());
- out.putNextEntry(entry);
- int count;
- byte data[] = new byte[BUFFER];
- while ((count = bis.read(data, 0, BUFFER)) != -1) {
- out.write(data, 0, count);
- }
- bis.close();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
第四步:压缩完成之后。此时的包是没有签名过的,所以还须要签名。签名能够使用jarsigner工具,首先我们要寻找到java的安装文件夹
- <span style="white-space:pre"> </span>public String getJavaPath() {
- String javaPath = (String) System.getenv("Path");
- String paths[]= javaPath.split(";");
- String myPath = null;
- for(String path:paths){
- if (path.contains("Java")&&!path.contains("jre")
- &&path.contains("bin") ){
- myPath = path;
- break;
- }
- }
- return myPath+"\\";
- }
签名:
- <span style="white-space:pre"> </span>String javaPath = getJavaPath();
- Runtime rt = Runtime.getRuntime();
- String cmd = javaPath
- + "jarsigner -verbose"
- + " -keystore "+ keystorePath
- + " -storepass player"// password
- + " -signedjar "+signedApkPath // 签名后的apk存放位置
- + " -digestalg SHA1 -sigalg MD5withRSA "
- + unsignedApkPath//未签名的apk
- + " player";// 别名
- Process child = rt.exec(cmd);
OK,签名成功。