diff --git a/pom.xml b/pom.xml
index 4b97e1a..d9f6468 100644
--- a/pom.xml
+++ b/pom.xml
@@ -118,6 +118,13 @@
1.10.0
+
+ cn.hutool
+ hutool-all
+ 5.8.26
+
+
+
com.sunyard.cisd
device-fingerprint-tool
diff --git a/src/main/java/com/cisd/tms/TmsApplication.java b/src/main/java/com/cisd/tms/TmsApplication.java
index fb73702..9764663 100644
--- a/src/main/java/com/cisd/tms/TmsApplication.java
+++ b/src/main/java/com/cisd/tms/TmsApplication.java
@@ -1,16 +1,32 @@
package com.cisd.tms;
import com.cisd.tms.common.crypto.config.ConfigCryptoCli;
-import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import com.cisd.tms.modules.log.entity.OperationAuditLogEntity;
+import com.cisd.tms.modules.log.enums.ActionType;
+import com.cisd.tms.modules.log.enums.AuthLevel;
+import com.cisd.tms.modules.log.enums.AuditStatus;
+import com.cisd.tms.modules.log.enums.ModuleCode;
+import com.cisd.tms.modules.log.enums.OperationResult;
+import com.cisd.tms.modules.log.enums.OperatorRoleCode;
+import com.cisd.tms.modules.log.repository.OperationAuditLogRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableScheduling;
+import java.time.LocalDateTime;
+import java.util.UUID;
+
@SpringBootApplication
@ConfigurationPropertiesScan
@EnableScheduling
public class TmsApplication {
+ private static final Logger log = LoggerFactory.getLogger(TmsApplication.class);
+
public static void main(String[] args) {
if (ConfigCryptoCli.shouldHandle(args)) {
int exitCode = ConfigCryptoCli.run(args, System.in, System.out, System.err);
diff --git a/src/main/java/com/cisd/tms/infrastructure/persistence/mybatis/CommonMetaObjectHandler.java b/src/main/java/com/cisd/tms/infrastructure/persistence/mybatis/CommonMetaObjectHandler.java
index 69d9f79..bb9f3e9 100644
--- a/src/main/java/com/cisd/tms/infrastructure/persistence/mybatis/CommonMetaObjectHandler.java
+++ b/src/main/java/com/cisd/tms/infrastructure/persistence/mybatis/CommonMetaObjectHandler.java
@@ -2,20 +2,44 @@ package com.cisd.tms.infrastructure.persistence.mybatis;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import java.time.LocalDateTime;
+
+import com.cisd.tms.modules.datamac.helper.Sm3MacCalculator;
import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
+@Primary
public class CommonMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
- this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
- this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ if ( metaObject.hasSetter("createTime") ) {
+ this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
+ }
+
+ if ( metaObject.hasSetter("updateTime") ) {
+ this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ }
+
+ // 插入时,直接基于当前实体对象计算 MAC
+ if (metaObject.hasSetter("mac")) {
+ Object entity = metaObject.getOriginalObject();
+ String mac = Sm3MacCalculator.calculateForEntity(entity);
+ this.strictInsertFill(metaObject, "mac", String.class, mac);
+ }
}
@Override
public void updateFill(MetaObject metaObject) {
- this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ if ( metaObject.hasSetter("updateTime") ) {
+ this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
+ }
+ // 插入时,直接基于当前实体对象计算 MAC
+ if (metaObject.hasSetter("mac")) {
+ Object entity = metaObject.getOriginalObject();
+ String mac = Sm3MacCalculator.calculateForEntity(entity);
+ this.strictInsertFill(metaObject, "mac", String.class, mac);
+ }
}
}
diff --git a/src/main/java/com/cisd/tms/modules/cert/entity/KeyEntity.java b/src/main/java/com/cisd/tms/modules/cert/entity/KeyEntity.java
index 4afe9d2..e5bf231 100644
--- a/src/main/java/com/cisd/tms/modules/cert/entity/KeyEntity.java
+++ b/src/main/java/com/cisd/tms/modules/cert/entity/KeyEntity.java
@@ -1,5 +1,7 @@
package com.cisd.tms.modules.cert.entity;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.cisd.tms.infrastructure.persistence.entity.BaseEntity;
import lombok.Data;
@@ -26,4 +28,8 @@ public class KeyEntity extends BaseEntity {
private String allowIps;
private String signData;
private String createBy;
+
+ // 标记此字段在插入和更新时都需要自动填充
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private String mac;
}
diff --git a/src/main/java/com/cisd/tms/modules/datamac/entity/DataMacEntity.java b/src/main/java/com/cisd/tms/modules/datamac/entity/DataMacEntity.java
new file mode 100644
index 0000000..9611ab5
--- /dev/null
+++ b/src/main/java/com/cisd/tms/modules/datamac/entity/DataMacEntity.java
@@ -0,0 +1,12 @@
+package com.cisd.tms.modules.datamac.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+
+@Data
+public class DataMacEntity {
+ // 标记此字段在插入和更新时都需要自动填充
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private String mac;
+}
\ No newline at end of file
diff --git a/src/main/java/com/cisd/tms/modules/datamac/hanlder/DataMacMetaObjectHandler.java b/src/main/java/com/cisd/tms/modules/datamac/hanlder/DataMacMetaObjectHandler.java
new file mode 100644
index 0000000..a0038d9
--- /dev/null
+++ b/src/main/java/com/cisd/tms/modules/datamac/hanlder/DataMacMetaObjectHandler.java
@@ -0,0 +1,45 @@
+package com.cisd.tms.modules.datamac.hanlder;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.cisd.tms.modules.datamac.helper.Sm3MacCalculator;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DataMacMetaObjectHandler implements MetaObjectHandler {
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ // 插入时,直接基于当前实体对象计算 MAC
+ Object entity = metaObject.getOriginalObject();
+ String mac = Sm3MacCalculator.calculateForEntity(entity);
+ this.strictInsertFill(metaObject, "mac", String.class, mac);
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+
+ }
+
+// @Override
+// public void updateFill(MetaObject metaObject) {
+// // 更新时,需要先获取 ID,再查询完整数据
+// Object entity = metaObject.getOriginalObject();
+// Object id = this.getFieldValByName("id", metaObject);
+// if (id == null) {
+// // 若没有 ID,则降级使用当前对象中的字段(不完整)
+// String mac = Sm3MacCalculator.calculateForEntity(entity);
+// this.strictUpdateFill(metaObject, "mac", String.class, mac);
+// return;
+// }
+//
+// // 通过 AOP 或手动调用 Service,在此处获取完整实体
+// Object fullEntity = fetchFullEntityFromService(entity.getClass(), id);
+// if (fullEntity != null) {
+// // 将当前更新内容合并到完整实体
+// mergeChanges(fullEntity, entity);
+// String mac = Sm3MacCalculator.calculateForEntity(fullEntity);
+// this.strictUpdateFill(metaObject, "mac", String.class, mac);
+// }
+// }
+}
diff --git a/src/main/java/com/cisd/tms/modules/datamac/helper/Sm3MacCalculator.java b/src/main/java/com/cisd/tms/modules/datamac/helper/Sm3MacCalculator.java
new file mode 100644
index 0000000..9787377
--- /dev/null
+++ b/src/main/java/com/cisd/tms/modules/datamac/helper/Sm3MacCalculator.java
@@ -0,0 +1,39 @@
+package com.cisd.tms.modules.datamac.helper;
+import cn.hutool.crypto.SmUtil;
+import cn.hutool.core.util.ArrayUtil;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Comparator;
+
+
+
+public class Sm3MacCalculator {
+ public static String calculateForEntity(Object entity) {
+ // 1. 获取所有字段并按字段名排序,保证不同实体计算的顺序一致
+ Field[] fields = entity.getClass().getDeclaredFields();
+ Arrays.sort(fields, Comparator.comparing(Field::getName));
+
+ // 2. 构建待摘要的数据
+ StringBuilder dataBuilder = new StringBuilder();
+ for (Field field : fields) {
+ // 排除 mac 字段自身
+ if ("mac".equals(field.getName())) {
+ continue;
+ }
+ try {
+ field.setAccessible(true);
+ Object value = field.get(entity);
+ dataBuilder.append(field.getName()).append("=")
+ .append(value != null ? value.toString() : "null")
+ .append(";");
+ } catch (IllegalAccessException e) {
+ // 适当的日志记录
+ throw new RuntimeException("Failed to read field: " + field.getName(), e);
+ }
+ }
+
+ // 3. 生成最终的 SM3 哈希值
+ return SmUtil.sm3(dataBuilder.toString());
+ }
+}
diff --git a/src/main/java/com/cisd/tms/modules/datamac/interceptor/MacValidationInterceptor.java b/src/main/java/com/cisd/tms/modules/datamac/interceptor/MacValidationInterceptor.java
new file mode 100644
index 0000000..2fb25d9
--- /dev/null
+++ b/src/main/java/com/cisd/tms/modules/datamac/interceptor/MacValidationInterceptor.java
@@ -0,0 +1,42 @@
+package com.cisd.tms.modules.datamac.interceptor;
+
+
+import com.cisd.tms.modules.datamac.entity.DataMacEntity;
+import com.cisd.tms.modules.datamac.helper.Sm3MacCalculator;
+import org.apache.ibatis.executor.resultset.ResultSetHandler;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Signature;
+import org.springframework.stereotype.Component;
+
+import java.sql.Statement;
+import java.util.List;
+
+
+@Intercepts(@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}))
+@Component
+public class MacValidationInterceptor implements Interceptor {
+
+ @Override
+ public Object intercept(Invocation invocation) throws Throwable {
+ Object result = invocation.proceed();
+ if (result instanceof List) {
+ ((List>) result).forEach(this::validateEntity);
+ } else {
+ validateEntity(result);
+ }
+ return result;
+ }
+
+ private void validateEntity(Object obj) {
+ if (obj instanceof DataMacEntity) {
+ DataMacEntity entity = (DataMacEntity) obj;
+ String storedMac = entity.getMac();
+ String calculatedMac = Sm3MacCalculator.calculateForEntity(entity);
+ if (!storedMac.equals(calculatedMac)) {
+ throw new RuntimeException("Data integrity validation failed. The data has been tampered with.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cisd/tms/modules/log/entity/OperationAuditLogEntity.java b/src/main/java/com/cisd/tms/modules/log/entity/OperationAuditLogEntity.java
index fe0acb8..633cce5 100644
--- a/src/main/java/com/cisd/tms/modules/log/entity/OperationAuditLogEntity.java
+++ b/src/main/java/com/cisd/tms/modules/log/entity/OperationAuditLogEntity.java
@@ -1,8 +1,10 @@
package com.cisd.tms.modules.log.entity;
import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
+import com.cisd.tms.modules.datamac.entity.DataMacEntity;
import com.cisd.tms.modules.log.enums.*;
import lombok.Data;
import lombok.experimental.Accessors;
@@ -12,7 +14,7 @@ import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@TableName("tms_operation_audit_log")
-public class OperationAuditLogEntity {
+public class OperationAuditLogEntity extends DataMacEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private String logId;
diff --git a/src/main/java/com/cisd/tms/modules/mk/config/MasterKeyActivateProperties.java b/src/main/java/com/cisd/tms/modules/mk/config/MasterKeyActivateProperties.java
index e14a10a..60a7e89 100644
--- a/src/main/java/com/cisd/tms/modules/mk/config/MasterKeyActivateProperties.java
+++ b/src/main/java/com/cisd/tms/modules/mk/config/MasterKeyActivateProperties.java
@@ -7,9 +7,9 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class MasterKeyActivateProperties {
//todo 后续添加到配置文件
- private String activateUrl = "http://127.0.0.1:8080/device/activate";
+ private String platformUrl = "http://127.0.0.1:8080/device/active";
- public String getActivateUrl() { return activateUrl; }
+ public String getPlatformUrl() { return platformUrl; }
- public void setActivateUrl(String activateUrl) { this.activateUrl = activateUrl; }
+ public void setPlatformUrl(String platformUrl) { this.platformUrl = platformUrl; }
}
diff --git a/src/main/java/com/cisd/tms/modules/mk/service/impl/MasterKeyActivateServiceImpl.java b/src/main/java/com/cisd/tms/modules/mk/service/impl/MasterKeyActivateServiceImpl.java
index 34d92b2..ab3977d 100644
--- a/src/main/java/com/cisd/tms/modules/mk/service/impl/MasterKeyActivateServiceImpl.java
+++ b/src/main/java/com/cisd/tms/modules/mk/service/impl/MasterKeyActivateServiceImpl.java
@@ -21,26 +21,24 @@ import com.sunyard.cisd.device.tool.DeviceFingerprint;
import com.sunyard.cisd.device.tool.DeviceFingerprintService;
import lombok.Data;
import lombok.RequiredArgsConstructor;
-import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.digests.SM3Digest;
-import org.bouncycastle.crypto.params.ECDomainParameters;
-import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
-import org.bouncycastle.crypto.params.ECPublicKeyParameters;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.crypto.signers.SM2Signer;
-import org.bouncycastle.jce.ECNamedCurveTable;
-import org.bouncycastle.jce.interfaces.ECPrivateKey;
-import org.bouncycastle.jce.interfaces.ECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.jce.spec.ECParameterSpec;
import org.springframework.stereotype.Service;
-import java.math.BigInteger;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
+import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
+import java.security.Signature;
+import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Optional;
@@ -48,6 +46,10 @@ import java.util.Optional;
@RequiredArgsConstructor
public class MasterKeyActivateServiceImpl implements MasterKeyActivateService {
+ private static final String DEVICE_DIR = "/home/tms/device";
+ public static final String SIGN_FILE = DEVICE_DIR + "/dev.sign";
+ private static final String ACTIVATION_PUBLIC_KEY_FILE = DEVICE_DIR + "/dev.active.pub";
+
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
@@ -59,6 +61,9 @@ public class MasterKeyActivateServiceImpl implements MasterKeyActivateService {
private final DeviceService deviceService;
private final MasterKeyActivateRepository masterKeyActivateRepository;
+ private static final String PROVIDER = "BC";
+ private static final String SIGNATURE_ALGO = "SM3withSM2";
+
static {
if (Security.getProvider("BC") == null) {
Security.addProvider(new BouncyCastleProvider());
@@ -127,90 +132,67 @@ public class MasterKeyActivateServiceImpl implements MasterKeyActivateService {
DeviceFingerprint deviceFingerprint = deviceFingerprintService.getDeviceFingerprint();
String deviceSerial = deviceFingerprint.getDeviceSerialNumber();
- String motherboardSerial = deviceFingerprint.getMotherboardSerialNumber();
- String cpuModel = deviceFingerprint.getCpuModel();
- String cpuSerial = deviceFingerprint.getCpuSerialNumber();
- String memorySize = deviceFingerprint.getMemorySize();
- String macAddress = deviceFingerprint.getFirstNetworkMacAddress();
- String diskSerial = deviceFingerprint.getHardDiskSerialNumber();
String timestamp = String.valueOf(System.currentTimeMillis());
String nonce = randomHex(16);
- //todo 后面读取文件
- MasterKeyActivateServiceImpl.Sm2KeyPair sm2KeyPair = generateSm2KeyPair();
-
- String deviceFingerprintSource =
- deviceSerial +
- motherboardSerial +
- cpuModel +
- cpuSerial +
- memorySize +
- macAddress +
- diskSerial;
-
- String deviceFingerprintSm2 = sm2SignBase64(
- deviceFingerprintSource.getBytes(StandardCharsets.UTF_8),
- sm2KeyPair.privateKey
- );
-
+ byte[] deviceFingerprintBytes = readFileBytes(SIGN_FILE);
+ String deviceFingerprintJson = Base64.getEncoder().encodeToString(deviceFingerprintBytes);
String userId = trim(masterKeyActivateRequest.getUserId());
String credential = trim(masterKeyActivateRequest.getCredential());
String activationCode = trim(masterKeyActivateRequest.getActivationCode());
-
-
String hashSource = userId + credential + activationCode + deviceSerial +
deviceFingerprint + timestamp + nonce;
String hash = sm3Hex(hashSource);
Params params = new Params();
- params.userId = userId;
- params.credential = credential;
- params.activationCode = activationCode;
- params.deviceSerial = deviceSerial;
- params.deviceFingerprint = deviceFingerprintSm2;
- params.timestamp = timestamp;
- params.nonce = nonce;
+ params.setUserId(userId);
+ params.setCredential(credential);
+ params.setDeviceActivationCode(activationCode);
+ params.setDeviceSerialNumber(deviceSerial);
+ params.setDeviceFingerprintJson(deviceFingerprintJson);
+ params.setTimestamp(timestamp);
+ params.setNonce(nonce);
ActivateRequest request = new ActivateRequest();
- request.params = params;
- request.hash = hash;
+ request.setParams(params);
+ request.setHash(hash);
- //todo 先写死后续改实际的激活服务
- masterKeyActivateEntity.setActivationStatus(true);
- masterKeyActivateRepository.update(masterKeyActivateEntity);
-// String json = OBJECT_MAPPER.writeValueAsString(request);
-//// System.out.print(json);
-// HttpRequest httpRequest = HttpRequest.newBuilder()
-// .uri(URI.create(masterKeyActivateProperties.getActivateUrl()))
-// .header("Content-Type", "application/json;charset=UTF-8")
-// .header("Accept", "application/json")
-// .POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8))
-// .build();
-//
-// HttpClient client = HttpClient.newHttpClient();
-//
-// HttpResponse response = client.send(
-// httpRequest,
-// HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
-// );
-//
-// ServerResponse result =
-// OBJECT_MAPPER.readValue(response.body(), ServerResponse.class);
-//
-// if ("ok".equals(result.status)) {
-// MasterKeyActivateData dataResponse = result.getData();
-// String inputHash = dataResponse.getInputHash();
-// String deviceActivationPublicKey = dataResponse.getDeviceActivationPublicKey();
-// String activationResult = dataResponse.getActivationResult();
-//
-// } else {
-// throw new Exception(result.getMsg());
-// }
+ String json = OBJECT_MAPPER.writeValueAsString(request);
+// System.out.print(json);
+ HttpRequest httpRequest = HttpRequest.newBuilder()
+ .uri(URI.create(masterKeyActivateProperties.getPlatformUrl()))
+ .header("Content-Type", "application/json;charset=UTF-8")
+ .header("Accept", "application/json")
+ .POST(HttpRequest.BodyPublishers.ofString(json, StandardCharsets.UTF_8))
+ .build();
+
+ HttpClient client = HttpClient.newHttpClient();
+
+ HttpResponse response = client.send(
+ httpRequest,
+ HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)
+ );
+
+ ServerResponse result =
+ OBJECT_MAPPER.readValue(response.body(), ServerResponse.class);
+
+ if ("ok".equals(result.status)) {
+ MasterKeyActivateData dataResponse = result.getData();
+ String Hash = dataResponse.getHash();
+ String deviceActivationPublicKey = dataResponse.getDeviceActivationPublicKey();
+ String deviceActivationResult = dataResponse.getDeviceActivationResult();
+ verifySm2Signature(Hash, deviceActivationResult, deviceActivationPublicKey);
+ masterKeyActivateEntity.setActivationStatus(true);
+ masterKeyActivateRepository.update(masterKeyActivateEntity);
+ writeFile(ACTIVATION_PUBLIC_KEY_FILE, deviceActivationPublicKey.getBytes(StandardCharsets.UTF_8));
+ } else {
+ throw new Exception(result.getMsg());
+ }
} catch (Exception e) {
masterKeyActivateEntity.setActivationStatus(false);
@@ -220,63 +202,6 @@ public class MasterKeyActivateServiceImpl implements MasterKeyActivateService {
}
-
-
- private String sm2SignBase64(
- byte[] data,
- ECPrivateKeyParameters privateKey
- ) throws Exception {
-
- SM2Signer signer = new SM2Signer(new SM3Digest());
-
- CipherParameters parameters = new ParametersWithRandom(privateKey, SECURE_RANDOM);
-
- signer.init(true, parameters);
- signer.update(data, 0, data.length);
-
- byte[] signature = signer.generateSignature();
-
- return Base64.getEncoder().encodeToString(signature);
- }
-
-
- private Sm2KeyPair generateSm2KeyPair(){
- try{
- KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");
- ECParameterSpec sm2Spec = ECNamedCurveTable.getParameterSpec("sm2p256v1");
- keyPairGenerator.initialize(sm2Spec, SECURE_RANDOM);
- KeyPair keyPair = keyPairGenerator.generateKeyPair();
-
- ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
- ECPublicKey publicKey = (ECPublicKey) keyPair.getPublic();
- BigInteger d = privateKey.getD();
-
- ECDomainParameters domainParameters = new ECDomainParameters(
- sm2Spec.getCurve(),
- sm2Spec.getG(),
- sm2Spec.getN(),
- sm2Spec.getH()
- );
-
- ECPrivateKeyParameters privateKeyParameters =
- new ECPrivateKeyParameters(d, domainParameters);
-
- ECPublicKeyParameters publicParameters =
- new ECPublicKeyParameters(publicKey.getQ(), domainParameters);
-
-
- Sm2KeyPair sm2KeyPair = new Sm2KeyPair();
- sm2KeyPair.privateKey = privateKeyParameters;
- sm2KeyPair.publicKeyParameters = publicParameters;
- sm2KeyPair.publicKey = publicKey;
-
- return sm2KeyPair;
- } catch (Exception e){
- throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "sm2密钥对生成失败");
- }
-
- }
-
private String sm3Hex(String input) {
byte[] data = input.getBytes(StandardCharsets.UTF_8);
@@ -306,80 +231,49 @@ public class MasterKeyActivateServiceImpl implements MasterKeyActivateService {
}
- private boolean sm2VerifyActivationResult(
- String inputHash,
- String activationResult,
- ECPublicKeyParameters publicKeyParameters
- ) {
+ public boolean verifySm2Signature(String data, String signatureBase64, String publicKeyBase64) {
try {
+ byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64);
+ X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
+ java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("EC", PROVIDER);
+ PublicKey publicKey = keyFactory.generatePublic(keySpec);
- byte[] signatureBytes = parseP1Signature(activationResult);
-
- SM2Signer signer = new SM2Signer(new SM3Digest());
-
-
- signer.init(false, publicKeyParameters);
-
- byte[] data = inputHash.getBytes(StandardCharsets.UTF_8);
-
- signer.update(data, 0, data.length);
-
- return signer.verifySignature(signatureBytes);
-
+ Signature signature = Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
+ signature.initVerify(publicKey);
+ signature.update(data.getBytes(StandardCharsets.UTF_8));
+ byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64);
+ return signature.verify(signatureBytes);
} catch (Exception e) {
- e.printStackTrace();
- return false;
+ throw new RuntimeException("SM2验签失败", e);
}
}
- private byte[] parseP1Signature(String activationResult) {
- String value = activationResult.trim();
- if (value.startsWith("P1:")) {
- value = value.substring(3);
- }
- return Base64.getDecoder().decode(value);
- }
+
+
+ @Data
public static class ActivateRequest {
- public Params params;
- public String hash;
- }
-
- public static class Params {
-
- public String userId;
- public String credential;
- public String activationCode;
- public String deviceSerial;
- public String deviceFingerprint;
- public String timestamp;
- public String nonce;
+ private Params params;
+ private String hash;
}
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public static class ServerResponse {
- public String status;
- public String msg;
- public Integer code;
- public MasterKeyActivateData data;
+ private String status;
+ private String msg;
+ private Integer code;
+ private MasterKeyActivateData data;
}
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public static class MasterKeyActivateData {
-
- public String inputHash;
- public String deviceActivationPublicKey;
- public String activationResult;
- }
-
- private static class Sm2KeyPair {
- private ECPrivateKeyParameters privateKey;
- private ECPublicKeyParameters publicKeyParameters;
- private ECPublicKey publicKey;
+ private String hash;
+ private String deviceActivationPublicKey;
+ private String deviceActivationResult;
}
private String loadTmsVersion() {
@@ -387,7 +281,100 @@ public class MasterKeyActivateServiceImpl implements MasterKeyActivateService {
return version.map(DeviceSoftwareVersionEntity::getCurrentVersion).map(MasterKeyActivateServiceImpl::trim).orElse("");
}
+
+ public static byte[] readFileBytes(String filePath) throws Exception {
+ File file = new File(filePath);
+ try (FileInputStream fis = new FileInputStream(file)) {
+ byte[] content = new byte[(int) file.length()];
+ fis.read(content);
+ return content;
+ }
+ }
+
+ public static void writeFile(String filePath, byte[] content) throws Exception {
+ File file = new File(filePath);
+ File parentDir = file.getParentFile();
+ if (parentDir != null && !parentDir.exists()) {
+ parentDir.mkdirs();
+ }
+ try (OutputStream os = new FileOutputStream(file)) {
+ os.write(content);
+ }
+ }
+
+
private static String trim(String value) {
return value == null ? "" : value.trim();
}
+
+
+
+ public static class Params {
+ private String userId;
+ private String credential;
+ private String deviceActivationCode;
+ private String deviceSerialNumber;
+ private String deviceFingerprintJson;
+ private String timestamp;
+ private String nonce;
+
+ public Params() {
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ public String getCredential() {
+ return credential;
+ }
+
+ public void setCredential(String credential) {
+ this.credential = credential;
+ }
+
+ public String getDeviceActivationCode() {
+ return deviceActivationCode;
+ }
+
+ public void setDeviceActivationCode(String deviceActivationCode) {
+ this.deviceActivationCode = deviceActivationCode;
+ }
+
+ public String getDeviceSerialNumber() {
+ return deviceSerialNumber;
+ }
+
+ public void setDeviceSerialNumber(String deviceSerialNumber) {
+ this.deviceSerialNumber = deviceSerialNumber;
+ }
+
+ public String getDeviceFingerprintJson() {
+ return deviceFingerprintJson;
+ }
+
+ public void setDeviceFingerprintJson(String deviceFingerprintJson) {
+ this.deviceFingerprintJson = deviceFingerprintJson;
+ }
+
+ public String getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(String timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getNonce() {
+ return nonce;
+ }
+
+ public void setNonce(String nonce) {
+ this.nonce = nonce;
+ }
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 377f673..27a97e6 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -334,6 +334,9 @@ tms:
old-pin-base64: ${TMS_MK_INIT_IDENTIFY_OLD_PIN_BASE64:NjY2NjY2}
# SDFE_InitIdentify 新 PIN(Base64 编码);主密钥初始化接口会读取该值。
new-pin-base64: ${TMS_MK_INIT_IDENTIFY_NEW_PIN_BASE64:NjY2NjY2}
+ activate:
+ # 激活服务平台路径
+ platform-url: ${TMS_MK_ACTIVATE_PLATFORM_URL:http://127.0.0.1:8080/device/active}
security:
# 内部接口鉴权 token(前后端内部调用需一致)。
internal-token: ${TMS_INTERNAL_TOKEN:change-me-internal-token}
diff --git a/src/main/resources/db/migration/V1__tms_schema_full.sql b/src/main/resources/db/migration/V1__tms_schema_full.sql
index a9c6890..cb8c691 100644
--- a/src/main/resources/db/migration/V1__tms_schema_full.sql
+++ b/src/main/resources/db/migration/V1__tms_schema_full.sql
@@ -706,3 +706,7 @@ INSERT IGNORE INTO tms_master_key_activate (
ever_initialized,
activation_status
) VALUES (1, true, true);
+
+
+ALTER TABLE TMS.tms_operation_audit_log ADD mac varchar(100) NULL COMMENT '完整性校验值';
+ALTER TABLE TMS.tms_key_entity ADD mac varchar(100) NULL COMMENT '完整性校验值';