diff --git a/src/main/java/com/cisd/tms/modules/log/dto/AuditLogReviewRequest.java b/src/main/java/com/cisd/tms/modules/log/dto/AuditLogReviewRequest.java index b3febe8..546f93a 100644 --- a/src/main/java/com/cisd/tms/modules/log/dto/AuditLogReviewRequest.java +++ b/src/main/java/com/cisd/tms/modules/log/dto/AuditLogReviewRequest.java @@ -8,8 +8,8 @@ import lombok.Data; @Schema(description = "日志审计请求参数") public class AuditLogReviewRequest { -// @Schema(description = "审计管理员签名值", example = "MEUC...") -// private String auditSign; + @Schema(description = "审计管理员签名值", example = "MEUC") + private String auditSign; @Schema(description = "审计结果 (PASS/ REJECT / NEED_VERIFY)", example = "PASS") private AuditResult auditResult; @Schema(description = "审计意见", example = "审核通过") diff --git a/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogPageResponse.java b/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogPageResponse.java index 803602e..14fe5a1 100644 --- a/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogPageResponse.java +++ b/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogPageResponse.java @@ -57,7 +57,7 @@ public class OperationAuditLogPageResponse { // @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...") // private String payloadHash; // SM3摘要 - @Schema(description = "请求 SM2 签名值", example = "MEUC...") + @Schema(description = "SM2 签名值", example = "MEUC...") private String signValue; // SM2签名值 @Schema(description = "操作实际发生时间", type = "string", example = "2026-04-16 09:10:16") diff --git a/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogResponse.java b/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogResponse.java index c750108..2b526bf 100644 --- a/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogResponse.java +++ b/src/main/java/com/cisd/tms/modules/log/dto/OperationAuditLogResponse.java @@ -54,8 +54,8 @@ public class OperationAuditLogResponse { // @Schema(description = "请求数据体 SM3 摘要", example = "1ab2c3d4e5f6...") // private String payloadHash; // SM3摘要 - @Schema(description = "请求 SM2 签名值", example = "MEUC...") - private String signValue; // SM2签名值 + @Schema(description = "SM2 签名值", example = "MEUC...") + private String signValue; // SM2签名值 @Schema(description = "操作实际发生时间", type = "string", format = "date-time", example = "2026-04-16 23:59:59") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") diff --git a/src/main/java/com/cisd/tms/modules/log/service/OperationAuditService.java b/src/main/java/com/cisd/tms/modules/log/service/OperationAuditService.java index 8944688..ee16e7c 100644 --- a/src/main/java/com/cisd/tms/modules/log/service/OperationAuditService.java +++ b/src/main/java/com/cisd/tms/modules/log/service/OperationAuditService.java @@ -3,17 +3,16 @@ package com.cisd.tms.modules.log.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.cisd.tms.common.enums.ErrorCode; import com.cisd.tms.common.exception.BizException; -import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService; +import com.cisd.tms.modules.auth.entity.RoleUkeyBindingEntity; +import com.cisd.tms.modules.auth.repository.RoleUkeyBindingRepository; import com.cisd.tms.modules.log.annotation.AuditedOperation; import com.cisd.tms.modules.log.dto.*; import com.cisd.tms.modules.log.entity.OperationAuditLogEntity; -import com.cisd.tms.modules.log.enums.AuditStatus; -import com.cisd.tms.modules.log.enums.AuthLevel; -import com.cisd.tms.modules.log.enums.OperationResult; -import com.cisd.tms.modules.log.enums.OperatorRoleCode; +import com.cisd.tms.modules.log.enums.*; import com.cisd.tms.modules.log.repository.OperationAuditLogRepository; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.encoders.Hex; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,25 +21,37 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.stereotype.Service; import org.springframework.web.method.HandlerMethod; +import java.nio.charset.StandardCharsets; +import java.security.PublicKey; +import java.security.Security; +import java.security.Signature; +import java.security.spec.X509EncodedKeySpec; import java.time.LocalDateTime; import java.util.Base64; +import java.util.List; import java.util.UUID; @Service @RequiredArgsConstructor public class OperationAuditService { - public static final String ATTR_ROLE_CODE = "CURRENT_ROLE_CODE"; - public static final String ATTR_AUTH_LEVEL = "CURRENT_AUTH_LEVEL"; + private static final String PROVIDER = "BC"; + private static final String SIGNATURE_ALGO = "SM3withSM2"; + private static final String ATTR_ROLE_CODE = "CURRENT_ROLE_CODE"; + private static final String ATTR_AUTH_LEVEL = "CURRENT_AUTH_LEVEL"; private static final Logger log = LoggerFactory.getLogger(OperationAuditService.class); - private final OperationAuditLogRepository operationAuditLogRepository; - + private final RoleUkeyBindingRepository roleUkeyBindingRepository; private final OperationAuditSigner auditSigner; - private final PcieCryptoService pcieCryptoService; + + static { + if (Security.getProvider(PROVIDER) == null) { + Security.addProvider(new BouncyCastleProvider()); + } + } public void record(OperationAuditCommand command, boolean isSensitive) { try { @@ -183,45 +194,87 @@ public class OperationAuditService { OperationAuditLogEntity existLog = operationAuditLogRepository.findByLogId(logId) .orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "未找到该logId")); -// String signValue = trim(req.getAuditSign()); -// if (signValue.isEmpty()){ -// throw new IllegalArgumentException("签名值不能为空"); -// } -// -// String payload = auditSigner.buildAuditPayload(existLog); -// -// UserKeyVerifyRequest userKeyVerifyRequest = new UserKeyVerifyRequest(); -// userKeyVerifyRequest.setKeyIndex(1); -// userKeyVerifyRequest.setData(payload.getBytes(StandardCharsets.UTF_8)); -// userKeyVerifyRequest.setSignature(decodeBlob(signValue, "signature is invalid")); -// try { -// pcieCryptoService.userKeyVerifyWithSm3(userKeyVerifyRequest); -// } catch (BizException ex) { -// throw ex; -// } catch (RuntimeException ex) { -// throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "signature verification 执行失败"); -// } - - - if (!AuditStatus.PENDING.equals(existLog.getAuditStatus())) { throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "该日志已审计,禁止重复操作"); } - OperationAuditLogEntity updateEntity = new OperationAuditLogEntity(); - updateEntity.setLogId(existLog.getLogId()); - updateEntity.setAuditStatus(AuditStatus.REVIEWED); - updateEntity.setAuditResult(req.getAuditResult()); - updateEntity.setAuditComment(trim(req.getAuditComment())); - updateEntity.setAuditedBy(auditorUser); - updateEntity.setAuditedAt(LocalDateTime.now()); + String auditSign = trim(req.getAuditSign()); + if (auditSign.isEmpty()){ + throw new IllegalArgumentException("签名值不能为空"); + } + + String signValueSource = buildSignValueSource(existLog, req.getAuditResult()); + + + List list = roleUkeyBindingRepository.findActiveByRoleCode("AUDIT_ADMIN"); + if (list.isEmpty()){ + throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "未找到审计管理员UKey"); + } + + RoleUkeyBindingEntity roleUkeyBindingEntity = list.get(0); + String publicKey = roleUkeyBindingEntity.getUkeyPubkey(); + try{ + verifySm2Signature(signValueSource, auditSign, publicKey); + } catch (Exception e){ + throw new BizException(ErrorCode.BIZ_ERROR.getCode(), e.getMessage()); + } + + + try{ + OperationAuditLogEntity updateEntity = new OperationAuditLogEntity(); + updateEntity.setLogId(existLog.getLogId()); + updateEntity.setAuditStatus(AuditStatus.REVIEWED); + updateEntity.setAuditResult(req.getAuditResult()); + updateEntity.setAuditComment(trim(req.getAuditComment())); + updateEntity.setAuditedBy(auditorUser); + updateEntity.setAuditedAt(LocalDateTime.now()); + + operationAuditLogRepository.updateByLogId(updateEntity); + } catch (Exception e){ + throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "日志审计更新失败"); + } - operationAuditLogRepository.updateByLogId(updateEntity); log.info("日志复核完成, logId: {}, 审计人: {}, 结果: {}, {}", logId, auditorUser, req.getAuditResult(), req.getAuditComment()); } + private static String buildSignValueSource(OperationAuditLogEntity existLog, AuditResult auditResult) { + String signValueSource = trim(existLog.getLogId()) + + existLog.getOperatorRoleCode() + + existLog.getOperatorAuthLevel() + + existLog.getModuleCode() + + existLog.getActionType() + + trim(existLog.getRemoteIp()) + + existLog.getOperationResult() + + trim(existLog.getSummary()) + + trim(existLog.getErrorMessage()) + + existLog.getAuditStatus() + + auditResult + + trim(existLog.getSignValue()) + + existLog.getOccurredAt(); + + return signValueSource; + } + + + public boolean verifySm2Signature(String data, String signatureBase64, String publicKeyBase64) { + try { + byte[] keyBytes = Base64.getDecoder().decode(publicKeyBase64); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + java.security.KeyFactory keyFactory = java.security.KeyFactory.getInstance("EC", PROVIDER); + PublicKey publicKey = keyFactory.generatePublic(keySpec); + + Signature signature = Signature.getInstance(SIGNATURE_ALGO, PROVIDER); + signature.initVerify(publicKey); + signature.update(data.getBytes(StandardCharsets.UTF_8)); + byte[] signatureBytes = Base64.getDecoder().decode(signatureBase64); + return signature.verify(signatureBytes); + } catch (Exception e) { + throw new RuntimeException("SM2验签失败", e); + } + } + private byte[] decodeBlob(String value, String message) { String normalized = value == null ? "" : value.trim(); if (normalized.isEmpty()) { @@ -238,7 +291,7 @@ public class OperationAuditService { } - private String trim(String s){ + private static String trim(String s){ return s == null ? "" : s.trim(); } }