2023-02-16 15:28:15 +08:00
|
|
|
|
package com.zhgd.xmgl.util;
|
|
|
|
|
|
|
|
|
|
|
|
import io.xjar.XCryptos;
|
|
|
|
|
|
import io.xjar.XKit;
|
|
|
|
|
|
import io.xjar.boot.XBoot;
|
|
|
|
|
|
import io.xjar.key.XKey;
|
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
import org.apache.commons.lang.StringUtils;
|
|
|
|
|
|
|
|
|
|
|
|
import javax.swing.*;
|
|
|
|
|
|
import java.awt.event.ActionEvent;
|
|
|
|
|
|
import java.awt.event.ActionListener;
|
|
|
|
|
|
import java.text.SimpleDateFormat;
|
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @desc:xjar 加密工具,针对java开发项目jar包进行加密,jar加密后,大小会翻倍,同时swagger无法访问,接口暂时未发现问题
|
|
|
|
|
|
* @author: wangsh
|
|
|
|
|
|
* @time: 2021/4/15 9:34
|
|
|
|
|
|
*/
|
|
|
|
|
|
@Slf4j
|
|
|
|
|
|
public class XjarEncryUtil {
|
|
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
|
createJFrame();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建面板,这个类似于 HTML 的 div 标签,我们可以创建多个面板并在 JFrame 中指定位置,面板中我们可以添加文本字段,按钮及其他组件。
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static void createJFrame() {
|
|
|
|
|
|
// 创建 JFrame 实例
|
|
|
|
|
|
JFrame frame = new JFrame("XJar加密,防止反编译");
|
|
|
|
|
|
// 设置窗口大小
|
|
|
|
|
|
frame.setSize(600, 400);
|
|
|
|
|
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
|
|
|
|
|
|
|
|
|
|
//创建面板,这个类似于 HTML 的 div 标签,可以创建多个面板并在 JFrame 中指定位置,
|
|
|
|
|
|
// 面板中可以添加文本字段,按钮及其他组件。
|
|
|
|
|
|
JPanel panel = new JPanel();
|
|
|
|
|
|
// 添加面板
|
|
|
|
|
|
frame.add(panel);
|
|
|
|
|
|
//调用用户定义的方法并添加组件到面板
|
|
|
|
|
|
addComponents(panel);
|
|
|
|
|
|
// 设置界面可见
|
|
|
|
|
|
frame.setVisible(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 调用用户定义的方法并添加组件到面板
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static void addComponents(JPanel panel) {
|
|
|
|
|
|
//布局部分我们这边不多做介绍,这边设置布局为 null
|
|
|
|
|
|
panel.setLayout(null);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建 需要加密的jar JLabel
|
|
|
|
|
|
JLabel fromLabel = new JLabel("选择加密的jar:");
|
|
|
|
|
|
fromLabel.setBounds(10, 20, 120, 30);
|
|
|
|
|
|
panel.add(fromLabel);
|
|
|
|
|
|
|
|
|
|
|
|
//用于记录未加密jar的文本域
|
|
|
|
|
|
final JTextField fromText = new JTextField(20);
|
|
|
|
|
|
fromText.setBounds(100, 20, 300, 30);
|
|
|
|
|
|
panel.add(fromText);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建选择按钮
|
|
|
|
|
|
JButton fromButton = new JButton("选择");
|
|
|
|
|
|
fromButton.setBounds(420, 20, 80, 30);
|
|
|
|
|
|
panel.add(fromButton);
|
|
|
|
|
|
|
|
|
|
|
|
// 加密后jar要保存的位置
|
|
|
|
|
|
JLabel toLabel = new JLabel("选择保存位置:");
|
|
|
|
|
|
toLabel.setBounds(10, 50, 120, 30);
|
|
|
|
|
|
panel.add(toLabel);
|
|
|
|
|
|
|
|
|
|
|
|
//文本域用于记录保存路径
|
|
|
|
|
|
final JTextField toText = new JTextField(20);
|
|
|
|
|
|
toText.setBounds(100, 50, 300, 30);
|
|
|
|
|
|
panel.add(toText);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建选择按钮
|
|
|
|
|
|
JButton toButton = new JButton("选择");
|
|
|
|
|
|
toButton.setBounds(420, 50, 80, 30);
|
|
|
|
|
|
panel.add(toButton);
|
|
|
|
|
|
|
|
|
|
|
|
// 输入密码的文本域
|
|
|
|
|
|
JLabel passwordLabel = new JLabel("加密密码:");
|
|
|
|
|
|
passwordLabel.setBounds(10, 80, 80, 30);
|
|
|
|
|
|
panel.add(passwordLabel);
|
|
|
|
|
|
|
|
|
|
|
|
//这个类似用于输入的文本域,但是输入的信息会以点号代替,用于包含密码的安全性
|
|
|
|
|
|
final JPasswordField passwordText = new JPasswordField(20);
|
|
|
|
|
|
passwordText.setBounds(100, 80, 300, 30);
|
|
|
|
|
|
panel.add(passwordText);
|
|
|
|
|
|
|
|
|
|
|
|
// 创建开始按钮
|
|
|
|
|
|
JButton startButton = new JButton("加密");
|
|
|
|
|
|
startButton.setBounds(100, 110, 80, 30);
|
|
|
|
|
|
panel.add(startButton);
|
|
|
|
|
|
// 创建开始按钮
|
|
|
|
|
|
JButton endButton = new JButton("解密");
|
|
|
|
|
|
endButton.setBounds(200, 110, 80, 30);
|
|
|
|
|
|
panel.add(endButton);
|
|
|
|
|
|
|
|
|
|
|
|
//日志显示框
|
|
|
|
|
|
final JTextArea textArea = new JTextArea("");
|
|
|
|
|
|
textArea.setBounds(10, 150, 550, 150);
|
|
|
|
|
|
textArea.setLineWrap(true);
|
|
|
|
|
|
textArea.setWrapStyleWord(true);
|
|
|
|
|
|
panel.add(textArea);
|
|
|
|
|
|
|
|
|
|
|
|
//选择jar按钮监听事件
|
|
|
|
|
|
fromButton.addActionListener(new ActionListener() {
|
|
|
|
|
|
public void actionPerformed(ActionEvent e) { //按钮点击事件
|
|
|
|
|
|
JFileChooser chooser = new JFileChooser(); //设置选择器
|
|
|
|
|
|
chooser.setMultiSelectionEnabled(false); //设为单选
|
|
|
|
|
|
int returnVal = chooser.showOpenDialog(null); //是否打开文件选择框
|
|
|
|
|
|
if (returnVal == JFileChooser.APPROVE_OPTION) { //如果符合文件类型
|
|
|
|
|
|
String filepath = chooser.getSelectedFile().getAbsolutePath(); //获取绝对路径
|
|
|
|
|
|
if (!".jar".equals(filepath.substring(filepath.length() - 4))) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "文件格式不正确,请选择jar文件!", "文件格式错误", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
fromText.setText(filepath);
|
|
|
|
|
|
}
|
|
|
|
|
|
log.info(filepath);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//选择加密后jar保存路径按钮监听事件
|
|
|
|
|
|
toButton.addActionListener(new ActionListener() {
|
|
|
|
|
|
public void actionPerformed(ActionEvent e) { //按钮点击事件
|
|
|
|
|
|
JFileChooser chooser = new JFileChooser(); //设置选择器
|
|
|
|
|
|
chooser.setMultiSelectionEnabled(false); //设为单选
|
|
|
|
|
|
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); //设置只选目录
|
|
|
|
|
|
chooser.setDialogTitle("选择加密后jar保存位置");
|
|
|
|
|
|
int returnVal = chooser.showOpenDialog(null); //是否打开文件选择框
|
|
|
|
|
|
if (returnVal == JFileChooser.APPROVE_OPTION) { //如果符合文件类型
|
|
|
|
|
|
String filepath = chooser.getSelectedFile().getAbsolutePath(); //获取绝对路径
|
|
|
|
|
|
toText.setText(filepath);
|
|
|
|
|
|
log.info(filepath);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//选择开始径按钮的监听事件
|
|
|
|
|
|
startButton.addActionListener(new ActionListener() {
|
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
|
String fromJarPath = fromText.getText();
|
|
|
|
|
|
String toJarPath = toText.getText() + "\\encrypt" + getNowDateTime() + ".jar";
|
|
|
|
|
|
String password = new String(passwordText.getPassword());
|
|
|
|
|
|
log.info("fromJarPath=" + fromJarPath);
|
|
|
|
|
|
log.info("toJarPath=" + toJarPath);
|
|
|
|
|
|
log.info("password=" + password);
|
|
|
|
|
|
if (StringUtils.isEmpty(fromJarPath)) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "jar文件不能为空!", "jar文件不能为空!", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (StringUtils.isEmpty(toJarPath)) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "保存路径不能为空!", "保存路径不能为空!", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (StringUtils.isEmpty(toJarPath)) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "请输入加密的密码!", "密码不能为空!", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//打印输入日志
|
|
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
|
|
builder.append("fromJarPath=" + fromJarPath + "\n");
|
|
|
|
|
|
builder.append("toJarPath=" + toJarPath + "\n");
|
|
|
|
|
|
textArea.setText(builder.toString());
|
|
|
|
|
|
//开始加密文件
|
|
|
|
|
|
encryptJar(fromJarPath, toJarPath, password);
|
|
|
|
|
|
textArea.append("jar加密成功!\n请测试接口是否正常(注意:Swagger不可用)");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
//选择开始径按钮的监听事件
|
|
|
|
|
|
endButton.addActionListener(new ActionListener() {
|
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
|
String fromJarPath = fromText.getText();
|
|
|
|
|
|
String toJarPath = toText.getText() + "\\encrypt" + getNowDateTime() + ".jar";
|
|
|
|
|
|
String password = new String(passwordText.getPassword());
|
|
|
|
|
|
log.info("fromJarPath=" + fromJarPath);
|
|
|
|
|
|
log.info("toJarPath=" + toJarPath);
|
|
|
|
|
|
log.info("password=" + password);
|
|
|
|
|
|
if (StringUtils.isEmpty(fromJarPath)) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "jar文件不能为空!", "jar文件不能为空!", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (StringUtils.isEmpty(toJarPath)) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "保存路径不能为空!", "保存路径不能为空!", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (StringUtils.isEmpty(toJarPath)) {
|
|
|
|
|
|
JOptionPane.showMessageDialog(null, "请输入加密的密码!", "密码不能为空!", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//打印输入日志
|
|
|
|
|
|
StringBuilder builder = new StringBuilder();
|
|
|
|
|
|
builder.append("fromJarPath=" + fromJarPath + "\n");
|
|
|
|
|
|
builder.append("toJarPath=" + toJarPath + "\n");
|
|
|
|
|
|
textArea.setText(builder.toString());
|
|
|
|
|
|
//开始加密文件
|
|
|
|
|
|
decryptJar(fromJarPath, toJarPath, password);
|
|
|
|
|
|
textArea.append("jar加密成功!\n请测试接口是否正常(注意:Swagger不可用)");
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前格式化时间的方法
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static String getNowDateTime() {
|
|
|
|
|
|
String dateNow = "";
|
|
|
|
|
|
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
|
|
|
|
|
|
Date date = new Date();
|
|
|
|
|
|
dateNow = format.format(date);
|
|
|
|
|
|
return dateNow;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* jar包加密,防止反编译
|
|
|
|
|
|
* 此编译方式在运行jar包时需要输入密码
|
|
|
|
|
|
* 运行方式一 :
|
|
|
|
|
|
* // 命令行运行JAR 然后在提示输入密码的时候输入密码后按回车即可正常启动
|
|
|
|
|
|
* java -jar /path/to/encrypted.jar
|
|
|
|
|
|
* 运行方式二:
|
|
|
|
|
|
* // 也可以通过传参的方式直接启动,不太推荐这种方式,因为泄露的可能性更大!
|
|
|
|
|
|
* java -jar /path/to/encrypted.jar --xjar.password=PASSWORD
|
|
|
|
|
|
* 运行方式三:
|
|
|
|
|
|
* // 对于 nohup 或 javaw 这种后台启动方式,无法使用控制台来输入密码,推荐使用指定密钥文件的方式启动
|
|
|
|
|
|
* nohup java -jar /path/to/encrypted.jar --xjar.keyfile=/path/to/xjar.key
|
|
|
|
|
|
* xjar.key 文件说明:
|
|
|
|
|
|
* 格式:
|
|
|
|
|
|
* password: PASSWORD
|
|
|
|
|
|
* algorithm: ALGORITHM
|
|
|
|
|
|
* keysize: KEYSIZE
|
|
|
|
|
|
* ivsize: IVSIZE
|
|
|
|
|
|
* hold: HOLD
|
|
|
|
|
|
* 参数说明:
|
|
|
|
|
|
* password 密码 无 密码字符串
|
|
|
|
|
|
* algorithm 密钥算法 AES 支持JDK所有内置算法,如AES / DES ...
|
|
|
|
|
|
* keysize 密钥长度 128 根据不同的算法选取不同的密钥长度。
|
|
|
|
|
|
* ivsize 向量长度 128 根据不同的算法选取不同的向量长度。
|
|
|
|
|
|
* hold 是否保留 false 读取后是否保留密钥文件。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param fromJarPath 需要加密的jar
|
|
|
|
|
|
* @param toJarPath 加密后的jar
|
|
|
|
|
|
* @param password 加密密码
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static void encryptJar(String fromJarPath, String toJarPath, String password) {
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
XCryptos.encryption()
|
|
|
|
|
|
.from(fromJarPath)
|
|
|
|
|
|
.use(password)
|
|
|
|
|
|
.include("/com/**/*")
|
|
|
|
|
|
.to(toJarPath);
|
|
|
|
|
|
} catch (Exception e) {
|
2024-04-14 21:05:01 +08:00
|
|
|
|
log.error("error:", e);
|
2023-02-16 15:28:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* jar包解密
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param fromJarPath 已通过Xjar加密的jar文件路径
|
|
|
|
|
|
* @param toJarPath 解密后的jar文件
|
|
|
|
|
|
* @param password 密码
|
|
|
|
|
|
*/
|
|
|
|
|
|
public static void decryptJar(String fromJarPath, String toJarPath, String password) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
XKey xKey = XKit.key(password);
|
|
|
|
|
|
XBoot.decrypt(fromJarPath, toJarPath, xKey);
|
|
|
|
|
|
} catch (Exception e) {
|
2024-04-14 21:05:01 +08:00
|
|
|
|
log.error("error:", e);
|
2023-02-16 15:28:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|