设备激活

This commit is contained in:
xydkj 2026-05-19 16:33:37 +08:00
parent a30c029fc4
commit 12778d938d
3 changed files with 182 additions and 192 deletions

View File

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

View File

@ -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 先写死后续改实际的激活服务
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<String> 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);
// 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<String> 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());
// }
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;
}
}
}

View File

@ -335,6 +335,9 @@ tms:
old-pin-base64: ${TMS_MK_INIT_IDENTIFY_OLD_PIN_BASE64:NjY2NjY2}
# SDFE_InitIdentify 新 PINBase64 编码);主密钥初始化接口会读取该值。
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}