应用管理

This commit is contained in:
liulu 2024-11-01 15:51:47 +08:00
parent b5df4c2406
commit c82f7d8013
12 changed files with 296 additions and 42 deletions

View File

@ -0,0 +1,37 @@
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.AppService;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
/**
* @author liulu
* @since 2024/10/29
*/
@Mapper
public interface AppServiceMapper extends BaseMapper<AppService> {
default List<AppService> selectByAppIds(List<Long> appIds) {
if (CollectionUtils.isEmpty(appIds)) {
return Collections.emptyList();
}
return selectList(
new LambdaQueryWrapper<AppService>()
.in(AppService::getApplicationId, appIds)
);
}
default void deleteByAppId(Long appId) {
delete(
new LambdaQueryWrapper<AppService>()
.eq(AppService::getApplicationId, appId)
);
}
}

View File

@ -0,0 +1,24 @@
package com.sunyard.chsm.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @author liulu
* @since 2024/10/31
*/
@Data
@TableName("sp_app_service")
public class AppService {
private Long id;
private Long applicationId;
private Long serviceId;
private String remark;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}

View File

@ -15,12 +15,11 @@ public class Application {
private Long id;
private String name;
private String bindService;
private String status;
// 32位uuid
private String appKey;
// 32位uuid
private String appSecrete;
private String appSecret;
private Long creatorId;
private String remark;

View File

@ -42,15 +42,11 @@ public class BCSdfApiService implements SdfApiService {
BCECPrivateKey priKey = (BCECPrivateKey) keyPair.getPrivate();
byte[] x = pubKey.getQ().getXCoord().getEncoded();
byte[] y = pubKey.getQ().getYCoord().getEncoded();
byte[] d = BigIntegers.asUnsignedByteArray(64, priKey.getD());
byte[] d = BigIntegers.asUnsignedByteArray(32, priKey.getD());
return new EccKey(new EccPubKey(256, x, y), new EccPriKey(256, d));
}
@Override
public EccKey genEccKeyPairAndEncByKek() {
return null;
}
@Override
public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) {
@ -75,12 +71,12 @@ public class BCSdfApiService implements SdfApiService {
}
@Override
public byte[] encryptByMKNoPadding(byte[] data) {
public byte[] encryptByMK(byte[] data) {
return data;
}
@Override
public byte[] decryptByMKNoPadding(byte[] data) {
public byte[] decryptByMK(byte[] data) {
return data;
}

View File

@ -25,14 +25,6 @@ public interface SdfApiService {
*/
EccKey genKeyPairEcc();
/**
* 产生ECC加密密钥对并使用kek加密私钥输出
*
* @return pubKey 返回公钥 | priKey 返回私钥
*/
EccKey genEccKeyPairAndEncByKek();
/**
* 计算MAC
*
@ -46,15 +38,14 @@ public interface SdfApiService {
byte[] hmac(byte[] key, byte[] srcData);
/**
* 多包杂凑运算
* 杂凑运算
*
* @param pucData 缓冲区指针用于存放输入的数据明文
* @return hash值
*/
byte[] hash(byte[] pucData);
byte[] encryptByMK(byte[] data);
byte[] encryptByMKNoPadding(byte[] data);
byte[] decryptByMKNoPadding(byte[] data);
byte[] decryptByMK(byte[] data);
}

View File

@ -62,6 +62,20 @@ public class ApplicationController {
return R.data(String.valueOf(id));
}
/**
* 修改应用
*
* @param update 参数
* @return 应用
*/
@PutMapping
@AuditControllerLog(description = "修改应用", operateType = AuditLogConst.UPDATE)
public R<Void> update(@Valid @RequestBody AppSave update) {
Assert.notNull(update.getId(), "应用id不能为空");
applicationService.update(update);
return R.ok();
}
/**
* 启用应用
*
@ -91,18 +105,20 @@ public class ApplicationController {
}
/**
* 修改应用
* 更新访问凭证
*
* @param update 参数
* @return 应用
* @param id 应用id
* @return
*/
@PutMapping
public R<Void> update(@Valid @RequestBody AppSave update) {
Assert.notNull(update.getId(), "应用id不能为空");
applicationService.update(update);
@PostMapping("/reset/credential")
@AuditControllerLog(description = "更新访问凭证", operateType = AuditLogConst.UPDATE)
public R<Void> resetCredential(Long id) {
Assert.notNull(id, "应用id不能为空");
applicationService.resetCredential(id);
return R.ok();
}
/**
* 删除应用
*

View File

@ -2,6 +2,11 @@ package com.sunyard.chsm.dto;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
import java.util.List;
/**
* @author liulu
* @since 2024/10/30
@ -9,7 +14,18 @@ import lombok.Data;
@Data
public class AppSave {
private Long id;
/**
* 应用名称
*/
@NotBlank(message = "应用名称不能为空")
private String name;
/**
* 密码服务ids
*/
@NotEmpty(message = "密码服务不能为空")
private List<Long> serviceIds;
@Size(max = 500, message = "备注长度在1-500之间")
private String remark;
}

View File

@ -2,6 +2,9 @@ package com.sunyard.chsm.dto;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author liulu
* @since 2024/10/30
@ -9,8 +12,16 @@ import lombok.Data;
@Data
public class AppView {
private Long id;
private String name;
private List<String> serviceIds;
private String serviceNames;
private String status;
private String statusText;
private String appKey;
private String appSecret;
private String remark;
private LocalDateTime createTime;
}

View File

@ -28,6 +28,7 @@ public abstract class KeyInfoDTO {
@Data
public static class KeySave {
@NotNull(message = "所属应用不能为空")
private Long applicationId;
/**
* 密钥模版编码
@ -45,6 +46,8 @@ public abstract class KeyInfoDTO {
@Data
public static class KeyView {
private Long id;
private Long applicationId;
private String appName;
/**
* KEY_ID
*/
@ -81,7 +84,7 @@ public abstract class KeyInfoDTO {
/**
* 密钥id列表
*/
@Size(min = 1, max = 100,message = "密钥id列表长度在1-100之间")
@Size(min = 1, max = 100, message = "密钥id列表长度在1-100之间")
@NotNull(message = "密钥id列表不能为空")
private List<Long> ids;
@ -90,7 +93,7 @@ public abstract class KeyInfoDTO {
@EqualsAndHashCode(callSuper = true)
@Data
public static class KeyUpdate extends IDs{
public static class KeyUpdate extends IDs {
/**
* 新密钥生效时间 yyyy-MM-dd
*/

View File

@ -21,5 +21,7 @@ public interface ApplicationService {
void disable(Long id);
void resetCredential(Long id);
void delete(Long id);
}

View File

@ -1,15 +1,35 @@
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.AppQuery;
import com.sunyard.chsm.dto.AppSave;
import com.sunyard.chsm.dto.AppView;
import com.sunyard.chsm.enums.EnableStatus;
import com.sunyard.chsm.mapper.AppServiceMapper;
import com.sunyard.chsm.mapper.ApplicationMapper;
import com.sunyard.chsm.mapper.CryptoServiceMapper;
import com.sunyard.chsm.model.entity.AppService;
import com.sunyard.chsm.model.entity.Application;
import com.sunyard.chsm.model.entity.CryptoService;
import com.sunyard.chsm.service.ApplicationService;
import lombok.extern.slf4j.Slf4j;
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.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author liulu
@ -22,34 +42,162 @@ public class ApplicationServiceImpl implements ApplicationService {
@Resource
private ApplicationMapper applicationMapper;
@Resource
private AppServiceMapper appServiceMapper;
@Resource
private CryptoServiceMapper cryptoServiceMapper;
@Override
public Page<AppView> selectPageList(AppQuery query) {
return null;
IPage<Application> page = applicationMapper.selectPage(
new Page<>(query.getPageNumber(), query.getPageSize()),
new LambdaQueryWrapper<Application>()
.like(StringUtils.hasText(query.getName()), Application::getName, query.getName())
.eq(StringUtils.hasText(query.getStatus()), Application::getStatus, query.getStatus())
.orderByDesc(Application::getCreateTime)
);
List<Application> records = page.getRecords();
if (CollectionUtils.isEmpty(records)) {
return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
}
List<Long> appIds = records.stream().map(Application::getId).collect(Collectors.toList());
List<AppService> appServices = appServiceMapper.selectByAppIds(appIds);
Map<Long, List<Long>> appServiceMap = appServices.stream()
.collect(Collectors.groupingBy(AppService::getApplicationId,
Collectors.mapping(AppService::getServiceId, Collectors.toList())));
List<CryptoService> services = cryptoServiceMapper.selectBatchIds(appServices.stream().map(AppService::getServiceId).collect(Collectors.toList()));
Map<Long, String> snMap = services.stream().collect(Collectors.toMap(CryptoService::getId, CryptoService::getName));
List<AppView> viewList = records.stream()
.map(it -> {
AppView view = new AppView();
BeanUtils.copyProperties(it, view);
List<Long> sIds = appServiceMap.getOrDefault(it.getId(), Collections.emptyList());
view.setServiceIds(sIds.stream().map(String::valueOf).collect(Collectors.toList()));
String sn = sIds.stream()
.map(snMap::get)
.filter(Objects::nonNull)
.collect(Collectors.joining(","));
view.setServiceNames(sn);
view.setStatusText(EnableStatus.of(it.getStatus()).getDesc());
return view;
})
.collect(Collectors.toList());
return new Page<AppView>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(viewList);
}
@Override
public Long save(AppSave save) {
return null;
checkName(save.getName());
LocalDateTime now = LocalDateTime.now();
Application app = new Application();
app.setId(IdWorker.getId());
app.setAppKey(IdWorker.get32UUID());
app.setAppSecret(IdWorker.get32UUID());
app.setName(save.getName());
app.setStatus(EnableStatus.ENABLED.getCode());
app.setRemark(save.getRemark());
app.setCreateTime(now);
applicationMapper.insert(app);
for (Long serviceId : save.getServiceIds()) {
AppService as = new AppService();
as.setId(IdWorker.getId());
as.setApplicationId(app.getId());
as.setServiceId(serviceId);
as.setCreateTime(now);
appServiceMapper.insert(as);
}
return app.getId();
}
@Override
public void update(AppSave update) {
Assert.notNull(update.getId(), "id不能为空");
Application exist = applicationMapper.selectById(update.getId());
Assert.notNull(exist, "要更新的数据不存在");
if (!Objects.equals(exist.getName(), update.getName())) {
checkName(update.getName());
}
LocalDateTime now = LocalDateTime.now();
Application upApp = new Application();
upApp.setId(update.getId());
upApp.setName(update.getName());
upApp.setRemark(update.getRemark());
upApp.setUpdateTime(now);
applicationMapper.updateById(upApp);
appServiceMapper.deleteByAppId(update.getId());
for (Long serviceId : update.getServiceIds()) {
AppService as = new AppService();
as.setId(IdWorker.getId());
as.setApplicationId(exist.getId());
as.setServiceId(serviceId);
as.setCreateTime(now);
appServiceMapper.insert(as);
}
}
private void checkName(String name) {
Assert.hasText(name, "名称不能为空");
Application exist = applicationMapper.selectOne(
new LambdaQueryWrapper<Application>()
.eq(Application::getName, name)
);
Assert.notNull(exist, "应用名称已存在");
}
@Override
public void enable(Long id) {
Application app = applicationMapper.selectById(id);
Assert.notNull(app, "业务应用不存在");
Assert.isTrue(Objects.equals(EnableStatus.DISABLED.getCode(), app.getStatus()),
"当前应用不是停用状态,不支持启用");
Application upApp = new Application();
upApp.setId(id);
upApp.setStatus(EnableStatus.ENABLED.getCode());
upApp.setUpdateTime(LocalDateTime.now());
applicationMapper.updateById(upApp);
}
@Override
public void disable(Long id) {
Application app = applicationMapper.selectById(id);
Assert.notNull(app, "业务应用不存在");
Assert.isTrue(Objects.equals(EnableStatus.ENABLED.getCode(), app.getStatus()),
"当前应用不是启用状态,不支持停用");
Application upApp = new Application();
upApp.setId(id);
upApp.setStatus(EnableStatus.DISABLED.getCode());
upApp.setUpdateTime(LocalDateTime.now());
applicationMapper.updateById(upApp);
}
@Override
public void resetCredential(Long id) {
Application app = applicationMapper.selectById(id);
Assert.notNull(app, "业务应用不存在");
Application upApp = new Application();
upApp.setId(id);
upApp.setAppKey(IdWorker.get32UUID());
upApp.setAppSecret(IdWorker.get32UUID());
upApp.setUpdateTime(LocalDateTime.now());
applicationMapper.updateById(upApp);
}
@Override
public void delete(Long id) {
applicationMapper.deleteById(id);
appServiceMapper.deleteByAppId(id);
}
}

View File

@ -6,13 +6,16 @@ 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.KeyInfoDTO;
import com.sunyard.chsm.enums.EnableStatus;
import com.sunyard.chsm.enums.KeyCategory;
import com.sunyard.chsm.enums.KeyStatus;
import com.sunyard.chsm.enums.KeyUsage;
import com.sunyard.chsm.mapper.ApplicationMapper;
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.Application;
import com.sunyard.chsm.model.entity.KeyCsr;
import com.sunyard.chsm.model.entity.KeyInfo;
import com.sunyard.chsm.model.entity.KeyRecord;
@ -83,6 +86,8 @@ public class KeyInfoServiceImpl implements KeyInfoService {
@Resource
private KeyTemplateMapper keyTemplateMapper;
@Resource
private ApplicationMapper applicationMapper;
@Resource
private SdfApiService sdfApiService;
@ -120,13 +125,16 @@ public class KeyInfoServiceImpl implements KeyInfoService {
return new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
}
List<Long> appIds = records.stream().map(KeyInfo::getId).collect(Collectors.toList());
Map<Long, String> appNameMap = applicationMapper.selectBatchIds(appIds)
.stream().collect(Collectors.toMap(Application::getId, Application::getName));
List<KeyInfoDTO.KeyView> viewList = records.stream()
.map(it -> {
KeyInfoDTO.KeyView view = new KeyInfoDTO.KeyView();
BeanUtils.copyProperties(it, view);
Optional.ofNullable(KeyCategory.of(it.getKeyType()))
.map(KeyCategory::getDesc)
.ifPresent(view::setKeyTypeText);
Optional.ofNullable(it.getApplicationId()).map(appNameMap::get).ifPresent(view::setAppName);
Optional.of(it.getKeyType()).map(KeyCategory::of).map(KeyCategory::getDesc).ifPresent(view::setKeyTypeText);
Map<String, String> usageMap = KeyUsage.getUsage(it.getKeyUsage())
.stream()
.collect(Collectors.toMap(KeyUsage::getCode, KeyUsage::getDesc));
@ -160,6 +168,9 @@ public class KeyInfoServiceImpl implements KeyInfoService {
.eq(KeyTemplate::getCode, save.getKeyTemplateCode())
);
Assert.notNull(keyTemplate, "密钥模版不存在");
Application app = applicationMapper.selectById(save.getApplicationId());
Assert.notNull(app, "所属应用不存在");
Assert.isTrue(EnableStatus.DISABLED.getCode().equals(app.getStatus()), "应用不是启用状态");
LocalDateTime now = LocalDateTime.now();
@ -267,14 +278,14 @@ public class KeyInfoServiceImpl implements KeyInfoService {
if (KeyCategory.SYM_KEY.getCode().equals(info.getKeyType())) {
byte[] symKey = sdfApiService.generateRandom(16);
byte[] encSymKey = sdfApiService.encryptByMKNoPadding(symKey);
byte[] encSymKey = sdfApiService.encryptByMK(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();
byte[] encD = sdfApiService.encryptByMKNoPadding(d);
byte[] encD = sdfApiService.encryptByMK(d);
record.setKeyData(Hex.toHexString(encD));
String checkHash = Hex.toHexString(sdfApiService.hash(d));
record.setCheckValue(checkHash);
@ -381,7 +392,7 @@ public class KeyInfoServiceImpl implements KeyInfoService {
byte[] y = Arrays.copyOfRange(xy, 32, 64);
ECPublicKeyParameters pubKeyParam = BCECUtils.createECPublicKeyParameters(x, y);
byte[] priKeyBytes = sdfApiService.decryptByMKNoPadding(Hex.decode(record.getKeyData()));
byte[] priKeyBytes = sdfApiService.decryptByMK(Hex.decode(record.getKeyData()));
// byte[][] pri18 = LangUtils.splitAverage(priKeyBytes);
ECPrivateKeyParameters priKeyParam = BCECUtils.createECPrivateKeyParameters(priKeyBytes);
ECDomainParameters domainParams = priKeyParam.getParameters();