对称密钥测试

This commit is contained in:
liulu 2024-12-18 11:34:59 +08:00
parent 36a2066542
commit 427df73a40
19 changed files with 551 additions and 41 deletions

View File

@ -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");
}

View File

@ -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<String, SunyardSdfLibrary> 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;

View File

@ -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 设备句柄

View File

@ -28,6 +28,9 @@ public abstract class CodecUtils {
}
public static String encodeHex(byte[] data) {
if (data == null) {
return "";
}
return Hex.toHexString(data);
}

View File

@ -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);
}

View File

@ -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);
};

View File

@ -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;
}
}

View File

@ -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);
};

View File

@ -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<TMKContext> pool = device.getPool();
tmkContext = pool.borrowObject(2000);

View File

@ -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);
}
}

View File

@ -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<TMKContext> pool;
@Override
public String toString() {
return "TMKContext{" +
"keyHandle='" + keyHandle + '\'' +
", sessionHandle='" + sessionHandle + '\'' +
", deviceHandle='" + deviceHandle + '\'' +
", encTmk='" + encTmk + '\'' +
'}';
}
}

View File

@ -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<TMKContext> {
}
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) {

View File

@ -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()), "此密钥不是启用状态, 无法操作");

View File

@ -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

View File

@ -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> T execute(String url, Object req, Class<T> tClass) {
try {
RequestEntity<byte[]> 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()

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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, "没有可用的主密钥设备");
}
}

View File

@ -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