286 lines
12 KiB
Java
286 lines
12 KiB
Java
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.ObjectUtils;
|
|
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 (Objects.equals(device.getManufacturerModel(), BouncyCastleProvider.PROVIDER_NAME)) {
|
|
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 {
|
|
log.debug("tmk not init, start encrypt and import test ...");
|
|
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);
|
|
res.setSdfApiAdapter(sdfApi);
|
|
log.debug("==========> end check device : {}", res);
|
|
return res;
|
|
}
|
|
|
|
|
|
public void checkSoftDeviceTmk() {
|
|
if (Objects.nonNull(getSoftDeviceEncTmk())) {
|
|
return;
|
|
}
|
|
if (!isTmkInit() || !isEnableSoftDevice()) {
|
|
return;
|
|
}
|
|
log.warn("enabled soft device but no tmk in soft");
|
|
Device device = getOneByStatus(DeviceTmkStatus.finished);
|
|
if (device == null || Objects.equals(device.getManufacturerModel(), BouncyCastleProvider.PROVIDER_NAME)) {
|
|
log.warn("data error, no tmk found in system");
|
|
return;
|
|
}
|
|
SdfApiAdapter softAdapter = SdfApiAdapterFactory.getBcAdapter();
|
|
EccPubKey pubKey = softAdapter.exportEncPublicKeyECC("", 1);
|
|
|
|
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());
|
|
EccCipher cipher = tmkAdapter.exchangeDigitEnvelopeBaseOnECC(tmkHs, device.getEncKeyIdx(), pubKey, EccCipher.fromHex(device.getEncTmk()));
|
|
|
|
updateSoftDeviceEncTmk(cipher.getC1C3C2Bytes());
|
|
}
|
|
|
|
private Device getOneByStatus(DeviceTmkStatus status) {
|
|
Device device = spDeviceMapper.selectOneByStatus(status);
|
|
if (Objects.nonNull(device)) {
|
|
return device;
|
|
}
|
|
if (isEnableSoftDevice()) {
|
|
device = new Device();
|
|
device.setManufacturerModel(BouncyCastleProvider.PROVIDER_NAME);
|
|
device.setEncKeyIdx(1);
|
|
device.setServiceIp("127.0.0.1");
|
|
device.setServicePort(8889);
|
|
device.setAccessCredentials("543");
|
|
if (isTmkInit()) {
|
|
device.setEncTmk(CodecUtils.encodeHex(getSoftDeviceEncTmk()));
|
|
}
|
|
return device;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private boolean tmkInit;
|
|
private boolean enableSoftDevice;
|
|
private byte[] softEncTmk;
|
|
|
|
public synchronized boolean isTmkInit() {
|
|
if (tmkInit) {
|
|
return true;
|
|
}
|
|
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
|
|
tmkInit = conf != null && String.valueOf(true).equals(conf.getValue());
|
|
return tmkInit;
|
|
}
|
|
|
|
public boolean isEnableSoftDevice() {
|
|
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.ENABLE_SOFT_DEVICE);
|
|
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);
|
|
}
|
|
}
|
|
|
|
public synchronized byte[] getSoftDeviceEncTmk() {
|
|
boolean tmkInit = isTmkInit();
|
|
Assert.isTrue(tmkInit, "主密钥未初始化");
|
|
boolean enabled = isEnableSoftDevice();
|
|
Assert.isTrue(enabled, "未启用软设备");
|
|
if (softEncTmk != null) {
|
|
return softEncTmk;
|
|
}
|
|
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.SOFT_ENC_TMK);
|
|
if (conf == null || ObjectUtils.isEmpty(conf.getValue())) {
|
|
return null;
|
|
}
|
|
softEncTmk = CodecUtils.decodeBase64(conf.getValue());
|
|
return softEncTmk;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
}
|