2
0

feat: license防篡改系统时间

This commit is contained in:
caiyuchao
2025-04-27 15:41:33 +08:00
parent 3a69f83746
commit ffd27558d1
4 changed files with 102 additions and 35 deletions

View File

@@ -10,13 +10,17 @@ import de.schlichtherle.xml.GenericCertificate;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.wfc.common.license.domain.LicenseCheckModel; import org.wfc.common.license.domain.LicenseCheckModel;
import org.wfc.common.license.domain.LicenseConstants;
import org.wfc.common.license.serverinfo.AbstractServerInfos; import org.wfc.common.license.serverinfo.AbstractServerInfos;
import org.wfc.common.license.serverinfo.LinuxServerInfos; import org.wfc.common.license.serverinfo.LinuxServerInfos;
import org.wfc.common.license.serverinfo.WindowsServerInfos; import org.wfc.common.license.serverinfo.WindowsServerInfos;
import org.wfc.common.license.utils.LicenseUtils;
import java.beans.XMLDecoder; import java.beans.XMLDecoder;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.FileReader;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@@ -171,8 +175,30 @@ public class CustomLicenseManager extends LicenseManager {
throw new LicenseContentException("不能获取服务器硬件信息"); throw new LicenseContentException("不能获取服务器硬件信息");
} }
} }
// 校验服务器时间是否被篡改
if (!verityTimeFile()) {
throw new LicenseContentException("当前服务器的时间不正常");
}
} }
private boolean verityTimeFile() {
try {
// 取出时间文件
BufferedReader reader = new BufferedReader(new FileReader(LicenseConstants.TIME_PATH));
String fileJson = reader.readLine();
reader.close();
String decryptedJson = LicenseUtils.decrypt(fileJson);
if (Long.parseLong(decryptedJson) < System.currentTimeMillis()) {
return true;
}
} catch (Exception e) {
log.error("verity license time error", e);
return false;
}
return false;
}
/** /**
* 重写XMLDecoder解析XML * 重写XMLDecoder解析XML

View File

@@ -0,0 +1,12 @@
package org.wfc.common.license.domain;
/**
* @author: cyc
* @since: 2025-04-27
*/
public class LicenseConstants {
public final static String TIME_PATH = "/var/lib/pro/etc/serve/production.json";
public static final String KEY = "wfcwanfiAdmin6666";
}

View File

@@ -10,18 +10,17 @@ import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.wfc.common.license.LicenseVerify; import org.wfc.common.license.LicenseVerify;
import org.wfc.common.license.domain.LicenseCheckModel; import org.wfc.common.license.domain.LicenseCheckModel;
import org.wfc.common.license.domain.LicenseConstants;
import org.wfc.common.license.domain.LicenseVerifyParam; import org.wfc.common.license.domain.LicenseVerifyParam;
import org.wfc.common.license.serverinfo.AbstractServerInfos; import org.wfc.common.license.serverinfo.AbstractServerInfos;
import org.wfc.common.license.utils.LicenseUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileWriter; import java.io.FileWriter;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
@@ -38,12 +37,10 @@ public class LicenseCheckRunner implements ApplicationRunner {
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> scheduledFuture; private ScheduledFuture<?> scheduledFuture;
private static final String key = "wfcwanfiAdmin6666";
// 启动定时任务 // 启动定时任务
public void startTimer() { public void startTimer() {
scheduledFuture = scheduler.scheduleAtFixedRate(this::timer, 0, 30, TimeUnit.SECONDS); scheduledFuture = scheduler.scheduleAtFixedRate(this::timer, 0, 30, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(this::fileTimer, 0, 60, TimeUnit.SECONDS);
} }
// 停止定时任务 // 停止定时任务
@@ -100,7 +97,13 @@ public class LicenseCheckRunner implements ApplicationRunner {
install(); install();
LicenseCheckRunner.md5 = readMd5; LicenseCheckRunner.md5 = readMd5;
} }
}
/**
* 5秒检测一次不能太快也不能太慢
*/
protected void fileTimer() {
createTimeFile();
} }
private void install() { private void install() {
@@ -131,10 +134,10 @@ public class LicenseCheckRunner implements ApplicationRunner {
String jsonString = serverInfos.toJsonString(); String jsonString = serverInfos.toJsonString();
System.out.println("原始数据 = " + jsonString); System.out.println("原始数据 = " + jsonString);
//加密 //加密
String encryptedJson = encrypt(jsonString); String encryptedJson = LicenseUtils.encrypt(jsonString);
System.out.println("加密后 = " + encryptedJson); System.out.println("加密后 = " + encryptedJson);
// 解密 // 解密
String decryptedJson = decrypt(encryptedJson); String decryptedJson = LicenseUtils.decrypt(encryptedJson);
System.out.println("解密后 = " + decryptedJson); System.out.println("解密后 = " + decryptedJson);
// 比较解密后的字符串与原始字符串 // 比较解密后的字符串与原始字符串
if (jsonString.equals(decryptedJson)) { if (jsonString.equals(decryptedJson)) {
@@ -156,38 +159,22 @@ public class LicenseCheckRunner implements ApplicationRunner {
FileWriter writer = new FileWriter(licenseProperties.getCodePath()); FileWriter writer = new FileWriter(licenseProperties.getCodePath());
writer.write(encryptedJson); writer.write(encryptedJson);
writer.close(); writer.close();
log.info("licenseParam {}", licenseParam); log.debug("licenseParam {}", licenseParam);
} catch (Exception e) { } catch (Exception e) {
log.error("生成激活码失败", e); log.error("生成激活码失败", e);
} }
} }
private void createTimeFile() {
private String encrypt(String json) throws Exception { try {
byte[] keyBytes = key.getBytes(); String currentTime = System.currentTimeMillis() + "";
MessageDigest sha = MessageDigest.getInstance("SHA-256"); String encryptedJson = LicenseUtils.encrypt(currentTime);
keyBytes = sha.digest(keyBytes); FileWriter writer = new FileWriter(LicenseConstants.TIME_PATH);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16); writer.write(encryptedJson);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); writer.close();
} catch (Exception e) {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING"); log.error("license create temp error", e);
cipher.init(Cipher.ENCRYPT_MODE, secretKey); }
byte[] encryptedBytes = cipher.doFinal(json.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
private String decrypt(String encryptedJson) throws Exception {
byte[] keyBytes = key.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
keyBytes = sha.digest(keyBytes);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedJson);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
} }
/** /**

View File

@@ -0,0 +1,42 @@
package org.wfc.common.license.utils;
import org.wfc.common.license.domain.LicenseConstants;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Base64;
/**
* @author: cyc
* @since: 2025-04-27
*/
public class LicenseUtils {
public static String encrypt(String json) throws Exception {
byte[] keyBytes = LicenseConstants.KEY.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
keyBytes = sha.digest(keyBytes);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] encryptedBytes = cipher.doFinal(json.getBytes());
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decrypt(String encryptedJson) throws Exception {
byte[] keyBytes = LicenseConstants.KEY.getBytes();
MessageDigest sha = MessageDigest.getInstance("SHA-256");
keyBytes = sha.digest(keyBytes);
keyBytes = java.util.Arrays.copyOf(keyBytes, 16);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedJson);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return new String(decryptedBytes);
}
}