Class文件加密
一、概述
在项目开发中,我们常需要用到加解密算法,加解密算法主要分为三大类:
分析
分析:
JVM运行java文件是通过加载.class文件实现程序运行的,而且这个过程被我们称为类加载机制。执行加载这个动作的是类加载器(java.lang.ClassLoader):
除了JVM自己实现的几个加载器,我们还能通过继承父类ClassLoader,重写其findClass方法,实现自定义的类加载器。
重点之一就是我们自己的类加载器,它在这其中扮演着解密被加密的class文件的角色(同时加载加密文件)。
自然有加密.class文件步骤,写一个工具类完成这个角色。
其他的步骤是一些基本的文件流读写。
归纳一下思路:
- 加密.class文件:写一个工具类,确定加密方法。
- 解密·.class文件: 自定义的类加载器类。
- 加载.class文件到JVM:自定义的类加载器类。(主要步骤)
- 验证加载成功:测试类。
主要思路就是将class文件加载成字节流然后对字节流加密后重新输出成class文件,然后利用自定义类加载器读取解密后的class文件,然后解密运行即可
二、三大类加密算法
- 对称加密算法,如:AES、DES、3DES
- 非对称加密算法,如:RSA、DSA、ECC
- 散列算法,如:MD5、SHA1、HMAC
各算法对比
- 对称加密算法(加解密密钥相同)
-
非对称算法(加密密钥和解密密钥不同)
-
散列算法比较
对称与非对称算法比较
对称加密和非对称加密的区别
- 对称加密: 加密和解密的秘钥使用的是同一个.
- 非对称加密: 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。
1.对称加密算法:
密钥较短,破译困难,除了数据加密标准(DES),另一个对称密钥加密系统是国际数据加密算(IDEA),它比DES的加密性好,且对计算机性能要求也没有那么高。
优点:
- 算法公开、计算量小、加密速度快、加密效率高
缺点:
- 在数据传送前,发送方和接收方必须商定好秘钥,然后 使双方都能保存好秘钥。其次如果一方的秘钥被泄露,那么加密信息也就不安全了。另外,每对用户每次使用对称加密算法时,都需要使用其他人不知道的唯一秘钥,这会使得收、发双方所拥有的钥匙数量巨大,密钥管理成为双方的负担。
2.非对称加密算法:
公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
非对称加密算法实现机密信息交换的基本过程是:甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开;得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方;甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。甲方只能用其专用密钥解密由其公用密钥加密后的任何信息。
优点:
- 安全
缺点:
- 速度较慢
三、项目中常用总结
-
对称加密: DES(56位)、AES(128位),
-
非对称加密: ECC(160位)或RSA(1024),
-
消息摘要: MD5
-
数字签名:DSA
-
其中,AES和MD5最为常用。
四、代码示例
添加第三方包的依赖:项目用到两个第三方包,在pom中添加这两个包的依赖:
<!-- 添加加解密算法的依赖 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.directory.studio</groupId>
<artifactId>org.apache.commons.codec</artifactId>
<version>1.8</version>
</dependency>
AES、DES加密示例
1.加密工具类(EncryptUtil.java)
package com.gong.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//引入第三方包
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
/**
* Created by xiaopeng.
* Date: 2022/6/15 9:37
* 描述:
*/
public class EncryptUtil {
//--------------AES---------------
private static final String KEY = "1234567812345678"; // 密匙,必须16位
private static final String OFFSET = "1234567812345678"; // 偏移量
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "AES"; //算法
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式
//--------------DES---------------
/*private static final String KEY = "12345678"; // 密匙,必须8位
private static final String OFFSET = "12345678"; // 偏移量
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "DES"; //算法
private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式*/
public static String AESencrypt(String data) throws Exception {
//指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式)
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//根据自定义的加密密匙和算法模式初始化密钥规范
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
//CBC模式偏移量IV
IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());
//初始化加密模式
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
//单部分加密结束,重置Cipher
byte[] encrypted = cipher.doFinal(data.getBytes(ENCODING));
//加密后再使用BASE64做转码
return new Base64().encodeToString(encrypted);
}
/**
* AES解密
*
* @param data
* @return String
* @author xiaopeng
* @date 2022-6-15 16:46:07
*/
public static String AESdecrypt(String data) throws Exception {
//指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式)
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//根据自定义的加密密匙和算法模式初始化密钥规范
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
//CBC模式偏移量IV
IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());
//初始化解密模式
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先用base64解码
byte[] buffer = new Base64().decode(data);
//单部分加密结束,重置Cipher
byte[] encrypted = cipher.doFinal(buffer);
return new String(encrypted, ENCODING);
}
测试方法:
@Test
void AESencrypt() throws Exception {
String str = "123456789";
System.out.println("加密前:" + str);
String aeSencrypt = EncryptUtil.AESencrypt(str);
System.out.println("加密后:" + aeSencrypt);
String aesCecrypt = EncryptUtil.AESdecrypt(aeSencrypt);
System.out.println("解密后:" + aesCecrypt);
}
@Test
void AEDencrypt() throws Exception {
String str = "Lkkl95IF0IM";
String aesCecrypt = EncryptUtil.AESdecrypt(str);
System.out.println("解密后:" + aesCecrypt);
}
MD5加密示例
- 加密工具类(MD5Util.java)
package cn.kt.aesdemo.utils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//引入第三方包
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
/**
* Created by xiaopeng.
* Date: 2022/6/15 9:37
* 描述:
*/
public class MD5Util{
//---------------MD5-------------------
private static final String MD5KEY = "1205529635"; // 密匙
/**
* MD5方法
*
* @param text 明文
* @return 密文
* @author xiaopeng
* @date 2022-6-15 16:54:42
*/
public static String MD5encrypt(String text) throws Exception {
//加密后的字符串
String encodeStr = DigestUtils.md5Hex(text + MD5KEY);
return encodeStr;
}
/**
* MD5验证方法
*
* @param text 明文
* @param md5 密文
* @return true/false
* @author xiaopeng
* @date 2022-6-16 15:32 :56
*/
public static boolean verify(String text, String md5) throws Exception {
//根据传入的密钥进行验证
String md5Text = MD5encrypt(text);
if (md5Text.equalsIgnoreCase(md5)) {
return true;
}
return false;
}
}
测试
@Test
void MD5encrypt() throws Exception {
System.out.println(System.currentTimeMillis());
String str = "1205529635";
System.out.println("加密前:" + str);
String md5 = EncryptUtil.MD5encrypt(str);
System.out.println("加密后:" + md5);
//MD5校验
boolean verify = EncryptUtil.verify(str, md5);
System.out.println("校验结果:" + verify); //true
}
RSA加密示例
- 加密工具类(RSAUtil.java)
package cn.kt.aesdemo.utils;
import javax.crypto.Cipher;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/**
* Created by xiaopeng.
* Date: 2022/6/24 14:20
* 描述:
*/
public class RSAUtil {
/* 指定加密算法为DESede */
private static String ALGORITHM = "RSA";
/* 指定key的大小 */
private static int KEYSIZE = 1024;
/* 指定公钥存放文件 */
private static String PUBLIC_KEY_FILE = "1205529635";
/* 指定私钥存放文件 */
private static String PRIVATE_KEY_FILE = "13767661359";
/**
* 生成密钥对
*/
private static void generateKeyPair() throws Exception {
/* RSA算法要求有一个可信任的随机数源 */
SecureRandom sr = new SecureRandom();
/* 为RSA算法创建一个KeyPairGenerator对象 */
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM);
/* 利用上面的随机数据源初始化这个KeyPairGenerator对象 */
kpg.initialize(KEYSIZE, sr);
/* 生成密匙对 */
KeyPair kp = kpg.generateKeyPair();
/* 得到公钥 */
Key publicKey = kp.getPublic();
/* 得到私钥 */
Key privateKey = kp.getPrivate();
/* 用对象流将生成的密钥写入文件 */
ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream(PUBLIC_KEY_FILE));
ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream(PRIVATE_KEY_FILE));
oos1.writeObject(publicKey);
oos2.writeObject(privateKey);
/* 清空缓存,关闭文件输出流 */
oos1.close();
oos2.close();
}
/**
* 公钥加密方法
* source: 源数据
*/
public static String publicEncrypt(String source) throws Exception {
generateKeyPair();
/* 将文件中的公钥对象读出 */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE));
Key key = (Key) ois.readObject();
ois.close();
/* 得到Cipher对象来实现对源数据的RSA加密 */
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] b = source.getBytes();
/* 执行加密操作 */
byte[] b1 = cipher.doFinal(b);
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(b1);
}
/**
* 私钥加密方法
* source: 源数据
*/
public static String privateEncrypts(String source) throws Exception {
generateKeyPair();
/* 将文件中的公钥对象读出 */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE));
Key key = (Key) ois.readObject();
ois.close();
/* 得到Cipher对象来实现对源数据的RSA加密 */
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] b = source.getBytes();
/* 执行加密操作 */
byte[] b1 = cipher.doFinal(b);
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(b1);
}
/**
* 私钥解密算法
* cryptograph:密文
*/
public static String privateDecrypt(String cryptograph) throws Exception {
/* 将文件中的私钥对象读出 */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(PRIVATE_KEY_FILE));
Key key = (Key) ois.readObject();
/* 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder decoder = new BASE64Decoder();
byte[] b1 = decoder.decodeBuffer(cryptograph);
/* 执行解密操作 */
byte[] b = cipher.doFinal(b1);
return new String(b);
}
/**
* 公钥解密算法
* cryptograph:密文
*/
public static String publicDecrypt(String cryptograph) throws Exception {
/* 将文件中的私钥对象读出 */
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(PUBLIC_KEY_FILE));
Key key = (Key) ois.readObject();
/* 得到Cipher对象对已用公钥加密的数据进行RSA解密 */
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
BASE64Decoder decoder = new BASE64Decoder();
byte[] b1 = decoder.decodeBuffer(cryptograph);
/* 执行解密操作 */
byte[] b = cipher.doFinal(b1);
return new String(b);
}
public static void main(String[] args) throws Exception {
String source = "干嘛这么想不开,要在脸上贴个输字!";//要加密的字符串
System.out.println("源数据:" + source);
String cryptograph = publicEncrypt(source);//生成的密文
System.out.println("生成的密文:" + cryptograph);
String target = privateDecrypt(cryptograph);//解密密文
System.out.println("解密密文:" + target);
}
}
以上内容为CSDN搜索文章偷师
以下为自己实战以及遇到的问题
五、实战环节
实战1
简介:利用AES算法对本地class文件进行加密解密
1.加密工具类(EncryptUtil.java)
package com.gong.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//引入第三方包
/**
* Created by xiaopeng.
* Date: 2022/6/15 9:37
* 描述:
*/
public class EncryptUtil {
//--------------AES---------------
private static final String KEY = "1234567812345678"; // 密匙,必须16位
private static final String OFFSET = "1234567812345678"; // 偏移量
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "AES"; //算法
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式
//--------------DES---------------
/*private static final String KEY = "12345678"; // 密匙,必须8位
private static final String OFFSET = "12345678"; // 偏移量
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "DES"; //算法
private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式*/
public static byte[] AESencrypt(byte[] data) throws Exception {
//指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式)
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//根据自定义的加密密匙和算法模式初始化密钥规范
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
//CBC模式偏移量IV
IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());
//初始化加密模式
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
//单部分加密结束,重置Cipher
byte[] encrypted = cipher.doFinal(data);
//加密后再使用BASE64做转码
return encrypted;
}
/**
* AES解密
*
* @param data
* @return String
* @author xiaopeng
* @date 2022-6-15 16:46:07
*/
public static byte[] AESdecrypt(byte[] data) throws Exception {
//指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式)
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//根据自定义的加密密匙和算法模式初始化密钥规范
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
//CBC模式偏移量IV
IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());
//初始化解密模式
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先用base64解码
//byte[] buffer = new Base64().decode(data);
//单部分加密结束,重置Cipher
byte[] encrypted = cipher.doFinal(data);
return encrypted;
}
}
2.加密解密实现类(Encrypt.cass、Decrypt.class)
package com.gong.safe;
import com.gong.util.EncryptUtil;
import java.io.*;
/**
* 解密工具没啥用测试用的
*/
public class Encrypt {
private String directory;
public Encrypt(String directory) {
this.directory = directory;
System.out.println("加密ok");
}
/**
* 加密类文件
*/
public void encryptMethod(String name) throws Exception{
// 类文件路径(绝对路径)
String path = directory + File.separator + name.replace(".", File.separator) + ".class"; //例如com.gong.demo变成d:com/gong/demo.class
//输入流
File file = new File(path);
InputStream in = new FileInputStream(file);
//字节输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1){
baos.write(buf,0,len);
}
//===================加密开始=======================
//(其他代码固定,选择不同加密算法改下面即可)
byte[] bytes = baos.toByteArray();
byte[] encrypted = EncryptUtil.AESencrypt(bytes);
// for (int i = 0; i < bytes.length; i++) {
// //System.out.println(i+":加密前"+bytes[i]);
// bytes[i] = (byte)(bytes[i]+i);
// //System.out.println("加密后"+bytes[i]);
// }
//===================加密结束=======================
//写出加密文件
FileOutputStream fis = new FileOutputStream(path);
fis.write(encrypted);
fis.close();
baos.close();
in.close();
}
//测试方法
// public static void main(String[] args) throws Exception {
// Encrypt encrypt = new Encrypt("/Users/gongjunpeng/IdeaProjects/LeetCode/encrypt/target/classes");
// encrypt.encryptMethod("com.gong.safe.ToBeEncrypt");
// }
}
package com.gong.safe;
import com.gong.util.EncryptUtil;
import java.io.*;
public class Decrypt {
private String directory;
public Decrypt(String directory) {
this.directory = directory;
System.out.println("解密ok");
}
/**
* 加密类文件
*/
public void decryptMethod(String name) throws Exception{
// 类文件路径(绝对路径)
String path = directory + File.separator + name.replace(".", File.separator) + ".class"; //例如com.gong.demo变成d:com/gong/demo.class
//String encryptPath = directory + File.separator + name.replace(".", File.separator) + "_en.class";
//输入流
File file = new File(path);
InputStream in = new FileInputStream(file);
//字节输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1){
baos.write(buf,0,len);
}
//===================解密开始=======================
//(其他代码固定,选择不同加/解密算法改下面即可)
byte[] bytes = baos.toByteArray();
byte[] decrypted = EncryptUtil.AESdecrypt(bytes);
// for (int i = 0; i < bytes.length; i++) {
// //System.out.println(i+":加密前"+bytes[i]);
// bytes[i] = (byte)(bytes[i]-i);
// //System.out.println("加密后"+bytes[i]);
// }
//===================解密结束=======================
//写出加密文件
FileOutputStream fis = new FileOutputStream(path);
fis.write(decrypted);
fis.close();
baos.close();
in.close();
}
//测试方法
// public static void main(String[] args) throws Exception {
// Decrypt decrypt = new Decrypt("/Users/gongjunpeng/IdeaProjects/LeetCode/encrypt/target/classes");
// decrypt.decryptMethod("com.gong.code.Test");
// }
}
3.用来测试加密的类(要bulid成class文件,然后加密拿来测试解密运行)ToBeEncrypt.class
package com.gong.safe;
public class ToBeEncrypt {
public void init(){
System.out.println("加密文件运行ok");
}
}
4.自定义类加载器MyClassLoader.class
package com.gong.safe;
import java.io.*;
import java.lang.reflect.Method;
/**
* 运行自定义类加载器运行指定class文件
*/
public class MyClassLoader extends ClassLoader {
private String directory;
public MyClassLoader(String directory){
this.directory = directory;
}
//findclass是指定我们运行class的路径
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
String filePath = directory + File.separator + name.replace(".", File.separator) + ".class";
//构建输入流
InputStream in = new FileInputStream(filePath);
//构建字节输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//缓冲区
byte buf[] = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1){
baos.write(buf,0,len);
}
byte data[] = baos.toByteArray();//读取到的字节码的二进制数据
baos.close();
in.close();
return defineClass(name, data, 0, data.length);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//直接就在
public static void main(String[] args) throws Exception {
//Test文件加密了要先解密
//参数是绝对地址,程序根目录
Decrypt decrypt = new Decrypt("/Users/gongjunpeng/IdeaProjects/LeetCode/encrypt/target/classes");
//参数是类的全名
decrypt.decryptMethod("com.gong.safe.ToBeEncrypt");
//用自己的类加载器运行
MyClassLoader myClassLoader = new MyClassLoader("/Users/gongjunpeng/IdeaProjects/LeetCode/encrypt/target/classes");
Class<?> Test = myClassLoader.loadClass("com.gong.safe.ToBeEncrypt");
Object o = Test.newInstance();//反射实例化
Method init = Test.getMethod("init", null);//得到类方法
init.invoke(o);//运行类方法
}
}
实战1我们可以看到我们解密时候需要写绝对路径,当我们将程序打成jar包运行加密的class文件时就会报错
ps:System.getProperty("user.dir");
可以获取程序运行的目录但是jar包内是整体找不到具体的class在哪里
实战2:进阶版
打成jar包后依旧可以运行jar包内加密的class文件
其实就是没有写具体路径,利用当前类.class.getResourceAsStream("资源名")
对于getResourceAsStream
详细可以在我博客中搜索有具体描述
1.依旧是加密工具类和之前一样的(EncryptUtil.class)
package com.gong.util;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//引入第三方包
/**
* Created by xiaopeng.
* Date: 2022/6/15 9:37
* 描述:
*/
public class EncryptUtil {
//--------------AES---------------
private static final String KEY = "1234567812345678"; // 密匙,必须16位
private static final String OFFSET = "1234567812345678"; // 偏移量
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "AES"; //算法
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式
//--------------DES---------------
/*private static final String KEY = "12345678"; // 密匙,必须8位
private static final String OFFSET = "12345678"; // 偏移量
private static final String ENCODING = "UTF-8"; // 编码
private static final String ALGORITHM = "DES"; //算法
private static final String CIPHER_ALGORITHM = "DES/CBC/PKCS5Padding"; // 默认的加密算法,CBC模式*/
public static byte[] AESencrypt(byte[] data) throws Exception {
//指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式)
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//根据自定义的加密密匙和算法模式初始化密钥规范
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
//CBC模式偏移量IV
IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());
//初始化加密模式
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
//单部分加密结束,重置Cipher
byte[] encrypted = cipher.doFinal(data);
//加密后再使用BASE64做转码
return encrypted;
}
/**
* AES解密
*
* @param data
* @return String
* @author xiaopeng
* @date 2022-6-15 16:46:07
*/
public static byte[] AESdecrypt(byte[] data) throws Exception {
//指定算法、获取Cipher对象(DES/CBC/PKCS5Padding:算法为,工作模式,填充模式)
Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
//根据自定义的加密密匙和算法模式初始化密钥规范
SecretKeySpec skeySpec = new SecretKeySpec(KEY.getBytes("ASCII"), ALGORITHM);
//CBC模式偏移量IV
IvParameterSpec iv = new IvParameterSpec(OFFSET.getBytes());
//初始化解密模式
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
//先用base64解码
//byte[] buffer = new Base64().decode(data);
//单部分加密结束,重置Cipher
byte[] encrypted = cipher.doFinal(data);
return encrypted;
}
//测试工具可用
// public static void main(String[] args) throws Exception {
// byte[] bytes = new byte[]{0,0,1,2,3,4,5,6,7,8,9};
// byte[] bytes1 = EncryptUtil.AESencrypt(bytes);
// byte[] bytes2 = EncryptUtil.AESdecrypt(bytes1);
// System.out.println(bytes2[2]==bytes[2]);
// }
}
2.加密解密方法也和实战1一样(Encrypt.cass、Decrypt.class)
package com.gong.safe;
import com.gong.util.EncryptUtil;
import java.io.*;
/**
* 解密工具没啥用测试用的
*/
public class Encrypt {
private String directory;
public Encrypt(String directory) {
this.directory = directory;
}
/**
* 加密类文件
*/
public void encryptMethod(String name) throws Exception{
// 类文件路径(绝对路径)
String path = directory + File.separator + name.replace(".", File.separator) + ".class"; //例如com.gong.demo变成d:com/gong/demo.class
//输入流
File file = new File(path);
InputStream in = new FileInputStream(file);
//字节输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1){
baos.write(buf,0,len);
}
//===================加密开始=======================
//(其他代码固定,选择不同加密算法改下面即可)
byte[] bytes = baos.toByteArray();
byte[] encrypted = EncryptUtil.AESencrypt(bytes);
//===================加密结束=======================
//写出加密文件
FileOutputStream fis = new FileOutputStream(path);
fis.write(encrypted);
fis.close();
baos.close();
in.close();
}
}
package com.gong.safe;
import com.gong.util.EncryptUtil;
import java.io.*;
public class Decrypt {
private String directory;
public Decrypt(String directory) {
this.directory = directory;
System.out.println("解密ok");
}
/**
* 加密类文件
*/
public void decryptMethod(String name) throws Exception{
// 类文件路径(绝对路径)
String path = directory + File.separator + name.replace(".", File.separator) + ".class"; //例如com.gong.demo变成d:com/gong/demo.class
//String encryptPath = directory + File.separator + name.replace(".", File.separator) + "_en.class";
//输入流
File file = new File(path);
InputStream in = new FileInputStream(file);
//字节输出流
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1){
baos.write(buf,0,len);
}
//===================解密开始=======================
//(其他代码固定,选择不同加/解密算法改下面即可)
byte[] bytes = baos.toByteArray();
byte[] decrypted = EncryptUtil.AESdecrypt(bytes);
//===================加密结束=======================
//写出加密文件
FileOutputStream fis = new FileOutputStream(path);
fis.write(decrypted);
fis.close();
baos.close();
in.close();
}
}
自定义类加载器MyClassLoader.class
package com.gong.safe;
import com.gong.util.EncryptUtil;
import java.io.*;
import java.lang.reflect.Method;
/**
* 运行自定义类加载器运行指定class文件
*/
public class MyClassLoader extends ClassLoader {
private String encryptClassName;
private Class<?> currentClass;
public MyClassLoader(String encryptClassName,Class<?> currentClass){
this.encryptClassName = encryptClassName;
this.currentClass = currentClass;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
//String filePath = directory + File.separator + name.replace(".", File.separator) + ".class";
//构建输入流
//InputStream in = new FileInputStream(filePath);
//构建字节输出流
InputStream in = currentClass.getResourceAsStream(encryptClassName);
if (in == null){
new RuntimeException("类不存在");
return null;
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//缓冲区
byte buf[] = new byte[1024];
int len = -1;
while ((len = in.read(buf)) != -1){
baos.write(buf,0,len);
}
byte data[] = baos.toByteArray();//读取到的字节码的二进制数据
byte[] bytes = null;
try {
bytes = EncryptUtil.AESdecrypt(data);
} catch (Exception e) {
e.printStackTrace();
}
baos.close();
in.close();
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//findClass是保护方法,类加载器具体我也不是很懂,也不知道这样调用会不会有什么隐藏问题
public Class<?> myFindClass(String name) throws ClassNotFoundException {
return findClass(name);
}
}
4.启动类
测试运行加密后的class文件是否可以
package com.gong.safe;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//注意加密只允许一次,运行一次后要注释掉,要不会造成二次加密报错(可以将加密单独写出来)
public class Start {
public static void main(String[] args) throws Exception{
//加密class文件
Encrypt encrypt = new Encrypt("/Users/gongjunpeng/IdeaProjects/LeetCode/suanfa/encrypt/target/classes");
encrypt.encryptMethod("com.gong.safe.ToBeEncrypt");//加密替换原class文件
//参数1需要加密的类名带后缀,参数2当前类
MyClassLoader myClassLoader = new MyClassLoader("ToBeEncrypt.class",Start.class);
//利用类加载器加载我们加密的类
Class<?> aClass = myClassLoader.myFindClass("com.gong.safe.ToBeEncrypt");
Object o = aClass.newInstance();//获得加密的实例对象
Method init = aClass.getMethod("init", null);//获得对象方//使用方法法
init.invoke(o);//使用加密类方法
}
}
最后打包成jar包可以正常运行,不会打jar包在我其他文章中有方法
方法已打成jar包存放在本人gitee上: