密钥管理接口

This commit is contained in:
liulu 2024-10-29 09:55:18 +08:00
parent 6d03864e23
commit be088194cc
8 changed files with 438 additions and 65 deletions

View File

@ -14,10 +14,10 @@ import java.util.Objects;
@AllArgsConstructor
public enum KeyStatus {
ENABLED("enabled", "启用"),
DISABLED("disabled", ""),
ENABLED("enabled", "启用"),
DISABLED("disabled", ""),
ARCHIVED("archived", "已归档"),
DELETED("deleted", "已销毁"),
DESTORY("destory", "已销毁"),
;
private final String code;
private final String desc;

View File

@ -1,8 +0,0 @@
package com.sunyard.chsm.controller;
/**
* @author liulu
* @since 2024/10/23
*/
public class AsymKeyController {
}

View File

@ -0,0 +1,41 @@
package com.sunyard.chsm.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.dto.KeyInfoDTO;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.model.R;
import com.sunyard.chsm.service.KeyInfoService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* 非对称密钥管理接口
* @author liulu
* @since 2024/10/28
*/
@RestController
@RequestMapping("/key/info/asym")
public class KeyInfoAsymController {
@Resource
private KeyInfoService keyInfoService;
/**
* 分页查询非对称密钥列表
*
* @param query 查询条件
* @return 分页列表
*/
@GetMapping("/pageList")
public R<Page<KeyInfoDTO.View>> aymPageList(KeyInfoDTO.Query query) {
query.setKeyType(KeyCategory.ASYM_KEY.getCode());
Page<KeyInfoDTO.View> page = keyInfoService.selectPageList(query);
return R.data(page);
}
}

View File

@ -0,0 +1,129 @@
package com.sunyard.chsm.controller;
import com.sunyard.chsm.dto.KeyInfoDTO;
import com.sunyard.chsm.enums.KeyStatus;
import com.sunyard.chsm.model.Option;
import com.sunyard.chsm.model.R;
import com.sunyard.chsm.service.KeyInfoService;
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;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 密钥管理公共接口
*
* @author liulu
* @since 2024/10/23
*/
@RestController
@RequestMapping("/key/info")
public class KeyInfoController {
@Resource
private KeyInfoService keyInfoService;
/**
* 获取密钥状态选项
*/
@GetMapping("/statusList")
public R<List<Option>> getStatusList() {
List<Option> options = Arrays.stream(KeyStatus.values())
.map(it -> new Option(it.getCode(), it.getDesc()))
.collect(Collectors.toList());
return R.data(options);
}
/**
* 创建密钥
*
* @param save 参数
* @return id
*/
@PostMapping("/create")
public R<String> save(@Valid @RequestBody KeyInfoDTO.Save save) {
Long id = keyInfoService.save(save);
return R.data(String.valueOf(id));
}
/**
* 更新密钥
*
* @param update 参数
* @return id
*/
@PostMapping("/update")
public R<Void> save(@Valid @RequestBody KeyInfoDTO.Update update) {
keyInfoService.update(update);
return R.ok();
}
/**
* 启用密钥
*
* @param param 密钥id
* @return id
*/
@PostMapping("/enable")
public R<Void> enableKey(@Valid @RequestBody KeyInfoDTO.IDs param) {
keyInfoService.enableKey(param.getIds());
return R.ok();
}
/**
* 停用密钥
*
* @param param 密钥id
* @return id
*/
@PostMapping("/disable")
public R<Void> disableKey(@Valid @RequestBody KeyInfoDTO.IDs param) {
keyInfoService.disableKey(param.getIds());
return R.ok();
}
/**
* 归档密钥
*
* @param param 密钥id
* @return id
*/
@PostMapping("/archive")
public R<Void> archiveKey(@Valid @RequestBody KeyInfoDTO.IDs param) {
keyInfoService.archiveKey(param.getIds());
return R.ok();
}
/**
* 恢复密钥
*
* @param param 密钥id
* @return id
*/
@PostMapping("/recovery")
public R<Void> recoveryKey(@Valid @RequestBody KeyInfoDTO.IDs param) {
keyInfoService.recoveryKey(param.getIds());
return R.ok();
}
/**
* 销毁密钥
*
* @param param 密钥id
* @return id
*/
@PostMapping("/destroy")
public R<Void> destroyKey(@Valid @RequestBody KeyInfoDTO.IDs param) {
keyInfoService.destroyKey(param.getIds());
return R.ok();
}
}

View File

@ -3,8 +3,6 @@ package com.sunyard.chsm.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.dto.KeyInfoDTO;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.enums.KeyStatus;
import com.sunyard.chsm.model.Option;
import com.sunyard.chsm.model.R;
import com.sunyard.chsm.service.KeyInfoService;
import org.springframework.web.bind.annotation.GetMapping;
@ -15,34 +13,20 @@ import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
/**
* 对称密钥管理接口
*
* @author liulu
* @since 2024/10/23
* @since 2024/10/28
*/
@RestController
@RequestMapping("/sym/key")
public class SymKeyController {
@RequestMapping("/key/info/sym")
public class KeyInfoSymController {
@Resource
private KeyInfoService keyInfoService;
/**
* 获取密钥状态列表
*/
@GetMapping("/statusList")
public R<List<Option>> getStatusList() {
List<Option> options = Arrays.stream(KeyStatus.values())
.map(it -> new Option(it.getCode(), it.getDesc()))
.collect(Collectors.toList());
return R.data(options);
}
/**
* 分页查询对称密钥列表
@ -51,7 +35,7 @@ public class SymKeyController {
* @return 分页列表
*/
@GetMapping("/pageList")
public R<Page<KeyInfoDTO.View>> servicePageList(KeyInfoDTO.Query query) {
public R<Page<KeyInfoDTO.View>> symPageList(KeyInfoDTO.Query query) {
query.setKeyType(KeyCategory.SYM_KEY.getCode());
Page<KeyInfoDTO.View> page = keyInfoService.selectPageList(query);
@ -59,15 +43,13 @@ public class SymKeyController {
}
/**
* 创建密钥
* 密钥备份
*
* @param save 参数
* @return id
* @param backup 参数
*/
@PostMapping
public R<String> save(@Valid @RequestBody KeyInfoDTO.Save save) {
Long id = keyInfoService.save(save);
return R.data(String.valueOf(id));
@PostMapping("/backup")
public void backupKey(@Valid @RequestBody KeyInfoDTO.Backup backup) {
}

View File

@ -5,7 +5,9 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.Max;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@ -39,7 +41,6 @@ public abstract class KeyInfoDTO {
private Integer genNumber;
}
@Data
public static class View {
private Long id;
@ -73,4 +74,44 @@ public abstract class KeyInfoDTO {
private LocalDateTime expiredTime;
private LocalDateTime createTime;
}
@Data
public static class IDs {
/**
* 密钥id列表
*/
@NotEmpty(message = "密钥id列表不能为空")
private List<Long> ids;
}
@Data
public static class Update {
/**
* 密钥id列表
*/
@NotEmpty(message = "密钥id列表不能为空")
private List<Long> ids;
/**
* 新密钥生效时间 yyyy-MM-dd
*/
private LocalDate effectiveTime;
}
@Data
public static class Backup {
/**
* 开始日期 yyyy-MM-dd
*/
@NotNull(message = "开始日期不能为空")
private LocalDate startTime;
/**
* 结束日期 yyyy-MM-dd
*/
@NotNull(message = "结束日期不能为空")
private LocalDate endTime;
}
}

View File

@ -3,6 +3,8 @@ package com.sunyard.chsm.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.dto.KeyInfoDTO;
import java.util.List;
/**
* @author liulu
* @since 2024/10/23
@ -13,4 +15,15 @@ public interface KeyInfoService {
Long save(KeyInfoDTO.Save save);
void update(KeyInfoDTO.Update update);
void enableKey(List<Long> ids);
void disableKey(List<Long> ids);
void archiveKey(List<Long> ids);
void recoveryKey(List<Long> ids);
void destroyKey(List<Long> ids);
}

View File

@ -1,6 +1,7 @@
package com.sunyard.chsm.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@ -28,10 +29,12 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@ -125,36 +128,208 @@ public class KeyInfoServiceImpl implements KeyInfoService {
info.setExpiredTime(info.getEffectiveTime().plusSeconds(validUnit.getDuration().getSeconds() * keyTemplate.getValidTime()));
// 具体使用的密钥值
KeyRecord record = new KeyRecord();
record.setId(IdWorker.getId());
record.setKeyId(info.getId());
record.setKeyIndex(String.valueOf(record.getId()));
record.setCheckAlg(keyTemplate.getCheckAlg());
record.setEffectiveTime(info.getEffectiveTime());
record.setExpiredTime(info.getExpiredTime());
record.setCreateTime(LocalDateTime.now());
if (KeyCategory.SYM_KEY.getCode().equals(keyTemplate.getKeyType())) {
byte[] symKey = sdfApiService.generateRandom(16);
record.setKeyData(Hex.encodeHexString(symKey));
String checkHash = Hex.encodeHexString(sdfApiService.hash(symKey));
record.setCheckValue(checkHash);
info.setCheckValue(checkHash);
} else {
EccKey eccKey = sdfApiService.genKeyPairEcc();
byte[] d = eccKey.getPriKey().getD();
record.setKeyData(Hex.encodeHexString(d));
String checkHash = Hex.encodeHexString(sdfApiService.hash(d));
record.setCheckValue(checkHash);
info.setCheckValue(checkHash);
byte[] pubKeyBytes = eccKey.getPubKey().getPubKeyBytes();
record.setPubKey(Hex.encodeHexString(pubKeyBytes));
}
KeyRecord record = genKeyRecord(info);
info.setCheckValue(record.getCheckValue());
keyInfoMapper.insert(info);
spKeyRecordMapper.insert(record);
}
return 0L;
}
@Transactional
@Override
public void update(KeyInfoDTO.Update update) {
List<Long> ids = update.getIds();
if (CollectionUtils.isEmpty(ids)) {
return;
}
LocalDateTime now = LocalDateTime.now();
LocalDateTime newEffectTime = LocalDateTime.of(update.getEffectiveTime(), LocalTime.MIN);
Assert.isTrue(now.isBefore(newEffectTime), "生效日期必须在当前时间之后");
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
if (CollectionUtils.isEmpty(keyInfos)) {
log.warn("enableKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
return;
}
List<String> unNormalCodes = keyInfos.stream()
.filter(it -> now.isBefore(it.getEffectiveTime())
|| now.isAfter(it.getExpiredTime())
|| newEffectTime.isAfter(it.getExpiredTime())
|| !Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus())
)
.map(KeyInfo::getCode)
.collect(Collectors.toList());
Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
"密钥id: " + String.join(",", unNormalCodes) + "不是启用状态或者新生效时间超过密钥过期时间, 无法更新");
// 更新实际密钥值
spKeyRecordMapper.update(
new LambdaUpdateWrapper<KeyRecord>()
.set(KeyRecord::getExpiredTime, newEffectTime)
.in(KeyRecord::getKeyId, ids)
.lt(KeyRecord::getEffectiveTime, now)
.gt(KeyRecord::getExpiredTime, now)
);
for (KeyInfo info : keyInfos) {
// 具体使用的密钥值
KeyRecord record = genKeyRecord(info);
KeyInfo upInfo = new KeyInfo();
upInfo.setId(info.getId());
upInfo.setCheckValue(record.getCheckValue());
keyInfoMapper.updateById(upInfo);
spKeyRecordMapper.insert(record);
}
}
private KeyRecord genKeyRecord(KeyInfo info) {
KeyRecord record = new KeyRecord();
record.setId(IdWorker.getId());
record.setKeyId(info.getId());
record.setKeyIndex(String.valueOf(record.getId()));
record.setCheckAlg(info.getCheckAlg());
record.setEffectiveTime(info.getEffectiveTime());
record.setExpiredTime(info.getExpiredTime());
record.setCreateTime(LocalDateTime.now());
if (KeyCategory.SYM_KEY.getCode().equals(info.getKeyType())) {
byte[] symKey = sdfApiService.generateRandom(16);
record.setKeyData(Hex.encodeHexString(symKey));
String checkHash = Hex.encodeHexString(sdfApiService.hash(symKey));
record.setCheckValue(checkHash);
} else {
EccKey eccKey = sdfApiService.genKeyPairEcc();
byte[] d = eccKey.getPriKey().getD();
record.setKeyData(Hex.encodeHexString(d));
String checkHash = Hex.encodeHexString(sdfApiService.hash(d));
record.setCheckValue(checkHash);
byte[] pubKeyBytes = eccKey.getPubKey().getPubKeyBytes();
record.setPubKey(Hex.encodeHexString(pubKeyBytes));
}
return record;
}
@Override
public void enableKey(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return;
}
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
if (CollectionUtils.isEmpty(keyInfos)) {
log.warn("enableKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
return;
}
List<String> unNormalCodes = keyInfos.stream()
.filter(it -> !Objects.equals(KeyStatus.DISABLED.getCode(), it.getStatus()))
.map(KeyInfo::getCode)
.collect(Collectors.toList());
Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
"密钥id: " + String.join(",", unNormalCodes) + "不是已停用状态, 无法启用");
keyInfoMapper.update(
new LambdaUpdateWrapper<KeyInfo>()
.set(KeyInfo::getStatus, KeyStatus.ENABLED.getCode())
.in(KeyInfo::getId, ids)
);
}
@Override
public void disableKey(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return;
}
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
if (CollectionUtils.isEmpty(keyInfos)) {
log.warn("disableKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
return;
}
List<String> unNormalCodes = keyInfos.stream()
.filter(it -> !Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus()))
.map(KeyInfo::getCode)
.collect(Collectors.toList());
Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
"密钥id: " + String.join(",", unNormalCodes) + "不是启用状态, 无法停用");
keyInfoMapper.update(
new LambdaUpdateWrapper<KeyInfo>()
.set(KeyInfo::getStatus, KeyStatus.DISABLED.getCode())
.in(KeyInfo::getId, ids)
);
}
@Override
public void archiveKey(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return;
}
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
if (CollectionUtils.isEmpty(keyInfos)) {
log.warn("archiveKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
return;
}
// List<String> unNormalCodes = keyInfos.stream()
// .filter(it -> !Objects.equals(KeyStatus.ENABLED.getCode(), it.getStatus()))
// .map(KeyInfo::getCode)
// .collect(Collectors.toList());
// Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
// "密钥id: " + String.join(",", unNormalCodes) + "不是启用状态, 无法停用");
keyInfoMapper.update(
new LambdaUpdateWrapper<KeyInfo>()
.set(KeyInfo::getStatus, KeyStatus.ARCHIVED.getCode())
.in(KeyInfo::getId, ids)
);
}
@Override
public void recoveryKey(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return;
}
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
if (CollectionUtils.isEmpty(keyInfos)) {
log.warn("recoveryKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
return;
}
List<String> unNormalCodes = keyInfos.stream()
.filter(it -> !Objects.equals(KeyStatus.ARCHIVED.getCode(), it.getStatus()))
.map(KeyInfo::getCode)
.collect(Collectors.toList());
Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
"密钥id: " + String.join(",", unNormalCodes) + "不是归档状态, 无法恢复");
keyInfoMapper.update(
new LambdaUpdateWrapper<KeyInfo>()
.set(KeyInfo::getStatus, KeyStatus.DISABLED.getCode())
.in(KeyInfo::getId, ids)
);
}
@Override
public void destroyKey(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
return;
}
List<KeyInfo> keyInfos = keyInfoMapper.selectBatchIds(ids);
if (CollectionUtils.isEmpty(keyInfos)) {
log.warn("destroyKey no exist key with ids: {}", ids.stream().map(String::valueOf).collect(Collectors.joining(",")));
return;
}
List<String> unNormalCodes = keyInfos.stream()
.filter(it -> !Objects.equals(KeyStatus.ARCHIVED.getCode(), it.getStatus()))
.map(KeyInfo::getCode)
.collect(Collectors.toList());
Assert.isTrue(CollectionUtils.isEmpty(unNormalCodes),
"密钥id: " + String.join(",", unNormalCodes) + "不是归档状态, 无法销毁");
keyInfoMapper.update(
new LambdaUpdateWrapper<KeyInfo>()
.set(KeyInfo::getStatus, KeyStatus.DESTORY.getCode())
.in(KeyInfo::getId, ids)
);
}
}