设备激活
This commit is contained in:
parent
a30c029fc4
commit
12778d938d
@ -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; }
|
||||
}
|
||||
|
||||
@ -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<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());
|
||||
// }
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,6 +335,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}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user