(四)反篡改介绍

先来看看AI的介绍,为了防止Java代码打包的JAR包被篡改,可以采取以下几种措施:

  1. 使用数字签名:可以使用Java的签名工具(如Jarsigner)对JAR包进行数字签名。签名可以确保JAR包的完整性和来源可信性。在验证JAR包时,可以使用Java的验证工具(如Jarsigner或Java Web Start)来验证签名。

  2. 使用加密技术:可以使用加密算法对JAR包进行加密,确保只有授权的用户才能解密和使用JAR包。可以使用Java的加密库(如javax.crypto)来实现加密和解密操作。

  3. 使用文件校验和:可以使用文件校验和算法(如MD5或SHA-256)计算JAR包的校验和,并将校验和值保存在可信任的位置(如服务器)。在使用JAR包时,可以重新计算校验和并与保存的值进行比较,以验证JAR包的完整性。

  4. 使用安全沙箱:可以使用Java的安全沙箱机制来限制JAR包的访问权限,防止恶意代码对系统进行篡改或攻击。可以使用Java的安全管理器(SecurityManager)来配置和管理安全沙箱。

  5. 使用代码混淆:可以使用代码混淆工具对Java代码进行混淆,使代码难以阅读和理解。混淆后的代码更难被篡改和逆向工程。

综合使用上述措施可以提高JAR包的安全性,减少被篡改的风险。然而,完全防止JAR包被篡改是很困难的,因此还应定期更新和验证JAR包,以及采取其他安全措施来保护系统的安全。


前面我们已经对代码进行了混淆,想修改代码是很难的,如果我们打包包含的代码很多,为了功能的完整性,我们可能会要求核心代码不允许被覆盖,被混淆的代码通过某些工具还是可以改名的,比如把超长的名字改短,然后再覆盖代码,有很多手段能绕过,为了再增加一些难度,本文考虑使用RSA对称加密对混淆后的代码进行加密,不允许修改混淆后的类名,不允许修改文件内容。

使用hutool提供的RSA可以很简单的生成私钥和公钥,可以很方便的加解密,下面是示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
RSA rsa = new RSA();
PrivateKey privateKey = rsa.getPrivateKey();
PublicKey publicKey = rsa.getPublicKey();
String home = System.getProperty("user.home");
String privateKeyPath = home + File.separator + ".yguard" + File.separator + "license-keys.pri";
String publicKeyPath = home + File.separator + ".yguard" + File.separator + "license-keys.pub";
if (!new File(privateKeyPath).exists()) {
FileUtil.writeBytes(privateKey.getEncoded(), privateKeyPath);
FileUtil.writeBytes(publicKey.getEncoded(), publicKeyPath);
String publicKeyHex = HexUtil.encodeHexStr(publicKey.getEncoded());
FileUtil.writeString(publicKeyHex, publicKeyPath + ".hex", StandardCharsets.UTF_8);
}

通过上面的代码将私钥和公钥生成到用户目录下面的 .yguard 目录中。

这里还将公钥生成了一个16进制字符串形式的文本,方便直接签入到解密程序中使用。

可以使用下面的方法进行加密和解密:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 使用私钥加密
*/
private static String encryptHex(String str) {
String home = System.getProperty("user.home");
String privateKeyPath = home + File.separator + ".yguard" + File.separator + "license-keys.pri";
RSA rsa = new RSA(FileUtil.readBytes(privateKeyPath), null);
byte[] bytes = rsa.encrypt(str, StandardCharsets.UTF_8, KeyType.PrivateKey);
return HexUtil.encodeHexStr(bytes);
}

public static final String PUB_KEY = "****很长的公钥,使用上面生成后的内容替换****";

/**
* 使用公钥解密
*/
private static String decryptStr(String str) {
RSA rsa = new RSA(null, HexUtil.decodeHex(PUB_KEY));
return rsa.decryptStr(str, KeyType.PublicKey, StandardCharsets.UTF_8);
}

使用下面代码进行简单测试:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
String url = "https://mybatis.io";
System.out.println("原始: " + url);
String sign = encryptHex(url);
System.out.println("加密: " + sign);
String str = decryptStr(sign);
System.out.println("解密: " + str);
}

输出内容如下:

1
2
3
原始: https://mybatis.io
加密: 8d13070b469768ea57cef0800ed9be4e3e6303ad10cbd308ed9d959ea4caace0d2c17b020af33bca563aa0f45e1b4e8a82bbc4ee67fbb80bc26f5da5c24865fb851ed5c4868631bde5f324213b77a432fa40a761412bb5ac3b380ea4f07496665369b9076bc2091f3707fe4b0c0e1a337394736085a2198f2ff82d3ba06eff19
解密: https://mybatis.io

有了这些工具后,我们就需要考虑如何在混淆后的代码上增加加密签名防止混淆了。


(四)反篡改介绍
https://blog.mybatis.io/post/7e8639bd.html
作者
Liuzh
发布于
2024年6月22日
许可协议