diff --git a/device-activation-platform/Readme.md b/device-activation-platform/Readme.md
new file mode 100644
index 0000000..7425d4f
--- /dev/null
+++ b/device-activation-platform/Readme.md
@@ -0,0 +1,68 @@
+激活登录请求参数 req {
+ params {
+ 使用者ID
+ 初始密码
+ }
+}
+
+激活登录响应参数 res {
+ status : 'ok' 成功; 'error' 错误
+ msg: "提示消息"
+ code: "状态码"
+}
+
+
+激活申请请求参数 req {
+ params {
+ 设备的序列号
+ 激活原因
+ }
+}
+
+激活申请响应参数 res {
+ 身份凭证
+ 设备激活码
+}
+
+
+设备激活请求参数 req {
+ params {
+ 使用者ID
+ 身份凭证
+ 设备激活码
+ 设备序列号
+ 设备指纹
+ 当前时间戳
+ 随机数
+ }
+ hash: params 对象所有属性字符串按顺序累计计算 SM3 摘要值
+}
+
+设备激活响应参数 res {
+ status : 'ok' 成功; 'error' 错误
+ msg: "提示消息"
+ code: "状态码"
+ data: {
+ hash: 入参的 hash
+ 设备激活公钥: 公钥格式
+ 设备激活结果: SM2 算法对 hash 值的签名, pkcs1 格式。
+ }
+}
+
+设备指纹信息 {
+ 主板序列号
+ 设备序列号
+ CPU型号
+ CPU序列号
+ 内存大小
+ 首块网卡MAC地址
+ 硬盘序列号
+ 起始日期
+ 终止日期
+}
+
+设备指纹是设备指纹信息的 SM2 签名(pkcs1 格式)。
+~~设备指纹信息存储于文件 /home/tms/device/dev.info 文件,json 格式。~~
+设备指纹存放于签名文件 /home/tms/device/dev.sign, pkcs1 格式。校验值 /home/tms/device/dev.sign.sm3。
+设备指纹密钥公钥存储于文件 /home/tms/device/dev.fingerprint.pub, 公钥格式。校验值 /home/tms/device/dev.fingerprint.pub.sm3。
+设备激活密钥公钥存储于文件 /home/tms/device/dev.active.pub, 公钥格式。校验值 /home/tms/device/dev.active.pub.sm3。
\ No newline at end of file
diff --git a/device-activation-platform/pom.xml b/device-activation-platform/pom.xml
new file mode 100644
index 0000000..6d1c599
--- /dev/null
+++ b/device-activation-platform/pom.xml
@@ -0,0 +1,63 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.5.11
+
+
+
+ com.sunyar
+ device-activation-platform
+ 1.0.0
+ device-activation-platform
+ Device Activation Platform
+
+
+ 17
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.bouncycastle
+ bcprov-jdk18on
+ 1.83
+
+
+
+ org.bouncycastle
+ bcpkix-jdk18on
+ 1.83
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationApplication.java b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationApplication.java
new file mode 100644
index 0000000..42a4599
--- /dev/null
+++ b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationApplication.java
@@ -0,0 +1,11 @@
+package com.sunyar.cisd;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DeviceActivationApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(DeviceActivationApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationController.java b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationController.java
new file mode 100644
index 0000000..69571c2
--- /dev/null
+++ b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationController.java
@@ -0,0 +1,40 @@
+package com.sunyar.cisd;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/device")
+public class DeviceActivationController {
+
+ @Autowired
+ private DeviceActivationService deviceActivationService;
+
+ @PostMapping("/active")
+ public DeviceActivationResponse activate(@RequestBody DeviceActivationRequest request) {
+ try {
+ if (request.getParams() == null) {
+ return DeviceActivationResponse.error("参数不能为空", "1001");
+ }
+
+ DeviceActivationRequest.Params params = request.getParams();
+ String concatenatedParams = params.toConcatenatedString();
+ String calculatedHash = deviceActivationService.calculateSm3Hash(concatenatedParams);
+
+ String deviceActivationPublicKey = deviceActivationService.generateDeviceActivationPublicKey();
+ String deviceActivationResult = deviceActivationService.signWithSm2Pkcs1(calculatedHash, deviceActivationPublicKey);
+
+ DeviceActivationResponse.Data data = new DeviceActivationResponse.Data();
+ data.setHash(calculatedHash);
+ data.setDeviceActivationPublicKey(deviceActivationPublicKey);
+ data.setDeviceActivationResult(deviceActivationResult);
+
+ return DeviceActivationResponse.ok("设备激活成功", data);
+ } catch (Exception e) {
+ return DeviceActivationResponse.error("设备激活失败: " + e.getMessage(), "1002");
+ }
+ }
+}
\ No newline at end of file
diff --git a/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationRequest.java b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationRequest.java
new file mode 100644
index 0000000..2e11ce0
--- /dev/null
+++ b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationRequest.java
@@ -0,0 +1,126 @@
+package com.sunyar.cisd;
+
+public class DeviceActivationRequest {
+ private Params params;
+ private String hash;
+
+ public DeviceActivationRequest() {
+ }
+
+ public Params getParams() {
+ return params;
+ }
+
+ public void setParams(Params params) {
+ this.params = params;
+ }
+
+ public String getHash() {
+ return hash;
+ }
+
+ public void setHash(String hash) {
+ this.hash = hash;
+ }
+
+ public static class Params {
+ private String userId;
+ private String credential;
+ private String deviceActivationCode;
+ private String deviceSerialNumber;
+ private String deviceFingerprintJson;
+ private String deviceSignature;
+ private String devicePublicKey;
+ 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 getDeviceSignature() {
+ return deviceSignature;
+ }
+
+ public void setDeviceSignature(String deviceSignature) {
+ this.deviceSignature = deviceSignature;
+ }
+
+ public String getDevicePublicKey() {
+ return devicePublicKey;
+ }
+
+ public void setDevicePublicKey(String devicePublicKey) {
+ this.devicePublicKey = devicePublicKey;
+ }
+
+ 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;
+ }
+
+ public String toConcatenatedString() {
+ StringBuilder sb = new StringBuilder();
+ if (userId != null) sb.append(userId);
+ if (credential != null) sb.append(credential);
+ if (deviceActivationCode != null) sb.append(deviceActivationCode);
+ if (deviceSerialNumber != null) sb.append(deviceSerialNumber);
+ if (deviceFingerprintJson != null) sb.append(deviceFingerprintJson);
+ if (deviceSignature != null) sb.append(deviceSignature);
+ if (devicePublicKey != null) sb.append(devicePublicKey);
+ if (timestamp != null) sb.append(timestamp);
+ if (nonce != null) sb.append(nonce);
+ return sb.toString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationResponse.java b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationResponse.java
new file mode 100644
index 0000000..bb2adf2
--- /dev/null
+++ b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationResponse.java
@@ -0,0 +1,94 @@
+package com.sunyar.cisd;
+
+public class DeviceActivationResponse {
+ private String status;
+ private String msg;
+ private String code;
+ private Data data;
+
+ public DeviceActivationResponse() {
+ }
+
+ public static DeviceActivationResponse ok(String msg, Data data) {
+ DeviceActivationResponse response = new DeviceActivationResponse();
+ response.status = "ok";
+ response.msg = msg;
+ response.code = "0";
+ response.data = data;
+ return response;
+ }
+
+ public static DeviceActivationResponse error(String msg, String code) {
+ DeviceActivationResponse response = new DeviceActivationResponse();
+ response.status = "error";
+ response.msg = msg;
+ response.code = code;
+ response.data = null;
+ return response;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public Data getData() {
+ return data;
+ }
+
+ public void setData(Data data) {
+ this.data = data;
+ }
+
+ public static class Data {
+ private String hash;
+ private String deviceActivationPublicKey;
+ private String deviceActivationResult;
+
+ public Data() {
+ }
+
+ public String getHash() {
+ return hash;
+ }
+
+ public void setHash(String hash) {
+ this.hash = hash;
+ }
+
+ public String getDeviceActivationPublicKey() {
+ return deviceActivationPublicKey;
+ }
+
+ public void setDeviceActivationPublicKey(String deviceActivationPublicKey) {
+ this.deviceActivationPublicKey = deviceActivationPublicKey;
+ }
+
+ public String getDeviceActivationResult() {
+ return deviceActivationResult;
+ }
+
+ public void setDeviceActivationResult(String deviceActivationResult) {
+ this.deviceActivationResult = deviceActivationResult;
+ }
+ }
+}
\ No newline at end of file
diff --git a/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationService.java b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationService.java
new file mode 100644
index 0000000..e74d993
--- /dev/null
+++ b/device-activation-platform/src/main/java/com/sunyar/cisd/DeviceActivationService.java
@@ -0,0 +1,61 @@
+package com.sunyar.cisd;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.springframework.stereotype.Service;
+
+import java.nio.charset.StandardCharsets;
+import java.security.Security;
+import java.security.Signature;
+import java.util.Base64;
+
+@Service
+public class DeviceActivationService {
+
+ private static final String PROVIDER = "BC";
+ private static final String SIGNATURE_ALGO = "SM3withSM2";
+
+ static {
+ if (Security.getProvider(PROVIDER) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ }
+
+ public String calculateSm3Hash(String data) {
+ SM3Digest digest = new SM3Digest();
+ byte[] input = data.getBytes(StandardCharsets.UTF_8);
+ digest.update(input, 0, input.length);
+ byte[] hash = new byte[digest.getDigestSize()];
+ digest.doFinal(hash, 0);
+ return Hex.toHexString(hash);
+ }
+
+ public String signWithSm2Pkcs1(String data, String privateKeyBase64) {
+ try {
+ byte[] keyBytes = Base64.getDecoder().decode(privateKeyBase64);
+ java.security.spec.PKCS8EncodedKeySpec keySpec = new java.security.spec.PKCS8EncodedKeySpec(keyBytes);
+ java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("EC", PROVIDER);
+ java.security.PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
+
+ Signature signature = Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
+ signature.initSign(privateKey);
+ signature.update(data.getBytes(StandardCharsets.UTF_8));
+ byte[] signatureBytes = signature.sign();
+ return Base64.getEncoder().encodeToString(signatureBytes);
+ } catch (Exception e) {
+ throw new RuntimeException("SM2签名失败", e);
+ }
+ }
+
+ public String generateDeviceActivationPublicKey() {
+ try {
+ java.security.KeyPairGenerator keyGen = java.security.KeyPairGenerator.getInstance("EC", PROVIDER);
+ keyGen.initialize(256);
+ java.security.KeyPair keyPair = keyGen.generateKeyPair();
+ return Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
+ } catch (Exception e) {
+ throw new RuntimeException("生成设备激活公钥失败", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/device-activation-platform/src/main/java/com/sunyar/cisd/Main.java b/device-activation-platform/src/main/java/com/sunyar/cisd/Main.java
deleted file mode 100644
index 22d61ce..0000000
--- a/device-activation-platform/src/main/java/com/sunyar/cisd/Main.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.sunyar.cisd;
-
-//TIP 要运行代码,请按 或
-// 点击装订区域中的 图标。
-public class Main {
- public static void main(String[] args) {
- //TIP 当文本光标位于高亮显示的文本处时按
- // 查看 IntelliJ IDEA 建议如何修正。
- System.out.printf("Hello and welcome!");
-
- for (int i = 1; i <= 5; i++) {
- //TIP 按 开始调试代码。我们已经设置了一个 断点
- // 但您始终可以通过按 添加更多断点。
- System.out.println("i = " + i);
- }
- }
-}
\ No newline at end of file
diff --git a/device-activation-platform/src/main/resources/application.yml b/device-activation-platform/src/main/resources/application.yml
new file mode 100644
index 0000000..5a4408a
--- /dev/null
+++ b/device-activation-platform/src/main/resources/application.yml
@@ -0,0 +1,6 @@
+server:
+ port: 8080
+
+spring:
+ application:
+ name: device-activation-platform
\ No newline at end of file