This commit is contained in:
liulu 2024-11-12 14:50:37 +08:00
parent c18cfda28b
commit a9b35329ce
11 changed files with 428 additions and 14 deletions

View File

@ -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<CaCert> {
}

View File

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

View File

@ -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<Page<CertDTO.CAView>> queryPageList(CertDTO.CAQuery query) {
Page<CertDTO.CAView> page = caCertService.selectPageList(query);
return R.data(page);
}
/**
* 新增CA
*
* @param caInfo 证书
*/
@PostMapping
public R<Long> 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<Void> 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<Void> delete(Long id) {
caCertService.delete(id);
return R.ok();
}
}

View File

@ -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<TmkStatus> getTMKStatus() {
TmkStatus status = deviceService.getTMKStatus();
return R.data(status);
}
/**
* 初始化主密钥
*/

View File

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

View File

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

View File

@ -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<CertDTO.CAView> selectPageList(CertDTO.CAQuery query);
Long save(CertDTO.CaInfo caInfo);
void update(CertDTO.CaInfo update);
void delete(Long id);
}

View File

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

View File

@ -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<CertDTO.CAView> selectPageList(CertDTO.CAQuery query) {
IPage<CaCert> page = caCertMapper.selectPage(
new Page<>(query.getPageNumber(), query.getPageSize()),
new LambdaQueryWrapper<CaCert>()
.like(StringUtils.hasText(query.getCaName()), CaCert::getCaName, query.getCaName())
.orderByDesc(CaCert::getCreateTime)
);
List<CaCert> records = page.getRecords();
if (CollectionUtils.isEmpty(records)) {
return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
}
List<CertDTO.CAView> viewList = records.stream()
.map(it -> {
CertDTO.CAView view = new CertDTO.CAView();
BeanUtils.copyProperties(it, view);
return view;
})
.collect(Collectors.toList());
return new Page<CertDTO.CAView>(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<CaCert>().eq(CaCert::getCaName, name)
);
Assert.isNull(exist, "CA名称已经存在");
}
@Override
public void delete(Long id) {
caCertMapper.deleteById(id);
}
}

View File

@ -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");

View File

@ -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',
@ -214,3 +214,22 @@ CREATE TABLE sp_app_cert (
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)
);