主密钥校验值
This commit is contained in:
parent
8be178317a
commit
ad679f48fd
@ -13,6 +13,7 @@ public interface ParamConfKeyConstant {
|
||||
String IP_WHITELIST_SWITCH = "ipWhitelistSwitch";
|
||||
|
||||
String TMK_INIT = "tmk_init";
|
||||
String TMK_CHECK_VALUE = "tmk_check_value";
|
||||
|
||||
String ENABLE_SOFT_DEVICE = "enable_soft_device";
|
||||
|
||||
|
@ -17,6 +17,9 @@ public class TmkStatus {
|
||||
* 主密钥是否初始化
|
||||
*/
|
||||
private boolean tmkInit;
|
||||
|
||||
/**
|
||||
* 主密钥校验值
|
||||
*/
|
||||
private String checkValue;
|
||||
|
||||
}
|
||||
|
@ -14,9 +14,16 @@ 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.sdf.util.LangUtils;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM3Utils;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
@ -59,6 +66,8 @@ public class TmkService {
|
||||
byte[] prk = sdfApi.symDecrypt(hs, hk, AlgId.SGD_SM4_ECB, new byte[0], encrk);
|
||||
Assert.isTrue(Arrays.equals(rk, prk), "密码机加解密异常");
|
||||
|
||||
byte[] hash = BCSM3Utils.hash(rk);
|
||||
|
||||
sdfApi.destroyKey(hs, hk);
|
||||
sdfApi.closeSession(hs);
|
||||
sdfApi.closeDevice(hd);
|
||||
@ -72,7 +81,7 @@ public class TmkService {
|
||||
up.setTmkStatus(DeviceTmkStatus.finished.name());
|
||||
spDeviceMapper.updateById(up);
|
||||
}
|
||||
updateTmkInit(true);
|
||||
updateTmkInit(true, hash);
|
||||
|
||||
}
|
||||
|
||||
@ -82,6 +91,10 @@ public class TmkService {
|
||||
if (init) {
|
||||
status.setHasDevice(true);
|
||||
status.setTmkInit(true);
|
||||
ParamConf paramConf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_CHECK_VALUE);
|
||||
if (paramConf != null) {
|
||||
status.setCheckValue(paramConf.getValue());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
status.setTmkInit(false);
|
||||
@ -90,6 +103,88 @@ public class TmkService {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String backup(String pubKey) {
|
||||
|
||||
boolean tmkInit = isTmkInit();
|
||||
Assert.isTrue(tmkInit, "主密钥未初始化");
|
||||
Device device = getOneByStatus(DeviceTmkStatus.finished);
|
||||
Assert.notNull(device, "没有可以用于备份主密钥的设备");
|
||||
byte[] xy;
|
||||
try {
|
||||
String pubBase6 = pubKey.replace("-----BEGIN ECDSA PUBLIC KEY-----", "").replace("-----END ECDSA PUBLIC KEY-----", "")
|
||||
.replace("\n", "");
|
||||
byte[] pubBytes = CodecUtils.decodeBase64(pubBase6);
|
||||
SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pubBytes);
|
||||
BCECPublicKey key = BCECUtils.createPublicKeyFromSubjectPublicKeyInfo(keyInfo);
|
||||
xy = LangUtils.merge(key.getQ().getXCoord().getEncoded(), key.getQ().getYCoord().getEncoded());
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
|
||||
SdfApiAdapter sdfApi = SdfApiAdapterFactory.newInstance(device.getManufacturerModel(), device.getServiceIp(), device.getServicePort());
|
||||
|
||||
String hd = sdfApi.openDevice();
|
||||
String hs = sdfApi.openSession(hd);
|
||||
sdfApi.getPrivateKeyAccessRight(hs, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||
|
||||
EccCipher cipher = sdfApi.exchangeDigitEnvelopeBaseOnECC(hs, device.getEncKeyIdx(), EccPubKey.fromBytes(xy), EccCipher.fromHex(device.getEncTmk()));
|
||||
|
||||
try {
|
||||
byte[] der = BCSM2Utils.encodeSM2CipherToDER(LangUtils.merge(new byte[]{0x04}, cipher.getC1C3C2Bytes()));
|
||||
return CodecUtils.encodeBase64(der);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Pair<Long, String> getDevicePubKey() {
|
||||
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);
|
||||
EccPubKey pubKey = sdfApi.exportEncPublicKeyECC(hs, device.getEncKeyIdx());
|
||||
|
||||
BCECPublicKey publicKey = BCECUtils.createPublicKey(pubKey.getPubKeyHex());
|
||||
return Pair.of(device.getId(), CodecUtils.encodeBase64(publicKey.getEncoded()));
|
||||
}
|
||||
|
||||
public void importTmk(Long deviceId, String encTmk) {
|
||||
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 = CodecUtils.decodeBase64(encTmk);
|
||||
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), "密码机加解密异常");
|
||||
|
||||
byte[] hash = BCSM3Utils.hash(rk);
|
||||
|
||||
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, hash);
|
||||
}
|
||||
|
||||
|
||||
public DeviceCheckRes checkDevice(Device check) {
|
||||
|
||||
@ -209,6 +304,7 @@ public class TmkService {
|
||||
}
|
||||
if (isEnableSoftDevice()) {
|
||||
device = new Device();
|
||||
device.setId(0L);
|
||||
device.setManufacturerModel(BouncyCastleProvider.PROVIDER_NAME);
|
||||
device.setEncKeyIdx(1);
|
||||
device.setServiceIp("127.0.0.1");
|
||||
@ -241,7 +337,7 @@ public class TmkService {
|
||||
return conf != null && String.valueOf(true).equals(conf.getValue());
|
||||
}
|
||||
|
||||
private void updateTmkInit(boolean value) {
|
||||
private void updateTmkInit(boolean value, byte[] hash) {
|
||||
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
|
||||
if (conf == null) {
|
||||
conf = new ParamConf();
|
||||
@ -253,6 +349,18 @@ public class TmkService {
|
||||
conf.setValue(String.valueOf(value));
|
||||
paramConfMapper.updateById(conf);
|
||||
}
|
||||
|
||||
ParamConf check = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_CHECK_VALUE);
|
||||
if (check == null) {
|
||||
check = new ParamConf();
|
||||
check.setValue(CodecUtils.encodeHex(hash));
|
||||
check.setKey(ParamConfKeyConstant.TMK_CHECK_VALUE);
|
||||
check.setCreatTime(LocalDateTime.now());
|
||||
paramConfMapper.insert(conf);
|
||||
} else {
|
||||
check.setValue(CodecUtils.encodeHex(hash));
|
||||
paramConfMapper.updateById(conf);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized byte[] getSoftDeviceEncTmk() {
|
||||
|
@ -61,7 +61,7 @@ import java.util.Arrays;
|
||||
/**
|
||||
* 这个工具类的方法,也适用于其他基于BC库的ECC算法
|
||||
*/
|
||||
public class BCECUtils {
|
||||
public class BCECUtils extends GMBaseUtil {
|
||||
private static final String ALGO_NAME_EC = "EC";
|
||||
private static final String PEM_STRING_PUBLIC = "PUBLIC KEY";
|
||||
private static final String PEM_STRING_ECPRIVATEKEY = "EC PRIVATE KEY";
|
||||
|
@ -3,13 +3,19 @@ package com.sunyard.chsm.controller;
|
||||
import com.sunyard.chsm.model.R;
|
||||
import com.sunyard.chsm.model.dto.TmkStatus;
|
||||
import com.sunyard.chsm.service.TmkService;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
/**
|
||||
* 主密钥管理
|
||||
@ -45,5 +51,63 @@ public class TmkController {
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份主密钥
|
||||
*/
|
||||
@PostMapping("/backup")
|
||||
public R<String> backup(@Valid @RequestBody TmkBackupReq req) {
|
||||
String en = tmkService.backup(req.pubKey);
|
||||
return R.data(en);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备公钥
|
||||
*/
|
||||
@GetMapping("/devicePubKey")
|
||||
public R<DevicePubKey> getDevicePubKey() {
|
||||
Pair<Long, String> pair = tmkService.getDevicePubKey();
|
||||
DevicePubKey res = new DevicePubKey();
|
||||
res.deviceId = pair.getLeft();
|
||||
res.pubKey = pair.getRight();
|
||||
return R.data(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入主密钥
|
||||
*/
|
||||
@PostMapping("/import")
|
||||
public R<Void> importTmk(@Valid @RequestBody TmkImportReq req) {
|
||||
tmkService.importTmk(req.deviceId, req.encTmk);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DevicePubKey {
|
||||
private Long deviceId;
|
||||
private String pubKey;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TmkBackupReq {
|
||||
/**
|
||||
* ukey公钥
|
||||
*/
|
||||
@NotEmpty(message = "公钥不能为空")
|
||||
private String pubKey;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TmkImportReq {
|
||||
/**
|
||||
* 设备id
|
||||
*/
|
||||
@NotNull(message = "设备id不能为空")
|
||||
private Long deviceId;
|
||||
/**
|
||||
* 加密主密钥
|
||||
*/
|
||||
@NotEmpty(message = "加密主密钥不能为空")
|
||||
private String encTmk;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ CREATE TABLE "SC_PERMISSION"(
|
||||
|
||||
|
||||
-- 初始化数据
|
||||
INSERT INTO SC_PARAM_CONF (ITEM, KEY, VALUE, TYPE, STATUS, MEMO) VALUES (0, 'ipWhitelistSwitch', 'true', 'OBJECT', 0, null);
|
||||
INSERT INTO SC_PARAM_CONF (ITEM, KEY, VALUE, TYPE, STATUS, MEMO) VALUES (0, 'ipWhitelistSwitch', 'false', 'OBJECT', 0, null);
|
||||
INSERT INTO SC_PARAM_CONF (ITEM, KEY, VALUE, TYPE, STATUS, MEMO) VALUES (1, 'communicateTimeOut', '30', 'OBJECT', 0, null);
|
||||
INSERT INTO SC_PARAM_CONF (ITEM, KEY, VALUE, TYPE, STATUS, MEMO) VALUES (1, 'heartDetectTime', '5', 'OBJECT', 0, null);
|
||||
INSERT INTO SC_PARAM_CONF (ITEM, KEY, VALUE, TYPE, STATUS, MEMO) VALUES (1, 'ftpUploadPath', '/app/upload', 'OBJECT', 0, null);
|
||||
@ -216,7 +216,7 @@ INSERT INTO SC_DICT (ID, TYPE, TITLE, SCOPE, SORT_ORDER, DESCRIPTION) VALUES (42
|
||||
INSERT INTO SC_DICT (ID, TYPE, TITLE, SCOPE, SORT_ORDER, DESCRIPTION) VALUES (76, 'Publickey_format', '公钥格式', 1, 0, null);
|
||||
INSERT INTO SC_DICT (ID, TYPE, TITLE, SCOPE, SORT_ORDER, DESCRIPTION) VALUES (81, 'version', '系统版本号', 1, 0, '');
|
||||
|
||||
SET IDENTITY_INSERT SC_DICT_DATA ON
|
||||
SET IDENTITY_INSERT SC_DICT_DATA ON;
|
||||
INSERT INTO SC_DICT_DATA (ID, DICT_ID, TITLE, VALUE, SORT_ORDER, STATUS, DESCRIPTION) VALUES (25, 76, 'DER', 'DER', 1, 0, null);
|
||||
INSERT INTO SC_DICT_DATA (ID, DICT_ID, TITLE, VALUE, SORT_ORDER, STATUS, DESCRIPTION) VALUES (26, 76, 'BASE64', 'BASE64', 2, 0, null);
|
||||
INSERT INTO SC_DICT_DATA (ID, DICT_ID, TITLE, VALUE, SORT_ORDER, STATUS, DESCRIPTION) VALUES (27, 38, '30秒', '30000', 1, 0, null);
|
||||
|
Loading…
Reference in New Issue
Block a user