添加设备激活相关模块,设计 Bean 的属性。

This commit is contained in:
cheney 2026-05-06 16:32:23 +08:00
parent e14ee17d2c
commit a89fd0c116
9 changed files with 469 additions and 17 deletions

View File

@ -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。

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.11</version>
<relativePath/>
</parent>
<groupId>com.sunyar</groupId>
<artifactId>device-activation-platform</artifactId>
<version>1.0.0</version>
<name>device-activation-platform</name>
<description>Device Activation Platform</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.83</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.83</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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");
}
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -1,17 +0,0 @@
package com.sunyar.cisd;
//TIP <b>运行</b>代码请按 <shortcut actionId="Run"/>
// 点击装订区域中的 <icon src="AllIcons.Actions.Execute"/> 图标
public class Main {
public static void main(String[] args) {
//TIP 当文本光标位于高亮显示的文本处时按 <shortcut actionId="ShowIntentionActions"/>
// 查看 IntelliJ IDEA 建议如何修正
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP <shortcut actionId="Debug"/> 开始调试代码我们已经设置了一个 <icon src="AllIcons.Debugger.Db_set_breakpoint"/> 断点
// 但您始终可以通过按 <shortcut actionId="ToggleLineBreakpoint"/> 添加更多断点
System.out.println("i = " + i);
}
}
}

View File

@ -0,0 +1,6 @@
server:
port: 8080
spring:
application:
name: device-activation-platform