密钥管理接口

This commit is contained in:
liulu
2024-10-30 15:29:04 +08:00
parent b6ff674d0b
commit f4030f9265
34 changed files with 3052 additions and 39 deletions
+4
View File
@@ -49,6 +49,10 @@
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
<dependency>
<groupId>com.dm</groupId>
<artifactId>DmJdbcDriver</artifactId>
@@ -0,0 +1,8 @@
package com.sunyard.chsm.controller;
/**
* @author liulu
* @since 2024/10/29
*/
public class ApplicationController {
}
@@ -21,6 +21,7 @@ import javax.validation.Valid;
/**
* 非对称密钥管理接口
*
* @author liulu
* @since 2024/10/28
*/
@@ -71,5 +72,14 @@ public class KeyInfoAsymController {
.body(new ByteArrayResource(content));
}
@PostMapping("/createCsr")
public R<KeyInfoDTO.CreateCSRResp> createCsr(KeyInfoDTO.CreateCsr createCsr) {
String csr = keyInfoService.createCsr(createCsr);
KeyInfoDTO.CreateCSRResp resp = new KeyInfoDTO.CreateCSRResp();
resp.setCsr(csr);
return R.data(resp);
}
}
@@ -1,6 +1,7 @@
package com.sunyard.chsm.dto;
import com.sunyard.chsm.model.PageQuery;
import com.sunyard.chsm.model.Subject;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -118,5 +119,21 @@ public abstract class KeyInfoDTO {
}
@EqualsAndHashCode(callSuper = true)
@Data
public static class CreateCsr extends Subject {
/**
* 密钥id
*/
@NotNull(message = "密钥id不能为空")
private Long id;
}
@Data
public static class CreateCSRResp {
// 证书请求内容
private String csr;
}
}
@@ -22,6 +22,8 @@ public interface KeyInfoService {
String recoveryKey(InputStream is);
String createCsr(KeyInfoDTO.CreateCsr createCsr);
void enableKey(List<Long> ids);
void disableKey(List<Long> ids);
@@ -9,9 +9,11 @@ import com.sunyard.chsm.dto.KeyInfoDTO;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.enums.KeyStatus;
import com.sunyard.chsm.enums.KeyUsage;
import com.sunyard.chsm.mapper.KeyCsrMapper;
import com.sunyard.chsm.mapper.KeyInfoMapper;
import com.sunyard.chsm.mapper.KeyTemplateMapper;
import com.sunyard.chsm.mapper.SpKeyRecordMapper;
import com.sunyard.chsm.model.entity.KeyCsr;
import com.sunyard.chsm.model.entity.KeyInfo;
import com.sunyard.chsm.model.entity.KeyRecord;
import com.sunyard.chsm.model.entity.KeyTemplate;
@@ -19,9 +21,23 @@ import com.sunyard.chsm.sdf.SdfApiService;
import com.sunyard.chsm.sdf.model.EccKey;
import com.sunyard.chsm.service.KeyInfoService;
import com.sunyard.chsm.utils.JsonUtils;
import com.sunyard.chsm.utils.gm.BCECUtils;
import com.sunyard.chsm.utils.gm.cert.CommonCertUtils;
import com.sunyard.chsm.utils.gm.cert.SM2PublicKey;
import com.sunyard.chsm.utils.gm.cert.SM2X509CertMaker;
import com.sunyard.ssp.common.exception.SspwebException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -35,12 +51,14 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -55,6 +73,8 @@ import java.util.stream.Collectors;
@Service
public class KeyInfoServiceImpl implements KeyInfoService {
@Resource
private KeyCsrMapper keyCsrMapper;
@Resource
private KeyInfoMapper keyInfoMapper;
@Resource
@@ -210,18 +230,20 @@ public class KeyInfoServiceImpl implements KeyInfoService {
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));
byte[] encSymKey = sdfApiService.encryptByMKNoPadding(symKey);
record.setKeyData(Hex.toHexString(encSymKey));
String checkHash = Hex.toHexString(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));
byte[] encD = sdfApiService.encryptByMKNoPadding(d);
record.setKeyData(Hex.toHexString(encD));
String checkHash = Hex.toHexString(sdfApiService.hash(d));
record.setCheckValue(checkHash);
byte[] pubKeyBytes = eccKey.getPubKey().getPubKeyBytes();
record.setPubKey(Hex.encodeHexString(pubKeyBytes));
record.setPubKey(Hex.toHexString(pubKeyBytes));
}
return record;
}
@@ -254,7 +276,7 @@ public class KeyInfoServiceImpl implements KeyInfoService {
public String recoveryKey(InputStream is) {
int suc = 0, count = 0, err = 0, exd = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))){
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
String line;
while (true) {
try {
@@ -290,6 +312,75 @@ public class KeyInfoServiceImpl implements KeyInfoService {
return String.format("恢复完成,共%d条数据,跳过已经存在的密钥%d条,恢复成功%d条,解析失败%d条", count, exd, suc, err);
}
@Override
public String createCsr(KeyInfoDTO.CreateCsr createCsr) {
String subject = createCsr.getDN();
KeyCsr exist = keyCsrMapper.selectBySubject(subject);
if (Objects.nonNull(exist)) {
if (Objects.equals(exist.getKeyId(), createCsr.getId())) {
return exist.getCsrTxt();
}
throw new IllegalArgumentException("此证书主题已经被使用!");
}
KeyInfo keyInfo = keyInfoMapper.selectById(createCsr.getId());
Assert.notNull(keyInfo, "密钥不存在!");
LocalDateTime now = LocalDateTime.now();
Assert.isTrue(KeyCategory.ASYM_KEY.getCode().equals(keyInfo.getKeyType())
&& KeyStatus.ENABLED.getCode().equals(keyInfo.getStatus())
&& now.isAfter(keyInfo.getEffectiveTime())
&& now.isBefore(keyInfo.getExpiredTime()),
"只能选择已启用的非对称密钥生成证书请求");
KeyRecord record = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
Assert.notNull(record, "数据异常,没有在使用中的密钥");
X500Name dn = new X500Name(subject);
byte[] xy = Hex.decode(record.getPubKey());
byte[] x = Arrays.copyOfRange(xy, 0, 32);
byte[] y = Arrays.copyOfRange(xy, 32, 64);
ECPublicKeyParameters pubKeyParam = BCECUtils.createECPublicKeyParameters(x, y);
byte[] priKeyBytes = sdfApiService.decryptByMKNoPadding(Hex.decode(record.getKeyData()));
// byte[][] pri18 = LangUtils.splitAverage(priKeyBytes);
ECPrivateKeyParameters priKeyParam = BCECUtils.createECPrivateKeyParameters(priKeyBytes);
ECDomainParameters domainParams = priKeyParam.getParameters();
ECParameterSpec spec = new ECParameterSpec(domainParams.getCurve(), domainParams.getG(), domainParams.getN(), domainParams.getH());
BCECPublicKey pubKey = new BCECPublicKey("EC", pubKeyParam, spec, BouncyCastleProvider.CONFIGURATION);
BCECPrivateKey priKey = new BCECPrivateKey("EC", priKeyParam, pubKey, spec, BouncyCastleProvider.CONFIGURATION);
//
SM2PublicKey sm2SubPub = new SM2PublicKey(pubKey.getAlgorithm(), pubKey);
String csrTxt;
try {
byte[] csr = CommonCertUtils.createCSR(dn, sm2SubPub, priKey, SM2X509CertMaker.SIGN_ALGO_SM3WITHSM2).getEncoded();
PemObject pem = new PemObject("CERTIFICATE REQUEST", csr);
StringWriter str = new StringWriter();
PemWriter pemWriter = new PemWriter(str);
pemWriter.writeObject(pem);
pemWriter.close();
str.close();
csrTxt = str.toString();
} catch (Exception e) {
log.error("生成CSR异常", e);
throw new IllegalArgumentException("生成证书请求异常");
}
KeyCsr csr = new KeyCsr();
csr.setId(IdWorker.getId());
csr.setApplicationId(keyInfo.getApplicationId());
csr.setKeyId(keyInfo.getId());
csr.setKeyRecordId(record.getId());
csr.setPubKey(record.getPubKey());
csr.setKeyData(record.getKeyData());
csr.setSubject(subject);
csr.setCreateTime(LocalDateTime.now());
csr.setCsrTxt(csrTxt);
keyCsrMapper.insert(csr);
return csrTxt;
}
@Override
public void enableKey(List<Long> ids) {
if (CollectionUtils.isEmpty(ids)) {
@@ -42,13 +42,13 @@ public class KeyTemplateServiceImpl implements KeyTemplateService {
@Override
public Page<KeyTemplateDTO.KTView> selectPageList(KeyTemplateDTO.Query query) {
LambdaQueryWrapper<KeyTemplate> wrapper = new LambdaQueryWrapper<KeyTemplate>()
.eq(StringUtils.hasText(query.getKeyType()), KeyTemplate::getKeyType, query.getKeyType())
.orderByDesc(KeyTemplate::getCreateTime);
IPage<KeyTemplate> page = keyTemplateMapper.selectPage(
new Page<>(query.getPageNumber(), query.getPageSize()),
wrapper);
new LambdaQueryWrapper<KeyTemplate>()
.eq(StringUtils.hasText(query.getKeyType()), KeyTemplate::getKeyType, query.getKeyType())
.orderByDesc(KeyTemplate::getCreateTime)
);
List<KeyTemplate> records = page.getRecords();
if (CollectionUtils.isEmpty(records)) {
return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
@@ -145,7 +145,7 @@ public class KeyTemplateServiceImpl implements KeyTemplateService {
.map(KeyUsage::valueOf)
.collect(Collectors.toList());
if (KeyCategory.SYM_KEY == keyCategory ) {
if (KeyCategory.SYM_KEY == keyCategory) {
Assert.isTrue(!usageList.contains(KeyUsage.SIGN_VERIFY), "对称密钥不能用于签名验签");
} else {
Assert.isTrue(!usageList.contains(KeyUsage.HMAC), "非对称密钥不能用于计算HMac");