From 427df73a40233654cf3f8e3347c1be745c84a49f Mon Sep 17 00:00:00 2001 From: liulu Date: Wed, 18 Dec 2024 11:34:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E7=A7=B0=E5=AF=86=E9=92=A5=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sunyard/chsm/constant/CryptoConst.java | 13 ++ .../sdf/adapter/SunyardJnaSdfAdaptor.java | 51 +++-- .../chsm/sdf/lib/SunyardSdfLibrary.java | 3 - .../com/sunyard/chsm/utils/CodecUtils.java | 3 + .../sunyard/chsm/sdf/SingleSdfApiService.java | 5 +- .../java/com/sunyard/config/WebConfig.java | 2 + .../chsm/config/GlobalExceptionResolver.java | 24 ++- .../com/sunyard/chsm/config/WebConfig.java | 14 +- .../com/sunyard/chsm/pool/DeviceManager.java | 6 + .../chsm/pool/LoadBalancedSdfApiService.java | 33 +++- .../com/sunyard/chsm/pool/TMKContext.java | 11 +- .../sunyard/chsm/pool/TMKContextFactory.java | 5 +- .../sunyard/chsm/service/SymKeyService.java | 2 +- .../src/main/resources/application.yml | 1 + .../src/test/java/api/BaseTest.java | 7 +- .../src/test/java/api/SymKeyTest.java | 56 ++++++ .../src/test/java/sdf/SdfApiServiceTest.java | 178 ++++++++++++++++++ .../java/sdf/adapter/SingleSdfApiService.java | 170 +++++++++++++++++ .../src/test/resources/application.yml | 8 +- 19 files changed, 551 insertions(+), 41 deletions(-) create mode 100644 chsm-common/src/main/java/com/sunyard/chsm/constant/CryptoConst.java create mode 100644 chsm-web-server/src/test/java/api/SymKeyTest.java create mode 100644 chsm-web-server/src/test/java/sdf/SdfApiServiceTest.java create mode 100644 chsm-web-server/src/test/java/sdf/adapter/SingleSdfApiService.java diff --git a/chsm-common/src/main/java/com/sunyard/chsm/constant/CryptoConst.java b/chsm-common/src/main/java/com/sunyard/chsm/constant/CryptoConst.java new file mode 100644 index 0000000..29ef717 --- /dev/null +++ b/chsm-common/src/main/java/com/sunyard/chsm/constant/CryptoConst.java @@ -0,0 +1,13 @@ +package com.sunyard.chsm.constant; + +import com.sunyard.chsm.utils.CodecUtils; + +/** + * @author liulu + * @since 2024/12/19 + */ +public interface CryptoConst { + + byte[] iv = CodecUtils.decodeHex("30303030303030303030303030303030"); + +} diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java index 91e291a..3c3ac86 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/adapter/SunyardJnaSdfAdaptor.java @@ -1,5 +1,6 @@ package com.sunyard.chsm.sdf.adapter; +import com.sun.jna.Native; import com.sun.jna.ptr.PointerByReference; import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.context.SdrCode; @@ -9,9 +10,13 @@ import com.sunyard.chsm.sdf.lib.SunyardSdfLibrary; import lombok.extern.slf4j.Slf4j; import org.springframework.util.ClassUtils; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.util.Map; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; /** * @author liulu @@ -20,6 +25,8 @@ import java.util.UUID; @Slf4j public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor { + private static final Map SDF_LIB_MAP = new ConcurrentHashMap<>(); + private final String ip; private final Integer port; private final Integer connTimeout; @@ -32,24 +39,33 @@ public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor { public SunyardJnaSdfAdaptor(String ip, int port, int connTimeout, int dealTimeout) { super((SdfLibrary) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class[]{SdfLibrary.class}, - (proxy, method, args) -> { - Object res = method.invoke(SunyardSdfLibrary.INSTANCE, args); - if (!(res instanceof Integer)) { + new InvocationHandler() { + private final SunyardSdfLibrary sunyardSdfLibrary; + + { + sunyardSdfLibrary = SDF_LIB_MAP.computeIfAbsent("sdf", k -> Native.load(k, SunyardSdfLibrary.class)); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object res = method.invoke(sunyardSdfLibrary, args); + if (!(res instanceof Integer)) { + return res; + } + int resInt = (Integer) res; + if (Objects.equals("SDF_ExternalVerify_ECC", method.getName())) { + log.info("SDF_ExternalVerify_ECC: {}, 返回值: {}", method.getName(), Integer.toHexString(resInt)); + return resInt; + } + if (method.getName().startsWith("SDF_")) { + if (resInt != SdrCode.SDR_OK.getCode()) { + log.warn("调用异常: {}, 返回值: {} -{}", method.getName(), Integer.toHexString(resInt), resInt); + SdrCode sdrCode = SdrCode.of((Integer) res); + throw new IllegalArgumentException(sdrCode != null ? sdrCode.getMsg() : "调用sdf接口异常: " + Integer.toHexString(resInt)); + } + } return res; } - int resInt = (Integer) res; - if (Objects.equals("SDF_ExternalVerify_ECC", method.getName())) { - log.info("SDF_ExternalVerify_ECC: {}, 返回值: {}", method.getName(), Integer.toHexString(resInt)); - return resInt; - } - if (method.getName().startsWith("SDF_")) { - if (resInt != SdrCode.SDR_OK.getCode()) { - log.warn("调用异常: {}, 返回值: {} -{}", method.getName(), Integer.toHexString(resInt), resInt); - SdrCode sdrCode = SdrCode.of((Integer) res); - throw new IllegalArgumentException(sdrCode != null ? sdrCode.getMsg() : "调用sdf接口异常: " + Integer.toHexString(resInt)); - } - } - return res; } )); this.ip = ip; @@ -60,8 +76,9 @@ public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor { @Override public String openDevice() { + SunyardSdfLibrary sunyardSdfLibrary = SDF_LIB_MAP.computeIfAbsent("sdf", k -> Native.load(k, SunyardSdfLibrary.class)); PointerByReference phDeviceHandle = new PointerByReference(); - SunyardSdfLibrary.INSTANCE.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0); + sunyardSdfLibrary.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0); String key = UUID.randomUUID().toString(); DEVICE_HANDLE_CONTEXT.put(key, phDeviceHandle.getValue()); return key; diff --git a/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java index 7e5d5da..2f31ddb 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/sdf/lib/SunyardSdfLibrary.java @@ -1,6 +1,5 @@ package com.sunyard.chsm.sdf.lib; -import com.sun.jna.Native; import com.sun.jna.ptr.PointerByReference; /** @@ -9,8 +8,6 @@ import com.sun.jna.ptr.PointerByReference; */ public interface SunyardSdfLibrary extends SdfLibrary { - SunyardSdfLibrary INSTANCE = Native.load("sdf", SunyardSdfLibrary.class); - /** * 打开设备 * @param phDeviceHandle 设备句柄 diff --git a/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java b/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java index e90cb0a..4a70048 100644 --- a/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java +++ b/chsm-common/src/main/java/com/sunyard/chsm/utils/CodecUtils.java @@ -28,6 +28,9 @@ public abstract class CodecUtils { } public static String encodeHex(byte[] data) { + if (data == null) { + return ""; + } return Hex.toHexString(data); } 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 696bcaa..daebaa6 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,5 +1,6 @@ package com.sunyard.chsm.sdf; +import com.sunyard.chsm.constant.CryptoConst; import com.sunyard.chsm.enums.DeviceTmkStatus; import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.mapper.SpDeviceMapper; @@ -168,13 +169,13 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean { public byte[] encryptByTMK(byte[] data) { checkKey(); byte[] pad = PaddingUtil.PKCS7Padding(data); - return sdfApiAdapter.symEncrypt(sessionHandle, tmkHandle, AlgId.SGD_SM4_CBC, new byte[8], pad); + return sdfApiAdapter.symEncrypt(sessionHandle, tmkHandle, AlgId.SGD_SM4_CBC, CryptoConst.iv, pad); } @Override public byte[] decryptByTMK(byte[] data) { checkKey(); - byte[] decrypt = sdfApiAdapter.symDecrypt(sessionHandle, tmkHandle, AlgId.SGD_SM4_CBC, new byte[8], data); + byte[] decrypt = sdfApiAdapter.symDecrypt(sessionHandle, tmkHandle, AlgId.SGD_SM4_CBC, CryptoConst.iv, data); return PaddingUtil.PKCS7Unpadding(decrypt); } diff --git a/chsm-web-manage/src/main/java/com/sunyard/config/WebConfig.java b/chsm-web-manage/src/main/java/com/sunyard/config/WebConfig.java index 0b0cd16..f9d7cf3 100644 --- a/chsm-web-manage/src/main/java/com/sunyard/config/WebConfig.java +++ b/chsm-web-manage/src/main/java/com/sunyard/config/WebConfig.java @@ -1,6 +1,7 @@ package com.sunyard.config; import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; @@ -61,6 +62,7 @@ public class WebConfig { javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateFormat.DATE)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateFormat.DATE_TIME)); builder.modules(javaTimeModule); + builder.serializerByType(Long.class, ToStringSerializer.instance); builder.serializationInclusion(JsonInclude.Include.NON_NULL); builder.failOnUnknownProperties(false); }; diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java b/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java index 7f691c7..9f3b534 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/config/GlobalExceptionResolver.java @@ -9,8 +9,11 @@ import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.WebUtils; import javax.servlet.http.HttpServletRequest; +import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.IllegalFormatException; import java.util.List; @@ -41,7 +44,10 @@ public class GlobalExceptionResolver { } @ExceptionHandler(MethodArgumentNotValidException.class) - public R validExceptionHandler(MethodArgumentNotValidException ex) { + public R validExceptionHandler(MethodArgumentNotValidException ex, HttpServletRequest request) { + String payload = getMessagePayload(request); + log.warn("Validation failed: {}, payload: {}", request.getRequestURI(), payload); + log.warn("error message -> {}", ex.getMessage()); return Optional.of(ex) .map(MethodArgumentNotValidException::getFieldError) .map(FieldError::getDefaultMessage) @@ -98,4 +104,20 @@ public class GlobalExceptionResolver { return sb.toString(); } + protected String getMessagePayload(HttpServletRequest request) { + ContentCachingRequestWrapper wrapper = + WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class); + if (wrapper != null) { + byte[] buf = wrapper.getContentAsByteArray(); + if (buf.length > 0) { + int length = Math.min(buf.length, 1000); + try { + return new String(buf, 0, length, wrapper.getCharacterEncoding()); + } catch (UnsupportedEncodingException ex) { + return "[unknown]"; + } + } + } + return null; + } } diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/config/WebConfig.java b/chsm-web-server/src/main/java/com/sunyard/chsm/config/WebConfig.java index 482a5c1..bc8c42a 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/config/WebConfig.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/config/WebConfig.java @@ -1,7 +1,6 @@ package com.sunyard.chsm.config; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; @@ -14,6 +13,7 @@ import com.sunyard.chsm.utils.DateFormat; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.CommonsRequestLoggingFilter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @@ -39,6 +39,17 @@ public class WebConfig { }; } + @Bean + public CommonsRequestLoggingFilter requestLoggingFilter() { + CommonsRequestLoggingFilter loggingFilter = new CommonsRequestLoggingFilter(); + loggingFilter.setIncludeHeaders(false); + loggingFilter.setIncludeClientInfo(true); + loggingFilter.setIncludeQueryString(true); + loggingFilter.setIncludePayload(true); + loggingFilter.setMaxPayloadLength(2000); + loggingFilter.setAfterMessagePrefix("http request ["); + return loggingFilter; + } @Bean public Jackson2ObjectMapperBuilderCustomizer objectMapperBuilderCustomizer() { @@ -52,7 +63,6 @@ public class WebConfig { javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateFormat.DATE)); javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateFormat.DATE_TIME)); builder.modules(javaTimeModule); - builder.serializerByType(Long.class, ToStringSerializer.instance); builder.serializationInclusion(JsonInclude.Include.NON_NULL); builder.failOnUnknownProperties(false); }; diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/DeviceManager.java b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/DeviceManager.java index 01553d4..383c13c 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/DeviceManager.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/DeviceManager.java @@ -23,7 +23,9 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; @@ -87,6 +89,10 @@ public class DeviceManager implements InitializingBean { if (device == null) { tmkContext = getSoftContext(); } else { + if (log.isDebugEnabled()) { + HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + log.debug("request: {}, choose device: {}", request.getRequestURI(), device.getIpPort()); + } try { GenericObjectPool pool = device.getPool(); tmkContext = pool.borrowObject(2000); 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 5bb23a7..8671929 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,7 @@ package com.sunyard.chsm.pool; +import com.sunyard.chsm.constant.CryptoConst; +import com.sunyard.chsm.enums.AlgMode; import com.sunyard.chsm.enums.Padding; import com.sunyard.chsm.sdf.SdfApiService; import com.sunyard.chsm.sdf.context.AlgId; @@ -9,6 +11,7 @@ import com.sunyard.chsm.sdf.model.EccPriKey; import com.sunyard.chsm.sdf.model.EccPubKey; import com.sunyard.chsm.sdf.model.EccSignature; import com.sunyard.chsm.sdf.util.PaddingUtil; +import com.sunyard.chsm.utils.CodecUtils; import com.sunyard.chsm.utils.gm.BCECUtils; import com.sunyard.chsm.utils.gm.BCSM3Utils; import com.sunyard.chsm.utils.gm.SM2PreprocessSigner; @@ -16,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.params.ParametersWithID; import org.springframework.stereotype.Service; +import org.springframework.util.Assert; import javax.annotation.Resource; import java.util.function.Function; @@ -48,6 +52,17 @@ public class LoadBalancedSdfApiService implements SdfApiService { @Override public byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { + if (alg.name().contains(AlgMode.CBC.getCode())) { + Assert.notNull(iv, "CBC模式下, iv不能为空"); + Assert.isTrue(iv.length >= key.length, "CBC模式下, iv长度不能低于key的长度"); + } + log.info("symEncrypt: alg:{}, padding: {}, key: {}, iv:{}, plainData:{}", + alg.name(), + padding.getCode(), + CodecUtils.encodeHex(key), + CodecUtils.encodeHex(iv), + CodecUtils.encodeHex(data) + ); byte[] paddingData = PaddingUtil.padding(padding, data); return apply(s -> { String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key); @@ -59,6 +74,17 @@ public class LoadBalancedSdfApiService implements SdfApiService { @Override public byte[] symDecrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { + if (alg.name().contains(AlgMode.CBC.getCode())) { + Assert.notNull(iv, "CBC模式下, iv不能为空"); + Assert.isTrue(iv.length >= key.length, "CBC模式下, iv长度不能低于key的长度"); + } + log.info("symDecrypt: alg:{}, padding: {}, key: {}, iv:{}, cipherData:{}", + alg.name(), + padding.getCode(), + CodecUtils.encodeHex(key), + CodecUtils.encodeHex(iv), + CodecUtils.encodeHex(data) + ); byte[] decrypt = apply(s -> { String keyHandle = s.getSdfApiAdapter().importKey(s.getSessionHandle(), key); byte[] d = s.getSdfApiAdapter().symDecrypt(s.getSessionHandle(), keyHandle, alg, iv, data); @@ -157,12 +183,15 @@ public class LoadBalancedSdfApiService implements SdfApiService { @Override public byte[] encryptByTMK(byte[] data) { byte[] pad = PaddingUtil.PKCS7Padding(data); - return apply(s -> s.getSdfApiAdapter().symEncrypt(s.getSessionHandle(), s.getKeyHandle(), AlgId.SGD_SM4_CBC, new byte[8], pad)); + return apply(s -> s.getSdfApiAdapter().symEncrypt(s.getSessionHandle(), s.getKeyHandle(), AlgId.SGD_SM4_CBC, CryptoConst.iv, pad)); } @Override public byte[] decryptByTMK(byte[] data) { - byte[] decrypt = apply(s -> s.getSdfApiAdapter().symDecrypt(s.getSessionHandle(), s.getKeyHandle(), AlgId.SGD_SM4_CBC, new byte[8], data)); + byte[] decrypt = apply(s -> s.getSdfApiAdapter().symDecrypt(s.getSessionHandle(), s.getKeyHandle(), AlgId.SGD_SM4_CBC, CryptoConst.iv, data)); + if (log.isDebugEnabled()) { + log.debug("decryptByTMK res: {}", CodecUtils.encodeHex(decrypt)); + } return PaddingUtil.PKCS7Unpadding(decrypt); } } diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContext.java b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContext.java index cd06a75..bf32b45 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContext.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContext.java @@ -11,7 +11,6 @@ import org.apache.commons.pool2.impl.GenericObjectPool; @Data public class TMKContext { - private String encTmk; private String deviceHandle; private String sessionHandle; @@ -19,5 +18,13 @@ public class TMKContext { private SdfApiAdapter sdfApiAdapter; private GenericObjectPool pool; - + @Override + public String toString() { + return "TMKContext{" + + "keyHandle='" + keyHandle + '\'' + + ", sessionHandle='" + sessionHandle + '\'' + + ", deviceHandle='" + deviceHandle + '\'' + + ", encTmk='" + encTmk + '\'' + + '}'; + } } diff --git a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContextFactory.java b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContextFactory.java index 87762b9..d022a5f 100644 --- a/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContextFactory.java +++ b/chsm-web-server/src/main/java/com/sunyard/chsm/pool/TMKContextFactory.java @@ -1,5 +1,6 @@ package com.sunyard.chsm.pool; +import com.sunyard.chsm.constant.CryptoConst; import com.sunyard.chsm.sdf.adapter.SdfApiAdapter; import com.sunyard.chsm.sdf.context.AlgId; import com.sunyard.chsm.sdf.model.EccCipher; @@ -71,9 +72,9 @@ public class TMKContextFactory extends BasePooledObjectFactory { } try { byte[] encData = sdfApiAdapter.symEncrypt(handle.getSessionHandle(), - handle.getKeyHandle(), AlgId.SGD_SM4_ECB, new byte[0], checkData); + handle.getKeyHandle(), AlgId.SGD_SM4_CBC, CryptoConst.iv, checkData); byte[] decData = sdfApiAdapter.symDecrypt(handle.getSessionHandle(), - handle.getKeyHandle(), AlgId.SGD_SM4_ECB, new byte[0], encData); + handle.getKeyHandle(), AlgId.SGD_SM4_CBC, CryptoConst.iv, encData); Assert.isTrue(Arrays.equals(checkData, decData), "密码机加解密异常"); return true; } catch (Exception e) { 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 index a5f3598..9cc20f3 100644 --- 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 @@ -218,7 +218,7 @@ public class SymKeyService { Assert.isTrue(Objects.equals(keyInfo.getApplicationId(), UserContext.getCurrentAppId()), "您无权使用此密钥ID"); Assert.isTrue(KeyCategory.SYM_KEY.getCode().equals(keyInfo.getKeyType()), "此密钥不是对称密钥"); - KeyStatus status = KeyStatus.of(keyInfo.getCode()); + KeyStatus status = KeyStatus.of(keyInfo.getStatus()); LocalDateTime now = LocalDateTime.now(); Assert.isTrue(KeyStatus.ENABLED == status, "此密钥不是启用状态, 无法操作"); Assert.isTrue(now.isAfter(keyInfo.getEffectiveTime()) && now.isBefore(keyInfo.getExpiredTime()), "此密钥不是启用状态, 无法操作"); diff --git a/chsm-web-server/src/main/resources/application.yml b/chsm-web-server/src/main/resources/application.yml index 027647b..6b124ce 100644 --- a/chsm-web-server/src/main/resources/application.yml +++ b/chsm-web-server/src/main/resources/application.yml @@ -57,6 +57,7 @@ logging: level: root: info com.sunyard.chsm.mapper: debug + com.sunyard.chsm.pool: debug # org.springframework.web: trace # config: classpath:log4j2.xml diff --git a/chsm-web-server/src/test/java/api/BaseTest.java b/chsm-web-server/src/test/java/api/BaseTest.java index 5ac043c..281e2c3 100644 --- a/chsm-web-server/src/test/java/api/BaseTest.java +++ b/chsm-web-server/src/test/java/api/BaseTest.java @@ -27,7 +27,7 @@ public abstract class BaseTest { protected static final String keyTemplate = "sym-sm4-001"; 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 String server = "http://172.16.18.46:8900"; protected static final RestTemplate restTemplate; protected static final String token; @@ -58,11 +58,6 @@ public abstract class BaseTest { 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); JsonNode jsonNode = JsonUtils.objectMapper() diff --git a/chsm-web-server/src/test/java/api/SymKeyTest.java b/chsm-web-server/src/test/java/api/SymKeyTest.java new file mode 100644 index 0000000..d688072 --- /dev/null +++ b/chsm-web-server/src/test/java/api/SymKeyTest.java @@ -0,0 +1,56 @@ +package api; + +import com.sunyard.chsm.enums.AlgMode; +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.utils.CodecUtils; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.util.StringUtils; + +/** + * @author liulu + * @since 2024/12/18 + */ +@Slf4j +public class SymKeyTest extends BaseTest { + + private static final String plain = "hjsu234127qikqwndqqw13412as324"; + private final static byte[] iv = "ghwikdhj1234713v".getBytes(); + + @Test + public void testEncrypt() { + Long keyId = 1869562427636801538L; + + SymEncryptReq symEncryptReq = new SymEncryptReq(); + symEncryptReq.setKeyId(keyId); + symEncryptReq.setPlainData(CodecUtils.encodeBase64(plain.getBytes())); + symEncryptReq.setMode(AlgMode.ECB); + + SymEncryptResp symEncryptResp = execute("/sym/encrypt", symEncryptReq, SymEncryptResp.class); + log.info("SymEncryptResp: {}", symEncryptResp); + Assertions.assertNotNull(symEncryptResp); + Assertions.assertTrue(StringUtils.hasText(symEncryptResp.getKeyIndex())); + byte[] cipherData = CodecUtils.decodeBase64(symEncryptResp.getCipherData()); + Assertions.assertEquals(32, cipherData.length); + + SymDecryptReq decryptReq = new SymDecryptReq(); + decryptReq.setKeyId(keyId); + decryptReq.setKeyIndex(symEncryptResp.getKeyIndex()); + decryptReq.setMode(AlgMode.ECB); + decryptReq.setCipherData(symEncryptResp.getCipherData()); + SymDecryptResp decryptResp = execute("/sym/decrypt", decryptReq, SymDecryptResp.class); + log.info("SymDecryptResp: {}", decryptResp); + Assertions.assertNotNull(decryptResp); + String calPlain = new String(CodecUtils.decodeBase64(decryptResp.getPlainData())); + Assertions.assertEquals(plain, calPlain); + + + + } + + +} diff --git a/chsm-web-server/src/test/java/sdf/SdfApiServiceTest.java b/chsm-web-server/src/test/java/sdf/SdfApiServiceTest.java new file mode 100644 index 0000000..b58e069 --- /dev/null +++ b/chsm-web-server/src/test/java/sdf/SdfApiServiceTest.java @@ -0,0 +1,178 @@ +package sdf; + +import com.sunyard.chsm.WebServerApp; +import com.sunyard.chsm.auth.AppUser; +import com.sunyard.chsm.constant.SecurityConstant; +import com.sunyard.chsm.enums.Padding; +import com.sunyard.chsm.sdf.SdfApiService; +import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory; +import com.sunyard.chsm.sdf.context.AlgId; +import com.sunyard.chsm.sdf.model.EccCipher; +import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPriKey; +import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; +import com.sunyard.chsm.utils.CodecUtils; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import sdf.adapter.SingleSdfApiService; + +import javax.annotation.Resource; +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; + +/** + * @author liulu + * @since 2024/12/13 + */ +@Slf4j +@SpringBootTest(classes = WebServerApp.class) +public class SdfApiServiceTest { + + private final static byte[] symKey = CodecUtils.decodeHex("a00f10444a727d09b94e2112cd662ea4"); + private final static byte[] iv = "hjdashde83252i23".getBytes(); + private final static String plain = "hjsu234127qikqwndqqw13412as324"; + // private final static String ip1 = "172.16.18.41"; +// private final static int port = 8889; + private static final SdfApiService bcService = new SingleSdfApiService(SdfApiAdapterFactory.getBcAdapter()); +// private static final SdfApiService sdfService = new SingleSdfApiService(SdfApiAdapterFactory.newInstance(ManufacturerModelEnum.enc001.getModel(), ip1, port)); + + private static EccPubKey pubKey; + private static EccPriKey priKey; + + @Resource + private SdfApiService sdfService; + + + @BeforeAll + public static void before() throws Exception { + EccKey eccKey = bcService.genKeyPairEcc(); + pubKey = EccPubKey.fromBytes(eccKey.getPubKey()); + priKey = EccPriKey.fromBytes(eccKey.getPriKey()); + log.info("public key: {}", pubKey.getPubKeyHex()); + log.info("private key: {}", CodecUtils.encodeHex(priKey.getD())); + } + + @BeforeEach + public void beforeEach() throws Exception { + RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); + AppUser appUser = new AppUser(); + appUser.setTokenId(UUID.randomUUID().toString()); + appUser.setAppId(1863480782392377345L); + appUser.setServiceIds(Collections.singletonList(1863480690759417858L)); + attributes.setAttribute(SecurityConstant.ATTRIBUTE_APP_USER, appUser, RequestAttributes.SCOPE_REQUEST); + attributes.setAttribute("used_service_ids", appUser.getServiceIds(), RequestAttributes.SCOPE_REQUEST); + } + + + @Test + public void testRandom() { + byte[] sdfRandom = sdfService.generateRandom(64); + log.info("sdf Random: {}", CodecUtils.encodeHex(sdfRandom)); + Assertions.assertEquals(64, sdfRandom.length); + + byte[] bcRandom = bcService.generateRandom(64); + log.info("bc Random: {}", CodecUtils.encodeHex(bcRandom)); + Assertions.assertEquals(64, bcRandom.length); + + Assertions.assertFalse(Arrays.equals(sdfRandom, bcRandom)); + } + + @Test + public void testSM2EncAndDec() { + EccCipher sdfCipher = sdfService.externalEncryptECC(pubKey.getPubKeyBytes(), plain.getBytes()); + log.info("sdf sm2 cipher: {}", sdfCipher.getC1C3C2Hex()); + byte[] bcPlain = bcService.externalDecryptECC(priKey.getD(), sdfCipher.getC1C3C2Bytes()); + log.info("bc sm2 plain: {}", new String(bcPlain)); + Assertions.assertEquals(plain, new String(bcPlain)); + + EccCipher bcCipher = bcService.externalEncryptECC(pubKey.getPubKeyBytes(), plain.getBytes()); + log.info("bc sm2 cipher: {}", bcCipher.getC1C3C2Hex()); + byte[] sm2Plain = sdfService.externalDecryptECC(priKey.getD(), bcCipher.getC1C3C2Bytes()); + log.info("sdf sm2 plain: {}", new String(sm2Plain)); + Assertions.assertEquals(plain, new String(sm2Plain)); + } + + @Test + public void testSM2SignAndVerify() { + EccSignature bcSign = bcService.externalSignECC(priKey.getD(), plain.getBytes()); + log.info("bc sm2 signature: {}", bcSign.getRawSignHex()); + boolean sdfVerified = sdfService.externalVerifyECC(pubKey.getPubKeyBytes(), plain.getBytes(), bcSign.getDerSignBytes()); + log.info("sdf sm2 verified: {}", sdfVerified); + Assertions.assertTrue(sdfVerified); + + EccSignature sdfSign = sdfService.externalSignECC(priKey.getD(), plain.getBytes()); + log.info("sdf sm2 signature: {}", sdfSign.getRawSignHex()); + boolean bcVerified = bcService.externalVerifyECC(pubKey.getPubKeyBytes(), plain.getBytes(), sdfSign.getRawSignBytes()); + log.info("bc sm2 verified: {}", bcVerified); + Assertions.assertTrue(bcVerified); + } + + @Test + public void testTmk() { + byte[] tmkCipher = sdfService.encryptByTMK(plain.getBytes()); + log.info("tmk_cipher: {}", CodecUtils.encodeHex(tmkCipher)); + + byte[] tmkPlain = sdfService.decryptByTMK(tmkCipher); + log.info("tmk_plain:{}, {}", new String(tmkPlain), CodecUtils.encodeHex(tmkPlain)); + } + + + @Test + public void testSymEncAndDec() { + 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, Padding.PCKS7Padding, symKey, iv, plain.getBytes()); + log.info("cbc_cipher: {}", CodecUtils.encodeHex(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, 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, 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(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(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes()); + log.info("bc mac: {}", CodecUtils.encodeHex(bcMac)); + Assertions.assertEquals(16, bcMac.length); + + Assertions.assertArrayEquals(sdfMac, bcMac); + } + + @Test + public void testSm3() { + byte[] sdfHash = sdfService.hash(plain.getBytes()); + log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash)); + Assertions.assertEquals(32, sdfHash.length); + + byte[] bcHash = bcService.hash(plain.getBytes()); + log.info("bc hash: {}", CodecUtils.encodeHex(bcHash)); + Assertions.assertEquals(32, bcHash.length); + + Assertions.assertArrayEquals(sdfHash, bcHash); + } + +} diff --git a/chsm-web-server/src/test/java/sdf/adapter/SingleSdfApiService.java b/chsm-web-server/src/test/java/sdf/adapter/SingleSdfApiService.java new file mode 100644 index 0000000..b23deb2 --- /dev/null +++ b/chsm-web-server/src/test/java/sdf/adapter/SingleSdfApiService.java @@ -0,0 +1,170 @@ +package sdf.adapter; + +import com.sunyard.chsm.constant.CryptoConst; +import com.sunyard.chsm.enums.Padding; +import com.sunyard.chsm.sdf.SdfApiService; +import com.sunyard.chsm.sdf.adapter.SdfApiAdapter; +import com.sunyard.chsm.sdf.context.AlgId; +import com.sunyard.chsm.sdf.model.EccCipher; +import com.sunyard.chsm.sdf.model.EccKey; +import com.sunyard.chsm.sdf.model.EccPriKey; +import com.sunyard.chsm.sdf.model.EccPubKey; +import com.sunyard.chsm.sdf.model.EccSignature; +import com.sunyard.chsm.sdf.util.PaddingUtil; +import com.sunyard.chsm.utils.gm.BCECUtils; +import com.sunyard.chsm.utils.gm.BCSM3Utils; +import com.sunyard.chsm.utils.gm.SM2PreprocessSigner; +import lombok.NoArgsConstructor; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.ParametersWithID; +import org.springframework.util.Assert; + +/** + * @author liulu + * @since 2024/12/11 + */ +@NoArgsConstructor +public class SingleSdfApiService implements SdfApiService { + + private String tmkHandle; + private String deviceHandle; + private String sessionHandle; + private SdfApiAdapter sdfApiAdapter; + + public SingleSdfApiService(SdfApiAdapter sdfApiAdapter) { + this.sdfApiAdapter = sdfApiAdapter; + this.deviceHandle = sdfApiAdapter.openDevice(); + this.sessionHandle = sdfApiAdapter.openSession(deviceHandle); + } + + @Override + public byte[] generateRandom(int len) { + checkStatus(); + return sdfApiAdapter.generateRandom(sessionHandle, len); + } + + @Override + public byte[] symEncrypt(AlgId alg, Padding padding, byte[] key, byte[] iv, byte[] data) { + checkStatus(); + 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); + return encrypt; + } + + @Override + 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.unpadding(padding, decrypt); + } + + @Override + public EccKey genKeyPairEcc() { + checkStatus(); + return sdfApiAdapter.generateKeyPairECC(sessionHandle, AlgId.SGD_SM2_1); + } + + @Override + public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) { + return externalSignWithIdECC(privateKey, pucData, new byte[0]); + } + + @Override + public EccSignature externalSignWithIdECC(byte[] privateKey, byte[] pucData, byte[] userId) { + EccPriKey sdfEccPrivateKey = EccPriKey.fromBytes(privateKey); + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + CipherParameters keyParameters = BCECUtils.createECPrivateKeyParameters(sdfEccPrivateKey.getD()); + if (userId != null && userId.length > 0) { + keyParameters = new ParametersWithID(keyParameters, userId); + } + signer.init(true, keyParameters); + byte[] preHash = signer.preprocess(pucData); + return sdfApiAdapter.externalSignECC(sessionHandle, sdfEccPrivateKey, preHash); + } + + @Override + public boolean externalVerifyWithIdECC(byte[] publicKey, byte[] pubData, byte[] signData, byte[] userId) { + EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey); + CipherParameters parameters = BCECUtils.createECPublicKeyParameters(eccPublicKey.getX(), eccPublicKey.getY()); + if (userId != null && userId.length > 0) { + parameters = new ParametersWithID(parameters, userId); + } + SM2PreprocessSigner signer = new SM2PreprocessSigner(); + signer.init(false, parameters); + byte[] preHash = signer.preprocess(pubData); + EccSignature eccSignature = EccSignature.fromBytes(signData); + + return sdfApiAdapter.externalVerifyECC(sessionHandle, eccPublicKey, preHash, eccSignature); + } + + @Override + public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) { + return externalVerifyWithIdECC(publicKey, pubData, signData, new byte[0]); + } + + @Override + public EccCipher externalEncryptECC(byte[] publicKey, byte[] pucData) { + EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey); + return sdfApiAdapter.externalEncryptECC(sessionHandle, eccPublicKey, pucData); + } + + @Override + public byte[] externalDecryptECC(byte[] privateKey, byte[] encData) { + EccPriKey sdfEccPrivateKey = EccPriKey.fromBytes(privateKey); + EccCipher eccCipher = EccCipher.fromBytes(encData); + return sdfApiAdapter.externalDecryptECC(sessionHandle, sdfEccPrivateKey, eccCipher); + } + + @Override + 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, pucIv, pad); + sdfApiAdapter.destroyKey(sessionHandle, hk); + return mac; + } + + @Override + public byte[] hmac(byte[] key, byte[] srcData) { + return BCSM3Utils.hmac(key, srcData); + } + + @Override + public byte[] hash(byte[] pucData) { + checkStatus(); + String newSession = sdfApiAdapter.openSession(deviceHandle); + sdfApiAdapter.hashInit(newSession, AlgId.SGD_SM3, null, null); + sdfApiAdapter.hashUpdate(newSession, pucData); + byte[] hash = sdfApiAdapter.hashFinish(newSession); + sdfApiAdapter.closeSession(newSession); + return hash; + } + + @Override + public byte[] encryptByTMK(byte[] data) { + checkKey(); + byte[] pad = PaddingUtil.PKCS7Padding(data); + return sdfApiAdapter.symEncrypt(sessionHandle, tmkHandle, AlgId.SGD_SM4_CBC, CryptoConst.iv, pad); + } + + @Override + public byte[] decryptByTMK(byte[] data) { + checkKey(); + byte[] decrypt = sdfApiAdapter.symDecrypt(sessionHandle, tmkHandle, AlgId.SGD_SM4_CBC, CryptoConst.iv, data); + return PaddingUtil.PKCS7Unpadding(decrypt); + } + + private void checkStatus() { + Assert.notNull(sdfApiAdapter, "没有可用设备"); + } + + private void checkKey() { + checkStatus(); + Assert.hasText(tmkHandle, "没有可用的主密钥设备"); + } +} diff --git a/chsm-web-server/src/test/resources/application.yml b/chsm-web-server/src/test/resources/application.yml index 78ac13a..027647b 100644 --- a/chsm-web-server/src/test/resources/application.yml +++ b/chsm-web-server/src/test/resources/application.yml @@ -1,5 +1,5 @@ server: - port: 89 + port: 8900 tomcat: uri-encoding: UTF-8 threads: @@ -12,7 +12,7 @@ spring: # 数据源 datasource: driverClassName: dm.jdbc.driver.DmDriver - url: jdbc:dm://172.16.18.44:5236?schema=SSP&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8 + url: jdbc:dm://172.16.18.44:5236?schema=SUNYARD_SSP&useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=UTF-8 username: SUNYARD # Jasypt加密 可到common-utils中找到JasyptUtil加解密工具类生成加密结果 格式为ENC(加密结果) password: 123456 @@ -29,9 +29,11 @@ spring: jackson: time-zone: GMT+8 date-format: yyyy-MM-dd HH:mm:ss + mapper: + ACCEPT_CASE_INSENSITIVE_ENUMS: true mybatis-plus: - mapper-locations: classpath*:mapper/**/*Mapper.xml + mapper-locations: classpath:mapper/**/*Mapper.xml # 原生配置 configuration: log-impl: org.apache.ibatis.logging.slf4j.Slf4jImpl