设备连接检查

This commit is contained in:
liulu
2024-12-10 10:02:54 +08:00
parent f4361d2fd5
commit 29c5fb261f
11 changed files with 388 additions and 230 deletions
@@ -4,7 +4,7 @@ package com.sunyard.chsm.enums;
* @author liulu
* @since 2024/12/7
*/
public enum TmkStatusEnum {
public enum DeviceTmkStatus {
device_error,
key_error,
@@ -3,7 +3,7 @@ package com.sunyard.chsm.mapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.enums.TmkStatusEnum;
import com.sunyard.chsm.enums.DeviceTmkStatus;
import com.sunyard.chsm.model.entity.Device;
import org.apache.ibatis.annotations.Mapper;
@@ -16,14 +16,7 @@ import java.util.List;
@Mapper
public interface SpDeviceMapper extends BaseMapper<Device> {
default List<Device> selectConnedList() {
return selectList(
new LambdaQueryWrapper<Device>()
.eq(Device::getConnected, true)
);
}
default Device selectOneByStatus(TmkStatusEnum status) {
default Device selectOneByStatus(DeviceTmkStatus status) {
Page<Device> devicePage = selectPage(
Page.of(1L, 1L),
new LambdaQueryWrapper<Device>().eq(Device::getTmkStatus, status.name())
@@ -0,0 +1,21 @@
package com.sunyard.chsm.model.dto;
import com.sunyard.chsm.enums.DeviceTmkStatus;
import lombok.Data;
/**
* @author liulu
* @since 2024/12/10
*/
@Data
public class DeviceCheckRes {
private DeviceTmkStatus status;
private String deviceSerial;
private String pubKey;
private String encTmk;
private boolean hasError = false;
private String message;
}
@@ -0,0 +1,22 @@
package com.sunyard.chsm.model.dto;
import lombok.Data;
/**
* @author liulu
* @since 2024/11/11
*/
@Data
public class TmkStatus {
/**
* 是否已经存在设备
*/
private boolean hasDevice;
/**
* 主密钥是否初始化
*/
private boolean tmkInit;
}
@@ -120,7 +120,7 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(pubKey.getX(), pubKey.getY());
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD());
try {
byte[] k = BCSM2Utils.decrypt(pri, pucEncDateIn.getC1C3C2Bytes());
byte[] k = BCSM2Utils.decrypt(pri, LangUtils.merge(new byte[]{0x04}, pucEncDateIn.getC1C3C2Bytes()));
byte[] encrypt = BCSM2Utils.encrypt(pub, k);
return EccCipher.fromBytes(encrypt);
} catch (InvalidCipherTextException e) {
@@ -133,7 +133,7 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(getD());
try {
byte[] pucKey = BCSM2Utils.decrypt(pri, eccCipher.getC1C3C2Bytes());
byte[] pucKey = BCSM2Utils.decrypt(pri, LangUtils.merge(new byte[]{0x04}, eccCipher.getC1C3C2Bytes()));
Assert.isTrue(pucKey.length == 16, "密钥长度错误");
String key = UUID.randomUUID().toString();
KEY_HANDLE_CONTEXT.put(key, pucKey);
@@ -0,0 +1,236 @@
package com.sunyard.chsm.service;
import com.sunyard.chsm.constant.ParamConfKeyConstant;
import com.sunyard.chsm.enums.DeviceTmkStatus;
import com.sunyard.chsm.mapper.ParamConfMapper;
import com.sunyard.chsm.mapper.SpDeviceMapper;
import com.sunyard.chsm.model.dto.DeviceCheckRes;
import com.sunyard.chsm.model.dto.TmkStatus;
import com.sunyard.chsm.model.entity.Device;
import com.sunyard.chsm.model.entity.ParamConf;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
import com.sunyard.chsm.sdf.context.AlgId;
import com.sunyard.chsm.sdf.model.DeviceInfo;
import com.sunyard.chsm.sdf.model.EccCipher;
import com.sunyard.chsm.sdf.model.EccPubKey;
import com.sunyard.chsm.utils.CodecUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
/**
* @author liulu
* @since 2024/12/10
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class TmkService {
private final SpDeviceMapper spDeviceMapper;
private final ParamConfMapper paramConfMapper;
public void initTmk() {
boolean tmkInit = isTmkInit();
Assert.isTrue(!tmkInit, "主密钥已经初始化");
Device device = getOneByStatus(DeviceTmkStatus.available);
Assert.notNull(device, "没有可以用于初始化主密钥的设备");
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
String hd = sdfApi.openDevice();
String hs = sdfApi.openSession(hd);
byte[] rk = sdfApi.generateRandom(hs, 16);
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, device.getEncKeyIdx());
EccCipher cipher = sdfApi.externalEncryptECC(hs, pubKey, rk);
sdfApi.getPrivateKeyAccessRight(hs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
String hk = sdfApi.importKeyWithISKECC(hs, device.getEncKeyIdx(), cipher);
byte[] encrk = sdfApi.symEncrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], rk);
byte[] prk = sdfApi.symDecrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], encrk);
Assert.isTrue(Arrays.equals(rk, prk), "密码机加解密异常");
sdfApi.destroyKey(hs, hk);
sdfApi.closeSession(hs);
sdfApi.closeDevice(hd);
if (enableSoftDevice()) {
updateSoftDeviceEncTmk(cipher.getC1C3C2Bytes());
} else {
Device up = new Device();
up.setId(device.getId());
up.setEncTmk(cipher.getC1C3C2Hex());
up.setTmkStatus(DeviceTmkStatus.finished.name());
spDeviceMapper.updateById(up);
}
updateTmkInit(true);
}
public TmkStatus getTMKStatus() {
TmkStatus status = new TmkStatus();
boolean init = isTmkInit();
if (init) {
status.setHasDevice(true);
status.setTmkInit(true);
return status;
}
status.setTmkInit(false);
Device device = getOneByStatus(DeviceTmkStatus.available);
status.setHasDevice(device != null);
return status;
}
public DeviceCheckRes checkDevice(Device check) {
log.debug("==========> begin check device : {}", check);
DeviceCheckRes res = new DeviceCheckRes();
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(check.getManufacturerModel(), check.getServiceIp(), check.getServicePort());
String hd = sdfApi.openDevice();
String hs = null;
try {
hs = sdfApi.openSession(hd);
DeviceInfo info = sdfApi.getDeviceInfo(hs);
log.debug("get DeviceInfo: {}", info);
res.setDeviceSerial(info.getDeviceSerial());
res.setStatus(DeviceTmkStatus.key_error);
} catch (Exception e) {
log.warn("check device connect error: {}:{}", check.getServiceIp(), check.getServicePort(), e);
res.setHasError(true);
res.setStatus(DeviceTmkStatus.device_error);
res.setMessage(e.getMessage());
}
if (res.isHasError()) {
Optional.ofNullable(hs).ifPresent(sdfApi::closeSession);
Optional.ofNullable(hd).ifPresent(sdfApi::closeDevice);
log.debug("==========> end check device : {}", res);
return res;
}
try {
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, check.getEncKeyIdx());
if (Arrays.equals(pubKey.getPubKeyBytes(), new byte[64])) {
throw new IllegalArgumentException("加密密钥索引不正确");
}
res.setPubKey(pubKey.getPubKeyHex());
log.debug("export enc pubKey at index: {}, {}", check.getEncKeyIdx(), res.getPubKey());
sdfApi.getPrivateKeyAccessRight(hs, check.getEncKeyIdx(), check.getAccessCredentials().getBytes());
if (isTmkInit()) {
log.debug("tmk is init...");
EccCipher cipher;
if (Objects.equals(check.getDeviceSerial(), res.getDeviceSerial())
&& Objects.equals(check.getPubKey(), res.getPubKey())
&& StringUtils.hasText(check.getEncTmk())) {
cipher = EccCipher.fromHex(check.getEncTmk());
log.debug("device serial, pubKey not changed, use origin device enc tmk");
} else {
log.debug("device serial, pubKey is changed, or no tmk in origin device");
Device device = getOneByStatus(DeviceTmkStatus.finished);
Assert.notNull(device, "系统主密钥设备异常,请联系管理员排查");
log.debug("get device from finished: {}", device);
SdfApiAdapter tmkAdapter = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
String tmkHd = tmkAdapter.openDevice();
String tmkHs = tmkAdapter.openSession(tmkHd);
tmkAdapter.getPrivateKeyAccessRight(tmkHs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
cipher = tmkAdapter.exchangeDigitEnvelopeBaseOnECC(tmkHs, device.getEncKeyIdx(), pubKey, EccCipher.fromHex(device.getEncTmk()));
log.debug("exchanged envelope from finished success ...");
}
String hk = sdfApi.importKeyWithISKECC(hs, check.getEncKeyIdx(), cipher);
sdfApi.destroyKey(hs, hk);
if (!Objects.equals(check.getEncTmk(), cipher.getC1C3C2Hex())) {
res.setEncTmk(cipher.getC1C3C2Hex());
}
res.setStatus(DeviceTmkStatus.finished);
} else {
byte[] random = sdfApi.generateRandom(hs, 16);
EccCipher eccCipher = sdfApi.externalEncryptECC(hs, pubKey, random);
String hk = sdfApi.importKeyWithISKECC(hs, check.getEncKeyIdx(), eccCipher);
sdfApi.destroyKey(hs, hk);
res.setStatus(DeviceTmkStatus.available);
}
} catch (Exception e) {
log.warn("check device encKey error: {}:{}", check.getServiceIp(), check.getServicePort(), e);
res.setHasError(true);
res.setStatus(DeviceTmkStatus.key_error);
res.setMessage(e.getMessage());
}
Optional.ofNullable(hs).ifPresent(sdfApi::closeSession);
Optional.ofNullable(hd).ifPresent(sdfApi::closeDevice);
log.debug("==========> end check device : {}", res);
return res;
}
private Device getOneByStatus(DeviceTmkStatus status) {
if (enableSoftDevice()) {
Device device = new Device();
device.setManufacturerModel(BouncyCastleProvider.PROVIDER_NAME);
device.setEncKeyIdx(1);
device.setServiceIp("127.0.0.1");
device.setServicePort(8889);
device.setAccessCredentials("543");
device.setEncTmk(CodecUtils.encodeHex(getSoftDeviceEncTmk()));
return device;
}
return spDeviceMapper.selectOneByStatus(status);
}
private boolean isTmkInit() {
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
return conf != null && String.valueOf(true).equals(conf.getValue());
}
private void updateTmkInit(boolean value) {
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
if (conf == null) {
conf = new ParamConf();
conf.setValue(String.valueOf(value));
conf.setKey(ParamConfKeyConstant.TMK_INIT);
conf.setCreatTime(LocalDateTime.now());
paramConfMapper.insert(conf);
} else {
conf.setValue(String.valueOf(value));
paramConfMapper.updateById(conf);
}
}
private boolean enableSoftDevice() {
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.ENABLE_SOFT_DEVICE);
return conf != null && String.valueOf(true).equals(conf.getValue());
}
private byte[] getSoftDeviceEncTmk() {
boolean tmkInit = isTmkInit();
Assert.isTrue(tmkInit, "主密钥未初始化");
boolean enabled = enableSoftDevice();
Assert.isTrue(enabled, "未启用软设备");
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.SOFT_ENC_TMK);
Assert.notNull(conf, "数据异常, 未找到软设备主密钥记录");
return CodecUtils.decodeBase64(conf.getValue());
}
private void updateSoftDeviceEncTmk(byte[] encTmk) {
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.SOFT_ENC_TMK);
if (conf == null) {
conf = new ParamConf();
conf.setValue(CodecUtils.encodeBase64(encTmk));
conf.setKey(ParamConfKeyConstant.SOFT_ENC_TMK);
conf.setCreatTime(LocalDateTime.now());
paramConfMapper.insert(conf);
} else {
conf.setValue(CodecUtils.encodeBase64(encTmk));
paramConfMapper.updateById(conf);
}
}
}