diff --git a/chsm-common/src/main/java/com/sunyard/chsm/mapper/CaCertMapper.java b/chsm-common/src/main/java/com/sunyard/chsm/mapper/CaCertMapper.java new file mode 100644 index 0000000..3ab5be4 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/mapper/CaCertMapper.java @@ -0,0 +1,15 @@ +package com.sunyard.chsm.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.sunyard.chsm.model.entity.CaCert; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author liulu + * @since 2024/11/6 + */ +@Mapper +public interface CaCertMapper extends BaseMapper { + + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/CaCert.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/CaCert.java new file mode 100644 index 0000000..ca02ac2 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/CaCert.java @@ -0,0 +1,38 @@ +package com.sunyard.chsm.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Date; + +/** + * @author liulu + * @since 2024/11/11 + */ +@Data +@TableName("sp_ca_cert") +public class CaCert { + + private Long id; + private String caName; + private String caUrl; + // SM2, RSA + private String keyAlg; + private String status; + private String subject; + private String serialNumber; + private String issuerDn; + private Date notBefore; + private Date notAfter; + private String pubKey; + + private String certText; + + private String remark; + private LocalDateTime createTime; + private LocalDateTime updateTime; + + + +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/CaController.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/CaController.java new file mode 100644 index 0000000..ec3e385 --- /dev/null +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/CaController.java @@ -0,0 +1,87 @@ +package com.sunyard.chsm.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.sunyard.chsm.constant.AuditLogConst; +import com.sunyard.chsm.dto.CertDTO; +import com.sunyard.chsm.model.R; +import com.sunyard.chsm.service.CaCertService; +import com.sunyard.ssp.common.annotation.AuditControllerLog; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +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; + +/** + * CA管理接口 + * + * @author liulu + * @since 2024/11/11 + */ +@Slf4j +@RestController +@RequestMapping("/ca/cert") +public class CaController { + + @Resource + private CaCertService caCertService; + + /** + * 分页查询CA列表 + * + * @param query query + * @return 列表 + */ + @GetMapping("/pageList") + public R> queryPageList(CertDTO.CAQuery query) { + + Page page = caCertService.selectPageList(query); + + return R.data(page); + } + + /** + * 新增CA + * + * @param caInfo 证书 + */ + @PostMapping + public R save(@Valid @RequestBody CertDTO.CaInfo caInfo) { + Long id = caCertService.save(caInfo); + return R.data(id); + } + + /** + * 修改CA + * + * @param update 参数 + * @return void + */ + @PutMapping + @AuditControllerLog(description = "修改CA", operateType = AuditLogConst.UPDATE) + public R update(@Valid @RequestBody CertDTO.CaInfo update) { + caCertService.update(update); + return R.ok(); + } + + /** + * 删除CA + * + * @param id id + * @return void + */ + @DeleteMapping + @AuditControllerLog(description = "删除CA", operateType = AuditLogConst.DELETE) + public R delete(Long id) { + caCertService.delete(id); + return R.ok(); + } + + +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/TmkController.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/TmkController.java index 427f987..7f68423 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/TmkController.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/TmkController.java @@ -1,8 +1,10 @@ package com.sunyard.chsm.controller; +import com.sunyard.chsm.dto.TmkStatus; import com.sunyard.chsm.model.R; import com.sunyard.chsm.service.DeviceService; import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -23,6 +25,12 @@ public class TmkController { @Resource private DeviceService deviceService; + @GetMapping("/status") + public R getTMKStatus() { + TmkStatus status = deviceService.getTMKStatus(); + return R.data(status); + } + /** * 初始化主密钥 */ diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/CertDTO.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/CertDTO.java index 3033557..5f34871 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/CertDTO.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/CertDTO.java @@ -95,8 +95,53 @@ public abstract class CertDTO { * 加密密钥信封 */ private String envelopedKey; + private String remark; } + @Data + public static class CaInfo { + private Long id; + /** + * 密钥算法 目前支持: SM2 + */ + @NotEmpty(message = "密钥算法不能为空") + private String keyAlg; + @NotEmpty(message = "CA名称不能为空") + private String caName; + private String caUrl; + + /** + * 根证书内容 + */ + @NotEmpty(message = "根证书不能为空") + private String certText; + + private String remark; + } + + @EqualsAndHashCode(callSuper = true) + @Data + public static class CAQuery extends PageQuery { + + private String caName; + } + + @Data + public static class CAView { + private Long id; + private String caName; + private String caUrl; + /** + * 密钥算法 + */ + private String keyAlg; + /** + * 证书DN + */ + private String subject; + private String remark; + private LocalDateTime createTime; + } } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/TmkStatus.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/TmkStatus.java new file mode 100644 index 0000000..d5ea4a8 --- /dev/null +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/TmkStatus.java @@ -0,0 +1,22 @@ +package com.sunyard.chsm.dto; + +import lombok.Data; + +/** + * @author liulu + * @since 2024/11/11 + */ +@Data +public class TmkStatus { + + /** + * 是否已经存在设备 + */ + private boolean hasDevice; + /** + * 主密钥是否初始化 + */ + private boolean tmkInit; + + +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/CaCertService.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/CaCertService.java new file mode 100644 index 0000000..ead4a6f --- /dev/null +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/CaCertService.java @@ -0,0 +1,19 @@ +package com.sunyard.chsm.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.sunyard.chsm.dto.CertDTO; + +/** + * @author liulu + * @since 2024/11/11 + */ +public interface CaCertService { + + Page selectPageList(CertDTO.CAQuery query); + + Long save(CertDTO.CaInfo caInfo); + + void update(CertDTO.CaInfo update); + + void delete(Long id); +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/DeviceService.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/DeviceService.java index 86116ac..5faf30c 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/DeviceService.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/DeviceService.java @@ -2,6 +2,7 @@ package com.sunyard.chsm.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.sunyard.chsm.dto.DeviceDTO; +import com.sunyard.chsm.dto.TmkStatus; import java.util.List; @@ -22,6 +23,7 @@ public interface DeviceService { void delete(Long id); - void initTmk(); + TmkStatus getTMKStatus(); + void initTmk(); } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/CaCertServiceImpl.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/CaCertServiceImpl.java new file mode 100644 index 0000000..2929dd8 --- /dev/null +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/CaCertServiceImpl.java @@ -0,0 +1,143 @@ +package com.sunyard.chsm.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.sunyard.chsm.dto.CertDTO; +import com.sunyard.chsm.mapper.CaCertMapper; +import com.sunyard.chsm.model.entity.CaCert; +import com.sunyard.chsm.service.CaCertService; +import com.sunyard.chsm.utils.gm.BCECUtils; +import com.sunyard.chsm.utils.gm.cert.BCSM2CertUtils; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author liulu + * @since 2024/11/11 + */ +@Slf4j +@Service +@Transactional +public class CaCertServiceImpl implements CaCertService { + + @Resource + private CaCertMapper caCertMapper; + + + @Override + public Page selectPageList(CertDTO.CAQuery query) { + IPage page = caCertMapper.selectPage( + new Page<>(query.getPageNumber(), query.getPageSize()), + new LambdaQueryWrapper() + .like(StringUtils.hasText(query.getCaName()), CaCert::getCaName, query.getCaName()) + .orderByDesc(CaCert::getCreateTime) + ); + List records = page.getRecords(); + if (CollectionUtils.isEmpty(records)) { + return new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); + } + + List viewList = records.stream() + .map(it -> { + CertDTO.CAView view = new CertDTO.CAView(); + BeanUtils.copyProperties(it, view); + return view; + }) + .collect(Collectors.toList()); + + return new Page(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(viewList); + } + + @Override + public Long save(CertDTO.CaInfo caInfo) { + checkName(caInfo.getCaName()); + + X509Certificate x509Cert; + try { + x509Cert = BCSM2CertUtils.getX509Cert(caInfo.getCertText()); + } catch (Exception ex) { + throw new IllegalArgumentException("证书内容格式错误,无法解析"); + } + + CaCert cert = genCaCert(caInfo, x509Cert); + cert.setId(IdWorker.getId()); + cert.setCreateTime(LocalDateTime.now()); + caCertMapper.insert(cert); + + return cert.getId(); + } + + @Override + public void update(CertDTO.CaInfo update) { + Assert.notNull(update.getId(), "id不能为空"); + CaCert exist = caCertMapper.selectById(update.getId()); + Assert.notNull(exist, "id对应的CA不存在"); + + if (!Objects.equals(exist.getCaName(), update.getCaName())) { + checkName(update.getCaName()); + } + + X509Certificate x509Cert; + try { + x509Cert = BCSM2CertUtils.getX509Cert(update.getCertText()); + } catch (Exception ex) { + throw new IllegalArgumentException("证书内容格式错误,无法解析"); + } + + CaCert cert = genCaCert(update, x509Cert); + cert.setId(update.getId()); + cert.setUpdateTime(LocalDateTime.now()); + caCertMapper.updateById(cert); + } + + + private static CaCert genCaCert(CertDTO.CaInfo caInfo, X509Certificate x509Cert) { + + CaCert cert = new CaCert(); + cert.setKeyAlg(caInfo.getKeyAlg()); + cert.setCaName(caInfo.getCaName()); + cert.setCaUrl(caInfo.getCaUrl()); + cert.setCertText(caInfo.getCertText()); + cert.setRemark(caInfo.getRemark()); + + cert.setSubject(x509Cert.getSubjectX500Principal().getName()); + cert.setSerialNumber(x509Cert.getSerialNumber().toString()); + cert.setIssuerDn(x509Cert.getIssuerX500Principal().getName()); + cert.setNotBefore(x509Cert.getNotBefore()); + cert.setNotAfter(x509Cert.getNotAfter()); + + PublicKey publicKey = x509Cert.getPublicKey(); + String hexPubKey = BCECUtils.getHexPubKey((BCECPublicKey) publicKey); + cert.setPubKey(hexPubKey); + return cert; + } + + private void checkName(String name) { + + CaCert exist = caCertMapper.selectOne( + new LambdaQueryWrapper().eq(CaCert::getCaName, name) + ); + Assert.isNull(exist, "CA名称已经存在"); + } + + @Override + public void delete(Long id) { + caCertMapper.deleteById(id); + } +} 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 72cc3c3..1896c3f 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 @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.sunyard.chsm.dto.DeviceDTO; +import com.sunyard.chsm.dto.TmkStatus; import com.sunyard.chsm.enums.ManufacturerEnum; import com.sunyard.chsm.enums.ManufacturerModelEnum; import com.sunyard.chsm.mapper.SpDeviceMapper; @@ -181,6 +182,21 @@ public class DeviceServiceImpl implements DeviceService { spDeviceMapper.deleteById(id); } + @Override + public TmkStatus getTMKStatus() { + TmkStatus status = new TmkStatus(); + ParamConf tmkInit = paramConfMapper.selectByKey("tmk_init"); + if (tmkInit != null && "true".equals(tmkInit.getValue())) { + status.setHasDevice(true); + status.setTmkInit(false); + return status; + } + status.setTmkInit(false); + Long c = spDeviceMapper.selectCount(new LambdaQueryWrapper<>()); + status.setHasDevice(c > 0L); + return status; + } + @Override public void initTmk() { ParamConf tmkInit = paramConfMapper.selectByKey("tmk_init"); @@ -189,20 +205,20 @@ public class DeviceServiceImpl implements DeviceService { // LocalDateTime now = LocalDateTime.now(); // if (CollectionUtils.isEmpty(conned)) { - // - BcSdfApiAdaptor sdfApi = new BcSdfApiAdaptor(); - byte[] sk = sdfApi.generateRandom("", 16); + // + BcSdfApiAdaptor sdfApi = new BcSdfApiAdaptor(); + byte[] sk = sdfApi.generateRandom("", 16); - byte[] publicKey = sdfApi.exportEncPublicKeyECC("", 1); - byte[] encSk = sdfApi.externalEncryptECC("", publicKey, sk); + byte[] publicKey = sdfApi.exportEncPublicKeyECC("", 1); + byte[] encSk = sdfApi.externalEncryptECC("", publicKey, sk); - TmkInfo info = new TmkInfo(); - info.setId(IdWorker.getId()); - info.setCreateTime(now); - info.setDeviceSerial(sdfApi.getDeviceInfo("").getDeviceSerial()); - info.setEncTmk(Hex.toHexString(encSk)); - info.setPubKey(Hex.toHexString(publicKey)); - tmkInfoMapper.insert(info); + TmkInfo info = new TmkInfo(); + info.setId(IdWorker.getId()); + info.setCreateTime(now); + info.setDeviceSerial(sdfApi.getDeviceInfo("").getDeviceSerial()); + info.setEncTmk(Hex.toHexString(encSk)); + info.setPubKey(Hex.toHexString(publicKey)); + tmkInfoMapper.insert(info); // return; // } // Device device = conned.iterator().next(); diff --git a/doc/ssp_dm.sql b/doc/ssp_dm.sql index d147470..163db98 100644 --- a/doc/ssp_dm.sql +++ b/doc/ssp_dm.sql @@ -189,7 +189,7 @@ CREATE TABLE sp_key_csr ( create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), PRIMARY KEY (id) ); --- 证书 +-- 应用证书 CREATE TABLE sp_app_cert ( id BIGINT NOT NULL COMMENT 'id', application_id BIGINT NOT NULL COMMENT '应用id', @@ -213,4 +213,23 @@ CREATE TABLE sp_app_cert ( update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(), PRIMARY KEY (id) +); +-- CA证书 +CREATE TABLE sp_ca_cert ( + id BIGINT NOT NULL COMMENT 'id', + ca_name VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'ca name', + ca_url VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'url', + key_alg VARCHAR(30) NOT NULL DEFAULT '' COMMENT '密钥算法', + status VARCHAR(30) DEFAULT '' COMMENT '状态', + subject VARCHAR(255) NOT NULL DEFAULT '' COMMENT 'DN', + serial_number VARCHAR(255) NOT NULL DEFAULT '' COMMENT '证书号', + issuer_dn VARCHAR(255) NOT NULL DEFAULT '' COMMENT '颁发者', + not_before TIMESTAMP NOT NULL COMMENT '开始时间', + not_after TIMESTAMP NOT NULL COMMENT '结束时间', + pub_key VARCHAR(255) NOT NULL DEFAULT '' COMMENT '公钥', + cert_text VARCHAR(4099) 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) ); \ No newline at end of file