From 350fa4d89d313581459141e5f7be34e66ec61def Mon Sep 17 00:00:00 2001 From: waner Date: Sat, 18 Apr 2026 10:23:24 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=AF=86=E9=92=A5=E5=8A=A0?= =?UTF-8?q?=E8=A7=A3=E5=AF=86=E7=AD=BE=E5=90=8D=E9=AA=8C=E7=AD=BE=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pcie/model/UserKeyDecryptRequest.java | 23 ++ .../pcie/model/UserKeyEncryptRequest.java | 23 ++ .../crypto/pcie/model/UserKeySignRequest.java | 23 ++ .../pcie/model/UserKeyVerifyRequest.java | 32 +++ .../pcie/service/JnaPcieCryptoService.java | 75 ++++++ .../pcie/service/MockPcieCryptoService.java | 67 +++++ .../pcie/service/PcieCryptoService.java | 24 ++ .../controller/CryptoCardController.java | 210 +++++++++++++++ ...oUserKeyCryptoSelfTestInternalRequest.java | 40 +++ .../CryptoUserKeyDecryptInternalRequest.java | 29 +++ .../CryptoUserKeyEncryptInternalRequest.java | 29 +++ .../CryptoUserKeySignInternalRequest.java | 29 +++ .../CryptoUserKeyVerifyInternalRequest.java | 40 +++ .../service/JnaPcieCryptoServiceTest.java | 133 ++++++++++ .../controller/CryptoCardControllerTest.java | 242 +++++++++++++++++- .../NetworkConfigControllerTest.java | 31 +++ .../InternalApiAuthInterceptorTest.java | 77 ++++++ 17 files changed, 1122 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyDecryptRequest.java create mode 100644 src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyEncryptRequest.java create mode 100644 src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeySignRequest.java create mode 100644 src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyVerifyRequest.java create mode 100644 src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyCryptoSelfTestInternalRequest.java create mode 100644 src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyDecryptInternalRequest.java create mode 100644 src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyEncryptInternalRequest.java create mode 100644 src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeySignInternalRequest.java create mode 100644 src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyVerifyInternalRequest.java create mode 100644 src/test/java/com/cisd/tms/modules/device/controller/NetworkConfigControllerTest.java create mode 100644 src/test/java/com/cisd/tms/security/internal/InternalApiAuthInterceptorTest.java diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyDecryptRequest.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyDecryptRequest.java new file mode 100644 index 0000000..d9afc0c --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyDecryptRequest.java @@ -0,0 +1,23 @@ +package com.cisd.tms.integration.crypto.pcie.model; + +public class UserKeyDecryptRequest { + + private int keyIndex; + private byte[] cipherBlob; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public byte[] getCipherBlob() { + return cipherBlob; + } + + public void setCipherBlob(byte[] cipherBlob) { + this.cipherBlob = cipherBlob; + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyEncryptRequest.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyEncryptRequest.java new file mode 100644 index 0000000..7b83194 --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyEncryptRequest.java @@ -0,0 +1,23 @@ +package com.cisd.tms.integration.crypto.pcie.model; + +public class UserKeyEncryptRequest { + + private int keyIndex; + private byte[] data; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeySignRequest.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeySignRequest.java new file mode 100644 index 0000000..b99fa56 --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeySignRequest.java @@ -0,0 +1,23 @@ +package com.cisd.tms.integration.crypto.pcie.model; + +public class UserKeySignRequest { + + private int keyIndex; + private byte[] data; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyVerifyRequest.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyVerifyRequest.java new file mode 100644 index 0000000..e815662 --- /dev/null +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/model/UserKeyVerifyRequest.java @@ -0,0 +1,32 @@ +package com.cisd.tms.integration.crypto.pcie.model; + +public class UserKeyVerifyRequest { + + private int keyIndex; + private byte[] data; + private byte[] signature; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public byte[] getData() { + return data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public byte[] getSignature() { + return signature; + } + + public void setSignature(byte[] signature) { + this.signature = signature; + } +} diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java index ad5145d..21308e0 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoService.java @@ -79,6 +79,10 @@ import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult; import com.cisd.tms.integration.crypto.pcie.model.UpdateBackedKeyRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest; import com.sun.jna.Memory; import com.sun.jna.Pointer; import com.sun.jna.Structure; @@ -86,6 +90,7 @@ import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.function.Supplier; public class JnaPcieCryptoService implements PcieCryptoService { @@ -96,6 +101,7 @@ public class JnaPcieCryptoService implements PcieCryptoService { private static final int IK_KEY_TYPE_AUTH = 2; private static final int HMAC_MAX_LENGTH = 64; private static final int ECC_CIPHER_PREFIX_LENGTH = (EccRefPublicKey.MAX_LEN * 2) + 32 + Integer.BYTES; + private static final String DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD = "88888888"; private final PcieSessionTemplate sessionTemplate; private final boolean strictAlgIdValidation; @@ -1642,6 +1648,55 @@ public class JnaPcieCryptoService implements PcieCryptoService { }); } + @Override + public BackupDataResult userKeyEncrypt(UserKeyEncryptRequest request) { + UserKeyEncryptRequest req = requireRequest("request", request); + EccInternalEncryptRequest encryptRequest = new EccInternalEncryptRequest(); + encryptRequest.setKeyIndex(requireNonNegative("keyIndex", req.getKeyIndex())); + encryptRequest.setData(requireBytes("data", req.getData())); + return sdfeInternalEncryptEcc(encryptRequest); + } + + @Override + public BackupDataResult userKeyDecrypt(UserKeyDecryptRequest request) { + UserKeyDecryptRequest req = requireRequest("request", request); + int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex()); + byte[] cipherBlob = requireNonEmptyBytes("cipherBlob", req.getCipherBlob()); + EccCipher cipher = toEccCipher("cipherBlob", cipherBlob); + int outBufferSize = cipher.l; + if (outBufferSize <= 0) { + throw new IllegalArgumentException("cipherBlob contains invalid plaintext length: " + outBufferSize); + } + + EccInternalDecryptRequest decryptRequest = new EccInternalDecryptRequest(); + decryptRequest.setKeyIndex(keyIndex); + decryptRequest.setCipherBlob(cipherBlob); + decryptRequest.setOutBufferSize(outBufferSize); + return withDefaultPrivateKeyAccess(keyIndex, () -> sdfeInternalDecryptEcc(decryptRequest)); + } + + @Override + public BackupDataResult userKeySignWithSm3(UserKeySignRequest request) { + UserKeySignRequest req = requireRequest("request", request); + int keyIndex = requireNonNegative("keyIndex", req.getKeyIndex()); + byte[] digest = digestWithSm3(requireBytes("data", req.getData())); + + EccInternalSignRequest signRequest = new EccInternalSignRequest(); + signRequest.setKeyIndex(keyIndex); + signRequest.setData(digest); + return withDefaultPrivateKeyAccess(keyIndex, () -> eccInternalSign(signRequest)); + } + + @Override + public void userKeyVerifyWithSm3(UserKeyVerifyRequest request) { + UserKeyVerifyRequest req = requireRequest("request", request); + EccInternalVerifyRequest verifyRequest = new EccInternalVerifyRequest(); + verifyRequest.setKeyIndex(requireNonNegative("keyIndex", req.getKeyIndex())); + verifyRequest.setData(digestWithSm3(requireBytes("data", req.getData()))); + verifyRequest.setSignature(requireNonEmptyBytes("signature", req.getSignature())); + eccInternalVerify(verifyRequest); + } + @Override public HmacResult hmac(HmacRequest request) { HmacRequest req = requireRequest("request", request); @@ -2128,4 +2183,24 @@ public class JnaPcieCryptoService implements PcieCryptoService { } return request; } + + private T withDefaultPrivateKeyAccess(int keyIndex, Supplier action) { + PrivateKeyAccessRequest accessRequest = new PrivateKeyAccessRequest(); + accessRequest.setKeyIndex(keyIndex); + accessRequest.setPassword(DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD.getBytes(StandardCharsets.UTF_8)); + getPrivateKeyAccessRight(accessRequest); + try { + return action.get(); + } finally { + releasePrivateKeyAccessRight(keyIndex); + } + } + + private byte[] digestWithSm3(byte[] data) { + DigestRequest digestRequest = new DigestRequest(); + digestRequest.setAlgId(Gm0018AlgorithmIds.SM3); + digestRequest.setData(data); + digestRequest.setOutBufferSize(32); + return requireNonEmptyBytes("sm3Digest", digest(digestRequest).getData()); + } } diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java index 5cb9fcd..d61a866 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/MockPcieCryptoService.java @@ -1,5 +1,6 @@ package com.cisd.tms.integration.crypto.pcie.service; +import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds; import com.cisd.tms.integration.crypto.pcie.PcieKeyState; import com.cisd.tms.integration.crypto.pcie.PcieUserKeyType; import com.cisd.tms.integration.crypto.pcie.jna.EccCipher; @@ -66,13 +67,20 @@ import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult; import com.cisd.tms.integration.crypto.pcie.model.UpdateBackedKeyRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.function.Supplier; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; public class MockPcieCryptoService implements PcieCryptoService { private static final int ECC_KEY_BITS = 256; + private static final String DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD = "88888888"; @Override public DeviceCountResult getDeviceCount() { @@ -701,6 +709,45 @@ public class MockPcieCryptoService implements PcieCryptoService { return mockBackup(size, 0x76); } + @Override + public BackupDataResult userKeyEncrypt(UserKeyEncryptRequest request) { + UserKeyEncryptRequest req = requireRequest("request", request); + EccInternalEncryptRequest encryptRequest = new EccInternalEncryptRequest(); + encryptRequest.setKeyIndex(requireNonNegative("keyIndex", req.getKeyIndex())); + encryptRequest.setData(requireBytes("data", req.getData())); + return sdfeInternalEncryptEcc(encryptRequest); + } + + @Override + public BackupDataResult userKeyDecrypt(UserKeyDecryptRequest request) { + UserKeyDecryptRequest req = requireRequest("request", request); + byte[] cipherBlob = requireNonEmptyBytes("cipherBlob", req.getCipherBlob()); + EccInternalDecryptRequest decryptRequest = new EccInternalDecryptRequest(); + decryptRequest.setKeyIndex(requireNonNegative("keyIndex", req.getKeyIndex())); + decryptRequest.setCipherBlob(cipherBlob); + decryptRequest.setOutBufferSize(Math.max(1, cipherBlob.length)); + return withDefaultPrivateKeyAccess(req.getKeyIndex(), () -> sdfeInternalDecryptEcc(decryptRequest)); + } + + @Override + public BackupDataResult userKeySignWithSm3(UserKeySignRequest request) { + UserKeySignRequest req = requireRequest("request", request); + EccInternalSignRequest signRequest = new EccInternalSignRequest(); + signRequest.setKeyIndex(requireNonNegative("keyIndex", req.getKeyIndex())); + signRequest.setData(digestWithSm3(requireBytes("data", req.getData()))); + return withDefaultPrivateKeyAccess(req.getKeyIndex(), () -> eccInternalSign(signRequest)); + } + + @Override + public void userKeyVerifyWithSm3(UserKeyVerifyRequest request) { + UserKeyVerifyRequest req = requireRequest("request", request); + EccInternalVerifyRequest verifyRequest = new EccInternalVerifyRequest(); + verifyRequest.setKeyIndex(requireNonNegative("keyIndex", req.getKeyIndex())); + verifyRequest.setData(digestWithSm3(requireBytes("data", req.getData()))); + verifyRequest.setSignature(requireNonEmptyBytes("signature", req.getSignature())); + eccInternalVerify(verifyRequest); + } + @Override public HmacResult hmac(HmacRequest request) { HmacRequest req = requireRequest("request", request); @@ -926,4 +973,24 @@ public class MockPcieCryptoService implements PcieCryptoService { } return request; } + + private T withDefaultPrivateKeyAccess(int keyIndex, Supplier action) { + PrivateKeyAccessRequest accessRequest = new PrivateKeyAccessRequest(); + accessRequest.setKeyIndex(requireNonNegative("keyIndex", keyIndex)); + accessRequest.setPassword(DEFAULT_PRIVATE_KEY_ACCESS_PASSWORD.getBytes(StandardCharsets.UTF_8)); + getPrivateKeyAccessRight(accessRequest); + try { + return action.get(); + } finally { + releasePrivateKeyAccessRight(keyIndex); + } + } + + private byte[] digestWithSm3(byte[] data) { + DigestRequest digestRequest = new DigestRequest(); + digestRequest.setAlgId(Gm0018AlgorithmIds.SM3); + digestRequest.setData(data); + digestRequest.setOutBufferSize(32); + return digest(digestRequest).getData(); + } } diff --git a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java index dad32e7..94825cf 100644 --- a/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java +++ b/src/main/java/com/cisd/tms/integration/crypto/pcie/service/PcieCryptoService.java @@ -57,6 +57,10 @@ import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult; import com.cisd.tms.integration.crypto.pcie.model.UpdateBackedKeyRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest; /** * PCIe 密码卡强类型服务接口。 @@ -455,6 +459,26 @@ public interface PcieCryptoService { */ BackupDataResult sdfeInternalDecryptEcc(EccInternalDecryptRequest request); + /** + * 使用指定用户加密密钥对原文执行内部 ECC 加密。 + */ + BackupDataResult userKeyEncrypt(UserKeyEncryptRequest request); + + /** + * 使用指定用户加密密钥对密文执行内部 ECC 解密,并自动处理私钥访问授权。 + */ + BackupDataResult userKeyDecrypt(UserKeyDecryptRequest request); + + /** + * 先对原文执行 SM3 摘要,再使用指定用户签名密钥完成内部 ECC 签名,并自动处理私钥访问授权。 + */ + BackupDataResult userKeySignWithSm3(UserKeySignRequest request); + + /** + * 先对原文执行 SM3 摘要,再使用指定用户签名公钥完成内部 ECC 验签。 + */ + void userKeyVerifyWithSm3(UserKeyVerifyRequest request); + /** * 使用 SD FE HMAC 流程计算 HMAC。 */ diff --git a/src/main/java/com/cisd/tms/modules/device/controller/CryptoCardController.java b/src/main/java/com/cisd/tms/modules/device/controller/CryptoCardController.java index e1e5b7d..9bc1bff 100644 --- a/src/main/java/com/cisd/tms/modules/device/controller/CryptoCardController.java +++ b/src/main/java/com/cisd/tms/modules/device/controller/CryptoCardController.java @@ -4,6 +4,7 @@ import static com.cisd.tms.modules.device.support.crypto.CryptoPayloadCodec.*; import static com.cisd.tms.modules.device.support.crypto.CryptoRequestMapper.*; import com.cisd.tms.common.api.ApiResponse; +import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds; import com.cisd.tms.modules.device.dto.crypto.request.*; import com.cisd.tms.integration.crypto.pcie.model.AgreementDataAndKeyEccResult; import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult; @@ -14,6 +15,10 @@ import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult; import com.cisd.tms.integration.crypto.pcie.model.DigestRequest; import com.cisd.tms.integration.crypto.pcie.model.EccAgreementSessionKeyRequest; import com.cisd.tms.integration.crypto.pcie.model.EccAgreementSessionKeyResult; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.ExchangeDigitEnvelopeEccRequest; import com.cisd.tms.integration.crypto.pcie.model.ExchangeDigitEnvelopeRsaRequest; import com.cisd.tms.integration.crypto.pcie.model.ExportKekComponentRequest; @@ -29,18 +34,25 @@ import com.cisd.tms.integration.crypto.pcie.model.HmacRequest; import com.cisd.tms.integration.crypto.pcie.model.HmacResult; import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult; import com.cisd.tms.integration.crypto.pcie.model.MacCalcRequest; +import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest; import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricKekCryptoRequest; import com.cisd.tms.integration.crypto.pcie.model.StandardKeyPairResult; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest; import com.cisd.tms.integration.crypto.pcie.service.PcieDeviceStatusFormatter; import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService; import com.cisd.tms.security.internal.ReplayProtected; +import com.google.gson.Gson; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; +import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Base64; import java.util.LinkedHashMap; @@ -65,6 +77,8 @@ import org.springframework.web.bind.annotation.RestController; @Tag(name = "密码卡调试", description = "密码卡设备调试、密钥操作和文件管理接口") public class CryptoCardController { + private static final String DEFAULT_USER_KEY_ACCESS_PASSWORD = "88888888"; + private final PcieCryptoService pcieCryptoService; @GetMapping("/device-count") @@ -514,6 +528,76 @@ public class CryptoCardController { return ApiResponse.success(hexPayload("dataHex", result.getData(), result.getLength())); } + @PostMapping("/user-key/encrypt") + @Operation(summary = "用户密钥加密", description = "使用用户加密密钥完成内部 ECC 加密,输入输出均使用 Base64。") + public ApiResponse> encryptUserKey( + @Valid @RequestBody CryptoUserKeyEncryptInternalRequest request + ) { + UserKeyEncryptRequest encryptRequest = new UserKeyEncryptRequest(); + encryptRequest.setKeyIndex(request.getKeyIndex()); + encryptRequest.setData(Base64.getDecoder().decode(request.getDataBase64())); + BackupDataResult result = pcieCryptoService.userKeyEncrypt(encryptRequest); + int length = result.getLength() > 0 && result.getLength() <= result.getData().length + ? result.getLength() + : result.getData().length; + byte[] cipherBlob = Arrays.copyOf(result.getData(), length); + return ApiResponse.success(Map.of( + "length", length, + "cipherBase64", Base64.getEncoder().encodeToString(cipherBlob) + )); + } + + @PostMapping("/user-key/decrypt") + @Operation(summary = "用户密钥解密", description = "使用用户加密密钥完成内部 ECC 解密,接口内部自动申请和释放私钥访问权限。") + public ApiResponse> decryptUserKey( + @Valid @RequestBody CryptoUserKeyDecryptInternalRequest request + ) { + UserKeyDecryptRequest decryptRequest = new UserKeyDecryptRequest(); + decryptRequest.setKeyIndex(request.getKeyIndex()); + decryptRequest.setCipherBlob(Base64.getDecoder().decode(request.getCipherBase64())); + BackupDataResult result = pcieCryptoService.userKeyDecrypt(decryptRequest); + int length = result.getLength() > 0 && result.getLength() <= result.getData().length + ? result.getLength() + : result.getData().length; + byte[] plain = Arrays.copyOf(result.getData(), length); + return ApiResponse.success(Map.of( + "length", length, + "dataBase64", Base64.getEncoder().encodeToString(plain) + )); + } + + @PostMapping("/user-key/sign") + @Operation(summary = "用户密钥签名", description = "使用用户签名密钥对原文先做 SM3,再完成内部 ECC 签名。") + public ApiResponse> signUserKey( + @Valid @RequestBody CryptoUserKeySignInternalRequest request + ) { + UserKeySignRequest signRequest = new UserKeySignRequest(); + signRequest.setKeyIndex(request.getKeyIndex()); + signRequest.setData(Base64.getDecoder().decode(request.getDataBase64())); + BackupDataResult result = pcieCryptoService.userKeySignWithSm3(signRequest); + int length = result.getLength() > 0 && result.getLength() <= result.getData().length + ? result.getLength() + : result.getData().length; + byte[] signature = Arrays.copyOf(result.getData(), length); + return ApiResponse.success(Map.of( + "length", length, + "signatureBase64", Base64.getEncoder().encodeToString(signature) + )); + } + + @PostMapping("/user-key/verify") + @Operation(summary = "用户密钥验签", description = "使用用户签名密钥对原文先做 SM3,再完成内部 ECC 验签。") + public ApiResponse> verifyUserKey( + @Valid @RequestBody CryptoUserKeyVerifyInternalRequest request + ) { + UserKeyVerifyRequest verifyRequest = new UserKeyVerifyRequest(); + verifyRequest.setKeyIndex(request.getKeyIndex()); + verifyRequest.setData(Base64.getDecoder().decode(request.getDataBase64())); + verifyRequest.setSignature(Base64.getDecoder().decode(request.getSignatureBase64())); + pcieCryptoService.userKeyVerifyWithSm3(verifyRequest); + return ApiResponse.success(Map.of("passed", true)); + } + @PostMapping("/user-key/self-test") @Operation(summary = "执行用户密钥全流程自测", description = "依次执行 ECC 用户密钥生成、备份、删除、重建槽位、恢复和状态校验,返回备份 HEX 与关键状态。") public ApiResponse> selfTestUserKey( @@ -556,6 +640,132 @@ public class CryptoCardController { return ApiResponse.success(payload); } + @PostMapping("/user-key/crypto-self-test") + @Operation(summary = "执行用户密钥算法全流程自测", description = "分阶段生成 ECC 用户加密钥和签名钥,完成内部加解密、签名验签,并在每个阶段结束后销毁槽位密钥。") + public ApiResponse> cryptoSelfTestUserKey( + @Valid @RequestBody CryptoUserKeyCryptoSelfTestInternalRequest request + ) { + byte[] encryptPlain = Base64.getDecoder().decode(request.getEncryptPlainBase64()); + byte[] signData = Base64.getDecoder().decode(request.getSignDataBase64()); + + GenerateKeyPairRequest encGenerateRequest = new GenerateKeyPairRequest(); + encGenerateRequest.setKeyBits(256); + encGenerateRequest.setKeyIndex(request.getKeyIndex()); + encGenerateRequest.setKeyType(1); + + GenerateKeyPairRequest signGenerateRequest = new GenerateKeyPairRequest(); + signGenerateRequest.setKeyBits(256); + signGenerateRequest.setKeyIndex(request.getKeyIndex()); + signGenerateRequest.setKeyType(0); + + boolean encDeleted = false; + boolean signDeleted = false; + try { + pcieCryptoService.generateKeyPairEcc(encGenerateRequest); + + EccInternalEncryptRequest encryptRequest = new EccInternalEncryptRequest(); + encryptRequest.setKeyIndex(request.getKeyIndex()); + encryptRequest.setData(encryptPlain); + BackupDataResult cipherResult = pcieCryptoService.sdfeInternalEncryptEcc(encryptRequest); + + int cipherLength = cipherResult.getLength() > 0 && cipherResult.getLength() <= cipherResult.getData().length + ? cipherResult.getLength() + : cipherResult.getData().length; + byte[] cipherBlob = Arrays.copyOf(cipherResult.getData(), cipherLength); + + EccInternalDecryptRequest decryptRequest = new EccInternalDecryptRequest(); + decryptRequest.setKeyIndex(request.getKeyIndex()); + decryptRequest.setCipherBlob(cipherBlob); + decryptRequest.setOutBufferSize(encryptPlain.length); + PrivateKeyAccessRequest decryptAccessRequest = new PrivateKeyAccessRequest(); + decryptAccessRequest.setKeyIndex(request.getKeyIndex()); + decryptAccessRequest.setPassword(DEFAULT_USER_KEY_ACCESS_PASSWORD.getBytes(StandardCharsets.UTF_8)); + BackupDataResult decryptResult; + pcieCryptoService.getPrivateKeyAccessRight(decryptAccessRequest); + try { + decryptResult = pcieCryptoService.sdfeInternalDecryptEcc(decryptRequest); + } finally { + pcieCryptoService.releasePrivateKeyAccessRight(request.getKeyIndex()); + } + int decryptLength = decryptResult.getLength() > 0 && decryptResult.getLength() <= decryptResult.getData().length + ? decryptResult.getLength() + : decryptResult.getData().length; + byte[] decryptPlain = Arrays.copyOf(decryptResult.getData(), decryptLength); + + Map payload = new LinkedHashMap<>(); + payload.put("keyIndex", request.getKeyIndex()); + payload.put("encryptPlainBase64", Base64.getEncoder().encodeToString(encryptPlain)); + payload.put("cipherLength", cipherLength); + payload.put("cipherBase64", Base64.getEncoder().encodeToString(cipherBlob)); + payload.put("decryptLength", decryptLength); + payload.put("decryptBase64", Base64.getEncoder().encodeToString(decryptPlain)); + payload.put("decryptMatched", Arrays.equals(encryptPlain, decryptPlain)); + + log.info("keyIndex:{}", request.getKeyIndex()); + log.info("encryptPlainBase64:{}", Base64.getEncoder().encodeToString(encryptPlain)); + log.info("cipherLength:{}", cipherLength); + log.info("cipherBase64:{}", Base64.getEncoder().encodeToString(cipherBlob)); + log.info("decryptLength:{}", decryptLength); + log.info("decryptBase64:{}", Base64.getEncoder().encodeToString(decryptPlain)); + log.info("decryptMatched:{}", Arrays.equals(encryptPlain, decryptPlain)); + pcieCryptoService.deleteUserKey(request.getKeyIndex()); + encDeleted = true; + KeyStatusResult encDeleteStatus = pcieCryptoService.statusUserKeyEcc(request.getKeyIndex(), 1); + + payload.put("encDeleteStatus", encDeleteStatus); + + + pcieCryptoService.generateKeyPairEcc(signGenerateRequest); + DigestRequest digestRequest = new DigestRequest(); + digestRequest.setAlgId(Gm0018AlgorithmIds.SM3); + digestRequest.setData(signData); + byte[] signDigest = pcieCryptoService.digest(digestRequest).getData(); + + EccInternalSignRequest signRequest = new EccInternalSignRequest(); + signRequest.setKeyIndex(request.getKeyIndex()); + signRequest.setData(signDigest); + PrivateKeyAccessRequest signAccessRequest = new PrivateKeyAccessRequest(); + signAccessRequest.setKeyIndex(request.getKeyIndex()); + signAccessRequest.setPassword(DEFAULT_USER_KEY_ACCESS_PASSWORD.getBytes(StandardCharsets.UTF_8)); + BackupDataResult signatureResult; + pcieCryptoService.getPrivateKeyAccessRight(signAccessRequest); + try { + signatureResult = pcieCryptoService.eccInternalSign(signRequest); + } finally { + pcieCryptoService.releasePrivateKeyAccessRight(request.getKeyIndex()); + } + int signatureLength = signatureResult.getLength() > 0 && signatureResult.getLength() <= signatureResult.getData().length + ? signatureResult.getLength() + : signatureResult.getData().length; + byte[] signature = Arrays.copyOf(signatureResult.getData(), signatureLength); + + EccInternalVerifyRequest verifyRequest = new EccInternalVerifyRequest(); + verifyRequest.setKeyIndex(request.getKeyIndex()); + verifyRequest.setData(signDigest); + verifyRequest.setSignature(signature); + pcieCryptoService.eccInternalVerify(verifyRequest); + + pcieCryptoService.deleteUserKey(request.getKeyIndex()); + signDeleted = true; + KeyStatusResult signDeleteStatus = pcieCryptoService.statusUserKeyEcc(request.getKeyIndex(), 0); + + payload.put("signatureLength", signatureLength); + payload.put("signatureBase64", Base64.getEncoder().encodeToString(signature)); + payload.put("signDigestBase64", Base64.getEncoder().encodeToString(signDigest)); + payload.put("verifyPassed", true); + payload.put("signDeleteStatus", signDeleteStatus); + return ApiResponse.success(payload); + } finally { + if (!signDeleted) { + try { + pcieCryptoService.deleteUserKey(request.getKeyIndex()); + } catch (Exception cleanupError) { + log.warn("failed to cleanup user key after crypto self test, keyIndex={}", request.getKeyIndex(), cleanupError); + } + } + } + } + @PostMapping("/user-key/recover") @Operation(summary = "恢复用户密钥", description = "从备份数据恢复用户密钥。") public ApiResponse recoverUserKey( diff --git a/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyCryptoSelfTestInternalRequest.java b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyCryptoSelfTestInternalRequest.java new file mode 100644 index 0000000..6e61b44 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyCryptoSelfTestInternalRequest.java @@ -0,0 +1,40 @@ +package com.cisd.tms.modules.device.dto.crypto.request; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class CryptoUserKeyCryptoSelfTestInternalRequest { + + @Min(0) + private int keyIndex; + + @NotBlank + private String encryptPlainBase64; + + @NotBlank + private String signDataBase64; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public String getEncryptPlainBase64() { + return encryptPlainBase64; + } + + public void setEncryptPlainBase64(String encryptPlainBase64) { + this.encryptPlainBase64 = encryptPlainBase64; + } + + public String getSignDataBase64() { + return signDataBase64; + } + + public void setSignDataBase64(String signDataBase64) { + this.signDataBase64 = signDataBase64; + } +} diff --git a/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyDecryptInternalRequest.java b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyDecryptInternalRequest.java new file mode 100644 index 0000000..c23f79f --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyDecryptInternalRequest.java @@ -0,0 +1,29 @@ +package com.cisd.tms.modules.device.dto.crypto.request; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class CryptoUserKeyDecryptInternalRequest { + + @Min(0) + private int keyIndex; + + @NotBlank + private String cipherBase64; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public String getCipherBase64() { + return cipherBase64; + } + + public void setCipherBase64(String cipherBase64) { + this.cipherBase64 = cipherBase64; + } +} diff --git a/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyEncryptInternalRequest.java b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyEncryptInternalRequest.java new file mode 100644 index 0000000..6f81504 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyEncryptInternalRequest.java @@ -0,0 +1,29 @@ +package com.cisd.tms.modules.device.dto.crypto.request; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class CryptoUserKeyEncryptInternalRequest { + + @Min(0) + private int keyIndex; + + @NotBlank + private String dataBase64; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public String getDataBase64() { + return dataBase64; + } + + public void setDataBase64(String dataBase64) { + this.dataBase64 = dataBase64; + } +} diff --git a/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeySignInternalRequest.java b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeySignInternalRequest.java new file mode 100644 index 0000000..0f1fd1c --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeySignInternalRequest.java @@ -0,0 +1,29 @@ +package com.cisd.tms.modules.device.dto.crypto.request; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class CryptoUserKeySignInternalRequest { + + @Min(0) + private int keyIndex; + + @NotBlank + private String dataBase64; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public String getDataBase64() { + return dataBase64; + } + + public void setDataBase64(String dataBase64) { + this.dataBase64 = dataBase64; + } +} diff --git a/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyVerifyInternalRequest.java b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyVerifyInternalRequest.java new file mode 100644 index 0000000..d3d1dd7 --- /dev/null +++ b/src/main/java/com/cisd/tms/modules/device/dto/crypto/request/CryptoUserKeyVerifyInternalRequest.java @@ -0,0 +1,40 @@ +package com.cisd.tms.modules.device.dto.crypto.request; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; + +public class CryptoUserKeyVerifyInternalRequest { + + @Min(0) + private int keyIndex; + + @NotBlank + private String dataBase64; + + @NotBlank + private String signatureBase64; + + public int getKeyIndex() { + return keyIndex; + } + + public void setKeyIndex(int keyIndex) { + this.keyIndex = keyIndex; + } + + public String getDataBase64() { + return dataBase64; + } + + public void setDataBase64(String dataBase64) { + this.dataBase64 = dataBase64; + } + + public String getSignatureBase64() { + return signatureBase64; + } + + public void setSignatureBase64(String signatureBase64) { + this.signatureBase64 = signatureBase64; + } +} diff --git a/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java b/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java index a914e6d..8e05b0f 100644 --- a/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java +++ b/src/test/java/com/cisd/tms/integration/crypto/pcie/service/JnaPcieCryptoServiceTest.java @@ -12,16 +12,24 @@ import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult; import com.cisd.tms.integration.crypto.pcie.model.DigestRequest; import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest; import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.FirmwareInfoResult; import com.cisd.tms.integration.crypto.pcie.model.IkSignRequest; import com.cisd.tms.integration.crypto.pcie.model.IkVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.SymmetricCryptoRequest; +import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mockito; class JnaPcieCryptoServiceTest { @@ -452,4 +460,129 @@ class JnaPcieCryptoServiceTest { Assertions.assertArrayEquals(new byte[] {0x31, 0x32}, data); Mockito.verify(sessionTemplate).withSession(Mockito.eq("SDFE_InternalDecrypt_ECC"), Mockito.any()); } + + @Test + void shouldEncryptViaHighLevelUserKeyEncrypt() { + PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class); + CryptoCardProperties properties = new CryptoCardProperties(); + JnaPcieCryptoService service = Mockito.spy(new JnaPcieCryptoService(sessionTemplate, properties)); + BackupDataResult encrypted = new BackupDataResult(); + encrypted.setData(new byte[] {0x01, 0x02, 0x03}); + encrypted.setLength(3); + Mockito.doReturn(encrypted).when(service).sdfeInternalEncryptEcc(Mockito.any(EccInternalEncryptRequest.class)); + + UserKeyEncryptRequest request = new UserKeyEncryptRequest(); + request.setKeyIndex(3); + request.setData(new byte[] {0x11, 0x22}); + + BackupDataResult result = service.userKeyEncrypt(request); + + Assertions.assertArrayEquals(new byte[] {0x01, 0x02, 0x03}, result.getData()); + ArgumentCaptor captor = ArgumentCaptor.forClass(EccInternalEncryptRequest.class); + Mockito.verify(service).sdfeInternalEncryptEcc(captor.capture()); + Assertions.assertEquals(3, captor.getValue().getKeyIndex()); + Assertions.assertArrayEquals(new byte[] {0x11, 0x22}, captor.getValue().getData()); + } + + @Test + void shouldDecryptViaHighLevelUserKeyDecrypt() { + PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class); + CryptoCardProperties properties = new CryptoCardProperties(); + JnaPcieCryptoService service = Mockito.spy(new JnaPcieCryptoService(sessionTemplate, properties)); + byte[] cipherBlob = new byte[164]; + cipherBlob[160] = 0x05; + BackupDataResult decrypted = new BackupDataResult(); + decrypted.setData(new byte[] {0x31, 0x32}); + decrypted.setLength(2); + Mockito.doNothing().when(service).getPrivateKeyAccessRight(Mockito.any()); + Mockito.doNothing().when(service).releasePrivateKeyAccessRight(5); + Mockito.doReturn(decrypted).when(service).sdfeInternalDecryptEcc(Mockito.any(EccInternalDecryptRequest.class)); + + UserKeyDecryptRequest request = new UserKeyDecryptRequest(); + request.setKeyIndex(5); + request.setCipherBlob(cipherBlob); + + BackupDataResult result = service.userKeyDecrypt(request); + + Assertions.assertArrayEquals(new byte[] {0x31, 0x32}, result.getData()); + ArgumentCaptor accessCaptor = ArgumentCaptor.forClass(PrivateKeyAccessRequest.class); + ArgumentCaptor decryptCaptor = ArgumentCaptor.forClass(EccInternalDecryptRequest.class); + Mockito.verify(service).getPrivateKeyAccessRight(accessCaptor.capture()); + Mockito.verify(service).sdfeInternalDecryptEcc(decryptCaptor.capture()); + Mockito.verify(service).releasePrivateKeyAccessRight(5); + Assertions.assertEquals(5, accessCaptor.getValue().getKeyIndex()); + Assertions.assertArrayEquals("88888888".getBytes(StandardCharsets.UTF_8), accessCaptor.getValue().getPassword()); + Assertions.assertEquals(5, decryptCaptor.getValue().getKeyIndex()); + Assertions.assertArrayEquals(cipherBlob, decryptCaptor.getValue().getCipherBlob()); + Assertions.assertEquals(5, decryptCaptor.getValue().getOutBufferSize()); + } + + @Test + void shouldSignViaHighLevelUserKeySignWithSm3() { + PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class); + CryptoCardProperties properties = new CryptoCardProperties(); + JnaPcieCryptoService service = Mockito.spy(new JnaPcieCryptoService(sessionTemplate, properties)); + BackupDataResult digest = new BackupDataResult(); + digest.setData(new byte[] {0x01, 0x02, 0x03}); + digest.setLength(3); + BackupDataResult signature = new BackupDataResult(); + signature.setData(new byte[] {0x11, 0x22}); + signature.setLength(2); + Mockito.doReturn(digest).when(service).digest(Mockito.any(DigestRequest.class)); + Mockito.doNothing().when(service).getPrivateKeyAccessRight(Mockito.any()); + Mockito.doNothing().when(service).releasePrivateKeyAccessRight(6); + Mockito.doReturn(signature).when(service).eccInternalSign(Mockito.any(EccInternalSignRequest.class)); + + UserKeySignRequest request = new UserKeySignRequest(); + request.setKeyIndex(6); + request.setData("plain".getBytes(StandardCharsets.UTF_8)); + + BackupDataResult result = service.userKeySignWithSm3(request); + + Assertions.assertArrayEquals(new byte[] {0x11, 0x22}, result.getData()); + ArgumentCaptor digestCaptor = ArgumentCaptor.forClass(DigestRequest.class); + ArgumentCaptor accessCaptor = ArgumentCaptor.forClass(PrivateKeyAccessRequest.class); + ArgumentCaptor signCaptor = ArgumentCaptor.forClass(EccInternalSignRequest.class); + Mockito.verify(service).digest(digestCaptor.capture()); + Mockito.verify(service).getPrivateKeyAccessRight(accessCaptor.capture()); + Mockito.verify(service).eccInternalSign(signCaptor.capture()); + Mockito.verify(service).releasePrivateKeyAccessRight(6); + Assertions.assertEquals(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM3, digestCaptor.getValue().getAlgId()); + Assertions.assertArrayEquals("plain".getBytes(StandardCharsets.UTF_8), digestCaptor.getValue().getData()); + Assertions.assertEquals(32, digestCaptor.getValue().getOutBufferSize()); + Assertions.assertEquals(6, accessCaptor.getValue().getKeyIndex()); + Assertions.assertArrayEquals("88888888".getBytes(StandardCharsets.UTF_8), accessCaptor.getValue().getPassword()); + Assertions.assertEquals(6, signCaptor.getValue().getKeyIndex()); + Assertions.assertArrayEquals(new byte[] {0x01, 0x02, 0x03}, signCaptor.getValue().getData()); + } + + @Test + void shouldVerifyViaHighLevelUserKeyVerifyWithSm3() { + PcieSessionTemplate sessionTemplate = Mockito.mock(PcieSessionTemplate.class); + CryptoCardProperties properties = new CryptoCardProperties(); + JnaPcieCryptoService service = Mockito.spy(new JnaPcieCryptoService(sessionTemplate, properties)); + BackupDataResult digest = new BackupDataResult(); + digest.setData(new byte[] {0x21, 0x22, 0x23}); + digest.setLength(3); + Mockito.doReturn(digest).when(service).digest(Mockito.any(DigestRequest.class)); + Mockito.doNothing().when(service).eccInternalVerify(Mockito.any(EccInternalVerifyRequest.class)); + + UserKeyVerifyRequest request = new UserKeyVerifyRequest(); + request.setKeyIndex(7); + request.setData("verify".getBytes(StandardCharsets.UTF_8)); + request.setSignature(new byte[] {0x31, 0x32}); + + service.userKeyVerifyWithSm3(request); + + ArgumentCaptor digestCaptor = ArgumentCaptor.forClass(DigestRequest.class); + ArgumentCaptor verifyCaptor = ArgumentCaptor.forClass(EccInternalVerifyRequest.class); + Mockito.verify(service).digest(digestCaptor.capture()); + Mockito.verify(service).eccInternalVerify(verifyCaptor.capture()); + Assertions.assertEquals(com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds.SM3, digestCaptor.getValue().getAlgId()); + Assertions.assertArrayEquals("verify".getBytes(StandardCharsets.UTF_8), digestCaptor.getValue().getData()); + Assertions.assertEquals(32, digestCaptor.getValue().getOutBufferSize()); + Assertions.assertEquals(7, verifyCaptor.getValue().getKeyIndex()); + Assertions.assertArrayEquals(new byte[] {0x21, 0x22, 0x23}, verifyCaptor.getValue().getData()); + Assertions.assertArrayEquals(new byte[] {0x31, 0x32}, verifyCaptor.getValue().getSignature()); + } } diff --git a/src/test/java/com/cisd/tms/modules/device/controller/CryptoCardControllerTest.java b/src/test/java/com/cisd/tms/modules/device/controller/CryptoCardControllerTest.java index c68ec8c..f2179b7 100644 --- a/src/test/java/com/cisd/tms/modules/device/controller/CryptoCardControllerTest.java +++ b/src/test/java/com/cisd/tms/modules/device/controller/CryptoCardControllerTest.java @@ -1,11 +1,22 @@ package com.cisd.tms.modules.device.controller; import com.cisd.tms.common.exception.GlobalExceptionHandler; +import com.cisd.tms.integration.crypto.pcie.Gm0018AlgorithmIds; import com.cisd.tms.integration.crypto.pcie.model.BackupDataResult; import com.cisd.tms.integration.crypto.pcie.model.DeviceStatusResult; +import com.cisd.tms.integration.crypto.pcie.model.DigestRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalSignRequest; +import com.cisd.tms.integration.crypto.pcie.model.EccInternalVerifyRequest; import com.cisd.tms.integration.crypto.pcie.model.GenerateKeyPairRequest; import com.cisd.tms.integration.crypto.pcie.model.KeyStatusResult; +import com.cisd.tms.integration.crypto.pcie.model.PrivateKeyAccessRequest; import com.cisd.tms.integration.crypto.pcie.model.RecoverUserKeyRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyDecryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyEncryptRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeySignRequest; +import com.cisd.tms.integration.crypto.pcie.model.UserKeyVerifyRequest; import com.cisd.tms.integration.crypto.pcie.service.PcieCryptoService; import org.mockito.ArgumentCaptor; import org.junit.jupiter.api.Test; @@ -252,20 +263,241 @@ class CryptoCardControllerTest { inOrder.verify(pcieCryptoService).backupUserKey(1, 0, 120); inOrder.verify(pcieCryptoService).deleteUserKey(1); inOrder.verify(pcieCryptoService).statusUserKeyEcc(1, 0); - inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateCaptor.capture()); inOrder.verify(pcieCryptoService).recoverUserKey(recoverCaptor.capture()); inOrder.verify(pcieCryptoService).statusUserKeyEcc(1, 0); - assertEquals(2, generateCaptor.getAllValues().size()); + assertEquals(1, generateCaptor.getAllValues().size()); assertEquals(256, generateCaptor.getAllValues().get(0).getKeyBits()); assertEquals(1, generateCaptor.getAllValues().get(0).getKeyIndex()); assertEquals(0, generateCaptor.getAllValues().get(0).getKeyType()); - assertEquals(256, generateCaptor.getAllValues().get(1).getKeyBits()); - assertEquals(1, generateCaptor.getAllValues().get(1).getKeyIndex()); - assertEquals(0, generateCaptor.getAllValues().get(1).getKeyType()); assertEquals(1, recoverCaptor.getValue().getKeyIndex()); assertEquals(0, recoverCaptor.getValue().getKeyType()); assertEquals(1, recoverCaptor.getValue().getStoreFlag()); assertArrayEquals(new byte[] {0x01, 0x23, 0x45, 0x67}, recoverCaptor.getValue().getEncryptedKey()); } + + @Test + void shouldEncryptUserKeyViaHighLevelEndpoint() throws Exception { + PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); + BackupDataResult result = new BackupDataResult(); + result.setLength(3); + result.setData(new byte[] {0x21, 0x43, 0x65}); + Mockito.when(pcieCryptoService.userKeyEncrypt(Mockito.any())).thenReturn(result); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(post("/api/v1/device/crypto/user-key/encrypt") + .contentType("application/json") + .content(""" + { + "keyIndex": 7, + "dataBase64": "aGVsbG8=" + } + """)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("\"length\":3"))) + .andExpect(content().string(containsString("\"cipherBase64\":\"IUNl\""))); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserKeyEncryptRequest.class); + Mockito.verify(pcieCryptoService).userKeyEncrypt(captor.capture()); + assertEquals(7, captor.getValue().getKeyIndex()); + assertArrayEquals("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getData()); + } + + @Test + void shouldDecryptUserKeyViaHighLevelEndpoint() throws Exception { + PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); + BackupDataResult result = new BackupDataResult(); + result.setLength(5); + result.setData("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + Mockito.when(pcieCryptoService.userKeyDecrypt(Mockito.any())).thenReturn(result); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(post("/api/v1/device/crypto/user-key/decrypt") + .contentType("application/json") + .content(""" + { + "keyIndex": 7, + "cipherBase64": "IUNl" + } + """)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("\"length\":5"))) + .andExpect(content().string(containsString("\"dataBase64\":\"aGVsbG8=\""))); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserKeyDecryptRequest.class); + Mockito.verify(pcieCryptoService).userKeyDecrypt(captor.capture()); + assertEquals(7, captor.getValue().getKeyIndex()); + assertArrayEquals(new byte[] {0x21, 0x43, 0x65}, captor.getValue().getCipherBlob()); + } + + @Test + void shouldSignUserKeyViaHighLevelEndpoint() throws Exception { + PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); + BackupDataResult result = new BackupDataResult(); + result.setLength(4); + result.setData(new byte[] {0x11, 0x22, 0x33, 0x44}); + Mockito.when(pcieCryptoService.userKeySignWithSm3(Mockito.any())).thenReturn(result); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(post("/api/v1/device/crypto/user-key/sign") + .contentType("application/json") + .content(""" + { + "keyIndex": 7, + "dataBase64": "c2lnbg==" + } + """)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("\"length\":4"))) + .andExpect(content().string(containsString("\"signatureBase64\":\"ESIzRA==\""))); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserKeySignRequest.class); + Mockito.verify(pcieCryptoService).userKeySignWithSm3(captor.capture()); + assertEquals(7, captor.getValue().getKeyIndex()); + assertArrayEquals("sign".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getData()); + } + + @Test + void shouldVerifyUserKeyViaHighLevelEndpoint() throws Exception { + PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(post("/api/v1/device/crypto/user-key/verify") + .contentType("application/json") + .content(""" + { + "keyIndex": 7, + "dataBase64": "c2lnbg==", + "signatureBase64": "ESIzRA==" + } + """)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("\"passed\":true"))); + + ArgumentCaptor captor = ArgumentCaptor.forClass(UserKeyVerifyRequest.class); + Mockito.verify(pcieCryptoService).userKeyVerifyWithSm3(captor.capture()); + assertEquals(7, captor.getValue().getKeyIndex()); + assertArrayEquals("sign".getBytes(java.nio.charset.StandardCharsets.UTF_8), captor.getValue().getData()); + assertArrayEquals(new byte[] {0x11, 0x22, 0x33, 0x44}, captor.getValue().getSignature()); + } + + @Test + void shouldRunUserKeyCryptoSelfTestFlow() throws Exception { + PcieCryptoService pcieCryptoService = Mockito.mock(PcieCryptoService.class); + BackupDataResult cipher = new BackupDataResult(); + cipher.setLength(3); + cipher.setData(new byte[] {0x21, 0x43, 0x65}); + BackupDataResult decrypt = new BackupDataResult(); + decrypt.setLength(5); + decrypt.setData("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8)); + BackupDataResult signature = new BackupDataResult(); + signature.setLength(4); + signature.setData(new byte[] {0x11, 0x22, 0x33, 0x44}); + BackupDataResult digest = new BackupDataResult(); + digest.setLength(32); + digest.setData(new byte[] { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20 + }); + KeyStatusResult deleted = new KeyStatusResult(); + deleted.setKeyState(0); + deleted.setKeyStateName("FREE"); + Mockito.when(pcieCryptoService.sdfeInternalEncryptEcc(Mockito.any())).thenReturn(cipher); + Mockito.when(pcieCryptoService.sdfeInternalDecryptEcc(Mockito.any())).thenReturn(decrypt); + Mockito.when(pcieCryptoService.digest(Mockito.any())).thenReturn(digest); + Mockito.when(pcieCryptoService.eccInternalSign(Mockito.any())).thenReturn(signature); + Mockito.when(pcieCryptoService.statusUserKeyEcc(7, 0)).thenReturn(deleted); + Mockito.when(pcieCryptoService.statusUserKeyEcc(7, 1)).thenReturn(deleted); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new CryptoCardController(pcieCryptoService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(post("/api/v1/device/crypto/user-key/crypto-self-test") + .contentType("application/json") + .content(""" + { + "keyIndex": 7, + "encryptPlainBase64": "aGVsbG8=", + "signDataBase64": "c2lnbg==" + } + """)) + .andExpect(status().isOk()) + .andExpect(content().string(containsString("\"keyIndex\":7"))) + .andExpect(content().string(containsString("\"encryptPlainBase64\":\"aGVsbG8=\""))) + .andExpect(content().string(containsString("\"cipherLength\":3"))) + .andExpect(content().string(containsString("\"cipherBase64\":\"IUNl\""))) + .andExpect(content().string(containsString("\"decryptLength\":5"))) + .andExpect(content().string(containsString("\"decryptBase64\":\"aGVsbG8=\""))) + .andExpect(content().string(containsString("\"decryptMatched\":true"))) + .andExpect(content().string(containsString("\"signatureLength\":4"))) + .andExpect(content().string(containsString("\"signatureBase64\":\"ESIzRA==\""))) + .andExpect(content().string(containsString("\"signDigestBase64\":\"AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyA=\""))) + .andExpect(content().string(containsString("\"verifyPassed\":true"))) + .andExpect(content().string(containsString("\"signDeleteStatus\":{\"keyState\":0,\"keyStateName\":\"FREE\"}"))) + .andExpect(content().string(containsString("\"encDeleteStatus\":{\"keyState\":0,\"keyStateName\":\"FREE\"}"))); + + ArgumentCaptor generateCaptor = ArgumentCaptor.forClass(GenerateKeyPairRequest.class); + ArgumentCaptor digestCaptor = ArgumentCaptor.forClass(DigestRequest.class); + ArgumentCaptor encryptCaptor = ArgumentCaptor.forClass(EccInternalEncryptRequest.class); + ArgumentCaptor decryptCaptor = ArgumentCaptor.forClass(EccInternalDecryptRequest.class); + ArgumentCaptor signCaptor = ArgumentCaptor.forClass(EccInternalSignRequest.class); + ArgumentCaptor verifyCaptor = ArgumentCaptor.forClass(EccInternalVerifyRequest.class); + ArgumentCaptor accessCaptor = ArgumentCaptor.forClass(PrivateKeyAccessRequest.class); + InOrder inOrder = Mockito.inOrder(pcieCryptoService); + inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateCaptor.capture()); + inOrder.verify(pcieCryptoService).sdfeInternalEncryptEcc(encryptCaptor.capture()); + inOrder.verify(pcieCryptoService).getPrivateKeyAccessRight(accessCaptor.capture()); + inOrder.verify(pcieCryptoService).sdfeInternalDecryptEcc(decryptCaptor.capture()); + inOrder.verify(pcieCryptoService).releasePrivateKeyAccessRight(7); + inOrder.verify(pcieCryptoService).deleteUserKey(7); + inOrder.verify(pcieCryptoService).statusUserKeyEcc(7, 1); + inOrder.verify(pcieCryptoService).generateKeyPairEcc(generateCaptor.capture()); + inOrder.verify(pcieCryptoService).digest(digestCaptor.capture()); + inOrder.verify(pcieCryptoService).getPrivateKeyAccessRight(accessCaptor.capture()); + inOrder.verify(pcieCryptoService).eccInternalSign(signCaptor.capture()); + inOrder.verify(pcieCryptoService).releasePrivateKeyAccessRight(7); + inOrder.verify(pcieCryptoService).eccInternalVerify(verifyCaptor.capture()); + inOrder.verify(pcieCryptoService).deleteUserKey(7); + inOrder.verify(pcieCryptoService).statusUserKeyEcc(7, 0); + + assertEquals(2, generateCaptor.getAllValues().size()); + assertEquals(256, generateCaptor.getAllValues().get(0).getKeyBits()); + assertEquals(7, generateCaptor.getAllValues().get(0).getKeyIndex()); + assertEquals(1, generateCaptor.getAllValues().get(0).getKeyType()); + assertEquals(256, generateCaptor.getAllValues().get(1).getKeyBits()); + assertEquals(7, generateCaptor.getAllValues().get(1).getKeyIndex()); + assertEquals(0, generateCaptor.getAllValues().get(1).getKeyType()); + assertEquals(7, encryptCaptor.getValue().getKeyIndex()); + assertArrayEquals("hello".getBytes(java.nio.charset.StandardCharsets.UTF_8), encryptCaptor.getValue().getData()); + assertEquals(7, decryptCaptor.getValue().getKeyIndex()); + assertArrayEquals(new byte[] {0x21, 0x43, 0x65}, decryptCaptor.getValue().getCipherBlob()); + assertEquals(5, decryptCaptor.getValue().getOutBufferSize()); + assertEquals(Gm0018AlgorithmIds.SM3, digestCaptor.getValue().getAlgId()); + assertArrayEquals("sign".getBytes(java.nio.charset.StandardCharsets.UTF_8), digestCaptor.getValue().getData()); + assertEquals(2, accessCaptor.getAllValues().size()); + assertEquals(7, accessCaptor.getAllValues().get(0).getKeyIndex()); + assertArrayEquals("88888888".getBytes(java.nio.charset.StandardCharsets.UTF_8), accessCaptor.getAllValues().get(0).getPassword()); + assertEquals(7, accessCaptor.getAllValues().get(1).getKeyIndex()); + assertArrayEquals("88888888".getBytes(java.nio.charset.StandardCharsets.UTF_8), accessCaptor.getAllValues().get(1).getPassword()); + assertEquals(7, signCaptor.getValue().getKeyIndex()); + assertArrayEquals(digest.getData(), signCaptor.getValue().getData()); + assertEquals(7, verifyCaptor.getValue().getKeyIndex()); + assertArrayEquals(digest.getData(), verifyCaptor.getValue().getData()); + assertArrayEquals(new byte[] {0x11, 0x22, 0x33, 0x44}, verifyCaptor.getValue().getSignature()); + } } diff --git a/src/test/java/com/cisd/tms/modules/device/controller/NetworkConfigControllerTest.java b/src/test/java/com/cisd/tms/modules/device/controller/NetworkConfigControllerTest.java new file mode 100644 index 0000000..d7a5a1b --- /dev/null +++ b/src/test/java/com/cisd/tms/modules/device/controller/NetworkConfigControllerTest.java @@ -0,0 +1,31 @@ +package com.cisd.tms.modules.device.controller; + +import com.cisd.tms.common.enums.ErrorCode; +import com.cisd.tms.common.exception.GlobalExceptionHandler; +import com.cisd.tms.modules.device.service.NetworkConfigService; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class NetworkConfigControllerTest { + + @Test + void shouldReturnBadRequestWhenBondModeDoesNotExist() throws Exception { + NetworkConfigService networkConfigService = Mockito.mock(NetworkConfigService.class); + Mockito.when(networkConfigService.getBondMode("bond0")).thenReturn(null); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new NetworkConfigController(networkConfigService)) + .setControllerAdvice(new GlobalExceptionHandler()) + .build(); + + mockMvc.perform(get("/api/v1/device/network-config/bond/mode/bond0")) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value(ErrorCode.BAD_REQUEST.getCode())) + .andExpect(jsonPath("$.msg").value("未找到该连接的 Bond 模式")); + } +} diff --git a/src/test/java/com/cisd/tms/security/internal/InternalApiAuthInterceptorTest.java b/src/test/java/com/cisd/tms/security/internal/InternalApiAuthInterceptorTest.java new file mode 100644 index 0000000..88a17ec --- /dev/null +++ b/src/test/java/com/cisd/tms/security/internal/InternalApiAuthInterceptorTest.java @@ -0,0 +1,77 @@ +package com.cisd.tms.security.internal; + +import com.cisd.tms.common.config.properties.TmsSecurityProperties; +import com.cisd.tms.common.enums.ErrorCode; +import com.cisd.tms.modules.auth.entity.AuthSessionEntity; +import com.cisd.tms.modules.auth.repository.AuthSessionRepository; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.time.Clock; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Optional; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +class InternalApiAuthInterceptorTest { + + private static final Clock FIXED_CLOCK = Clock.fixed(Instant.parse("2026-04-17T08:30:00Z"), ZoneOffset.UTC); + + @Test + void shouldReturnSessionInvalidWhenSessionTokenIsMissing() throws Exception { + AuthSessionRepository authSessionRepository = Mockito.mock(AuthSessionRepository.class); + InternalApiAuthInterceptor interceptor = new InternalApiAuthInterceptor( + authSessionRepository, + enabledSecurityProperties(), + new ObjectMapper(), + FIXED_CLOCK + ); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/v1/auth/me"); + MockHttpServletResponse response = new MockHttpServletResponse(); + + boolean allowed = interceptor.preHandle(request, response, new Object()); + + Assertions.assertFalse(allowed); + Assertions.assertEquals(401, response.getStatus()); + Assertions.assertTrue(response.getContentAsString().contains("\"code\":" + ErrorCode.SESSION_INVALID.getCode())); + Assertions.assertTrue(response.getContentAsString().contains("session token missing")); + Mockito.verifyNoInteractions(authSessionRepository); + } + + @Test + void shouldReturnSessionInvalidWhenSessionIsExpired() throws Exception { + AuthSessionRepository authSessionRepository = Mockito.mock(AuthSessionRepository.class); + AuthSessionEntity expired = new AuthSessionEntity(); + expired.setSessionToken("token-expired-001"); + expired.setRoleCode("AUDIT_ADMIN"); + expired.setAuthLevel("LIMITED"); + expired.setExpiresAt(LocalDateTime.ofInstant(FIXED_CLOCK.instant(), ZoneOffset.UTC).minusMinutes(1)); + Mockito.when(authSessionRepository.findBySessionToken("token-expired-001")).thenReturn(Optional.of(expired)); + + InternalApiAuthInterceptor interceptor = new InternalApiAuthInterceptor( + authSessionRepository, + enabledSecurityProperties(), + new ObjectMapper(), + FIXED_CLOCK + ); + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/v1/auth/me"); + request.addHeader("X-Session-Token", "token-expired-001"); + MockHttpServletResponse response = new MockHttpServletResponse(); + + boolean allowed = interceptor.preHandle(request, response, new Object()); + + Assertions.assertFalse(allowed); + Assertions.assertEquals(401, response.getStatus()); + Assertions.assertTrue(response.getContentAsString().contains("\"code\":" + ErrorCode.SESSION_INVALID.getCode())); + Assertions.assertTrue(response.getContentAsString().contains("session expired")); + } + + private static TmsSecurityProperties enabledSecurityProperties() { + TmsSecurityProperties properties = new TmsSecurityProperties(); + properties.getInternalAuth().setEnabled(true); + return properties; + } +}