王学岗性能优化10——APK加固(一)

所有的dex文件都加密,使用的时候通过代理解密,用户拿到以后是无法解读源代码的。加密算法可以自由选择,
第一:反编译工具
在这里插入图片描述
第二:Proguard的使用与配置
Proguard是一个代码优化和混淆工具。能够提供对Java类文件的压缩、优化、混淆,和预校验。压缩的步骤是检测并移除未使用的类、字段、方法和属性。优化的步骤是分析和优化方法的字节码。混淆的步骤是使用短的毫无意义的名称重命名剩余的类、字段和方法。压缩、优化、混淆使得代码更小,更高效。
比如整个APK中有一些没有用过的东西。在导包的时候可以把那些不需要的全去掉。
开启proguard

buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

minifyEnabled false:混淆功能默认是关闭的。改为true就是开启混淆功能
点击运行在gradle里面默认生成proguard-android,按照文件提供的规则,进行混淆。

 #不做优化
-dontoptimize
#不适用大小写
-dontusemixedcaseclassnames 
#不跳过公共库的class
-dontskipnonpubliclibraryclasses
-keep 指定类和类成员(变量和方法)不被混淆。
	-keep class com.dongnao.proxy.guard.test.Bug
	(保护了类名)
	-keep class com.dongnao.proxy.guard.test.Bug{
   		public static void *();
	}
	(保护了 public static void的没有参数的函数)
	-keep class com.dongnao.proxy.guard.test.Bug{
   		*;
	}
	(保护所有)
-keepclassmembers 指定类成员不被混淆(就是-keep的缩小版,不管类名了)。
	-keepclassmembers
	class com.dongnao.proxy.guard.test.Bug
	(都被混淆了)
-keepclasseswithmembers 指定类和类成员不被混淆,前提是指定的类成员存在。
	-keepclasseswithmembers class 	com.dongnao.proxy.guard.test.Bug
	(保护类名,但是没指定成员,所以函数名被混淆)
	-keepclasseswithmembers class   	com.dongnao.proxy.guard.test.Bug{
		native <methods>;
	}

proguard-rules.pro文件中的一些配置
1,指定类不被混淆的方法;
类前加注释@Keep
在proguard-rules.pro中添加代码

-keep class com.example.administrator.lsn_8_demo.User

如果user类需要反射,就不能混淆了。
3,

# We want to keep methods in Activity that could be used in the XML attribute onClick.
-keepclassmembers class * extends android.app.Activity {
    public void *(android.view.View);
}

保证所有继承Activity类,方法参数名是View的方法不被混淆
4,View 类的set get方法不被混淆

# Keep setters in Views so that animations can still work.
-keepclassmembers public class * extends android.view.View {
    void set*(***);
    *** get*();
}

5,静态内部类里的静态属性不做混淆

-keepclassmembers class **.R$* {
    public static <fields>;
}

6.native 方法是不能参与混淆的。如果混淆了,就无法使用JNI了。

	-keepclasseswithmembers class   	com.dongnao.proxy.guard.test.Bug{
		native <methods>;
	}

7,#只让一个类中的某个成员或某个方法不加参。

 -keep class com.example.administrator.lsn_8_demo.User{
   		public static void *();
 		java.lang.String *;
 }

该类String类型的成员变量不参与混淆,static void方法不被混淆。注意这样写类名也不会别混淆。我们看下下面这种写法

-keepclassmembers class com.example.administrator.lsn_8_demo.User{
                     		public static void *();
                     		java.lang.String *;
                  	}

这种写法和上面的区别是,类名也会被混淆

8,注意:混淆后的代码不能直接在android studio中运行,要在真机上运行。
9,如果运行出错,而又因为混淆无法定位到错误地方
,可以添加这句代码

-keepattributes SourceFile,LineNumberTable

在app build outputs mapping debug 路径下有mapping.txt文件,在这个文件中有混淆后的对应关系。
如何恢复呢?分两步
第一步:.把错误信息保存到文件
第二步:.使用工具 sdk/tools/groguard/bin/retrace.bat
先配置 -keepattributes SourceFile,LineNumberTable
再执行 retrace.bat -verbose mappint文件 bug文件
bug文件就是我们把错误的日志内容复制粘贴到这个文件里;
第三:加密技术
这里有两个加密需要用到的jar包sun.misc.BASE64Decoder.jar和commons-codec-1.11.jar
1,单项加密
使用加密算法生成一个密码,向服务器发送的数据包含明文和密码,服务器收到数据后,使用相同的算法对明文进行加密,得到的密码与收到的密码相同,这说明数据发送过程是安全的,没有问题的。

package com.example.administrator.lsn_10_demo;

import org.apache.commons.codec.digest.Sha2Crypt;
import org.junit.Test;

public class SHA {
    @Test
    public void test(){
		//对jett进行加密,得到加密的密码
        String result=Sha2Crypt.sha256Crypt("jett".getBytes());
        System.out.println(result);
    }
}

2,对称加密
一个密码对应着一个解密的秘钥,加密和解密是一对一的关系。比如AEA(https链接支付宝就是用的这种算法)加密算法,

package com.example.administrator.lsn_10_demo;

import org.junit.Test;

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class AES {
	//算法的名称
    public static String ALGORITHM="AES";
    //1,得到加密后的数据
	//2,content:加密的内容、
	//3,password:加密的密码
    public static byte[] encrypt(String content,String password) throws Exception{
		//根据AES算法生成键值,用来生成aes秘钥
        KeyGenerator kgen = KeyGenerator.getInstance(ALGORITHM);
        //1,用用户密码作为随机数初始化
		//2,大小128位
        kgen.init(128,new SecureRandom(password.getBytes()));
        //得到一个密钥,注意输入的密码和秘钥是不一样的。
        SecretKey secretKey=kgen.generateKey();
        //对钥密进行基本的编码
        byte[] enCodeFormat = secretKey.getEncoded();
        //转换成AES专用的密钥
        SecretKeySpec key=new SecretKeySpec(enCodeFormat,ALGORITHM);
        //创建一个密码器
        Cipher cipher=Cipher.getInstance(ALGORITHM);

        //加密的时候需要把数据填到一个byte数组
        byte[] byteContent=content.getBytes();
        //开始加密了
        cipher.init(Cipher.ENCRYPT_MODE,key);
		//加密后的结果就在resul里面
        byte[] result=cipher.doFinal(byteContent);

        return result;

    }
//解密
    public static byte[] decrypt(byte[] content,String password) throws Exception{
        //创建AES的key生产者
        KeyGenerator kgen=KeyGenerator.getInstance(ALGORITHM);
        //利用用户密码作为随机数初始化
        kgen.init(128,new SecureRandom(password.getBytes()));
        //根据用户密码,生成一个密钥  (所有对称算法通用的)
        SecretKey secretKey=kgen.generateKey();
        //对密钥进行基本的编码
        byte[] enCodeFormat=secretKey.getEncoded();
        //转换成AES专用的密钥 RoundKey
        SecretKeySpec key=new SecretKeySpec(enCodeFormat,ALGORITHM);
        //创建一个密码器
        Cipher cipher=Cipher.getInstance(ALGORITHM);

        //解密
        cipher.init(Cipher.DECRYPT_MODE,key);
        byte[] result=cipher.doFinal(content);
        return result;
    }
	//测试代码
    @Test
    public void test() throws Exception{
        String content="jett53425234523452345234523452345234";
        String password="123";
        //加密
        byte[] encryptByte=encrypt(content,password);
        System.out.println("加密的数据:"+new String(encryptByte));//打印内容显示,content已经面目全非了。
        //解密,要用同样的密码
        byte[] decrypt=decrypt(encryptByte,password);
        System.out.println("解密后的效果:"+new String(decrypt));//打印输出的内容就是content的内容

    }

}

3,rsa公钥私钥加密算法
假设有一万人注册了银行卡,如果是以前我们需要这么做,我给你一份密码,我银行这里还需要一个存根。如果是两个人,就需要两套。如果是一万人,那么银行就要有一万个存根。这样管理起来很不方便。
公钥私钥的好处在哪里呢?
在这里插入图片描述
公钥加密,私钥解密

package com.example.administrator.lsn_10_demo;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;

import javax.crypto.Cipher;

import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;

public class RSA {
	//算法名称
    public static String ALGORITHM="RSA";

    //指定key的位数,数字比较大生成钥匙对会需要很长时间。我们这里使用1024位
    public static int KEYSIZE=1024;//65536
	//公钥和私钥是分开存放的,一个存放在服务器,一个存放在客户端。

    //指定公钥存放的文件
    public static String PUBLIC_KEY_FILE="public_key.dat";

    //指定私钥存放的文件
    public static String PRIVATE_KEY_FILE="private_key.dat";

//公钥和私钥是成对出现的。本方法就是生成钥匙对
//执行这两个方法会生成两个文件
    public static void generateKeyPair() throws Exception{
        SecureRandom sr=new SecureRandom();
        //需要一个KeyPairGenerator来生成钥对
        KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEYSIZE,sr);
        //生成钥匙
        KeyPair keyPair=keyPairGenerator.generateKeyPair();

        Key publicKey=keyPair.getPublic();
        Key privateKey=keyPair.getPrivate();
        //把公钥私钥存放到文件里面
        ObjectOutputStream objectOutputStream1 = new ObjectOutputStream(new FileOutputStream(PUBLIC_KEY_FILE));
        ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(new FileOutputStream(PRIVATE_KEY_FILE));

        objectOutputStream1.writeObject(publicKey);
        objectOutputStream2.writeObject(privateKey);
        objectOutputStream2.close();
        objectOutputStream1.close();

    }

    /**
     * 加密
     */
    public static String encrypt(String source) throws Exception{
		//生成钥匙对
        generateKeyPair();
        //取出公钥
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE));
        Key key=(Key)ois.readObject();
        ois.close();
        //开始使用,拿到钥匙加密
        Cipher cipher=Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE,key);
		//把你输入的内容source,加密以后放到另外一个数组b1
        byte[] b=source.getBytes();
        byte[] b1=cipher.doFinal(b);
        //转一下base64,字符和二进制之间的转换
        BASE64Encoder encoder=new BASE64Encoder();
        return encoder.encode(b1);

    }
    /**
     * 解密
     */
    public static String decrypt(String source) throws Exception{

        //取出公钥
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE));
        Key key=(Key)ois.readObject();
        ois.close();
        //开始使用
        Cipher cipher=Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE,key);
        BASE64Decoder decoder=new BASE64Decoder();
		//source是要解码的文字,把内容解码到b数组
        byte[] b=decoder.decodeBuffer(source);
		//密码器解码,把数组b中的内容解密出来,放到数组b1
        byte[] b1=cipher.doFinal(b);

        return new String(b1);

    }
    @Test
    public void test() throws Exception{
		//
        String content="jett12121212121212121";
		//对content进行加密
        String password=encrypt(content);
		//打印输出几十个字母,反正看不懂
        System.out.println("密文"+password);

        //到了服务器以后,对密文进行解密
        String target=decrypt(password);
		//打印输出content
        System.out.println("明文"+target);
    }
}

https传输原理,首先把数据使用AES加密,然发送数据的时候是带着公钥一起发送的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值