2015年3月3日 星期二

Java混淆工具ProGuard的最简明使用方法及Warning: can’t find referenced class xxx的解决方法

http://quyuxjtu.sinaapp.com/?p=361

ProGuard是一个广受欢迎的Java混淆工具,很多Android应用也会使用该工具进行混淆。下面简单总结一下其出现的Warning: can’t find referenced class xxx这类问题的解决方法。
ProGuard最好是通过命令行运行,但是对于只想用一下工具以完成任务的朋友,也许GUI也就够用了。我们将压缩包解压后,在lib目录下双击proguardgui.jar,就可以启动其GUI。然后一步步按照引导来做就行,这方面网上已经有很多教程。需要指出的是,如果是第一次使用的朋友,往往会在进行“Process”之后出现如下面所示的问题:
1
Warning: org.makagiga.commons.crypto.MasterKey: can't find referenced class javax.crypto.KeyGenerator
解决方法也很简单,网上很多日志也说到了,但是大部分是针对命令行方式的。这里介绍一种最简单的方法:在其他选项都设置好后,在Process这一步之前,可以选择“Save Configuration”,保存成ProGuard的配置文件(无所谓文件后缀)。然后针对上面的问题,我们打开刚才保存的配置文件,可以看到类似这样的内容:
1
2
3
4
5
-injars 'D:\Projects\makagiga-source-4.8\dist\images.jar'
-injars 'D:\Projects\makagiga-source-4.8\dist\makagiga.jar'
-outjars 'D:\Projects\makagiga-obsfucated-jar'
 
-libraryjars 'D:\Program Files\Java\jre7\lib\rt.jar'
要解决刚才这个问题,我们在后面加上:
1
2
-dontwarn javax.crypto.**
-keep class javax.crypto.**
按照类似的方法将所有的Warning处理掉,然后再用ProGuard将刚才保存的配置文件打开(重启ProGuard之后,选择“Load Configuration”),如果不出意外,就可以生成混淆以后的JAR包或者class文件了。其实,个人感觉ProGuard这个功能挺弱智的。另外,通过直接在配置文件上修改,也免去了大家学习GUI复杂选项的过程,适合需要在半天内完成任务的朋友借鉴。

一步步教你使用Proguard混淆Java源代码


java代码很容易被反编译,以下使用proguard来保护我们的代码
proguard选项很多,容易迷糊,现在就把我的配置写下来(实际使用中),以供参考

2.准备好你的jar包,我在这里举例叫做test.jar。
3.解压proguard,执行 bin目录下的proguardgui.bat。
   如图1
  

 
4.运行如图,点击左边“input/output” 菜单,如图2

 
5.点击右边的“add input” 加入我们要混的jar包,test.jar
 点击右边的“add output” 填入我们要输出的jar包(命名随便),这里我写 test_out.jar。
   注意输出的jar包,要自己手工填写。
  
6.添加支持库,下边的 “Library jars,wars,ears .....” 那个框框。
   点击右边的“add”。
 说明一下,这里最好把你的myeclipse里java project里的libraries所有Library的jar包,copy到一个目录,然后在这里加入这些jar包,myeclipse的环境支持jar包一般,如图3


 
 7.做完以上步骤后,应该如下图4,这样就差不多了


 
8.点击“shrinking” ,设置成如图5。(可根据需要设置,这里只是我的配置)


 
 9.点击“obfuscation” ,设置如图6。(可根据需要设置,这里只是我的配置)
 

 
10.点击“optimization” 设置如图7。(可根据需要设置,这里只是我的配置)


 
11.点击“process”,再点击“save configuration”,在弹出的对话框中,输入要保存的配置文件名称(这里我的是test.pro),最后点击“保存”。如图8


 
至些图形化的设置部分已经完成。

12.最后,添加要保留的类与方法。
 用编辑器编辑刚才保存下来的“test.pro”,用记事本什么都可以,打开后大致应该是以下的样子

-injars test.jar
-outjars test_out.jar

-libraryjars 'C:\Program Files (x86)\Java\jdk1.6.0_10\jre\lib\rt.jar'
-libraryjars lib\ant.jar
-libraryjars lib\aopalliance-1.0.jar
-libraryjars lib\commons-dbcp-1.4.jar
-libraryjars lib\commons-fileupload-1.2.1.jar
-libraryjars lib\commons-io-1.4.jar
.....设置的支持库包,略

-dontskipnonpubliclibraryclassmembers
-dontshrink
-useuniqueclassmembernames
-keeppackagenames
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod
-keepparameternames
-ignorewarnings
....我们在这里加入要保持的类和方法,写法如下,红色部分是我们加的

-keep class org.bl.soa.components.constant.* {
    public <fields>;
    public <methods>;
    *** set*(***);
    *** get*();
}
-keep class org.bl.hibernate.* {
    public <fields>;
    public <methods>;
    *** set*(***);
    *** get*();
}

.....继续加其他的要保留的类和方法,有多少写多少。我是把test.jar里的所有类都加进来。

....后面还一些自动生成的配置,不用管它们。
....略


13.写完后,保存。
 重新打开progrard,执行 bin目录下的proguardgui.bat。
   点击第一个选项“Proguard”,再点击“Load configuration”,选择我们刚才保存的“test.pro”进行加载。
   如下图


 
14.开始混代码,点击右边“process”,再点击“process!”,如下图


 
15,在等待处理完成后,输出的“test_out.jar”,就是混过的jar包,你可以用xjad反编看下效果。

16,如果在处理过程中有问题,一般会有提示,大部分一般都是缺少关联类,如果少了相关类,在第7步的图,把缺少的支持包加进来就可以了。

2015年2月24日 星期二

java 加密解密简单实现

http://blog.csdn.net/qiushyfm/article/details/4464512

加密算法有很多种:这里只大约列举几例:

1:消息摘要:(数字指纹):既对一个任意长度的一个数据块进行计算,产生一个唯一指纹。MD5/SHA1
发送给其他人你的信息和摘要,其他人用相同的加密方法得到摘要,最后进行比较摘要是否相同。

2:单匙密码体制:DES:比较简便高效,密钥简短,加解密速度快,破译极其困难,但其安全性依赖于密匙的安全性。
DES(Data Encryption Standard)是发明最早的最广泛使用的分组对称加密算法。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密

3:数字签名:就是信息发送者用其私钥对从所传报文中提取出的特征数据(或称数字指纹)进行RSA算法操作,以保证发信人无法抵赖曾发过该信息(即不可抵赖性),同时也确保信息报文在经签名后末被篡改(即完整性)。当信息接收者收到报文后,就可以用发送者的公钥对数字签名进行验证。
代表:DSA

4:非对称密匙密码体制(公匙体系):加密密匙不同于解密密匙,加密密匙公之于众,谁都可以使用,解密密匙只有解密人自己知道。代表:RSA

下面是对上面几个例子进行的简单实现:
[java] view plaincopy
  1. package test;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.ObjectInputStream;  
  6. import java.io.ObjectOutputStream;  
  7. import java.security.*;  
  8. import javax.crypto.Cipher;  
  9. import javax.crypto.KeyGenerator;  
  10. import javax.crypto.SecretKey;  
  11. /** 
  12.  * 加密解密 
  13.  *  
  14.  * @author shy.qiu 
  15.  * @since  http://blog.csdn.net/qiushyfm 
  16.  */  
  17. public class CryptTest {  
  18.     /** 
  19.      * 进行MD5加密 
  20.      *  
  21.      * @param info 
  22.      *            要加密的信息 
  23.      * @return String 加密后的字符串 
  24.      */  
  25.     public String encryptToMD5(String info) {  
  26.         byte[] digesta = null;  
  27.         try {  
  28.             // 得到一个md5的消息摘要  
  29.             MessageDigest alga = MessageDigest.getInstance("MD5");  
  30.             // 添加要进行计算摘要的信息  
  31.             alga.update(info.getBytes());  
  32.             // 得到该摘要  
  33.             digesta = alga.digest();  
  34.         } catch (NoSuchAlgorithmException e) {  
  35.             e.printStackTrace();  
  36.         }  
  37.         // 将摘要转为字符串  
  38.         String rs = byte2hex(digesta);  
  39.         return rs;  
  40.     }  
  41.     /** 
  42.      * 进行SHA加密 
  43.      *  
  44.      * @param info 
  45.      *            要加密的信息 
  46.      * @return String 加密后的字符串 
  47.      */  
  48.     public String encryptToSHA(String info) {  
  49.         byte[] digesta = null;  
  50.         try {  
  51.             // 得到一个SHA-1的消息摘要  
  52.             MessageDigest alga = MessageDigest.getInstance("SHA-1");  
  53.             // 添加要进行计算摘要的信息  
  54.             alga.update(info.getBytes());  
  55.             // 得到该摘要  
  56.             digesta = alga.digest();  
  57.         } catch (NoSuchAlgorithmException e) {  
  58.             e.printStackTrace();  
  59.         }  
  60.         // 将摘要转为字符串  
  61.         String rs = byte2hex(digesta);  
  62.         return rs;  
  63.     }  
  64.     // //////////////////////////////////////////////////////////////////////////  
  65.     /** 
  66.      * 创建密匙 
  67.      *  
  68.      * @param algorithm 
  69.      *            加密算法,可用 DES,DESede,Blowfish 
  70.      * @return SecretKey 秘密(对称)密钥 
  71.      */  
  72.     public SecretKey createSecretKey(String algorithm) {  
  73.         // 声明KeyGenerator对象  
  74.         KeyGenerator keygen;  
  75.         // 声明 密钥对象  
  76.         SecretKey deskey = null;  
  77.         try {  
  78.             // 返回生成指定算法的秘密密钥的 KeyGenerator 对象  
  79.             keygen = KeyGenerator.getInstance(algorithm);  
  80.             // 生成一个密钥  
  81.             deskey = keygen.generateKey();  
  82.         } catch (NoSuchAlgorithmException e) {  
  83.             e.printStackTrace();  
  84.         }  
  85.         // 返回密匙  
  86.         return deskey;  
  87.     }  
  88.     /** 
  89.      * 根据密匙进行DES加密 
  90.      *  
  91.      * @param key 
  92.      *            密匙 
  93.      * @param info 
  94.      *            要加密的信息 
  95.      * @return String 加密后的信息 
  96.      */  
  97.     public String encryptToDES(SecretKey key, String info) {  
  98.         // 定义 加密算法,可用 DES,DESede,Blowfish  
  99.         String Algorithm = "DES";  
  100.         // 加密随机数生成器 (RNG),(可以不写)  
  101.         SecureRandom sr = new SecureRandom();  
  102.         // 定义要生成的密文  
  103.         byte[] cipherByte = null;  
  104.         try {  
  105.             // 得到加密/解密器  
  106.             Cipher c1 = Cipher.getInstance(Algorithm);  
  107.             // 用指定的密钥和模式初始化Cipher对象  
  108.             // 参数:(ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE,UNWRAP_MODE)  
  109.             c1.init(Cipher.ENCRYPT_MODE, key, sr);  
  110.             // 对要加密的内容进行编码处理,  
  111.             cipherByte = c1.doFinal(info.getBytes());  
  112.         } catch (Exception e) {  
  113.             e.printStackTrace();  
  114.         }  
  115.         // 返回密文的十六进制形式  
  116.         return byte2hex(cipherByte);  
  117.     }  
  118.     /** 
  119.      * 根据密匙进行DES解密 
  120.      *  
  121.      * @param key 
  122.      *            密匙 
  123.      * @param sInfo 
  124.      *            要解密的密文 
  125.      * @return String 返回解密后信息 
  126.      */  
  127.     public String decryptByDES(SecretKey key, String sInfo) {  
  128.         // 定义 加密算法,  
  129.         String Algorithm = "DES";  
  130.         // 加密随机数生成器 (RNG)  
  131.         SecureRandom sr = new SecureRandom();  
  132.         byte[] cipherByte = null;  
  133.         try {  
  134.             // 得到加密/解密器  
  135.             Cipher c1 = Cipher.getInstance(Algorithm);  
  136.             // 用指定的密钥和模式初始化Cipher对象  
  137.             c1.init(Cipher.DECRYPT_MODE, key, sr);  
  138.             // 对要解密的内容进行编码处理  
  139.             cipherByte = c1.doFinal(hex2byte(sInfo));  
  140.         } catch (Exception e) {  
  141.             e.printStackTrace();  
  142.         }  
  143.         // return byte2hex(cipherByte);  
  144.         return new String(cipherByte);  
  145.     }  
  146.     // /////////////////////////////////////////////////////////////////////////////  
  147.     /** 
  148.      * 创建密匙组,并将公匙,私匙放入到指定文件中 
  149.      *  
  150.      * 默认放入mykeys.bat文件中 
  151.      */  
  152.     public void createPairKey() {  
  153.         try {  
  154.             // 根据特定的算法一个密钥对生成器  
  155.             KeyPairGenerator keygen = KeyPairGenerator.getInstance("DSA");  
  156.             // 加密随机数生成器 (RNG)  
  157.             SecureRandom random = new SecureRandom();  
  158.             // 重新设置此随机对象的种子  
  159.             random.setSeed(1000);  
  160.             // 使用给定的随机源(和默认的参数集合)初始化确定密钥大小的密钥对生成器  
  161.             keygen.initialize(512, random);// keygen.initialize(512);  
  162.             // 生成密钥组  
  163.             KeyPair keys = keygen.generateKeyPair();  
  164.             // 得到公匙  
  165.             PublicKey pubkey = keys.getPublic();  
  166.             // 得到私匙  
  167.             PrivateKey prikey = keys.getPrivate();  
  168.             // 将公匙私匙写入到文件当中  
  169.             doObjToFile("mykeys.bat"new Object[] { prikey, pubkey });  
  170.         } catch (NoSuchAlgorithmException e) {  
  171.             e.printStackTrace();  
  172.         }  
  173.     }  
  174.     /** 
  175.      * 利用私匙对信息进行签名 把签名后的信息放入到指定的文件中 
  176.      *  
  177.      * @param info 
  178.      *            要签名的信息 
  179.      * @param signfile 
  180.      *            存入的文件 
  181.      */  
  182.     public void signToInfo(String info, String signfile) {  
  183.         // 从文件当中读取私匙  
  184.         PrivateKey myprikey = (PrivateKey) getObjFromFile("mykeys.bat"1);  
  185.         // 从文件中读取公匙  
  186.         PublicKey mypubkey = (PublicKey) getObjFromFile("mykeys.bat"2);  
  187.         try {  
  188.             // Signature 对象可用来生成和验证数字签名  
  189.             Signature signet = Signature.getInstance("DSA");  
  190.             // 初始化签署签名的私钥  
  191.             signet.initSign(myprikey);  
  192.             // 更新要由字节签名或验证的数据  
  193.             signet.update(info.getBytes());  
  194.             // 签署或验证所有更新字节的签名,返回签名  
  195.             byte[] signed = signet.sign();  
  196.             // 将数字签名,公匙,信息放入文件中  
  197.             doObjToFile(signfile, new Object[] { signed, mypubkey, info });  
  198.         } catch (Exception e) {  
  199.             e.printStackTrace();  
  200.         }  
  201.     }  
  202.     /** 
  203.      * 读取数字签名文件 根据公匙,签名,信息验证信息的合法性 
  204.      *  
  205.      * @return true 验证成功 false 验证失败 
  206.      */  
  207.     public boolean validateSign(String signfile) {  
  208.         // 读取公匙  
  209.         PublicKey mypubkey = (PublicKey) getObjFromFile(signfile, 2);  
  210.         // 读取签名  
  211.         byte[] signed = (byte[]) getObjFromFile(signfile, 1);  
  212.         // 读取信息  
  213.         String info = (String) getObjFromFile(signfile, 3);  
  214.         try {  
  215.             // 初始一个Signature对象,并用公钥和签名进行验证  
  216.             Signature signetcheck = Signature.getInstance("DSA");  
  217.             // 初始化验证签名的公钥  
  218.             signetcheck.initVerify(mypubkey);  
  219.             // 使用指定的 byte 数组更新要签名或验证的数据  
  220.             signetcheck.update(info.getBytes());  
  221.             System.out.println(info);  
  222.             // 验证传入的签名  
  223.             return signetcheck.verify(signed);  
  224.         } catch (Exception e) {  
  225.             e.printStackTrace();  
  226.             return false;  
  227.         }  
  228.     }  
  229.     /** 
  230.      * 将二进制转化为16进制字符串 
  231.      *  
  232.      * @param b 
  233.      *            二进制字节数组 
  234.      * @return String 
  235.      */  
  236.     public String byte2hex(byte[] b) {  
  237.         String hs = "";  
  238.         String stmp = "";  
  239.         for (int n = 0; n < b.length; n++) {  
  240.             stmp = (java.lang.Integer.toHexString(b[n] & 0XFF));  
  241.             if (stmp.length() == 1) {  
  242.                 hs = hs + "0" + stmp;  
  243.             } else {  
  244.                 hs = hs + stmp;  
  245.             }  
  246.         }  
  247.         return hs.toUpperCase();  
  248.     }  
  249.     /** 
  250.      * 十六进制字符串转化为2进制 
  251.      *  
  252.      * @param hex 
  253.      * @return 
  254.      */  
  255.     public byte[] hex2byte(String hex) {  
  256.         byte[] ret = new byte[8];  
  257.         byte[] tmp = hex.getBytes();  
  258.         for (int i = 0; i < 8; i++) {  
  259.             ret[i] = uniteBytes(tmp[i * 2], tmp[i * 2 + 1]);  
  260.         }  
  261.         return ret;  
  262.     }  
  263.     /** 
  264.      * 将两个ASCII字符合成一个字节; 如:"EF"--> 0xEF 
  265.      *  
  266.      * @param src0 
  267.      *            byte 
  268.      * @param src1 
  269.      *            byte 
  270.      * @return byte 
  271.      */  
  272.     public static byte uniteBytes(byte src0, byte src1) {  
  273.         byte _b0 = Byte.decode("0x" + new String(new byte[] { src0 }))  
  274.                 .byteValue();  
  275.         _b0 = (byte) (_b0 << 4);  
  276.         byte _b1 = Byte.decode("0x" + new String(new byte[] { src1 }))  
  277.                 .byteValue();  
  278.         byte ret = (byte) (_b0 ^ _b1);  
  279.         return ret;  
  280.     }  
  281.     /** 
  282.      * 将指定的对象写入指定的文件 
  283.      *  
  284.      * @param file 
  285.      *            指定写入的文件 
  286.      * @param objs 
  287.      *            要写入的对象 
  288.      */  
  289.     public void doObjToFile(String file, Object[] objs) {  
  290.         ObjectOutputStream oos = null;  
  291.         try {  
  292.             FileOutputStream fos = new FileOutputStream(file);  
  293.             oos = new ObjectOutputStream(fos);  
  294.             for (int i = 0; i < objs.length; i++) {  
  295.                 oos.writeObject(objs[i]);  
  296.             }  
  297.         } catch (Exception e) {  
  298.             e.printStackTrace();  
  299.         } finally {  
  300.             try {  
  301.                 oos.close();  
  302.             } catch (IOException e) {  
  303.                 e.printStackTrace();  
  304.             }  
  305.         }  
  306.     }  
  307.     /** 
  308.      * 返回在文件中指定位置的对象 
  309.      *  
  310.      * @param file 
  311.      *            指定的文件 
  312.      * @param i 
  313.      *            从1开始 
  314.      * @return 
  315.      */  
  316.     public Object getObjFromFile(String file, int i) {  
  317.         ObjectInputStream ois = null;  
  318.         Object obj = null;  
  319.         try {  
  320.             FileInputStream fis = new FileInputStream(file);  
  321.             ois = new ObjectInputStream(fis);  
  322.             for (int j = 0; j < i; j++) {  
  323.                 obj = ois.readObject();  
  324.             }  
  325.         } catch (Exception e) {  
  326.             e.printStackTrace();  
  327.         } finally {  
  328.             try {  
  329.                 ois.close();  
  330.             } catch (IOException e) {  
  331.                 e.printStackTrace();  
  332.             }  
  333.         }  
  334.         return obj;  
  335.     }  
  336.     /** 
  337.      * 测试 
  338.      *  
  339.      * @param args 
  340.      */  
  341.     public static void main(String[] args) {  
  342.         CryptTest jiami = new CryptTest();  
  343.         // 执行MD5加密"Hello world!"  
  344.         System.out.println("Hello经过MD5:" + jiami.encryptToMD5("Hello"));  
  345.         // 生成一个DES算法的密匙  
  346.         SecretKey key = jiami.createSecretKey("DES");  
  347.         // 用密匙加密信息"Hello world!"  
  348.         String str1 = jiami.encryptToDES(key, "Hello");  
  349.         System.out.println("使用des加密信息Hello为:" + str1);  
  350.         // 使用这个密匙解密  
  351.         String str2 = jiami.decryptByDES(key, str1);  
  352.         System.out.println("解密后为:" + str2);  
  353.         // 创建公匙和私匙  
  354.         jiami.createPairKey();  
  355.         // 对Hello world!使用私匙进行签名  
  356.         jiami.signToInfo("Hello""mysign.bat");  
  357.         // 利用公匙对签名进行验证。  
  358.         if (jiami.validateSign("mysign.bat")) {  
  359.             System.out.println("Success!");  
  360.         } else {  
  361.             System.out.println("Fail!");  
  362.         }  
  363.     }  
  364. }   

用到的重要的类
javax.crypto.KeyGenerator
     public final SecretKey generateKey()生成一个密钥
     public static final KeyGenerator getInstance(String algorithm) 返回生成指定算法的秘密密钥的KeyGenerator对象。
javax.crypto 接口 SecretKey
javax.crypto.Cipher  此类为加密和解密提供密码功能。它构成了 Java Cryptographic Extension (JCE) 框架的核心
     public final void init(int opmode,Key key)
     public final byte[] doFinal(byte[] input) 按单部分操作加密或解密数据,或者结束一个多部分操作
java.security.KeyPairGenerator
     static KeyPairGenerator getInstance(String algorithm)
         回生成指定算法的 public/private 密钥对的 KeyPairGenerator 对象。
java.security.Signature
     使用 Signature 对象签名数据或验证签名包括以下三个阶段:
     1:初始化,使用
        初始化验证签名的公钥(请参见 initVerify),或使用
        初始化签署签名的私钥(也可以选择“安全随机数生成器”)initSign(PrivateKey)和initSign(PrivateKey, SecureRandom))。
     2:更新
        根据初始化类型,这可更新要签名或验证的字节。请参见 update 方法。
     3:签署或验证所有更新字节的签名。请参见 sign 方法和 verify 方法。