证书管理
This commit is contained in:
parent
fdcc750eee
commit
e4af8ecbf4
@ -2,7 +2,7 @@ package com.sunyard.chsm.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.sunyard.chsm.model.entity.Cert;
|
||||
import com.sunyard.chsm.model.entity.AppCert;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@ -14,13 +14,13 @@ import java.util.List;
|
||||
* @since 2024/11/6
|
||||
*/
|
||||
@Mapper
|
||||
public interface CertMapper extends BaseMapper<Cert> {
|
||||
public interface AppCertMapper extends BaseMapper<AppCert> {
|
||||
|
||||
|
||||
default Cert selectBySN(String sn) {
|
||||
default AppCert selectBySN(String sn) {
|
||||
Assert.hasText(sn, "证书序列号不能为空");
|
||||
List<Cert> certs = selectList(new LambdaQueryWrapper<Cert>()
|
||||
.eq(Cert::getSerialNumber, sn)
|
||||
List<AppCert> certs = selectList(new LambdaQueryWrapper<AppCert>()
|
||||
.eq(AppCert::getSerialNumber, sn)
|
||||
);
|
||||
if (CollectionUtils.isEmpty(certs)) {
|
||||
return null;
|
@ -11,8 +11,8 @@ import java.util.Date;
|
||||
* @since 2024/11/6
|
||||
*/
|
||||
@Data
|
||||
@TableName("sp_cert")
|
||||
public class Cert {
|
||||
@TableName("sp_app_cert")
|
||||
public class AppCert {
|
||||
|
||||
private Long id;
|
||||
private Long applicationId;
|
||||
@ -22,6 +22,7 @@ public class Cert {
|
||||
private String keyAlg;
|
||||
// ENC, SIGN, CERT_CHAIN
|
||||
private String certType;
|
||||
private String status;
|
||||
private Boolean single;
|
||||
private String version;
|
||||
private String subject;
|
@ -0,0 +1,57 @@
|
||||
package com.sunyard.chsm.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunyard.chsm.dto.CertDTO;
|
||||
import com.sunyard.chsm.model.R;
|
||||
import com.sunyard.chsm.service.AppCertService;
|
||||
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.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 应用证书管理接口
|
||||
*
|
||||
* @author liulu
|
||||
* @since 2024/11/6
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/app/cert")
|
||||
public class AppCertController {
|
||||
|
||||
@Resource
|
||||
private AppCertService appCertService;
|
||||
|
||||
/**
|
||||
* 分页查询应用证书列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @return 应用列表
|
||||
*/
|
||||
@GetMapping("/pageList")
|
||||
public R<Page<CertDTO.ACView>> queryPageList(CertDTO.Query query) {
|
||||
|
||||
Page<CertDTO.ACView> page = appCertService.selectPageList(query);
|
||||
|
||||
return R.data(page);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 导入证书
|
||||
*
|
||||
* @param importCert 证书
|
||||
*/
|
||||
@PostMapping("/import")
|
||||
public void importCert(@Valid @RequestBody CertDTO.ImportCert importCert) {
|
||||
appCertService.importCert(importCert);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
package com.sunyard.chsm.controller;
|
||||
|
||||
import com.sunyard.chsm.dto.CertDTO;
|
||||
import com.sunyard.chsm.service.CertService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 应用证书管理接口
|
||||
*
|
||||
* @author liulu
|
||||
* @since 2024/11/6
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping
|
||||
public class CertController {
|
||||
|
||||
@Resource
|
||||
private CertService certService;
|
||||
|
||||
|
||||
/**
|
||||
* 导入证书
|
||||
*
|
||||
* @param importCert 证书
|
||||
*/
|
||||
@PostMapping("/app/cert/import")
|
||||
public void importCert(@Valid @RequestBody CertDTO.ImportCert importCert) {
|
||||
certService.importCert(importCert);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,9 +1,13 @@
|
||||
package com.sunyard.chsm.dto;
|
||||
|
||||
import com.sunyard.chsm.model.PageQuery;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -11,6 +15,53 @@ import javax.validation.constraints.NotNull;
|
||||
*/
|
||||
public abstract class CertDTO {
|
||||
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public static class Query extends PageQuery {
|
||||
|
||||
private Long appId;
|
||||
/**
|
||||
* 证书类型: encrypt_decrypt 加密证书, sign_verify 签名证书
|
||||
*/
|
||||
private String certType;
|
||||
}
|
||||
|
||||
|
||||
@Data
|
||||
public static class ACView {
|
||||
private Long id;
|
||||
private Long applicationId;
|
||||
private String appName;
|
||||
/**
|
||||
* 密钥算法
|
||||
*/
|
||||
private String keyAlg;
|
||||
// 证书类型
|
||||
private String certType;
|
||||
private String certTypeText;
|
||||
/**
|
||||
* 证书DN
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* 序列号
|
||||
*/
|
||||
private String serialNumber;
|
||||
/**
|
||||
* 颁发着
|
||||
*/
|
||||
private String issuerDn;
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
private Date notBefore;
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
private Date notAfter;
|
||||
private String remark;
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ImportCert {
|
||||
|
@ -1,13 +1,15 @@
|
||||
package com.sunyard.chsm.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.sunyard.chsm.dto.CertDTO;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
* @since 2024/11/6
|
||||
*/
|
||||
public interface CertService {
|
||||
public interface AppCertService {
|
||||
|
||||
Page<CertDTO.ACView> selectPageList(CertDTO.Query query);
|
||||
|
||||
void importCert(CertDTO.ImportCert importCert);
|
||||
|
||||
}
|
@ -1,17 +1,22 @@
|
||||
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.extension.plugins.pagination.Page;
|
||||
import com.sunyard.chsm.dto.CertDTO;
|
||||
import com.sunyard.chsm.enums.KeyCategory;
|
||||
import com.sunyard.chsm.enums.KeyStatus;
|
||||
import com.sunyard.chsm.enums.KeyUsage;
|
||||
import com.sunyard.chsm.mapper.CertMapper;
|
||||
import com.sunyard.chsm.mapper.AppCertMapper;
|
||||
import com.sunyard.chsm.mapper.ApplicationMapper;
|
||||
import com.sunyard.chsm.mapper.KeyInfoMapper;
|
||||
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
|
||||
import com.sunyard.chsm.model.entity.Cert;
|
||||
import com.sunyard.chsm.model.entity.AppCert;
|
||||
import com.sunyard.chsm.model.entity.Application;
|
||||
import com.sunyard.chsm.model.entity.KeyInfo;
|
||||
import com.sunyard.chsm.model.entity.KeyRecord;
|
||||
import com.sunyard.chsm.sdf.SdfApiService;
|
||||
import com.sunyard.chsm.service.CertService;
|
||||
import com.sunyard.chsm.service.AppCertService;
|
||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM2Utils;
|
||||
import com.sunyard.chsm.utils.gm.BCSM4Utils;
|
||||
@ -29,10 +34,13 @@ import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
|
||||
import org.bouncycastle.util.BigIntegers;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.util.Pair;
|
||||
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.math.BigInteger;
|
||||
@ -42,7 +50,11 @@ import java.security.cert.X509Certificate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author liulu
|
||||
@ -51,10 +63,12 @@ import java.util.Objects;
|
||||
@Slf4j
|
||||
@Service
|
||||
@Transactional
|
||||
public class CertServiceImpl implements CertService {
|
||||
public class AppCertServiceImpl implements AppCertService {
|
||||
|
||||
@Resource
|
||||
private CertMapper certMapper;
|
||||
private AppCertMapper appCertMapper;
|
||||
@Resource
|
||||
private ApplicationMapper applicationMapper;
|
||||
@Resource
|
||||
private SpKeyRecordMapper spKeyRecordMapper;
|
||||
@Resource
|
||||
@ -62,6 +76,45 @@ public class CertServiceImpl implements CertService {
|
||||
@Resource
|
||||
private SdfApiService sdfApiService;
|
||||
|
||||
@Override
|
||||
public Page<CertDTO.ACView> selectPageList(CertDTO.Query query) {
|
||||
|
||||
IPage<AppCert> page = appCertMapper.selectPage(
|
||||
new Page<>(query.getPageNumber(), query.getPageSize()),
|
||||
new LambdaQueryWrapper<AppCert>()
|
||||
.eq(Objects.nonNull(query.getAppId()), AppCert::getApplicationId, query.getAppId())
|
||||
.eq(StringUtils.hasText(query.getCertType()), AppCert::getCertType, query.getCertType())
|
||||
.orderByDesc(AppCert::getCreateTime)
|
||||
);
|
||||
List<AppCert> records = page.getRecords();
|
||||
if (CollectionUtils.isEmpty(records)) {
|
||||
return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
|
||||
}
|
||||
List<Long> appIds = records.stream().map(AppCert::getApplicationId).collect(Collectors.toList());
|
||||
Map<Long, String> appNameMap = applicationMapper.selectBatchIds(appIds)
|
||||
.stream().collect(Collectors.toMap(Application::getId, Application::getName));
|
||||
|
||||
List<CertDTO.ACView> viewList = records.stream()
|
||||
.map(it -> {
|
||||
CertDTO.ACView view = new CertDTO.ACView();
|
||||
BeanUtils.copyProperties(it, view);
|
||||
Optional.ofNullable(it.getApplicationId()).map(appNameMap::get).ifPresent(view::setAppName);
|
||||
KeyUsage keyUsage = KeyUsage.valueOf(it.getCertType().toUpperCase());
|
||||
switch (keyUsage) {
|
||||
case ENCRYPT_DECRYPT:
|
||||
view.setCertTypeText("加密证书");
|
||||
break;
|
||||
case SIGN_VERIFY:
|
||||
view.setCertTypeText("签名证书");
|
||||
break;
|
||||
}
|
||||
return view;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return new Page<CertDTO.ACView>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(viewList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void importCert(CertDTO.ImportCert importCert) {
|
||||
if (importCert.getSingle()) {
|
||||
@ -98,11 +151,11 @@ public class CertServiceImpl implements CertService {
|
||||
&& now.isBefore(keyInfo.getExpiredTime()),
|
||||
"此证书对应的密钥ID:" + keyInfo.getId() + "不是启用状态,无法导入");
|
||||
|
||||
Cert exist = certMapper.selectBySN(x509Cert.getSerialNumber().toString());
|
||||
AppCert exist = appCertMapper.selectBySN(x509Cert.getSerialNumber().toString());
|
||||
Assert.isNull(exist, "此证书已经存在");
|
||||
|
||||
Cert cert = genCert(x509Cert, keyInfo.getApplicationId(), record, importCert);
|
||||
certMapper.insert(cert);
|
||||
AppCert cert = genCert(x509Cert, keyInfo.getApplicationId(), record, importCert);
|
||||
appCertMapper.insert(cert);
|
||||
}
|
||||
|
||||
|
||||
@ -140,28 +193,28 @@ public class CertServiceImpl implements CertService {
|
||||
}
|
||||
Assert.isTrue(Objects.equals(encPkHex, keys.getFirst()), "加密证书和私钥不匹配");
|
||||
|
||||
Cert exist = certMapper.selectBySN(signCert.getSerialNumber().toString());
|
||||
AppCert exist = appCertMapper.selectBySN(signCert.getSerialNumber().toString());
|
||||
Assert.isNull(exist, "签名证书已经存在");
|
||||
exist = certMapper.selectBySN(encCert.getSerialNumber().toString());
|
||||
exist = appCertMapper.selectBySN(encCert.getSerialNumber().toString());
|
||||
Assert.isNull(exist, "加密证书已经存在");
|
||||
|
||||
importCert.setCertType(KeyUsage.SIGN_VERIFY.getCode());
|
||||
importCert.setCertText(importCert.getSignCertText());
|
||||
Cert sign = genCert(signCert, keyInfo.getApplicationId(), record, importCert);
|
||||
certMapper.insert(sign);
|
||||
AppCert sign = genCert(signCert, keyInfo.getApplicationId(), record, importCert);
|
||||
appCertMapper.insert(sign);
|
||||
|
||||
importCert.setCertType(KeyUsage.ENCRYPT_DECRYPT.getCode());
|
||||
importCert.setCertText(importCert.getEncCertText());
|
||||
Cert enc = genCert(encCert, keyInfo.getApplicationId(), record, importCert);
|
||||
AppCert enc = genCert(encCert, keyInfo.getApplicationId(), record, importCert);
|
||||
enc.setPubKey(keys.getFirst());
|
||||
byte[] encPri = sdfApiService.encryptByTMK(keys.getSecond());
|
||||
enc.setEncPriKey(Hex.toHexString(encPri));
|
||||
certMapper.insert(enc);
|
||||
appCertMapper.insert(enc);
|
||||
|
||||
}
|
||||
|
||||
private Cert genCert(X509Certificate x509Cert, Long appId, KeyRecord record, CertDTO.ImportCert importCert) {
|
||||
Cert cert = new Cert();
|
||||
private AppCert genCert(X509Certificate x509Cert, Long appId, KeyRecord record, CertDTO.ImportCert importCert) {
|
||||
AppCert cert = new AppCert();
|
||||
cert.setApplicationId(appId);
|
||||
cert.setKeyId(record.getKeyId());
|
||||
cert.setKeyRecordId(record.getId());
|
@ -168,7 +168,7 @@ public class KeyInfoServiceImpl implements KeyInfoService {
|
||||
Assert.notNull(keyTemplate, "密钥模版不存在");
|
||||
Application app = applicationMapper.selectById(save.getApplicationId());
|
||||
Assert.notNull(app, "所属应用不存在");
|
||||
Assert.isTrue(EnableStatus.DISABLED.getCode().equals(app.getStatus()), "应用不是启用状态");
|
||||
Assert.isTrue(EnableStatus.ENABLED.getCode().equals(app.getStatus()), "应用不是启用状态");
|
||||
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
|
@ -54,6 +54,9 @@ spring:
|
||||
max-file-size: 50MB
|
||||
#默认上传文件总大小是10MB
|
||||
max-request-size: 50MB
|
||||
jackson:
|
||||
time-zone: GMT+8
|
||||
date-format: yyyy-MM-dd HH:mm:ss
|
||||
#开启切面
|
||||
aop:
|
||||
proxy-target-class: true
|
||||
|
@ -160,6 +160,7 @@ CREATE TABLE sp_key_record (
|
||||
key_id BIGINT NOT NULL COMMENT '密钥id',
|
||||
key_index VARCHAR(100) NOT NULL DEFAULT '' COMMENT '密钥索引',
|
||||
key_data VARCHAR(255) NOT NULL DEFAULT '' COMMENT '密钥密文',
|
||||
pub_idx VARCHAR(10) NOT NULL DEFAULT '' COMMENT '公钥',
|
||||
pub_key VARCHAR(400) NOT NULL DEFAULT '' COMMENT '公钥',
|
||||
check_alg VARCHAR(30) NOT NULL DEFAULT '' COMMENT '校验算法',
|
||||
check_value VARCHAR(255) NOT NULL DEFAULT '' COMMENT '校验值',
|
||||
@ -170,6 +171,8 @@ CREATE TABLE sp_key_record (
|
||||
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
CREATE INDEX idx_kid ON sp_key_record(key_id);
|
||||
CREATE INDEX idx_pk ON sp_key_record(pub_idx);
|
||||
|
||||
-- 证书请求记录
|
||||
CREATE TABLE sp_key_csr (
|
||||
@ -185,4 +188,29 @@ CREATE TABLE sp_key_csr (
|
||||
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP(),
|
||||
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',
|
||||
key_id BIGINT NOT NULL COMMENT '密钥id',
|
||||
key_record_id BIGINT NOT NULL COMMENT '密钥记录id',
|
||||
key_alg VARCHAR(30) NOT NULL DEFAULT '' COMMENT '密钥算法',
|
||||
cert_type VARCHAR(30) NOT NULL DEFAULT '' COMMENT '证书类型,加密|签名',
|
||||
status VARCHAR(30) DEFAULT '' COMMENT '状态',
|
||||
single TINYINT NOT NULL DEFAULT 0 COMMENT '是否单证',
|
||||
version VARCHAR(10) NOT NULL 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 '结束时间',
|
||||
key_usage VARCHAR(200) NOT NULL DEFAULT '' COMMENT '密钥用途',
|
||||
pub_key VARCHAR(255) NOT NULL DEFAULT '' COMMENT '公钥',
|
||||
enc_pri_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)
|
||||
);
|
Loading…
Reference in New Issue
Block a user