From 9cb9b4305245c9701116d0de07e92468220c62b6 Mon Sep 17 00:00:00 2001 From: liulu Date: Wed, 18 Dec 2024 09:25:41 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E7=A7=B0=E8=BF=90=E7=AE=97=E7=B1=BB?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chsm-common/pom.xml | 6 +- .../com/sunyard/chsm/sdf/SdfApiService.java | 14 +- .../sunyard/chsm/sdf/util/PaddingUtil.java | 38 ++- .../java/com/sunyard/chsm/enums/AlgMode.java | 72 +++--- .../java/com/sunyard/chsm/enums/HashAlg.java | 10 + .../java/com/sunyard/chsm/enums/KeyAlg.java | 0 .../com/sunyard/chsm/enums/KeyCategory.java | 0 .../com/sunyard/chsm/enums/KeyStatus.java | 0 .../java/com/sunyard/chsm/enums/Padding.java | 72 +++--- .../com/sunyard/chsm/param/AppTokenReq.java | 0 .../com/sunyard/chsm/param/AppTokenResp.java | 0 .../com/sunyard/chsm/param/KeyCreateReq.java | 0 .../com/sunyard/chsm/param/KeyInfoQuery.java | 0 .../com/sunyard/chsm/param/KeyInfoResp.java | 2 +- .../com/sunyard/chsm/param/KeyManageReq.java | 0 .../com/sunyard/chsm/param/KeyUpdateReq.java | 0 .../com/sunyard/chsm/param/SymDecryptReq.java | 37 +++ .../sunyard/chsm/param/SymDecryptResp.java | 13 + .../com/sunyard/chsm/param/SymEncryptReq.java | 30 +++ .../sunyard/chsm/param/SymEncryptResp.java | 14 ++ .../sunyard/chsm/param/SymHmacCheckReq.java | 33 +++ .../sunyard/chsm/param/SymHmacCheckResp.java | 10 + .../com/sunyard/chsm/param/SymHmacReq.java | 22 ++ .../com/sunyard/chsm/param/SymHmacResp.java | 15 ++ .../sunyard/chsm/param/SymMacCheckReq.java | 37 +++ .../sunyard/chsm/param/SymMacCheckResp.java | 10 + .../com/sunyard/chsm/param/SymMacReq.java | 26 ++ .../com/sunyard/chsm/param/SymMacResp.java | 15 ++ .../sunyard/chsm/sdf/SingleSdfApiService.java | 14 +- .../src/test/java/sdf/SdfApiServiceTest.java | 17 +- chsm-web-server/pom.xml | 5 - .../chsm/controller/AppLoginController.java | 12 +- .../chsm/controller/KeyManageController.java | 2 +- .../chsm/controller/SymKeyController.java | 110 +++++++++ .../chsm/pool/LoadBalancedSdfApiService.java | 15 +- .../chsm/service/KeyManageService.java | 10 +- .../sunyard/chsm/service/SymKeyService.java | 231 ++++++++++++++++++ .../src/test/java/api/BaseTest.java | 70 ++++++ .../src/test/java/api/KeyManageTest.java | 48 ++++ 39 files changed, 891 insertions(+), 119 deletions(-) rename {chsm-common => chsm-params}/src/main/java/com/sunyard/chsm/enums/AlgMode.java (94%) create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/enums/HashAlg.java rename {chsm-common => chsm-params}/src/main/java/com/sunyard/chsm/enums/KeyAlg.java (100%) rename {chsm-common => chsm-params}/src/main/java/com/sunyard/chsm/enums/KeyCategory.java (100%) rename {chsm-common => chsm-params}/src/main/java/com/sunyard/chsm/enums/KeyStatus.java (100%) rename {chsm-common => chsm-params}/src/main/java/com/sunyard/chsm/enums/Padding.java (95%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/AppTokenReq.java (100%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/AppTokenResp.java (100%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/KeyCreateReq.java (100%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/KeyInfoQuery.java (100%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java (97%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/KeyManageReq.java (100%) rename {chsm-web-server => chsm-params}/src/main/java/com/sunyard/chsm/param/KeyUpdateReq.java (100%) create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptResp.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptResp.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckResp.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacResp.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckResp.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymMacReq.java create mode 100644 chsm-params/src/main/java/com/sunyard/chsm/param/SymMacResp.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/controller/SymKeyController.java create mode 100644 chsm-web-server/src/main/java/com/sunyard/chsm/service/SymKeyService.java create mode 100644 chsm-web-server/src/test/java/api/BaseTest.java create mode 100644 chsm-web-server/src/test/java/api/KeyManageTest.java diff --git a/chsm-common/pom.xml b/chsm-common/pom.xml index 62d71f0..748f20d 100644 --- a/chsm-common/pom.xml +++ b/chsm-common/pom.xml @@ -20,6 +20,11 @@ + + com.sunyard.chsm + chsm-params + ${project.version} + org.springframework.boot spring-boot-starter-web @@ -66,5 +71,4 @@ - \ No newline at end of file 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 f859b45..0ae4b07 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 @@ -1,6 +1,7 @@ package com.sunyard.chsm.sdf; +import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.model.EccCipher; import com.sunyard.chsm.sdf.model.EccKey; @@ -40,6 +41,7 @@ public interface SdfApiService { * 对称加密 * * @param alg 算法,只支持对称算法 + * @param padding * @param key 密钥值,明文 * @param data 原始数据 */ @@ -47,22 +49,22 @@ public interface SdfApiService { // // byte[] symEncrypt(KeyAlg alg, byte[] key, byte[] data); - byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data); + byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data); /** * 对称解密 * - * @param alg 算法,只支持对称算法 - * @param key 密钥值,明文 * @param mode 轮模式 + * @param alg 算法,只支持对称算法 * @param padding 填充模式 + * @param key 密钥值,明文 * @param data 密文数据 */ // byte[] symDecrypt(KeyAlg alg, AlgMode mode, Padding padding, byte[] key, byte[] data); // // byte[] symDecrypt(KeyAlg alg, byte[] key, byte[] data); - byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data); + byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data); /** @@ -119,12 +121,14 @@ public interface SdfApiService { /** * 计算MAC * + * @param algId algId + * @param padding padding * @param symKey 用户指定的密钥 * @param pucIv 缓冲区指针,用于存放输入和返回的IV数据 * @param pucData 缓冲区指针,用于存放输入的数据明文 * @return pucEncData 返回MAC值 | puiLength 返回MAC值长度 */ - byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData); + byte[] calculateMAC(AlgId algId, Padding padding, byte[] symKey, byte[] pucIv, byte[] pucData); byte[] hmac(byte[] key, byte[] srcData); diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/util/PaddingUtil.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/util/PaddingUtil.java index 7a71309..e8da295 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/util/PaddingUtil.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/util/PaddingUtil.java @@ -1,10 +1,38 @@ package com.sunyard.chsm.sdf.util; +import com.sunyard.chsm.enums.Padding; + /** * @author liulu */ public abstract class PaddingUtil { + + public static byte[] padding(Padding padding, byte[] data) { + switch (padding) { + case NOPadding: + return data; + case PCKS5Padding: + return PKCS5Padding(data); + case PCKS7Padding: + return PKCS7Padding(data); + } + return null; + } + + public static byte[] unpadding(Padding padding, byte[] data) { + switch (padding) { + case NOPadding: + return data; + case PCKS5Padding: + return PKCS5Unpadding(data); + case PCKS7Padding: + return PKCS7Unpadding(data); + } + return null; + } + + public static byte[] PKCS7Padding(byte[] content, int blockSize) { if (content == null || blockSize < 8) throw new IllegalStateException("parameter error"); @@ -22,7 +50,7 @@ public abstract class PaddingUtil { return padded; } - public static byte[] PKCS7Padding(byte[] content) { + public static byte[] PKCS7Padding(byte[] content) { try { return PKCS7Padding(content, 16); } catch (Exception e) { @@ -34,10 +62,9 @@ public abstract class PaddingUtil { return PKCS7Padding(content, 8); } - public static byte[] PKCS7Unpadding(byte[] content, int blockSize) - throws Exception { + public static byte[] PKCS7Unpadding(byte[] content, int blockSize) { if (blockSize < 8 || content == null || content.length % blockSize != 0) - throw new Exception("parameter error"); + throw new IllegalStateException("parameter error"); return PKCS7Unpadding(content); } @@ -57,9 +84,8 @@ public abstract class PaddingUtil { return unpadded; } - public static byte[] PKCS5Unpadding(byte[] content) throws Exception { + public static byte[] PKCS5Unpadding(byte[] content) { return PKCS7Unpadding(content, 8); } - } diff --git a/chsm-common/src/main/java/com/sunyard/chsm/enums/AlgMode.java b/chsm-params/src/main/java/com/sunyard/chsm/enums/AlgMode.java similarity index 94% rename from chsm-common/src/main/java/com/sunyard/chsm/enums/AlgMode.java rename to chsm-params/src/main/java/com/sunyard/chsm/enums/AlgMode.java index e0bda2e..0a88630 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/enums/AlgMode.java +++ b/chsm-params/src/main/java/com/sunyard/chsm/enums/AlgMode.java @@ -1,36 +1,36 @@ -package com.sunyard.chsm.enums; - - -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Objects; - - -/** - * 算法的轮模式 - * @author Cheney - */ -@Getter -@AllArgsConstructor -public enum AlgMode { - ECB("ECB", "ECB"), - CBC( "CBC", "CBC"), - ; - - private final String code; - private final String desc; - - - public static AlgMode of(String code) { - if (code == null || code.trim().isEmpty()) { - return null; - } - - return Arrays.stream(AlgMode.values()) - .filter(it -> Objects.equals(it.getCode(), code)) - .findFirst() - .orElse(null); - } -} +package com.sunyard.chsm.enums; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + + +/** + * 算法的轮模式 + * @author Cheney + */ +@Getter +@AllArgsConstructor +public enum AlgMode { + ECB("ECB", "ECB"), + CBC( "CBC", "CBC"), + ; + + private final String code; + private final String desc; + + + public static AlgMode of(String code) { + if (code == null || code.trim().isEmpty()) { + return null; + } + + return Arrays.stream(AlgMode.values()) + .filter(it -> Objects.equals(it.getCode(), code)) + .findFirst() + .orElse(null); + } +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/enums/HashAlg.java b/chsm-params/src/main/java/com/sunyard/chsm/enums/HashAlg.java new file mode 100644 index 0000000..720b80f --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/enums/HashAlg.java @@ -0,0 +1,10 @@ +package com.sunyard.chsm.enums; + +/** + * @author liulu + * @since 2024/10/22 + */ +public enum HashAlg { + SM3, + ; +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/enums/KeyAlg.java b/chsm-params/src/main/java/com/sunyard/chsm/enums/KeyAlg.java similarity index 100% rename from chsm-common/src/main/java/com/sunyard/chsm/enums/KeyAlg.java rename to chsm-params/src/main/java/com/sunyard/chsm/enums/KeyAlg.java diff --git a/chsm-common/src/main/java/com/sunyard/chsm/enums/KeyCategory.java b/chsm-params/src/main/java/com/sunyard/chsm/enums/KeyCategory.java similarity index 100% rename from chsm-common/src/main/java/com/sunyard/chsm/enums/KeyCategory.java rename to chsm-params/src/main/java/com/sunyard/chsm/enums/KeyCategory.java diff --git a/chsm-common/src/main/java/com/sunyard/chsm/enums/KeyStatus.java b/chsm-params/src/main/java/com/sunyard/chsm/enums/KeyStatus.java similarity index 100% rename from chsm-common/src/main/java/com/sunyard/chsm/enums/KeyStatus.java rename to chsm-params/src/main/java/com/sunyard/chsm/enums/KeyStatus.java diff --git a/chsm-common/src/main/java/com/sunyard/chsm/enums/Padding.java b/chsm-params/src/main/java/com/sunyard/chsm/enums/Padding.java similarity index 95% rename from chsm-common/src/main/java/com/sunyard/chsm/enums/Padding.java rename to chsm-params/src/main/java/com/sunyard/chsm/enums/Padding.java index d699421..87fbbae 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/enums/Padding.java +++ b/chsm-params/src/main/java/com/sunyard/chsm/enums/Padding.java @@ -1,36 +1,36 @@ -package com.sunyard.chsm.enums; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Objects; - -/** - * 数据的填充模式 - * - * @author Cheney - */ -@Getter -@AllArgsConstructor -public enum Padding { - NOPadding("NoPadding", "NoPadding"), - PCKS5Padding("PKCS5Padding", "PKCS5Padding"), - PCKS7Padding("PKCS7Padding", "PKCS7Padding"), - ; - - private final String code; - private final String desc; - - - public static Padding of(String code) { - if (code == null || code.trim().isEmpty()) { - return null; - } - - return Arrays.stream(Padding.values()) - .filter(it -> Objects.equals(it.getCode(), code)) - .findFirst() - .orElse(null); - } -} +package com.sunyard.chsm.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 数据的填充模式 + * + * @author Cheney + */ +@Getter +@AllArgsConstructor +public enum Padding { + NOPadding("NoPadding", "NoPadding"), + PCKS5Padding("PKCS5Padding", "PKCS5Padding"), + PCKS7Padding("PKCS7Padding", "PKCS7Padding"), + ; + + private final String code; + private final String desc; + + + public static Padding of(String code) { + if (code == null || code.trim().isEmpty()) { + return null; + } + + return Arrays.stream(Padding.values()) + .filter(it -> Objects.equals(it.getCode(), code)) + .findFirst() + .orElse(null); + } +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/AppTokenReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenReq.java similarity index 100% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/AppTokenReq.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenReq.java diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/AppTokenResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenResp.java similarity index 100% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/AppTokenResp.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/AppTokenResp.java diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyCreateReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/KeyCreateReq.java similarity index 100% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyCreateReq.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/KeyCreateReq.java diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyInfoQuery.java b/chsm-params/src/main/java/com/sunyard/chsm/param/KeyInfoQuery.java similarity index 100% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyInfoQuery.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/KeyInfoQuery.java diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java similarity index 97% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java index c33f54d..69f820d 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/KeyInfoResp.java @@ -15,7 +15,7 @@ public class KeyInfoResp { /** * 密钥ID */ - private String KeyId; + private Long KeyId; /** * 密钥算法 */ diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyManageReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/KeyManageReq.java similarity index 100% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyManageReq.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/KeyManageReq.java diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyUpdateReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/KeyUpdateReq.java similarity index 100% rename from chsm-web-server/src/main/java/com/sunyard/chsm/param/KeyUpdateReq.java rename to chsm-params/src/main/java/com/sunyard/chsm/param/KeyUpdateReq.java diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptReq.java new file mode 100644 index 0000000..c3a9cfb --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptReq.java @@ -0,0 +1,37 @@ +package com.sunyard.chsm.param; + +import com.sunyard.chsm.enums.AlgMode; +import com.sunyard.chsm.enums.Padding; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class SymDecryptReq { + // 密钥ID + @NotNull(message = "密钥ID不能为空") + private Long keyId; + + // 密钥索引 + @NotEmpty(message = "密钥索引不能为空") + @Size(min = 15, max = 24, message = "密钥索引长度在15-24") + private String keyIndex; + + // 密文,使用Base64编码 + @NotBlank(message = "密文不能为空") + private String cipherData; + + // 填充方式, 默认PCKS7 + @NotNull(message = "填充方式不能为空") + private Padding padding = Padding.PCKS7Padding; + + // 加密模式, 默认ECB + @NotNull(message = "加密模式不能为空") + private AlgMode mode = AlgMode.ECB; + + // iv,CBC模式下不能为空,Base64编码 + private String iv; +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptResp.java new file mode 100644 index 0000000..e033f1b --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymDecryptResp.java @@ -0,0 +1,13 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +@Data +public class SymDecryptResp { + // 密钥ID + private Long keyId; + // 密钥索引 + private String keyIndex; + // 明文,使用Base64编码 + private String plainData; +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptReq.java new file mode 100644 index 0000000..1614834 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptReq.java @@ -0,0 +1,30 @@ +package com.sunyard.chsm.param; + +import com.sunyard.chsm.enums.AlgMode; +import com.sunyard.chsm.enums.Padding; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +public class SymEncryptReq { + + // 密钥ID + @NotNull(message = "密钥ID不能为空") + private Long keyId; + + // 待加密明文,使用Base64编码 + @NotBlank(message = "明文不能为空") + private String plainData; + + // 填充方式, 默认PCKS7 + private Padding padding = Padding.PCKS7Padding; + + // 加密模式, 默认ECB + @NotNull(message = "加密模式不能为空") + private AlgMode mode = AlgMode.ECB; + + // iv, CBC模式下不能为空,Base64编码 + private String iv; +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptResp.java new file mode 100644 index 0000000..0e1c30d --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymEncryptResp.java @@ -0,0 +1,14 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +@Data +public class SymEncryptResp { + // 密钥ID + private Long keyId; + // 密钥索引 + private String keyIndex; + + // 密文,使用Base64编码 + private String cipherData; +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckReq.java new file mode 100644 index 0000000..d1ff680 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckReq.java @@ -0,0 +1,33 @@ +package com.sunyard.chsm.param; + +import com.sunyard.chsm.enums.HashAlg; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class SymHmacCheckReq { + // 密钥ID + @NotNull(message = "密钥ID不能为空") + private Long keyId; + + // 密钥索引 + @NotEmpty(message = "密钥索引不能为空") + @Size(min = 15, max = 24, message = "密钥索引长度在15-24") + private String keyIndex; + + // Hash算法,默认SM3 + private HashAlg hashAlg = HashAlg.SM3; + + // 明文,使用Base64编码 + @NotBlank(message = "明文不能为空") + private String plainData; + + // hmac值,使用Base64编码 + @NotBlank(message = "hmac不能为空") + private String hmac; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckResp.java new file mode 100644 index 0000000..37b89b0 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacCheckResp.java @@ -0,0 +1,10 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +@Data +public class SymHmacCheckResp { + + private Boolean valid; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacReq.java new file mode 100644 index 0000000..c9644f4 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacReq.java @@ -0,0 +1,22 @@ +package com.sunyard.chsm.param; + +import com.sunyard.chsm.enums.HashAlg; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +public class SymHmacReq { + // 密钥ID + @NotNull(message = "密钥ID不能为空") + private Long keyId; + + // 明文,使用Base64编码 + @NotBlank(message = "明文不能为空") + private String plainData; + + // Hash算法,默认SM3 + private HashAlg hashAlg = HashAlg.SM3; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacResp.java new file mode 100644 index 0000000..ee0f9ba --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymHmacResp.java @@ -0,0 +1,15 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +@Data +public class SymHmacResp { + // 密钥ID + private Long keyId; + // 密钥索引 + private String keyIndex; + + // hmac值,使用Base64编码 + private String hmac; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckReq.java new file mode 100644 index 0000000..e2b28d0 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckReq.java @@ -0,0 +1,37 @@ +package com.sunyard.chsm.param; + +import com.sunyard.chsm.enums.Padding; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +@Data +public class SymMacCheckReq { + // 密钥ID + @NotNull(message = "密钥ID不能为空") + private Long keyId; + + // 密钥索引 + @NotEmpty(message = "密钥索引不能为空") + @Size(min = 15, max = 24, message = "密钥索引长度在15-24") + private String keyIndex; + + // 填充方式, 默认PCKS7 + private Padding padding = Padding.PCKS7Padding; + + // iv,Base64编码 + @NotBlank(message = "iv不能为空") + private String iv; + + // 明文,使用Base64编码 + @NotBlank(message = "明文不能为空") + private String plainData; + + // mac值,使用Base64编码 + @NotBlank(message = "mac不能为空") + private String mac; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckResp.java new file mode 100644 index 0000000..3a2a3c1 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacCheckResp.java @@ -0,0 +1,10 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +@Data +public class SymMacCheckResp { + + private Boolean valid; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacReq.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacReq.java new file mode 100644 index 0000000..c4fa7a0 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacReq.java @@ -0,0 +1,26 @@ +package com.sunyard.chsm.param; + +import com.sunyard.chsm.enums.Padding; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Data +public class SymMacReq { + // 密钥ID + @NotNull(message = "密钥ID不能为空") + private Long keyId; + + // 明文,使用Base64编码 + @NotBlank(message = "明文不能为空") + private String plainData; + + // 填充方式, 默认PCKS7 + private Padding padding = Padding.PCKS7Padding; + + // iv,Base64编码 + @NotBlank(message = "iv不能为空") + private String iv; + +} diff --git a/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacResp.java b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacResp.java new file mode 100644 index 0000000..45d0dc1 --- /dev/null +++ b/chsm-params/src/main/java/com/sunyard/chsm/param/SymMacResp.java @@ -0,0 +1,15 @@ +package com.sunyard.chsm.param; + +import lombok.Data; + +@Data +public class SymMacResp { + // 密钥ID + private Long keyId; + // 密钥索引 + private String keyIndex; + + // mac值,使用Base64编码 + private String mac; + +} diff --git a/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java b/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java index 91df4ff..80cdbf5 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java +++ b/chsm-web-manage/src/main/java/com/sunyard/chsm/sdf/SingleSdfApiService.java @@ -1,6 +1,7 @@ package com.sunyard.chsm.sdf; import com.sunyard.chsm.enums.DeviceTmkStatus; +import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.mapper.SpDeviceMapper; import com.sunyard.chsm.model.dto.DeviceCheckRes; import com.sunyard.chsm.model.entity.Device; @@ -62,9 +63,9 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean { } @Override - public byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) { + public byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { checkStatus(); - byte[] pad = PaddingUtil.PKCS7Padding(data); + byte[] pad = PaddingUtil.padding(padding, data); String hk = sdfApiAdapter.importKey(sessionHandle, key); byte[] encrypt = sdfApiAdapter.symEncrypt(sessionHandle, hk, alg, iv, pad); sdfApiAdapter.destroyKey(sessionHandle, hk); @@ -72,12 +73,12 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean { } @Override - public byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) { + public byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { checkStatus(); String hk = sdfApiAdapter.importKey(sessionHandle, key); byte[] decrypt = sdfApiAdapter.symDecrypt(sessionHandle, hk, alg, iv, data); sdfApiAdapter.destroyKey(sessionHandle, hk); - return PaddingUtil.PKCS7Unpadding(decrypt); + return PaddingUtil.unpadding(padding, decrypt); } @Override @@ -138,10 +139,11 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean { } @Override - public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) { + public byte[] calculateMAC(AlgId algId, Padding padding, byte[] symKey, byte[] pucIv, byte[] pucData) { checkStatus(); + byte[] pad = PaddingUtil.padding(padding, pucData); String hk = sdfApiAdapter.importKey(sessionHandle, symKey); - byte[] mac = sdfApiAdapter.calculateMAC(sessionHandle, hk, AlgId.SGD_SM4_MAC, pucIv, PaddingUtil.PKCS7Padding(pucData)); + byte[] mac = sdfApiAdapter.calculateMAC(sessionHandle, hk, algId, pucIv, pad); sdfApiAdapter.destroyKey(sessionHandle, hk); return mac; } diff --git a/chsm-web-manage/src/test/java/sdf/SdfApiServiceTest.java b/chsm-web-manage/src/test/java/sdf/SdfApiServiceTest.java index 7702b8b..87c8537 100644 --- a/chsm-web-manage/src/test/java/sdf/SdfApiServiceTest.java +++ b/chsm-web-manage/src/test/java/sdf/SdfApiServiceTest.java @@ -1,6 +1,7 @@ package sdf; import com.sunyard.chsm.enums.ManufacturerModelEnum; +import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.sdf.SdfApiService; import com.sunyard.chsm.sdf.SingleSdfApiService; import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory; @@ -92,35 +93,35 @@ public class SdfApiServiceTest { @Test public void testSymEncAndDec() { - byte[] ecbCipher = sdfService.symEncrypt(AlgId.SGD_SM4_ECB, symKey, null, plain.getBytes()); - byte[] ecbPlain = sdfService.symDecrypt(AlgId.SGD_SM4_ECB, symKey, null, ecbCipher); + byte[] ecbCipher = sdfService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, plain.getBytes()); + byte[] ecbPlain = sdfService.symDecrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, ecbCipher); log.info("ecb_cipher: {}", CodecUtils.encodeHex(ecbCipher)); Assertions.assertEquals(plain, new String(ecbPlain)); - byte[] cbcCipher = sdfService.symEncrypt(AlgId.SGD_SM4_CBC, symKey, iv, plain.getBytes()); + byte[] cbcCipher = sdfService.symEncrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, plain.getBytes()); log.info("cbc_cipher: {}", CodecUtils.encodeHex(cbcCipher)); - byte[] cbcPlain = sdfService.symDecrypt(AlgId.SGD_SM4_CBC, symKey, iv, cbcCipher); + byte[] cbcPlain = sdfService.symDecrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, cbcCipher); Assertions.assertEquals(plain, new String(cbcPlain)); Assertions.assertArrayEquals(ecbPlain, cbcPlain); Assertions.assertNotEquals(CodecUtils.encodeHex(ecbCipher), CodecUtils.encodeHex(cbcCipher)); - byte[] bcEcbCipher = bcService.symEncrypt(AlgId.SGD_SM4_ECB, symKey, null, plain.getBytes()); + byte[] bcEcbCipher = bcService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, plain.getBytes()); log.info("bc_ecb_cipher: {}", CodecUtils.encodeHex(bcEcbCipher)); Assertions.assertArrayEquals(ecbCipher, bcEcbCipher); - byte[] bcCbcCipher = bcService.symEncrypt(AlgId.SGD_SM4_CBC, symKey, iv, plain.getBytes()); + byte[] bcCbcCipher = bcService.symEncrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, plain.getBytes()); log.info("bc_cbc_cipher: {}", CodecUtils.encodeHex(bcCbcCipher)); Assertions.assertArrayEquals(cbcCipher, bcCbcCipher); } @Test public void testSm4Mac() { - byte[] sdfMac = sdfService.calculateMAC(symKey, iv, plain.getBytes()); + byte[] sdfMac = sdfService.calculateMAC(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes()); log.info("sdf mac: {}", CodecUtils.encodeHex(sdfMac)); Assertions.assertEquals(16, sdfMac.length); - byte[] bcMac = bcService.calculateMAC(symKey, iv, plain.getBytes()); + byte[] bcMac = bcService.calculateMAC(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes()); log.info("bc mac: {}", CodecUtils.encodeHex(bcMac)); Assertions.assertEquals(16, bcMac.length); diff --git a/chsm-web-server/pom.xml b/chsm-web-server/pom.xml index cf45222..69c9382 100644 --- a/chsm-web-server/pom.xml +++ b/chsm-web-server/pom.xml @@ -24,11 +24,6 @@ chsm-common ${project.version} - - com.sunyard.chsm - chsm-params - ${project.version} - com.dm diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java index 3751c89..054c6c1 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/AppLoginController.java @@ -1,5 +1,6 @@ package com.sunyard.chsm.controller; +import com.sunyard.chsm.model.R; import com.sunyard.chsm.param.AppTokenReq; import com.sunyard.chsm.param.AppTokenResp; import com.sunyard.chsm.service.AppLoginService; @@ -35,15 +36,16 @@ public class AppLoginController { * @return */ @PostMapping("/appUser/getAppToken") - public AppTokenResp getAppToken(@Valid @RequestBody AppTokenReq appTokenReq) { - return appLoginService.getAppToken(appTokenReq); + public R getAppToken(@Valid @RequestBody AppTokenReq appTokenReq) { + AppTokenResp appToken = appLoginService.getAppToken(appTokenReq); + return R.data(appToken); } @PostMapping("/appUser/getAppTokenTest") - public AppTokenResp getAppTokenForTest(@Valid @RequestBody AppTokenReq appTokenReq) { - return appLoginService.getAppTokenForTest(appTokenReq); + public R getAppTokenForTest(@Valid @RequestBody AppTokenReq appTokenReq) { + AppTokenResp appToken = appLoginService.getAppTokenForTest(appTokenReq); + return R.data(appToken); } - } diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java index dbc44ea..775985c 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/KeyManageController.java @@ -51,7 +51,7 @@ public class KeyManageController { * @return 分页列表 */ @PostMapping("/info") - public R queryInfo(Long id) { + public R queryInfo(@RequestBody Long id) { Assert.notNull(id, "密钥id不能为空"); KeyInfoResp resp = keyManageService.queryInfo(id); return R.data(resp); diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/controller/SymKeyController.java b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/SymKeyController.java new file mode 100644 index 0000000..ce68555 --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/controller/SymKeyController.java @@ -0,0 +1,110 @@ +package com.sunyard.chsm.controller; + +import com.sunyard.chsm.model.R; +import com.sunyard.chsm.param.SymDecryptReq; +import com.sunyard.chsm.param.SymDecryptResp; +import com.sunyard.chsm.param.SymEncryptReq; +import com.sunyard.chsm.param.SymEncryptResp; +import com.sunyard.chsm.param.SymHmacCheckReq; +import com.sunyard.chsm.param.SymHmacCheckResp; +import com.sunyard.chsm.param.SymHmacReq; +import com.sunyard.chsm.param.SymHmacResp; +import com.sunyard.chsm.param.SymMacCheckReq; +import com.sunyard.chsm.param.SymMacCheckResp; +import com.sunyard.chsm.param.SymMacReq; +import com.sunyard.chsm.param.SymMacResp; +import com.sunyard.chsm.service.SymKeyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * 对称运算类接口 + * + * @author liulu + * @since 2024/12/17 + */ +@RestController +@RequestMapping("/sym") +public class SymKeyController { + + @Autowired + private SymKeyService symKeyService; + + /** + * 对称加密 + * + * @param req + * @return + */ + @PostMapping("/encrypt") + public R encrypt(@Valid @RequestBody SymEncryptReq req) { + SymEncryptResp resp = symKeyService.encrypt(req); + return R.data(resp); + } + + /** + * 对称解密 + * + * @param req + * @return + */ + @PostMapping("/decrypt") + public R decrypt(@Valid @RequestBody SymDecryptReq req) { + SymDecryptResp resp = symKeyService.decrypt(req); + return R.data(resp); + } + + /** + * 计算Hmac + * + * @param req + * @return + */ + @PostMapping("/hmac") + public R hmac(@Valid @RequestBody SymHmacReq req) { + SymHmacResp resp = symKeyService.hmac(req); + return R.data(resp); + } + + /** + * 验证Hmac + * + * @param req + * @return + */ + @PostMapping("/hmac/check") + public R macCheck(@Valid @RequestBody SymHmacCheckReq req) { + SymHmacCheckResp resp = symKeyService.hmacCheck(req); + return R.data(resp); + } + + /** + * 计算Hmac + * + * @param req + * @return + */ + @PostMapping("/mac") + public R mac(@Valid @RequestBody SymMacReq req) { + SymMacResp resp = symKeyService.mac(req); + return R.data(resp); + } + + /** + * 验证Hmac + * + * @param req + * @return + */ + @PostMapping("/mac/check") + public R macCheck(@Valid @RequestBody SymMacCheckReq req) { + SymMacCheckResp resp = symKeyService.macCheck(req); + return R.data(resp); + } + +} diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java index b5aab50..5bb23a7 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/LoadBalancedSdfApiService.java @@ -1,5 +1,6 @@ package com.sunyard.chsm.pool; +import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.sdf.SdfApiService; import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.model.EccCipher; @@ -46,24 +47,25 @@ public class LoadBalancedSdfApiService implements SdfApiService { } @Override - public byte[] symEncrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) { + public byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { + byte[] paddingData = PaddingUtil.padding(padding, data); return apply(s -> { String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key); - byte[] encrypt = s.getSdfApiAdapter().symEncrypt(s.getSessionHandle(), keyHandle, alg, iv, PaddingUtil.PKCS7Padding(data)); + byte[] encrypt = s.getSdfApiAdapter().symEncrypt(s.getSessionHandle(), keyHandle, alg, iv, paddingData); s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle); return encrypt; }); } @Override - public byte[] symDecrypt(AlgId alg, byte[] key, byte[] iv, byte[] data) { + public byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { byte[] decrypt = apply(s -> { String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key); byte[] d = s.getSdfApiAdapter().symDecrypt(s.getSessionHandle(), keyHandle, alg, iv, data); s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle); return d; }); - return PaddingUtil.PKCS7Unpadding(decrypt); + return PaddingUtil.unpadding(padding, decrypt); } @Override @@ -125,10 +127,11 @@ public class LoadBalancedSdfApiService implements SdfApiService { } @Override - public byte[] calculateMAC(byte[] symKey, byte[] pucIv, byte[] pucData) { + public byte[] calculateMAC(AlgId algId, Padding padding, byte[] symKey, byte[] pucIv, byte[] pucData) { + byte[] pad = PaddingUtil.padding(padding, pucData); return apply(s -> { String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), symKey); - byte[] mac = s.getSdfApiAdapter().calculateMAC(s.getSessionHandle(), keyHandle, AlgId.SGD_SM4_MAC, pucIv, PaddingUtil.PKCS7Padding(pucData)); + byte[] mac = s.getSdfApiAdapter().calculateMAC(s.getSessionHandle(), keyHandle, algId, pucIv, pad); s.getSdfApiAdapter().destroyKey(s.getSessionHandle(), keyHandle); return mac; }); diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/service/KeyManageService.java b/chsm-web-server/src/main/java/com/sunyard/chsm/service/KeyManageService.java index 4373756..0d77c92 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/service/KeyManageService.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/service/KeyManageService.java @@ -3,6 +3,8 @@ package com.sunyard.chsm.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.sunyard.chsm.auth.AppUser; import com.sunyard.chsm.auth.UserContext; +import com.sunyard.chsm.enums.KeyCategory; +import com.sunyard.chsm.enums.KeyStatus; import com.sunyard.chsm.mapper.KeyInfoMapper; import com.sunyard.chsm.model.dto.KeyInfoDTO; import com.sunyard.chsm.model.entity.KeyInfo; @@ -22,6 +24,7 @@ import javax.annotation.Resource; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; /** @@ -40,7 +43,8 @@ public class KeyManageService { public Page queryPageList(KeyInfoQuery query) { KeyInfoDTO.Query nq = new KeyInfoDTO.Query(); - BeanUtils.copyProperties(query, nq); + nq.setKeyType(Optional.ofNullable(query.getKeyType()).map(KeyCategory::getCode).orElse(null)); + nq.setStatus(Optional.ofNullable(query.getStatus()).map(KeyStatus::getCode).orElse(null)); nq.setAppId(UserContext.getCurrentAppId()); Page page = keyInfoService.selectPageList(nq); List records = page.getRecords(); @@ -51,7 +55,7 @@ public class KeyManageService { .map(it -> { KeyInfoResp resp = new KeyInfoResp(); BeanUtils.copyProperties(it, resp); - resp.setKeyId(it.getCode()); + resp.setKeyId(it.getId()); return resp; }) .collect(Collectors.toList()); @@ -64,7 +68,7 @@ public class KeyManageService { KeyInfoDTO.KeyView view = keyInfoService.selectById(id); KeyInfoResp resp = new KeyInfoResp(); BeanUtils.copyProperties(view, resp); - resp.setKeyId(view.getCode()); + resp.setKeyId(view.getId()); return resp; } diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/service/SymKeyService.java b/chsm-web-server/src/main/java/com/sunyard/chsm/service/SymKeyService.java new file mode 100644 index 0000000..a5f3598 --- /dev/null +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/service/SymKeyService.java @@ -0,0 +1,231 @@ +package com.sunyard.chsm.service; + +import com.sunyard.chsm.auth.UserContext; +import com.sunyard.chsm.enums.AlgMode; +import com.sunyard.chsm.enums.KeyAlg; +import com.sunyard.chsm.enums.KeyCategory; +import com.sunyard.chsm.enums.KeyStatus; +import com.sunyard.chsm.enums.KeyUsage; +import com.sunyard.chsm.enums.Padding; +import com.sunyard.chsm.mapper.KeyInfoMapper; +import com.sunyard.chsm.mapper.SpKeyRecordMapper; +import com.sunyard.chsm.model.entity.KeyInfo; +import com.sunyard.chsm.model.entity.KeyRecord; +import com.sunyard.chsm.param.SymDecryptReq; +import com.sunyard.chsm.param.SymDecryptResp; +import com.sunyard.chsm.param.SymEncryptReq; +import com.sunyard.chsm.param.SymEncryptResp; +import com.sunyard.chsm.param.SymHmacCheckReq; +import com.sunyard.chsm.param.SymHmacCheckResp; +import com.sunyard.chsm.param.SymHmacReq; +import com.sunyard.chsm.param.SymHmacResp; +import com.sunyard.chsm.param.SymMacCheckReq; +import com.sunyard.chsm.param.SymMacCheckResp; +import com.sunyard.chsm.param.SymMacReq; +import com.sunyard.chsm.param.SymMacResp; +import com.sunyard.chsm.sdf.SdfApiService; +import com.sunyard.chsm.sdf.context.AlgId; +import com.sunyard.chsm.utils.CodecUtils; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.util.Assert; + +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Objects; + +/** + * @author liulu + * @since 2024/12/17 + */ +@Service +@RequiredArgsConstructor +public class SymKeyService { + + private final KeyInfoMapper keyInfoMapper; + private final SpKeyRecordMapper spKeyRecordMapper; + private final SdfApiService sdfApiService; + + + public SymEncryptResp encrypt(SymEncryptReq req) { + byte[] iv = new byte[0]; + if (AlgMode.CBC == req.getMode()) { + Assert.hasText(req.getIv(), "CBC模式iv不能为空"); + byte[] bytes = CodecUtils.decodeBase64(req.getIv()); + Assert.isTrue(bytes.length >= 16, "iv长度至少为16"); + iv = bytes; + } + byte[] plain = CodecUtils.decodeBase64(req.getPlainData()); + KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.ENCRYPT_DECRYPT); + KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg()); + Assert.notNull(keyAlg, "数据异常"); + AlgId algId = null; + switch (keyAlg) { + case SM4: + if (Padding.PCKS5Padding == req.getPadding()) { + req.setPadding(Padding.PCKS7Padding); + } + switch (req.getMode()) { + case ECB: + algId = AlgId.SGD_SM4_ECB; + break; + case CBC: + algId = AlgId.SGD_SM4_CBC; + break; + } + break; + default: + throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode()); + } + KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId()); + Assert.notNull(keyRecord, "数据异常"); + byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData())); + byte[] cipherData = sdfApiService.symEncrypt(algId, req.getPadding(), symKey, iv, plain); + + SymEncryptResp resp = new SymEncryptResp(); + resp.setKeyId(keyInfo.getId()); + resp.setKeyIndex(keyRecord.getKeyIndex()); + resp.setCipherData(CodecUtils.encodeBase64(cipherData)); + return resp; + } + + + public SymDecryptResp decrypt(SymDecryptReq req) { + byte[] iv = new byte[0]; + if (AlgMode.CBC == req.getMode()) { + Assert.hasText(req.getIv(), "CBC模式iv不能为空"); + byte[] bytes = CodecUtils.decodeBase64(req.getIv()); + Assert.isTrue(bytes.length >= 16, "iv长度至少为16"); + iv = bytes; + } + byte[] cipher = CodecUtils.decodeBase64(req.getCipherData()); + KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.ENCRYPT_DECRYPT); + KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg()); + Assert.notNull(keyAlg, "数据异常"); + AlgId algId = null; + switch (keyAlg) { + case SM4: + if (Padding.PCKS5Padding == req.getPadding()) { + req.setPadding(Padding.PCKS7Padding); + } + switch (req.getMode()) { + case ECB: + algId = AlgId.SGD_SM4_ECB; + break; + case CBC: + algId = AlgId.SGD_SM4_CBC; + break; + } + break; + default: + throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode()); + } + KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex())); + Assert.notNull(keyRecord, "数据异常"); + Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配"); + byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData())); + byte[] plain = sdfApiService.symDecrypt(algId, req.getPadding(), symKey, iv, cipher); + + SymDecryptResp resp = new SymDecryptResp(); + resp.setKeyId(keyInfo.getId()); + resp.setKeyIndex(keyRecord.getKeyIndex()); + resp.setPlainData(CodecUtils.encodeBase64(plain)); + return resp; + } + + + public SymHmacResp hmac(SymHmacReq req) { + + byte[] plain = CodecUtils.decodeBase64(req.getPlainData()); + KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.HMAC); + KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId()); + byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData())); + byte[] hmac = sdfApiService.hmac(symKey, plain); + + SymHmacResp resp = new SymHmacResp(); + resp.setKeyId(keyInfo.getId()); + resp.setKeyIndex(keyRecord.getKeyIndex()); + resp.setHmac(CodecUtils.encodeBase64(hmac)); + return resp; + } + + public SymHmacCheckResp hmacCheck(SymHmacCheckReq req) { + + byte[] plain = CodecUtils.decodeBase64(req.getPlainData()); + byte[] originHmac = CodecUtils.decodeBase64(req.getHmac()); + KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.HMAC); + + KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex())); + Assert.notNull(keyRecord, "数据异常"); + Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配"); + byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData())); + byte[] hmac = sdfApiService.hmac(symKey, plain); + + SymHmacCheckResp resp = new SymHmacCheckResp(); + resp.setValid(Arrays.equals(hmac, originHmac)); + return resp; + } + + + public SymMacResp mac(SymMacReq req) { + byte[] plain = CodecUtils.decodeBase64(req.getPlainData()); + byte[] iv = CodecUtils.decodeBase64(req.getIv()); + Assert.isTrue(iv.length >= 16, "iv长度至少为16"); + KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.MAC); + KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId()); + Assert.notNull(keyRecord, "数据异常"); + if (Padding.PCKS5Padding == req.getPadding()) { + req.setPadding(Padding.PCKS7Padding); + } + byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData())); + byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain); + + SymMacResp resp = new SymMacResp(); + resp.setKeyId(keyInfo.getId()); + resp.setKeyIndex(keyRecord.getKeyIndex()); + resp.setMac(CodecUtils.encodeBase64(mac)); + return resp; + } + + + public SymMacCheckResp macCheck(SymMacCheckReq req) { + + byte[] plain = CodecUtils.decodeBase64(req.getPlainData()); + byte[] iv = CodecUtils.decodeBase64(req.getIv()); + byte[] originMac = CodecUtils.decodeBase64(req.getMac()); + KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.MAC); + + KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex())); + Assert.notNull(keyRecord, "数据异常"); + Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配"); + if (Padding.PCKS5Padding == req.getPadding()) { + req.setPadding(Padding.PCKS7Padding); + } + byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData())); + byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain); + + SymMacCheckResp resp = new SymMacCheckResp(); + resp.setValid(Arrays.equals(mac, originMac)); + return resp; + + } + + + private KeyInfo checkKey(Long keyId, KeyUsage usage) { + + KeyInfo keyInfo = keyInfoMapper.selectById(keyId); + Assert.notNull(keyInfo, "密钥ID不存在"); + Assert.isTrue(Objects.equals(keyInfo.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID"); + Assert.isTrue(KeyCategory.SYM_KEY.getCode().equals(keyInfo.getKeyType()), "此密钥不是对称密钥"); + + KeyStatus status = KeyStatus.of(keyInfo.getCode()); + LocalDateTime now = LocalDateTime.now(); + Assert.isTrue(KeyStatus.ENABLED == status, "此密钥不是启用状态, 无法操作"); + Assert.isTrue(now.isAfter(keyInfo.getEffectiveTime()) && now.isBefore(keyInfo.getExpiredTime()), "此密钥不是启用状态, 无法操作"); + Assert.isTrue(KeyUsage.hasUsage(keyInfo.getKeyUsage(), usage), "此密钥无权进行" + usage.getDesc() + "操作"); + + return keyInfo; + } + + +} diff --git a/chsm-web-server/src/test/java/api/BaseTest.java b/chsm-web-server/src/test/java/api/BaseTest.java new file mode 100644 index 0000000..9245973 --- /dev/null +++ b/chsm-web-server/src/test/java/api/BaseTest.java @@ -0,0 +1,70 @@ +package api; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.sunyard.chsm.model.R; +import com.sunyard.chsm.param.AppTokenReq; +import com.sunyard.chsm.param.AppTokenResp; +import com.sunyard.chsm.utils.JsonUtils; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.MediaType; +import org.springframework.http.RequestEntity; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; + +/** + * @author liulu + * @since 2024/12/17 + */ +public abstract class BaseTest { + + protected static final String ak = "216205d408130d83d13c5072305b8b65"; + protected static final String sk = "ae64515d1d5adec2cc6ae8726d0c1bbc"; + protected static final String server = "http://127.0.0.1:8900"; + protected static final RestTemplate restTemplate; + protected static final String token; + + static { + AppTokenReq req = new AppTokenReq(); + req.setAppKey(ak); + req.setHmac(sk); + req.setRandom(System.currentTimeMillis()); + + RequestEntity request = RequestEntity.post(server + "/appUser/getAppTokenTest") + .contentType(MediaType.APPLICATION_JSON) + .body(JsonUtils.toJsonBytes(req)); + ResponseEntity response = new RestTemplate().exchange(request, String.class); + try { + R r = JsonUtils.objectMapper() + .readValue(response.getBody(), new TypeReference>() { + }); + token = r.getResult().getToken(); + restTemplate = new RestTemplateBuilder() + .rootUri(server) + .defaultHeader("Authorization", "Bearer " + token) + .defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .build(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + protected static T execute(String url, Object req, Class tClass) { + try { + RequestEntity request = RequestEntity.post("/appUser/getAppTokenTest") + .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", "Bearer " + token) + .body(JsonUtils.toJsonBytes(req)); + + byte[] res = restTemplate.postForObject(url, JsonUtils.toJsonBytes(req), byte[].class); + R r = JsonUtils.objectMapper() + .readValue(res, new TypeReference>() { + }); + return r.getResult(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/chsm-web-server/src/test/java/api/KeyManageTest.java b/chsm-web-server/src/test/java/api/KeyManageTest.java new file mode 100644 index 0000000..a0c5657 --- /dev/null +++ b/chsm-web-server/src/test/java/api/KeyManageTest.java @@ -0,0 +1,48 @@ +package api; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.fasterxml.jackson.core.type.TypeReference; +import com.sunyard.chsm.model.R; +import com.sunyard.chsm.model.entity.KeyInfo; +import com.sunyard.chsm.param.KeyInfoQuery; +import com.sunyard.chsm.param.KeyInfoResp; +import com.sunyard.chsm.utils.JsonUtils; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +/** + * @author liulu + * @since 2024/12/17 + */ +@Slf4j +public class KeyManageTest extends BaseTest { + + private static Long keyId; + + @BeforeAll + public static void before() throws Exception { + KeyInfoQuery query = new KeyInfoQuery(); + byte[] res = restTemplate.postForObject("/key/pageList", JsonUtils.toJsonBytes(query), byte[].class); + R> r = JsonUtils.objectMapper() + .readValue(res, new TypeReference>>() { + }); + Assertions.assertTrue(r.isSuccess()); + + List records = r.getResult().getRecords(); + Assertions.assertFalse(CollectionUtils.isEmpty(records)); + keyId = records.iterator().next().getKeyId(); + } + + @Test + public void keyInfoTest(){ + KeyInfo res = execute("/key/info", keyId, KeyInfo.class); + log.info("keyInfoTest: {}", JsonUtils.toJsonString(res)); + } + + +}