diff --git a/src/main/java/com/cisd/tms/common/util/DivisionUtils.java b/src/main/java/com/cisd/tms/common/util/DivisionUtils.java new file mode 100644 index 0000000..8ed5659 --- /dev/null +++ b/src/main/java/com/cisd/tms/common/util/DivisionUtils.java @@ -0,0 +1,21 @@ +package com.cisd.tms.common.util; + +/** + * 字符串工具类 + * + */ +public class DivisionUtils { + public static String[] equalDivision(String s,int num){ + int len = (int) Math.ceil((double) s.length() / num); + String[] array = new String[num]; + int end; + for(int i = 0; i < array.length ;i++){ + end = i * len + len; + if(end > s.length()){ + end = s.length(); + } + array[i] = s.substring(i * len,end); + } + return array; + } +} \ No newline at end of file diff --git a/src/main/java/com/cisd/tms/common/util/SerializationUtil.java b/src/main/java/com/cisd/tms/common/util/SerializationUtil.java new file mode 100644 index 0000000..6d5920f --- /dev/null +++ b/src/main/java/com/cisd/tms/common/util/SerializationUtil.java @@ -0,0 +1,28 @@ +package com.cisd.tms.common.util; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +public class SerializationUtil { + public static byte[] serializationObject(Object object){ + try (ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(byteArrayOut)){ + out.writeObject(object); + return byteArrayOut.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static Object deserializationObject(byte[] object) throws IOException, ClassNotFoundException { + try(ByteArrayInputStream byteArrayIn = new ByteArrayInputStream(object); + ObjectInputStream in = new ObjectInputStream(byteArrayIn)) { + return in.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw e; + } + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/common/LMKConstant.java b/src/main/java/com/cisd/tms/modules/mk/common/LMKConstant.java new file mode 100644 index 0000000..de4b47b --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/common/LMKConstant.java @@ -0,0 +1,13 @@ +package com.cisd.tms.modules.mk.common; + +/** + * 全局常量 + */ +public interface LMKConstant { + int COMPONENT_NUM = 3; + int COMPONENT_LENGTH = 64; + int[] KEY_ORDER = {1,2,3}; + int[][] COMB_LIST = new int[][]{{1,2},{1,3},{2,3}}; + int LMK_ZONE_NUM = 1; + int LMK_NUM = 50; +} diff --git a/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java b/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java new file mode 100644 index 0000000..d890e53 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/controller/LmkController.java @@ -0,0 +1,118 @@ +package com.cisd.tms.modules.mk.controller; + +import com.cisd.tms.common.api.ApiResponse; +import com.cisd.tms.modules.mk.dto.LMK; +import com.cisd.tms.modules.mk.dto.MasterKeyRestoreDTO; +import com.cisd.tms.modules.mk.dto.RecoveryResult; +import com.cisd.tms.modules.mk.dto.UKeySignDTO; +import com.cisd.tms.modules.mk.dto.UKeySignEntity; +import com.cisd.tms.modules.mk.dto.UKeySignResult; +import com.cisd.tms.modules.mk.enums.MasterKeyStatus; +import com.cisd.tms.modules.mk.service.LmkService; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import jakarta.annotation.Resource; +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 java.io.IOException; +import java.io.StreamCorruptedException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author liulu + * @since 2026/3/4 + */ +@RestController +@RequestMapping("/api/v1") +public class LmkController { + + @Resource + private LmkService lmkService; + @Resource + private ObjectMapper objectMapper; + + /** + * 生成主密钥 + * @return + */ + @PostMapping("/masterkey/generate") + public ApiResponse generateMasterKey() { + LMK lmk = lmkService.generateLMK(); + return ApiResponse.success(lmk); + } + + /** + * UKey签名并获取主密钥组件 + * @param uKeySignDTO + * @return + */ + @PostMapping("/masterkey/uKeySignAndKeyComponent") + public ApiResponse signAndKeyComponent(@RequestBody UKeySignDTO uKeySignDTO) { + String iPubKey = lmkService.exportIkPublicKeyHex(); + String payload; + try { + payload = objectMapper.writeValueAsString(UKeySignEntity.getInstance(uKeySignDTO, iPubKey)); + } catch (JsonProcessingException e) { + throw new IllegalStateException("serialize sign payload failed", e); + } + String signValue = lmkService.signIk(payload); + int uid = Integer.parseInt(uKeySignDTO.getUid()); + String components = lmkService.getComponent(uid); + return ApiResponse.success(UKeySignResult.builder().sign(signValue).extra(components).build()); + } + + @GetMapping("/masterKeyStatus") + public ApiResponse getMasterKeyStatus() { + return ApiResponse.success(lmkService.getMasterKeyStatus()); + } + + /** + * 恢复主密钥 + * @param masterKeyRestoreDTO + * @return + * @throws IOException + * @throws ClassNotFoundException + */ + @PostMapping("/recoveryMasterKey") + public ApiResponse recoveryMasterKey(@RequestBody MasterKeyRestoreDTO masterKeyRestoreDTO) + throws IOException, ClassNotFoundException { + RecoveryResult result; + String mac; + try { + Collection keyComponents = masterKeyRestoreDTO.getComponents().values(); + List keyComponentsList = new ArrayList<>(keyComponents); + mac = lmkService.recoveryLMK(keyComponentsList); + result = RecoveryResult.getInstance(false, mac); + } catch (StreamCorruptedException streamCorruptedException) { + String masterKey = lmkService.masterKeyCompose(masterKeyRestoreDTO.getComponents()); + mac = lmkService.recoveryLMKAndGenIK(masterKey); + result = RecoveryResult.getInstance(true, mac); + } + return ApiResponse.success(result); + } + + /** + * 分量合成恢复主密钥 + * @param masterKeyRestoreDTO + * @return + */ + @PostMapping("/masterKeyRecovery") + public ApiResponse masterKeyRecovery(@RequestBody MasterKeyRestoreDTO masterKeyRestoreDTO) { + String masterKey = lmkService.masterKeyCompose(masterKeyRestoreDTO.getComponents()); + LMK lmk = lmkService.recoveryLMK1(LMK.getInstance(masterKey, masterKeyRestoreDTO)); + return ApiResponse.success(lmk); + } + + @GetMapping("/destroyLMKAndIK") + public ApiResponse destroyLMKAndIK() { + lmkService.destroyLMKAndIK(); + return ApiResponse.success("主密钥和内部密钥销毁成功!"); + } + +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/Key.java b/src/main/java/com/cisd/tms/modules/mk/dto/Key.java new file mode 100644 index 0000000..bd4ffe3 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/Key.java @@ -0,0 +1,13 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Data; + +@Data +public class Key { + private String keySerial; + private int keyLen; + private int lmkIdx; + private int lmkAlgo; + private String schema; + private String key; +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/KeyBytesComponent.java b/src/main/java/com/cisd/tms/modules/mk/dto/KeyBytesComponent.java new file mode 100644 index 0000000..1a42e7e --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/KeyBytesComponent.java @@ -0,0 +1,84 @@ +package com.cisd.tms.modules.mk.dto; + +import com.cisd.tms.modules.mk.common.LMKConstant; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class KeyBytesComponent implements Serializable { + private int index; + private byte[] mkComponent; + private byte[] ikAuthComponent; + private byte[] ikDeviceComponent; + + public static KeyBytesComponent getInstance(int index, byte[] mk, byte[] ikAuth, byte[] ikDevice){ + KeyBytesComponent component = new KeyBytesComponent(); + component.setIndex(index); + component.setMkComponent(getComponent(index, mk)); + component.setIkAuthComponent(getComponent(index, ikAuth)); + component.setIkDeviceComponent(getComponent(index, ikDevice)); + return component; + } + +/* public static void main(String[] args) { + byte[] mk = new byte[]{ 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0 }; + byte[] ikAuth = new byte[]{ 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,}; + byte[] ikDevice = new byte[]{ 1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0 }; + KeyBytesComponent c1 = getInstance(1, mk, ikAuth, ikDevice); + System.out.println(JSON.toJSONString(c1)); + KeyBytesComponent c2 = getInstance(2, mk, ikAuth, ikDevice); + System.out.println(JSON.toJSONString(c2)); + KeyBytesComponent c3 = getInstance(3, mk, ikAuth, ikDevice); + System.out.println(JSON.toJSONString(c3)); + KeyBytesComponent result = getInstance(Arrays.asList(c1,c2,c3)); + System.out.println(JSON.toJSONString(result)); + }*/ + + public static byte[] getComponent(int index, byte[] key){ + int len = key.length / LMKConstant.COMPONENT_NUM; + int start = (index - 1) * len; + if(index == LMKConstant.COMPONENT_NUM){ + len += key.length % LMKConstant.COMPONENT_NUM; + } + byte[] component = new byte[len]; + System.arraycopy(key, start, component, 0, len); + return component; + } + + public static KeyBytesComponent getInstance(List keyBytesComponentList){ + byte[][] mkComponents = new byte[keyBytesComponentList.size()][]; + byte[][] ikAuthComponents = new byte[keyBytesComponentList.size()][]; + byte[][] ikDeviceComponents = new byte[keyBytesComponentList.size()][]; + for(int i = 0; i < keyBytesComponentList.size(); i++){ + for(KeyBytesComponent k : keyBytesComponentList){ + if(k.getIndex() == (i + 1)){ + mkComponents[i] = k.getMkComponent(); + ikAuthComponents[i] = k.getIkAuthComponent(); + ikDeviceComponents[i] = k.getIkDeviceComponent(); + } + } + } + KeyBytesComponent all = new KeyBytesComponent(); + all.setMkComponent(merge(mkComponents)); + all.setIkAuthComponent(merge(ikAuthComponents)); + all.setIkDeviceComponent(merge(ikDeviceComponents)); + return all; + } + + public static byte[] merge(byte[][] arrays){ + int len = 0; + for(int i = 0; i < arrays.length; i++){ + len += arrays[i].length; + } + byte[] result = new byte[len]; + int index = 0; + for(int i = 0; i < arrays.length; i++){ + System.arraycopy(arrays[i], 0, result, index, arrays[i].length); + index += arrays[i].length; + } + return result; + } + +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/KeyComponent.java b/src/main/java/com/cisd/tms/modules/mk/dto/KeyComponent.java new file mode 100644 index 0000000..3aa35ac --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/KeyComponent.java @@ -0,0 +1,57 @@ +package com.cisd.tms.modules.mk.dto; + +import com.cisd.tms.modules.mk.common.LMKConstant; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class KeyComponent implements Serializable { + private Map lmkComponent; + private Map authKeyComponent; + private Map deviceKeyComponent; + +// public static KeyComponent getFromJSON(String json){ +// return JSON.parseObject(json, KeyComponent.class); +// } + + public static List getInstances( + Map lmkComponent,Map authKeyComponent,Map deviceKeyComponent){ + List keyComponentList = new ArrayList<>(); + KeyComponent keyComponent = null; + for(int[] arr : LMKConstant.COMB_LIST){ + keyComponent = new KeyComponent(new HashMap<>(),new HashMap<>(),new HashMap<>()); + for(int i : arr){ + keyComponent.lmkComponent.put(i,lmkComponent.get(i)); + keyComponent.authKeyComponent.put(i,authKeyComponent.get(i)); + keyComponent.deviceKeyComponent.put(i,deviceKeyComponent.get(i)); + } + keyComponentList.add(keyComponent); + } + return keyComponentList; + } + + public static void main(String[] args) { + List result = getInstances(new HashMap(){{ + put(1,"1"); + put(2,"2"); + put(3,"3"); + }},new HashMap(){{ + put(1,"4"); + put(2,"5"); + put(3,"6"); + }},new HashMap(){{ + put(1,"7"); + put(2,"8"); + put(3,"9"); + }}); + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/LMK.java b/src/main/java/com/cisd/tms/modules/mk/dto/LMK.java new file mode 100644 index 0000000..3f62a37 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/LMK.java @@ -0,0 +1,78 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Data; + +/** + * 主秘钥信息实体 + */ +@Data +public class LMK { + /** + * 主密钥(48 字节)(注意:这里的主密钥是完整的主密钥,不是主密钥分量) + */ + private String pcLmk; + + /** + * 主秘钥长度 + */ + private int piLmkLen; + + /** + * 设备公钥(132 字节) + */ + private String pcDevPubkey; + + /** + * 设备公钥长度 + */ + private int iDevPubKeyLen; + + /** + * 设备私钥(68 字节) + */ + private String pcDevPrikey; + + /** + * 设备私钥长度 + */ + private int iDevPriKeyLen; + + /** + * 主秘钥校验值 + */ + private String ucSessionKey; + + /** + * 认证密钥对 + */ + private String AuthKeyPair; + + public static LMK getInstance(String masterKey, MasterKeyRestoreDTO masterKeyRestoreDTO){ + LMK lmk = new LMK(); + lmk.setPcLmk(masterKey); + lmk.setPiLmkLen(masterKey.length() / 2); + if (masterKeyRestoreDTO.getPcDevPubkey() == null || masterKeyRestoreDTO.getPcDevPubkey().equals("")){ + lmk.setPcDevPubkey("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + lmk.setIDevPubKeyLen(132); + }else { + lmk.setPcDevPubkey(masterKeyRestoreDTO.getPcDevPubkey()); + lmk.setIDevPubKeyLen(masterKeyRestoreDTO.getiDevPubKeyLen()); + } + if (masterKeyRestoreDTO.getPcDevPrikey() == null || masterKeyRestoreDTO.getPcDevPrikey().equals("")){ + lmk.setPcDevPrikey("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); + lmk.setIDevPriKeyLen(68); + }else { + lmk.setPcDevPrikey(masterKeyRestoreDTO.getPcDevPrikey()); + lmk.setIDevPriKeyLen(masterKeyRestoreDTO.getiDevPriKeyLen()); + } + return lmk; + } + + public static LMK getInstance(String ucSessionKey){ + LMK lmk = getInstance("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",new MasterKeyRestoreDTO()); + lmk.ucSessionKey = ucSessionKey; + lmk.AuthKeyPair = ""; + return lmk; + } + +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/LMKAndIK.java b/src/main/java/com/cisd/tms/modules/mk/dto/LMKAndIK.java new file mode 100644 index 0000000..1719361 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/LMKAndIK.java @@ -0,0 +1,54 @@ +package com.cisd.tms.modules.mk.dto; + +import com.cisd.tms.modules.mk.common.LMKConstant; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Data +@AllArgsConstructor +public class LMKAndIK { + private String lmk; + private String authKey; + private String deviceKey; + public static LMKAndIK getInstance(List keyComponentList){ + Map lmkComponents = new HashMap<>(); + Map authKeyComponents = new HashMap<>(); + Map deviceKeyComponents = new HashMap<>(); + + Map lmkCompParts = null; + Map authKeyCompParts = null; + Map deviceKeyCompParts = null; + + for(KeyComponent kc : keyComponentList){ + lmkCompParts = kc.getLmkComponent(); + authKeyCompParts = kc.getAuthKeyComponent(); + deviceKeyCompParts = kc.getDeviceKeyComponent(); + + for(int i : lmkCompParts.keySet()){ + lmkComponents.put(i,lmkCompParts.get(i)); + } + for(int i : authKeyCompParts.keySet()){ + authKeyComponents.put(i,authKeyCompParts.get(i)); + } + for(int i : deviceKeyCompParts.keySet()){ + deviceKeyComponents.put(i,deviceKeyCompParts.get(i)); + } + } + if(lmkComponents.size() < 3 || authKeyComponents.size() < 3 || deviceKeyComponents.size() < 3){ + throw new IllegalArgumentException("主密钥或内部密钥分量缺失"); + } + StringBuilder lmk = new StringBuilder(); + StringBuilder authKey = new StringBuilder(); + StringBuilder deviceKey = new StringBuilder(); + for(int i = 0; i < LMKConstant.COMPONENT_NUM; i++){ + lmk.append(lmkComponents.get(LMKConstant.KEY_ORDER[i])); + authKey.append(authKeyComponents.get(LMKConstant.KEY_ORDER[i])); + deviceKey.append(deviceKeyComponents.get(LMKConstant.KEY_ORDER[i])); + } + return new LMKAndIK(lmk.toString(),authKey.toString(), deviceKey.toString()); + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyRestoreDTO.java b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyRestoreDTO.java new file mode 100644 index 0000000..11ff0a8 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/MasterKeyRestoreDTO.java @@ -0,0 +1,96 @@ +package com.cisd.tms.modules.mk.dto; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class MasterKeyRestoreDTO { + /** + * 签名原始值及签名值 + */ + private List uKeySignVerifyDTOList; + + /** + * 设备公钥(132 字节) + */ + private String pcDevPubkey; + + /** + * 设备公钥长度 + */ + private int iDevPubKeyLen; + + /** + * 设备私钥(68 字节) + */ + private String pcDevPrikey; + + /** + * 设备私钥长度 + */ + private int iDevPriKeyLen; + + /** + * 认证密钥对 + */ + private String keyPair; + + public String getKeyPair() { + return keyPair; + } + + public void setKeyPair(String keyPair) { + this.keyPair = keyPair; + } + + public List getuKeySignVerifyDTOList() { + return uKeySignVerifyDTOList; + } + + public void setuKeySignVerifyDTOList(List uKeySignVerifyDTOList) { + this.uKeySignVerifyDTOList = uKeySignVerifyDTOList; + } + + public String getPcDevPubkey() { + return pcDevPubkey; + } + + public void setPcDevPubkey(String pcDevPubkey) { + this.pcDevPubkey = pcDevPubkey; + } + + public int getiDevPubKeyLen() { + return iDevPubKeyLen; + } + + public void setiDevPubKeyLen(int iDevPubKeyLen) { + this.iDevPubKeyLen = iDevPubKeyLen; + } + + public String getPcDevPrikey() { + return pcDevPrikey; + } + + public void setPcDevPrikey(String pcDevPrikey) { + this.pcDevPrikey = pcDevPrikey; + } + + public int getiDevPriKeyLen() { + return iDevPriKeyLen; + } + + public void setiDevPriKeyLen(int iDevPriKeyLen) { + this.iDevPriKeyLen = iDevPriKeyLen; + } + + public Map getComponents() { + Map componentMap = new HashMap<>(); + for(UKeySignVerifyDTO uKeySignVerifyDTO : uKeySignVerifyDTOList){ + if(null == uKeySignVerifyDTO.getKeyComponent()){ + throw new IllegalArgumentException("UKey无主密钥分量,UID:" + uKeySignVerifyDTO.getuKeySignDTO().getUid()); + } + componentMap.put(uKeySignVerifyDTO.getuKeySignDTO().getUid(),uKeySignVerifyDTO.getKeyComponent()); + } + return componentMap; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/RecoveryResult.java b/src/main/java/com/cisd/tms/modules/mk/dto/RecoveryResult.java new file mode 100644 index 0000000..a0782ab --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/RecoveryResult.java @@ -0,0 +1,16 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Data; + +@Data +public class RecoveryResult { + private boolean needNewUKey; + private String mac; + + public static RecoveryResult getInstance(boolean needNewUKey, String mac) { + RecoveryResult result = new RecoveryResult(); + result.needNewUKey = needNewUKey; + result.mac = mac; + return result; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKey.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKey.java new file mode 100644 index 0000000..2b50fae --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKey.java @@ -0,0 +1,82 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Data; + +@Data +public class UKey { + /** + * 用户 ID, 可以为空 + */ + private String uid; + + /** + * 角色 ID + */ + private String rid; + + /** + * 设备公钥 HEX(32 字节 x + 32 字节 y) + */ + private String dev_pk; + + /** + * U 盾公钥 HEX(32 字节 x + 32 字节 y) + */ + private String ukey_pk; + + /** + * 前端随机数 ra + */ + private Long ra; + + /** + * 缓存随机数 rb + */ + private Long rb; + + /** + * 签名 + */ + private String sa; + + /** + * 临时结果 + */ + private boolean result; + + public String getAuthKey(){ + return this.uid; + } + + @Data + public static class AuthResult{ + /** + * 认证结果;true:成功,false:失败 + */ + private boolean result; + /** + * 是否当前主机操作;true:是,false:否 + */ + private boolean isOperator; + /** + * 单个 U 盾重试次数 + */ + private int retryNum = 3; + + /** + * token,用于访问验证 + */ + private String token; + private AuthResult(boolean _result,boolean _isOperator){ + this.isOperator = _isOperator; + this.result = _result; + } + public static AuthResult get(boolean _result,boolean _isOperator){ + return new AuthResult(_result,_isOperator); + } + public AuthResult and(boolean result){ + this.result = this.result && result; + return this; + } + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java new file mode 100644 index 0000000..760accb --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignDTO.java @@ -0,0 +1,34 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Data; + +/** + * 发行UKey时前端传输的信息 + */ +@Data +public class UKeySignDTO { + /** + * UKey 公钥 + */ + private String pubKey; + + /** + * 签名的角色 + */ + private String role; + + /** + * UKey 中的认证信息 + */ + private String uid; + + /** + * UKey 中的认证信息 + */ + private String rid; + + /** + * 需要处理的其他信息,不参与签名,这里是主密钥复制是暂时存储的分量 + */ + private String extra; +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java new file mode 100644 index 0000000..90fc647 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignEntity.java @@ -0,0 +1,42 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Builder; +import lombok.Data; + +/** + * UKey签名实体,在发行密钥时对该实体的JSON字符串进行签名 + */ +@Data +@Builder +public class UKeySignEntity { + /** + * UKey公钥,前端传输 + */ + private String pubKey; + /** + * 认证密钥对, + */ + private String authKeyPair; + /** + * 登录角色名称,如:superadmin、keyadmin、configadmin、auditadmin + */ + private String role; + /** + * 登录时传输 + */ + private String uid; + /** + * 登录时传输 + */ + private String rid; + + public static UKeySignEntity getInstance(UKeySignDTO uKeySignDTO,String authKeyPair){ + return UKeySignEntity.builder() + .pubKey(uKeySignDTO.getPubKey()) + .authKeyPair(authKeyPair) + .role(uKeySignDTO.getRole()) + .uid(uKeySignDTO.getUid()) + .rid(uKeySignDTO.getRid()) + .build(); + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java new file mode 100644 index 0000000..19a15e8 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignResult.java @@ -0,0 +1,21 @@ +package com.cisd.tms.modules.mk.dto; + +import lombok.Builder; +import lombok.Data; + +/** + * UKey发行结果 + */ +@Data +@Builder +public class UKeySignResult { + /** + * 签名值 + */ + private String sign; + + /** + * 额外的数据,比如主密钥分量 + */ + private String extra; +} diff --git a/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignVerifyDTO.java b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignVerifyDTO.java new file mode 100644 index 0000000..b369560 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/dto/UKeySignVerifyDTO.java @@ -0,0 +1,43 @@ +package com.cisd.tms.modules.mk.dto; + + +public class UKeySignVerifyDTO { + /** + * 主密钥分量,合成主密钥时使用 + */ + private String keyComponent; + + /** + * 签名原始数据 + */ + private UKeySignDTO uKeySignDTO; + + /** + * UKey 签名 + */ + private String sign; + + public String getKeyComponent() { + return keyComponent; + } + + public void setKeyComponent(String keyComponent) { + this.keyComponent = keyComponent; + } + + public UKeySignDTO getuKeySignDTO() { + return uKeySignDTO; + } + + public void setuKeySignDTO(UKeySignDTO uKeySignDTO) { + this.uKeySignDTO = uKeySignDTO; + } + + public String getSign() { + return sign; + } + + public void setSign(String sign) { + this.sign = sign; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/enums/IKEnums.java b/src/main/java/com/cisd/tms/modules/mk/enums/IKEnums.java new file mode 100644 index 0000000..cdc658b --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/enums/IKEnums.java @@ -0,0 +1,18 @@ +package com.cisd.tms.modules.mk.enums; + +import lombok.Getter; + +@Getter +public enum IKEnums { + KEY_TYPE_DEVICE(0x00000001,"设备密钥对"), + KEY_TYPE_AUTH(0x00000002,"认证密钥对") + ; + + int code; + String desc; + + IKEnums(int _code, String _desc){ + this.code = _code; + this.desc = _desc; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/enums/LMKType.java b/src/main/java/com/cisd/tms/modules/mk/enums/LMKType.java new file mode 100644 index 0000000..8fdb758 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/enums/LMKType.java @@ -0,0 +1,14 @@ +package com.cisd.tms.modules.mk.enums; + +import lombok.Getter; + +@Getter +public enum LMKType { + LMK_TYPE_CUR(0x00000001), + LMK_TYPE_TMP(0x00000002) + ; + final int code; + LMKType(int _code){ + this.code = _code; + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/enums/LmkInitType.java b/src/main/java/com/cisd/tms/modules/mk/enums/LmkInitType.java new file mode 100644 index 0000000..4e7c6ae --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/enums/LmkInitType.java @@ -0,0 +1,6 @@ +package com.cisd.tms.modules.mk.enums; + +public enum LmkInitType { + GEN, + SET; +} diff --git a/src/main/java/com/cisd/tms/modules/mk/enums/MasterKeyStatus.java b/src/main/java/com/cisd/tms/modules/mk/enums/MasterKeyStatus.java new file mode 100644 index 0000000..96a4183 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/enums/MasterKeyStatus.java @@ -0,0 +1,46 @@ +package com.cisd.tms.modules.mk.enums; + +import lombok.Data; +import lombok.Getter; + +public enum MasterKeyStatus { + NORMAL(0, "已生成"), + ABNORMAL(1, "未生成"); + + MasterKeyStatus(int code, String desc) { + this.code = code; + this.desc = desc; + } + + @Getter + private final int code; + + @Getter + private final String desc; + + public StatusDetail getDetail() { + return new StatusDetail(this.code, this.desc); + } + + public StatusDetail getDetail(String ucSessionKey) { + return new StatusDetail(this.code, this.desc, ucSessionKey); + } + + @Data + public static class StatusDetail { + private int code; + private String desc; + private String ucSessionKey; + + public StatusDetail(int code, String desc) { + this.code = code; + this.desc = desc; + } + + public StatusDetail(int code, String desc, String ucSessionKey) { + this.code = code; + this.desc = desc; + this.ucSessionKey = ucSessionKey; + } + } +} diff --git a/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java b/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java new file mode 100644 index 0000000..dafa85e --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/mk/service/LmkService.java @@ -0,0 +1,221 @@ +package com.cisd.tms.modules.mk.service; + +import com.cisd.tms.common.util.DivisionUtils; +import com.cisd.tms.common.util.SerializationUtil; +import com.cisd.tms.integration.crypto.pcie.jna.EccRefPublicKey; +import com.cisd.tms.integration.crypto.pcie.jna.EccSignature; +import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest; +import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService; +import com.cisd.tms.modules.mk.common.LMKConstant; +import com.cisd.tms.modules.mk.dto.KeyComponent; +import com.cisd.tms.modules.mk.dto.LMK; +import com.cisd.tms.modules.mk.dto.LMKAndIK; +import com.cisd.tms.modules.mk.enums.IKEnums; +import com.cisd.tms.modules.mk.enums.MasterKeyStatus; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.util.encoders.Hex; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.StreamCorruptedException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author liulu + * @since 2026/3/5 + */ +@Slf4j +@Service +public class LmkService { + +// private List keyComponentList; + + private List keyComponentList; + + private static final int AUTH_SIGN_KEY_INDEX = IKEnums.KEY_TYPE_AUTH.getCode(); + + @Resource + private PcieCryptoService pcieCryptoService; + + /** + * 生成主秘钥 + */ + public LMK generateLMK() { + // todo 生成主密钥前删除所有的对称和非对称密钥 + // this.delAllKey(); + + //生成主密钥 + pcieCryptoService.generateLmk(); + //生成内部密钥 + pcieCryptoService.generateIk(IKEnums.KEY_TYPE_AUTH.getCode()); + pcieCryptoService.generateIk(IKEnums.KEY_TYPE_DEVICE.getCode()); + try { + Thread.sleep(5000L); + } catch (InterruptedException e) { + log.error("sleep error"); + } + pcieCryptoService.loadLmk(); + try { + Thread.sleep(5000L); + } catch (InterruptedException e) { + log.error("sleep error"); + } + backUp(); +// iLmkService.generateLMKAndIK(null) +// .load() +// .backUp(0,0); + byte[] seedMac = pcieCryptoService.exportLmkSeedMac(); +// LmkKey lmkKey = soLibraryAdapter.getLmkKeySession(); + log.info("主密钥校验值:" + Hex.toHexString(seedMac)); + return LMK.getInstance(Hex.toHexString(seedMac)); + } + + + + public void backUp() { + + BackupDataResult lmkEx = pcieCryptoService.backupLmkEx(56); + String lmk = Hex.toHexString(lmkEx.getData()); + + // todo 获取内部密钥 + BackupDataResult ikAuthEx = pcieCryptoService.backupLmkEx(56); + String authKey = Hex.toHexString(ikAuthEx.getData()); + BackupDataResult ikDeviceEx = pcieCryptoService.backupLmkEx(56); + String deviceKey = Hex.toHexString(ikDeviceEx.getData()); + +// BackupDataResult ikAuthEx = pcieCryptoService.backupIk(IKEnums.KEY_TYPE_AUTH.getCode()); +// String authKey = Hex.toHexString(ikAuthEx.getData()); +// BackupDataResult ikEx = pcieCryptoService.backupIk(IKEnums.KEY_TYPE_DEVICE.getCode()); +// String deviceKey = Hex.toHexString(ikEx .getData()); + + this.splitKey(lmk, authKey, deviceKey); + } + + private void splitKey(String lmk,String authKey,String deviceKey){ + //平分内部密钥和主密钥并编号 + Map lmkComponentMap = new HashMap<>(); + Map authKeyComponentMap = new HashMap<>(); + Map deviceKeyComponentMap = new HashMap<>(); + + String[] lmkComponents = DivisionUtils.equalDivision(lmk, LMKConstant.COMPONENT_NUM); + String[] authKeyComponents = DivisionUtils.equalDivision(authKey,LMKConstant.COMPONENT_NUM); + String[] deviceKeyComponents = DivisionUtils.equalDivision(deviceKey,LMKConstant.COMPONENT_NUM); + //这里内部密钥和主密钥map的key是123,表示切分后按该顺序拼接 + for(int i = 0; i < LMKConstant.COMPONENT_NUM; i++){ + lmkComponentMap.put(LMKConstant.KEY_ORDER[i],lmkComponents[i]); + authKeyComponentMap.put(LMKConstant.KEY_ORDER[i],authKeyComponents[i]); + deviceKeyComponentMap.put(LMKConstant.KEY_ORDER[i],deviceKeyComponents[i]); + } + //组合分割后的主密钥和内部密钥 + + List keyComponentList = KeyComponent.getInstances(lmkComponentMap,authKeyComponentMap,deviceKeyComponentMap); + List result = new ArrayList<>(); + keyComponentList.forEach(k -> result.add(Base64.getEncoder().encodeToString(SerializationUtil.serializationObject(k)))); + this.keyComponentList = result; + } + + public String exportIkPublicKeyHex() { + int outBufferSize = new EccRefPublicKey().size(); + BackupDataResult result = pcieCryptoService.exportSignPublicKeyEcc(AUTH_SIGN_KEY_INDEX, outBufferSize); + return Hex.toHexString(result.getData()); + } + + public String signIk(String rawData) { + EccInternalSignRequest request = new EccInternalSignRequest(); + request.setKeyIndex(AUTH_SIGN_KEY_INDEX); + request.setData(rawData.getBytes(StandardCharsets.UTF_8)); + request.setSignatureBufferSize(new EccSignature().size()); + BackupDataResult result = pcieCryptoService.eccInternalSign(request); + return Hex.toHexString(result.getData()); + } + + public String getComponent(int uid) { + if (keyComponentList == null || keyComponentList.isEmpty()) { + throw new IllegalStateException("key components not initialized"); + } + int index = uid - 1; + if (index < 0 || index >= keyComponentList.size()) { + throw new IllegalArgumentException("invalid component index: " + uid); + } + return keyComponentList.get(index); + } + + public String masterKeyCompose(Map componentMap) { + StringBuilder lmk = new StringBuilder(); + for (int i = 0; i < LMKConstant.COMPONENT_NUM; i++) { + lmk.append(componentMap.get(String.valueOf(LMKConstant.KEY_ORDER[i]))); + } + return lmk.toString(); + } + + public String recoveryLMK(List keyComponents) throws IOException, ClassNotFoundException { + List componentList = new ArrayList<>(); + for (String component : keyComponents) { + byte[] raw; + try { + raw = Base64.getDecoder().decode(component); + } catch (IllegalArgumentException e) { + StreamCorruptedException ex = new StreamCorruptedException("invalid component encoding"); + ex.initCause(e); + throw ex; + } + try { + Object obj = SerializationUtil.deserializationObject(raw); + componentList.add((KeyComponent) obj); + } catch (ClassCastException e) { + StreamCorruptedException ex = new StreamCorruptedException("invalid component payload"); + ex.initCause(e); + throw ex; + } catch (StreamCorruptedException e) { + throw e; + } + } + LMKAndIK lmkAndIK = LMKAndIK.getInstance(componentList); + pcieCryptoService.recoverLmkEx(Hex.decode(lmkAndIK.getLmk())); + pcieCryptoService.loadLmk(); + byte[] seedMac = pcieCryptoService.exportLmkSeedMac(); + return Hex.toHexString(seedMac); + } + + public String recoveryLMKAndGenIK(String lmkComponentHex) { + pcieCryptoService.recoverLmkEx(Hex.decode(lmkComponentHex)); + pcieCryptoService.generateIk(IKEnums.KEY_TYPE_AUTH.getCode()); + pcieCryptoService.generateIk(IKEnums.KEY_TYPE_DEVICE.getCode()); + pcieCryptoService.loadLmk(); + byte[] seedMac = pcieCryptoService.exportLmkSeedMac(); + return Hex.toHexString(seedMac); + } + + public LMK recoveryLMK1(LMK lmk) { + pcieCryptoService.recoverLmkEx(Hex.decode(lmk.getPcLmk())); + pcieCryptoService.loadLmk(); + byte[] seedMac = pcieCryptoService.exportLmkSeedMac(); + lmk.setUcSessionKey(Hex.toHexString(seedMac)); + return lmk; + } + + public void destroyLMKAndIK() { + pcieCryptoService.destroyLmk(); + pcieCryptoService.destroyIk(IKEnums.KEY_TYPE_AUTH.getCode()); + pcieCryptoService.destroyIk(IKEnums.KEY_TYPE_DEVICE.getCode()); + keyComponentList = null; + } + + public MasterKeyStatus.StatusDetail getMasterKeyStatus() { + boolean ok = pcieCryptoService.checkLmk(); + MasterKeyStatus status = ok ? MasterKeyStatus.NORMAL : MasterKeyStatus.ABNORMAL; + if (ok) { + String mac = Hex.toHexString(pcieCryptoService.exportLmkSeedMac()); + return status.getDetail(mac); + } + return status.getDetail(); + } + +}