feat: license防篡改系统时间
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user