diff --git a/chsm-common/src/main/java/com/sunyard/chsm/mapper/AppServiceMapper.java b/chsm-common/src/main/java/com/sunyard/chsm/mapper/AppServiceMapper.java new file mode 100644 index 0000000..3c18d93 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/mapper/AppServiceMapper.java @@ -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 { + + default List selectByAppIds(List appIds) { + if (CollectionUtils.isEmpty(appIds)) { + return Collections.emptyList(); + } + return selectList( + new LambdaQueryWrapper() + .in(AppService::getApplicationId, appIds) + ); + } + + + default void deleteByAppId(Long appId) { + delete( + new LambdaQueryWrapper() + .eq(AppService::getApplicationId, appId) + ); + + } +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/AppService.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/AppService.java new file mode 100644 index 0000000..8b771d1 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/AppService.java @@ -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; + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Application.java b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Application.java index 8b5b4e2..86f48ae 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Application.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/model/entity/Application.java @@ -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; diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/BCSdfApiService.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/BCSdfApiService.java index 3801c33..6e9cc2f 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/BCSdfApiService.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/BCSdfApiService.java @@ -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; } diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java index bed16b9..c5eb725 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/SdfApiService.java @@ -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); } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/ApplicationController.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/ApplicationController.java index a926316..12a528c 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/ApplicationController.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/controller/ApplicationController.java @@ -62,6 +62,20 @@ public class ApplicationController { return R.data(String.valueOf(id)); } + /** + * 修改应用 + * + * @param update 参数 + * @return 应用 + */ + @PutMapping + @AuditControllerLog(description = "修改应用", operateType = AuditLogConst.UPDATE) + public R 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 update(@Valid @RequestBody AppSave update) { - Assert.notNull(update.getId(), "应用id不能为空"); - applicationService.update(update); + @PostMapping("/reset/credential") + @AuditControllerLog(description = "更新访问凭证", operateType = AuditLogConst.UPDATE) + public R resetCredential(Long id) { + Assert.notNull(id, "应用id不能为空"); + applicationService.resetCredential(id); return R.ok(); } + /** * 删除应用 * diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppSave.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppSave.java index 774e94a..2b8dc90 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppSave.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppSave.java @@ -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 serviceIds; + @Size(max = 500, message = "备注长度在1-500之间") + private String remark; } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppView.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppView.java index b86f433..1152fef 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppView.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/AppView.java @@ -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 serviceIds; + private String serviceNames; + private String status; + private String statusText; + private String appKey; + private String appSecret; - - + private String remark; + private LocalDateTime createTime; } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/KeyInfoDTO.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/KeyInfoDTO.java index 70fc348..544d15b 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/KeyInfoDTO.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/dto/KeyInfoDTO.java @@ -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 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 */ diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/ApplicationService.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/ApplicationService.java index 12d6a18..27d427a 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/ApplicationService.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/ApplicationService.java @@ -21,5 +21,7 @@ public interface ApplicationService { void disable(Long id); + void resetCredential(Long id); + void delete(Long id); } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/ApplicationServiceImpl.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/ApplicationServiceImpl.java index 1d13fec..2582784 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/ApplicationServiceImpl.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/ApplicationServiceImpl.java @@ -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 selectPageList(AppQuery query) { - return null; + IPage page = applicationMapper.selectPage( + new Page<>(query.getPageNumber(), query.getPageSize()), + new LambdaQueryWrapper() + .like(StringUtils.hasText(query.getName()), Application::getName, query.getName()) + .eq(StringUtils.hasText(query.getStatus()), Application::getStatus, query.getStatus()) + .orderByDesc(Application::getCreateTime) + ); + List records = page.getRecords(); + if (CollectionUtils.isEmpty(records)) { + return new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); + } + List appIds = records.stream().map(Application::getId).collect(Collectors.toList()); + List appServices = appServiceMapper.selectByAppIds(appIds); + Map> appServiceMap = appServices.stream() + .collect(Collectors.groupingBy(AppService::getApplicationId, + Collectors.mapping(AppService::getServiceId, Collectors.toList()))); + List services = cryptoServiceMapper.selectBatchIds(appServices.stream().map(AppService::getServiceId).collect(Collectors.toList())); + Map snMap = services.stream().collect(Collectors.toMap(CryptoService::getId, CryptoService::getName)); + + List viewList = records.stream() + .map(it -> { + AppView view = new AppView(); + BeanUtils.copyProperties(it, view); + List 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(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() + .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); } } diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/KeyInfoServiceImpl.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/KeyInfoServiceImpl.java index 607a5a2..81613a1 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/KeyInfoServiceImpl.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/service/impl/KeyInfoServiceImpl.java @@ -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 appIds = records.stream().map(KeyInfo::getId).collect(Collectors.toList()); + Map appNameMap = applicationMapper.selectBatchIds(appIds) + .stream().collect(Collectors.toMap(Application::getId, Application::getName)); + List 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 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();