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