Merge branch 'dev-selfcheck' into V1.00
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
package com.cisd.tms.modules.system.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
@Schema(description = "自检结果")
|
||||
public class SelfCheckResult {
|
||||
|
||||
@Schema(description = "自检项目名称")
|
||||
private String checkName;
|
||||
|
||||
@Schema(description = "自检是否通过")
|
||||
private boolean passed;
|
||||
|
||||
@Schema(description = "自检详情信息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "自检耗时(毫秒)")
|
||||
private long durationMs;
|
||||
|
||||
public static SelfCheckResult success(String checkName, String message, long durationMs) {
|
||||
SelfCheckResult result = new SelfCheckResult();
|
||||
result.checkName = checkName;
|
||||
result.passed = true;
|
||||
result.message = message;
|
||||
result.durationMs = durationMs;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static SelfCheckResult failure(String checkName, String message, long durationMs) {
|
||||
SelfCheckResult result = new SelfCheckResult();
|
||||
result.checkName = checkName;
|
||||
result.passed = false;
|
||||
result.message = message;
|
||||
result.durationMs = durationMs;
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getCheckName() {
|
||||
return checkName;
|
||||
}
|
||||
|
||||
public void setCheckName(String checkName) {
|
||||
this.checkName = checkName;
|
||||
}
|
||||
|
||||
public boolean isPassed() {
|
||||
return passed;
|
||||
}
|
||||
|
||||
public void setPassed(boolean passed) {
|
||||
this.passed = passed;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public long getDurationMs() {
|
||||
return durationMs;
|
||||
}
|
||||
|
||||
public void setDurationMs(long durationMs) {
|
||||
this.durationMs = durationMs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.cisd.tms.modules.system.runner;
|
||||
|
||||
import com.cisd.tms.modules.system.dto.SelfCheckResult;
|
||||
import com.cisd.tms.modules.system.service.SelfCheckService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class SelfCheckStartupRunner implements CommandLineRunner {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SelfCheckStartupRunner.class);
|
||||
|
||||
@Autowired
|
||||
private SelfCheckService selfCheckService;
|
||||
|
||||
private static final String SELFCHECK_SKIP_ENV = "TMS_SELFCHECK_SKIP";
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
log.info("══════════════════════════════════════════════════════════════");
|
||||
log.info(" 系统启动自检流程");
|
||||
log.info("══════════════════════════════════════════════════════════════");
|
||||
|
||||
String skipSelfCheck = System.getenv(SELFCHECK_SKIP_ENV);
|
||||
if ("true".equalsIgnoreCase(skipSelfCheck)) {
|
||||
log.warn("检测到环境变量 {}=true,跳过自检流程", SELFCHECK_SKIP_ENV);
|
||||
log.info("══════════════════════════════════════════════════════════════");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
List<SelfCheckResult> results = selfCheckService.executeAllChecks();
|
||||
|
||||
boolean allPassed = results.stream().allMatch(SelfCheckResult::isPassed);
|
||||
|
||||
log.info("══════════════════════════════════════════════════════════════");
|
||||
if (allPassed) {
|
||||
log.info(" 自检流程全部通过");
|
||||
log.info("══════════════════════════════════════════════════════════════");
|
||||
} else {
|
||||
log.error(" 自检流程存在失败项");
|
||||
log.error("══════════════════════════════════════════════════════════════");
|
||||
for (SelfCheckResult result : results) {
|
||||
if (!result.isPassed()) {
|
||||
log.error(" 失败项: {} - {}", result.getCheckName(), result.getMessage());
|
||||
}
|
||||
}
|
||||
log.warn("系统将在 10 秒后关闭...");
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
log.error("系统自检未通过,进程退出");
|
||||
System.exit(-9);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("自检流程执行异常: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.cisd.tms.modules.system.service;
|
||||
|
||||
import com.cisd.tms.modules.system.dto.SelfCheckResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface SelfCheckService {
|
||||
|
||||
SelfCheckResult verifyDeviceFingerprint();
|
||||
|
||||
SelfCheckResult verifyTmsIntegrity();
|
||||
|
||||
SelfCheckResult verifyStandardTransceiverIntegrity();
|
||||
|
||||
List<SelfCheckResult> executeAllChecks();
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package com.cisd.tms.modules.system.service.impl;
|
||||
|
||||
import com.cisd.tms.modules.system.dto.SelfCheckResult;
|
||||
import com.cisd.tms.modules.system.service.SelfCheckService;
|
||||
import com.sunyard.cisd.device.tool.DeviceFingerprint;
|
||||
import com.sunyard.cisd.device.tool.DeviceFingerprintService;
|
||||
import com.sunyard.cisd.device.tool.SM2Util;
|
||||
import com.sunyard.cisd.device.tool.SM3Util;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.sunyard.cisd.device.tool.Main.*;
|
||||
|
||||
@Service
|
||||
public class SelfCheckServiceImpl implements SelfCheckService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SelfCheckServiceImpl.class);
|
||||
|
||||
private static final String CHECK_DEVICE_FINGERPRINT = "设备指纹验证";
|
||||
private static final String CHECK_TMS_INTEGRITY = "终端管理系统主程序完整性验证";
|
||||
private static final String CHECK_STANDARD_TRANSCEIVER = "标准收发器主程序完整性验证";
|
||||
|
||||
@Override
|
||||
public SelfCheckResult verifyDeviceFingerprint() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("========== 开始执行: {} ==========", CHECK_DEVICE_FINGERPRINT);
|
||||
|
||||
try {
|
||||
boolean result = doVerifyDeviceFingerprint();
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
if (result) {
|
||||
String message = "设备指纹验证通过";
|
||||
log.info("[PASS] {} - {}", CHECK_DEVICE_FINGERPRINT, message);
|
||||
return SelfCheckResult.success(CHECK_DEVICE_FINGERPRINT, message, duration);
|
||||
} else {
|
||||
String message = "设备指纹验证失败";
|
||||
log.error("[FAIL] {} - {}", CHECK_DEVICE_FINGERPRINT, message);
|
||||
return SelfCheckResult.failure(CHECK_DEVICE_FINGERPRINT, message, duration);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
String message = "设备指纹验证异常: " + e.getMessage();
|
||||
log.error("[ERROR] {} - {}", CHECK_DEVICE_FINGERPRINT, message, e);
|
||||
return SelfCheckResult.failure(CHECK_DEVICE_FINGERPRINT, message, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelfCheckResult verifyTmsIntegrity() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("========== 开始执行: {} ==========", CHECK_TMS_INTEGRITY);
|
||||
|
||||
try {
|
||||
boolean result = doVerifyTmsIntegrity();
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
if (result) {
|
||||
String message = "终端管理系统主程序完整性验证通过";
|
||||
log.info("[PASS] {} - {}", CHECK_TMS_INTEGRITY, message);
|
||||
return SelfCheckResult.success(CHECK_TMS_INTEGRITY, message, duration);
|
||||
} else {
|
||||
String message = "终端管理系统主程序完整性验证失败";
|
||||
log.error("[FAIL] {} - {}", CHECK_TMS_INTEGRITY, message);
|
||||
return SelfCheckResult.failure(CHECK_TMS_INTEGRITY, message, duration);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
String message = "终端管理系统主程序完整性验证异常: " + e.getMessage();
|
||||
log.error("[ERROR] {} - {}", CHECK_TMS_INTEGRITY, message, e);
|
||||
return SelfCheckResult.failure(CHECK_TMS_INTEGRITY, message, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelfCheckResult verifyStandardTransceiverIntegrity() {
|
||||
long startTime = System.currentTimeMillis();
|
||||
log.info("========== 开始执行: {} ==========", CHECK_STANDARD_TRANSCEIVER);
|
||||
|
||||
try {
|
||||
boolean result = doVerifyStandardTransceiverIntegrity();
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
|
||||
if (result) {
|
||||
String message = "标准收发器主程序完整性验证通过";
|
||||
log.info("[PASS] {} - {}", CHECK_STANDARD_TRANSCEIVER, message);
|
||||
return SelfCheckResult.success(CHECK_STANDARD_TRANSCEIVER, message, duration);
|
||||
} else {
|
||||
String message = "标准收发器主程序完整性验证失败";
|
||||
log.error("[FAIL] {} - {}", CHECK_STANDARD_TRANSCEIVER, message);
|
||||
return SelfCheckResult.failure(CHECK_STANDARD_TRANSCEIVER, message, duration);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
long duration = System.currentTimeMillis() - startTime;
|
||||
String message = "标准收发器主程序完整性验证异常: " + e.getMessage();
|
||||
log.error("[ERROR] {} - {}", CHECK_STANDARD_TRANSCEIVER, message, e);
|
||||
return SelfCheckResult.failure(CHECK_STANDARD_TRANSCEIVER, message, duration);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SelfCheckResult> executeAllChecks() {
|
||||
log.info("======================================");
|
||||
log.info(" 系统启动自检流程开始");
|
||||
log.info("======================================");
|
||||
|
||||
long totalStartTime = System.currentTimeMillis();
|
||||
List<SelfCheckResult> results = new ArrayList<>();
|
||||
|
||||
results.add(verifyDeviceFingerprint());
|
||||
results.add(verifyTmsIntegrity());
|
||||
results.add(verifyStandardTransceiverIntegrity());
|
||||
|
||||
long totalDuration = System.currentTimeMillis() - totalStartTime;
|
||||
long passedCount = results.stream().filter(SelfCheckResult::isPassed).count();
|
||||
long failedCount = results.size() - passedCount;
|
||||
|
||||
log.info("======================================");
|
||||
log.info(" 系统启动自检流程结束");
|
||||
log.info("======================================");
|
||||
log.info("自检总耗时: {} ms", totalDuration);
|
||||
log.info("检测项总数: {}, 通过: {}, 失败: {}", results.size(), passedCount, failedCount);
|
||||
|
||||
if (failedCount > 0) {
|
||||
log.warn("存在未通过的自检项,请检查相关日志");
|
||||
} else {
|
||||
log.info("所有自检项均已通过");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private boolean doVerifyDeviceFingerprint() {
|
||||
try {
|
||||
DeviceFingerprintService service = new DeviceFingerprintService();
|
||||
DeviceFingerprint fingerprint = service.getDeviceFingerprint();
|
||||
|
||||
log.info("=== 当前设备信息 ===");
|
||||
log.info("主板序列号: " + fingerprint.getMotherboardSerialNumber());
|
||||
log.info("设备序列号: " + fingerprint.getDeviceSerialNumber());
|
||||
log.info("CPU型号: " + fingerprint.getCpuModel());
|
||||
log.info("CPU序列号: " + fingerprint.getCpuSerialNumber());
|
||||
log.info("内存大小: " + fingerprint.getMemorySize());
|
||||
log.info("首块网卡MAC地址: " + fingerprint.getFirstNetworkMacAddress());
|
||||
log.info("硬盘序列号: " + fingerprint.getHardDiskSerialNumber());
|
||||
|
||||
String plainText = buildPlainText(fingerprint);
|
||||
log.info("\n当前拼接原文: " + plainText);
|
||||
|
||||
String savedPlainText = SM3Util.readFile(FINGERPRINT_DATA_FILE).trim();
|
||||
log.info("保存的拼接原文: " + savedPlainText);
|
||||
|
||||
byte[] signature = SM3Util.readFileBytes(SIGN_FILE);
|
||||
PublicKey publicKey = SM2Util.loadPublicKey(FINGERPRINT_PUBLIC_KEY_PATH);
|
||||
|
||||
boolean dataMatch = plainText.equals(savedPlainText);
|
||||
boolean signatureVerified = SM2Util.verify(savedPlainText.getBytes(StandardCharsets.UTF_8), signature, publicKey);
|
||||
|
||||
log.info("\n=== 验证结果 ===");
|
||||
log.info("设备信息匹配: " + dataMatch);
|
||||
log.info("签名验证: " + signatureVerified);
|
||||
log.info("总体验证: " + (dataMatch && signatureVerified));
|
||||
return (dataMatch && signatureVerified);
|
||||
} catch (Exception e) {
|
||||
log.error("[ERROR] {} - {}", CHECK_DEVICE_FINGERPRINT, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doVerifyTmsIntegrity() {
|
||||
try {
|
||||
log.info("=== 验证系统文件MAC校验值 ===");
|
||||
|
||||
PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH);
|
||||
byte[] savedSignature = SM3Util.readFileBytes(MAC_SIGN_FILE);
|
||||
String savedMacList = SM3Util.readFile(MAC_LIST_FILE);
|
||||
|
||||
boolean macListSignatureValid = SM2Util.verify(savedMacList.getBytes(StandardCharsets.UTF_8), savedSignature, publicKey);
|
||||
log.info("MAC列表签名验证: " + (macListSignatureValid ? "通过" : "失败"));
|
||||
|
||||
if (!macListSignatureValid) {
|
||||
log.info("错误: mac.list 签名验证失败,可能被篡改!");
|
||||
log.info("\n=== 验证结果 ===");
|
||||
log.info("完整性验证: false");
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, String> savedSm3Map = SM3Util.readSM3File(MAC_LIST_FILE);
|
||||
|
||||
Map<String, String> currentSm3Map = new java.util.LinkedHashMap<>();
|
||||
for (String target : MAC_TARGETS) {
|
||||
File file = new File(target);
|
||||
if (!file.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.isFile()) {
|
||||
String sm3Value = SM3Util.sm3FileHex(target);
|
||||
currentSm3Map.put(target, sm3Value);
|
||||
} else if (file.isDirectory()) {
|
||||
Map<String, String> dirSm3Map = SM3Util.calculateDirectorySM3(target);
|
||||
for (Map.Entry<String, String> entry : dirSm3Map.entrySet()) {
|
||||
String absolutePath = target + "/" + entry.getKey();
|
||||
currentSm3Map.put(absolutePath, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean allMatch = true;
|
||||
|
||||
for (Map.Entry<String, String> entry : savedSm3Map.entrySet()) {
|
||||
String filePath = entry.getKey();
|
||||
String savedHash = entry.getValue();
|
||||
String currentHash = currentSm3Map.get(filePath);
|
||||
|
||||
if (currentHash == null) {
|
||||
log.info("文件缺失: " + filePath);
|
||||
allMatch = false;
|
||||
} else if (!savedHash.equals(currentHash)) {
|
||||
log.info("文件被修改: " + filePath + " (期望: " + savedHash + ", 实际: " + currentHash + ")");
|
||||
allMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (String filePath : currentSm3Map.keySet()) {
|
||||
if (!savedSm3Map.containsKey(filePath)) {
|
||||
log.info("新增文件: " + filePath);
|
||||
allMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
log.info("");
|
||||
log.info("=== 验证结果 ===");
|
||||
log.info("完整性验证: " + allMatch);
|
||||
return allMatch;
|
||||
} catch (Exception e) {
|
||||
log.error("[ERROR] {} - {}", CHECK_TMS_INTEGRITY, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doVerifyStandardTransceiverIntegrity() {
|
||||
try {
|
||||
log.info("=== 验证CMEP文件MAC校验值 ===");
|
||||
|
||||
PublicKey publicKey = SM2Util.loadPublicKey(MAC_PUBLIC_KEY_PATH);
|
||||
byte[] savedSignature = SM3Util.readFileBytes(CMEP_MAC_SIGN_FILE);
|
||||
String savedMacList = SM3Util.readFile(CMEP_MAC_LIST_FILE);
|
||||
|
||||
boolean macListSignatureValid = SM2Util.verify(savedMacList.getBytes(StandardCharsets.UTF_8), savedSignature, publicKey);
|
||||
log.info("CMEP MAC列表签名验证: " + (macListSignatureValid ? "通过" : "失败"));
|
||||
|
||||
if (!macListSignatureValid) {
|
||||
log.info("错误: cmep.mac.list 签名验证失败,可能被篡改!");
|
||||
log.info("\n=== 验证结果 ===");
|
||||
log.info("完整性验证: false");
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<String, String> savedSm3Map = SM3Util.readSM3File(CMEP_MAC_LIST_FILE);
|
||||
|
||||
Map<String, String> currentSm3Map = new java.util.LinkedHashMap<>();
|
||||
for (String target : CMEP_TARGETS) {
|
||||
File file = new File(target);
|
||||
if (!file.exists()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.isFile()) {
|
||||
String sm3Value = SM3Util.sm3FileHex(target);
|
||||
currentSm3Map.put(target, sm3Value);
|
||||
}
|
||||
}
|
||||
|
||||
boolean allMatch = true;
|
||||
|
||||
for (Map.Entry<String, String> entry : savedSm3Map.entrySet()) {
|
||||
String filePath = entry.getKey();
|
||||
String savedHash = entry.getValue();
|
||||
String currentHash = currentSm3Map.get(filePath);
|
||||
|
||||
if (currentHash == null) {
|
||||
log.info("文件缺失: " + filePath);
|
||||
allMatch = false;
|
||||
} else if (!savedHash.equals(currentHash)) {
|
||||
log.info("文件被修改: " + filePath + " (期望: " + savedHash + ", 实际: " + currentHash + ")");
|
||||
allMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (String filePath : currentSm3Map.keySet()) {
|
||||
if (!savedSm3Map.containsKey(filePath)) {
|
||||
log.info("新增文件: " + filePath);
|
||||
allMatch = false;
|
||||
}
|
||||
}
|
||||
|
||||
log.info("");
|
||||
log.info("=== 验证结果 ===");
|
||||
log.info("完整性验证: " + allMatch);
|
||||
return allMatch;
|
||||
} catch (Exception e) {
|
||||
log.error("[ERROR] {} - {}", CHECK_STANDARD_TRANSCEIVER, e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user