diff --git a/chsm-common/pom.xml b/chsm-common/pom.xml index cfe4c33..e768b9f 100644 --- a/chsm-common/pom.xml +++ b/chsm-common/pom.xml @@ -41,6 +41,14 @@ org.bouncycastle bcpkix-jdk18on + + org.apache.commons + commons-pool2 + + + net.java.dev.jna + jna + diff --git a/chsm-common/src/main/java/com/sunyard/chsm/mapper/TmkInfoMapper.java b/chsm-common/src/main/java/com/sunyard/chsm/mapper/TmkInfoMapper.java new file mode 100644 index 0000000..dfc0391 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/mapper/TmkInfoMapper.java @@ -0,0 +1,16 @@ +package com.sunyard.chsm.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.sunyard.chsm.model.entity.TmkInfo; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author liulu + * @since 2024/10/29 + */ +@Mapper +public interface TmkInfoMapper extends BaseMapper { + + + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java index e32b273..bdd8527 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Device.java @@ -23,6 +23,10 @@ public class Device { private String manufacturerModel; private String accessCredentials; + private Boolean connected; + private LocalDateTime lastCheckTime; + private LocalDateTime lastConnectedTime; + private Long groupId; private String groupName; private Integer weight; diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/TmkInfo.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/TmkInfo.java new file mode 100644 index 0000000..3de73f9 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/TmkInfo.java @@ -0,0 +1,25 @@ +package com.sunyard.chsm.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author liulu + * @since 2024/11/4 + */ +@Data +@TableName("sp_tmk_info") +public class TmkInfo { + + private Long id; + + private String deviceSerial; + private String encTmk; + + private String remark; + private LocalDateTime createTime; + private LocalDateTime updateTime; + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java new file mode 100644 index 0000000..e547550 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/JnaSdfAdaptor.java @@ -0,0 +1,109 @@ +package com.sunyard.chsm.sdf.adapter; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; +import com.sunyard.chsm.sdf.lib.SdfLibrary; +import com.sunyard.chsm.sdf.model.DeviceInfo; +import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPubKey; +import org.springframework.util.Assert; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author liulu + * @since 2024/11/4 + */ +public abstract class JnaSdfAdaptor implements SdfApiAdapter { + + protected static final Map DEVICE_HANDLE_CONTEXT = new ConcurrentHashMap<>(); + protected static final Map SESSION_HANDLE_CONTEXT = new ConcurrentHashMap<>(); + protected static final Map KEY_HANDLE_CONTEXT = new ConcurrentHashMap<>(); + + + protected abstract SdfLibrary getSdfLibrary(); + + protected abstract int getAlgId(String alg); + + @Override + public String openDevice() { + PointerByReference phDeviceHandle = new PointerByReference(); + getSdfLibrary().SDF_OpenDevice(phDeviceHandle); + String key = UUID.randomUUID().toString(); + DEVICE_HANDLE_CONTEXT.put(key, phDeviceHandle.getValue()); + return key; + } + + @Override + public boolean closeDevice(String deviceHandle) { + Pointer pointer = DEVICE_HANDLE_CONTEXT.remove(deviceHandle); + if (pointer != null) { + int res = getSdfLibrary().SDF_CloseDevice(pointer); + return res == 0; + } + return true; + } + + @Override + public String openSession(String deviceHandle) { + Pointer device = getDeviceHandle(deviceHandle); + + PointerByReference phSessionHandle = new PointerByReference(); + getSdfLibrary().SDF_OpenSession(device, phSessionHandle); + + String key = UUID.randomUUID().toString(); + SESSION_HANDLE_CONTEXT.put(key, phSessionHandle.getValue()); + return key; + } + + @Override + public void closeSession(String sessionHandle) { + Pointer pointer = SESSION_HANDLE_CONTEXT.remove(sessionHandle); + if (pointer != null) { + getSdfLibrary().SDF_CloseSession(pointer); + } + } + + protected Pointer getDeviceHandle(String deviceHandle) { + Pointer pointer = DEVICE_HANDLE_CONTEXT.get(deviceHandle); + Assert.notNull(pointer, "设备句柄不存在"); + return pointer; + } + + protected Pointer getSessionHandle(String sessionHandle) { + Pointer pointer = SESSION_HANDLE_CONTEXT.get(sessionHandle); + Assert.notNull(pointer, "会话句柄不存在"); + return pointer; + } + + protected Pointer getKeyHandle(String keyHandle) { + Pointer pointer = KEY_HANDLE_CONTEXT.get(keyHandle); + Assert.notNull(pointer, "密钥句柄不存在"); + return pointer; + } + + @Override + public DeviceInfo getDeviceInfo(String sessionHandle) { + return null; + } + + @Override + public byte[] generateRandom(String sessionHandle, int uiLength) { + byte[] pucRandom = new byte[uiLength]; + Pointer hSessionHandle = getSessionHandle(sessionHandle); + getSdfLibrary().SDF_GenerateRandom(hSessionHandle, uiLength, pucRandom); + return pucRandom; + } + + @Override + public EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) { + return null; + } + + @Override + public EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits) { + return null; + } +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java new file mode 100644 index 0000000..d03100e --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapter.java @@ -0,0 +1,71 @@ +package com.sunyard.chsm.sdf.adapter; + +import com.sunyard.chsm.sdf.model.DeviceInfo; +import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPubKey; + +/** + * @author liulu + * @since 2024/11/4 + */ +public interface SdfApiAdapter { + + /** + * 打开设备 + * + * @return 设 备 句 柄 + */ + String openDevice(); + + /** + * 关闭设备 + * + * @param deviceHandle 已打开的设备句柄 + * @return 0 成功; 非0 失败,返回错误代码 + */ + boolean closeDevice(String deviceHandle); + + /** + * 创建会话 + * + * @return 与密码设备建立的新会话句柄 + */ + String openSession(String deviceHandle); + + void closeSession(String sessionHandle); + + /** + * 获取设备信息 + * + * @param sessionHandle 与设备建立的会话句柄 + * @return 设备能力描述信息,内容及格式见设备信息定义 + */ + DeviceInfo getDeviceInfo(String sessionHandle); + + /** + * 产生随机数 + * + * @param uiLength 随机数长度 + * @return pucRandom 返回随机数 + */ + byte[] generateRandom(String sessionHandle, int uiLength); + + /** + * 导出ECC加密公钥 + * + * @param uiKeyIndex 密码设备存储的ECC密钥对索引值 + * @return pucPublicKeyEcc 返回ECC加密公钥 + */ + EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex); + + /** + * 产生ECC密钥对并输出 + * + * @param alg 指定算法标识 SGD_SM2_1 + * @param uiKeyBits 指定密钥模长 + * @return pucPublicKeyEcc 返回公钥 | pucPrivateKeyEcc 返回私钥 + */ + EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits); + + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapterFactory.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapterFactory.java new file mode 100644 index 0000000..f118475 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SdfApiAdapterFactory.java @@ -0,0 +1,34 @@ +package com.sunyard.chsm.sdf.adapter; + +import com.sunyard.chsm.enums.ManufacturerModelEnum; +import com.sunyard.chsm.sdf.context.DeviceContext; +import org.springframework.util.Assert; + +import java.util.Objects; + +/** + * @author liulu + * @since 2024/11/5 + */ +public class SdfApiAdapterFactory { + + + public static SdfApiAdapter newInstance(DeviceContext device) { + + Assert.hasText(device.getManufacturerModel(), "设备型号不能为空"); + ManufacturerModelEnum model = ManufacturerModelEnum.of(device.getManufacturerModel()); + + if (Objects.isNull(model)) { + // bc adaptor + return null; + } + switch (model) { + case enc001: + return new SunyardJnaSdfAdaptor(device.getServiceIp(), device.getServicePort()); + default: + return null; + } + } + + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java new file mode 100644 index 0000000..0474e45 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java @@ -0,0 +1,75 @@ +package com.sunyard.chsm.sdf.adapter; + +import com.sun.jna.Native; +import com.sun.jna.ptr.PointerByReference; +import com.sunyard.chsm.sdf.lib.SdfLibrary; +import com.sunyard.chsm.sdf.lib.SunyardSdfLibrary; + +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author liulu + * @since 2024/11/4 + */ +public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor { + + private static final Map SDF_LIB_MAP = new ConcurrentHashMap<>(); + + + private final String ip; + private final Integer port; + private final String libName; + private final Integer connTimeout; + private final Integer dealTimeout; + + public SunyardJnaSdfAdaptor(String ip, int port) { + this(ip, port, "libsdf"); + } + + public SunyardJnaSdfAdaptor(String ip, int port, String libName) { + this(ip, port, libName, 3000, 3000); + } + + public SunyardJnaSdfAdaptor(String ip, int port, String libName, int connTimeout, int dealTimeout) { + this.ip = ip; + this.port = port; + this.libName = libName; + this.connTimeout = connTimeout; + this.dealTimeout = dealTimeout; + SDF_LIB_MAP.computeIfAbsent(libName, k -> Native.load(libName, SunyardSdfLibrary.class)); + } + + @Override + public String openDevice() { + SunyardSdfLibrary sdfLibrary = (SunyardSdfLibrary) getSdfLibrary(); + PointerByReference phDeviceHandle = new PointerByReference(); + sdfLibrary.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0); + String key = UUID.randomUUID().toString(); + DEVICE_HANDLE_CONTEXT.put(key, phDeviceHandle.getValue()); + return key; + } + + @Override + protected SdfLibrary getSdfLibrary() { + return SDF_LIB_MAP.get(libName); + } + + @Override + protected int getAlgId(String alg) { + + return 0; + } + + public static byte[] safeStringBytes(String str) { + if (null == str || str.isEmpty()) { + return new byte[]{0x30}; + } + int len = str.getBytes().length; + byte[] ret = new byte[len + 1]; + System.arraycopy(str.getBytes(), 0, ret, 0, len); + return ret; + } + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/context/DeviceContext.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/context/DeviceContext.java new file mode 100644 index 0000000..d133840 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/context/DeviceContext.java @@ -0,0 +1,22 @@ +package com.sunyard.chsm.sdf.context; + +import lombok.Data; + +/** + * @author liulu + * @since 2024/11/5 + */ +@Data +public class DeviceContext { + + private String serviceIp; + private Integer servicePort; + private String manufacturer; + private String manufacturerModel; + private String accessCredentials; + private String jnaLibName; + + private Integer weight; + + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SdfLibrary.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SdfLibrary.java new file mode 100644 index 0000000..f8bf741 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SdfLibrary.java @@ -0,0 +1,66 @@ +package com.sunyard.chsm.sdf.lib; + +import com.sun.jna.Library; +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + +/** + * @author liulu + * @since 2024/11/4 + */ +public interface SdfLibrary extends Library { + + /** + * 打开设备 + * + * @param phDeviceHandle 返回设备句柄 + * @return 0 成功; 非0 失败,返回错误代码 + */ + int SDF_OpenDevice(PointerByReference phDeviceHandle); + + /** + * 关闭设备 + * + * @param hDeviceHandle 已打开的设备句柄 + * @return 0 成功; 非0 失败,返回错误代码 + */ + int SDF_CloseDevice(Pointer hDeviceHandle); + + /** + * 创建会话 + * + * @param hDeviceHandle 已打开的设备句柄 + * @param phSessionHandle 返回与密码设备建立的新会话句柄 + * @return 0 成功; 非0 失败,返回错误代码 + */ + int SDF_OpenSession(Pointer hDeviceHandle, PointerByReference phSessionHandle); + + /** + * 关闭会话 + * + * @param hSessionHandle 与密码设备已建立的会话句柄 + * @return 0 成功; 非0 失败,返回错误代码 + */ + int SDF_CloseSession(Pointer hSessionHandle); + + /** + * 获取设备信息 + * + * @param hSessionHandle 与设备建立的会话句柄 + * @param pstDeviceInfo 设备能力描述信息,内容及格式见设备信息定义 + * @return 0 成功; 非0 失败,返回错误代码 + */ + int SDF_GetDeviceInfo(Pointer hSessionHandle, byte[] pstDeviceInfo); + + /** + * 产生随机数 + * + * @param hSessionHandle 与设备建立的会话句柄 + * @param uiLength 欲获取的随机数长度 + * @param pucRandom 缓冲区指针,用于存放获取的随机数 + * @return 0 成功; 非0 失败,返回错误代码 + */ + int SDF_GenerateRandom(Pointer hSessionHandle, int uiLength, byte[] pucRandom); + + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java new file mode 100644 index 0000000..3152f58 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java @@ -0,0 +1,21 @@ +package com.sunyard.chsm.sdf.lib; + +import com.sun.jna.ptr.PointerByReference; + +/** + * @author liulu + * @since 2024/11/4 + */ +public interface SunyardSdfLibrary extends SdfLibrary { + + + /** + * 打开设备 + * @param phDeviceHandle 设备句柄 + * @return int 响应码 + */ + int SDF_OpenDevice(PointerByReference phDeviceHandle, byte[] ip, int port, int connTimeout, int dealTimeout, int ipMode); + + + +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java index 84e520c..012b858 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/DeviceServiceImpl.java @@ -123,6 +123,7 @@ public class DeviceServiceImpl implements DeviceService { Assert.isTrue(manufacturerModel.getManufacturer() == manufacturer, "设备厂商和型号不匹配"); checkName(save.getName()); + checkIpPort(save.getServiceIp(), save.getServicePort()); Device device = new Device(); BeanUtils.copyProperties(save, device); @@ -136,6 +137,14 @@ public class DeviceServiceImpl implements DeviceService { return device.getId(); } + private void checkIpPort(String serviceIp, Integer servicePort) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(Device::getServiceIp, serviceIp) + .eq(Device::getServicePort, servicePort); + Device exist = spDeviceMapper.selectOne(wrapper); + Assert.isNull(exist, "设备IP和端口已存在"); + } + @Override public void update(DeviceDTO.DeviceSave update) { Assert.notNull(update.getId(), "id不能为空"); @@ -145,13 +154,16 @@ public class DeviceServiceImpl implements DeviceService { if (!Objects.equals(exist.getName(), update.getName())) { checkName(update.getName()); } + if (!Objects.equals(exist.getServiceIp(), update.getServiceIp()) + || !Objects.equals(exist.getServicePort(), update.getServicePort())) { + checkIpPort(update.getServiceIp(), update.getServicePort()); + } Device up = new Device(); BeanUtils.copyProperties(update, up); up.setUpdateTime(LocalDateTime.now()); spDeviceMapper.updateById(up); - } @Override diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/task/DeviceTask.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/task/DeviceTask.java new file mode 100644 index 0000000..1a76244 --- /dev/null +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/task/DeviceTask.java @@ -0,0 +1,61 @@ +package com.sunyard.chsm.task; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.sunyard.chsm.mapper.SpDeviceMapper; +import com.sunyard.chsm.model.entity.Device; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.time.Duration; +import java.util.List; + +/** + * @author liulu + * @since 2024/11/4 + */ +@Component +public class DeviceTask implements InitializingBean { + + @Resource + private SpDeviceMapper spDeviceMapper; + + @Resource + private ThreadPoolTaskScheduler threadPoolTaskScheduler; + @Resource + private ThreadPoolTaskExecutor threadPoolTaskExecutor; + + + private void checkDeviceStatus() { + + for (int i = 1; i < 500; i++) { + Page devicePage = spDeviceMapper.selectPage( + new Page<>(i, 100L), + new LambdaQueryWrapper().orderByAsc(Device::getId) + ); + List records = devicePage.getRecords(); + if (CollectionUtils.isEmpty(records)) { + break; + } + + for (Device record : records) { + + + + + + } + } + + + } + + @Override + public void afterPropertiesSet() throws Exception { + threadPoolTaskScheduler.scheduleWithFixedDelay(this::checkDeviceStatus, Duration.ofMinutes(5L)); + } +} diff --git a/doc/ssp_dm.sql b/doc/ssp_dm.sql index 71028ca..424a6b9 100644 --- a/doc/ssp_dm.sql +++ b/doc/ssp_dm.sql @@ -11,6 +11,9 @@ CREATE TABLE sp_device ( manage_ip VARCHAR(30) COMMENT '管理ip', manage_port INT COMMENT '管理端口', access_credentials VARCHAR(1000) COMMENT '访问凭证', + connected TINYINT NOT NULL DEFAULT 0, + last_connected_time TIMESTAMP , + last_check_time TIMESTAMP , status VARCHAR(25) DEFAULT '' COMMENT '设备状态', group_id BIGINT NOT NULL DEFAULT 0 COMMENT '设备组id', group_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备组名称', @@ -21,6 +24,7 @@ CREATE TABLE sp_device ( PRIMARY KEY (id) ); +-- 设备组 CREATE TABLE sp_device_group ( id BIGINT NOT NULL COMMENT 'id', name VARCHAR(255) NOT NULL DEFAULT '' COMMENT '服务名称', @@ -30,6 +34,18 @@ CREATE TABLE sp_device_group ( PRIMARY KEY (id) ); +-- TMK +CREATE TABLE sp_tmk_info ( + id BIGINT NOT NULL COMMENT 'id', + device_serial VARCHAR(255) NOT NULL DEFAULT '' COMMENT '设备序列号', + enc_tmk VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'tmk密文', + pub_key VARCHAR(400) NOT NULL DEFAULT '' COMMENT '设备公钥', + remark VARCHAR(500) NOT NULL DEFAULT '' COMMENT '备注', + update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), + PRIMARY KEY (id) +); + -- 密码服务 CREATE TABLE sp_crypto_service ( id BIGINT NOT NULL COMMENT 'id',