非对称加解密
This commit is contained in:
parent
0ba56c7c25
commit
7fe6482983
@ -8,6 +8,8 @@ import com.sunyard.chsm.utils.CodecUtils;
|
|||||||
*/
|
*/
|
||||||
public interface CryptoConst {
|
public interface CryptoConst {
|
||||||
|
|
||||||
|
byte[] USER_ID = CodecUtils.decodeHex("31323334353637383132333435363738");
|
||||||
|
|
||||||
static byte[] iv() {
|
static byte[] iv() {
|
||||||
return CodecUtils.decodeHex("30303030303030303030303030303030");
|
return CodecUtils.decodeHex("30303030303030303030303030303030");
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ public interface SdfApiService {
|
|||||||
/**
|
/**
|
||||||
* 计算MAC
|
* 计算MAC
|
||||||
*
|
*
|
||||||
* @param algId algId
|
* @param algId algId
|
||||||
* @param padding padding
|
* @param padding padding
|
||||||
* @param symKey 用户指定的密钥
|
* @param symKey 用户指定的密钥
|
||||||
* @param pucIv 缓冲区指针,用于存放输入和返回的IV数据
|
* @param pucIv 缓冲区指针,用于存放输入和返回的IV数据
|
||||||
@ -136,8 +136,12 @@ public interface SdfApiService {
|
|||||||
* 杂凑运算
|
* 杂凑运算
|
||||||
*
|
*
|
||||||
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
||||||
|
* @param pubKey 执行预处理的公钥
|
||||||
|
* @param userId 执行预处理的userId
|
||||||
* @return hash值
|
* @return hash值
|
||||||
*/
|
*/
|
||||||
|
byte[] hash(byte[] pucData, byte[] pubKey, byte[] userId);
|
||||||
|
|
||||||
byte[] hash(byte[] pucData);
|
byte[] hash(byte[] pucData);
|
||||||
|
|
||||||
byte[] encryptByTMK(byte[] data);
|
byte[] encryptByTMK(byte[] data);
|
||||||
|
@ -15,6 +15,9 @@ public abstract class CodecUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decodeBase64(String str) {
|
public static byte[] decodeBase64(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return Base64.getDecoder().decode(str);
|
return Base64.getDecoder().decode(str);
|
||||||
} catch (Exception var3) {
|
} catch (Exception var3) {
|
||||||
@ -35,6 +38,9 @@ public abstract class CodecUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] decodeHex(String str) {
|
public static byte[] decodeHex(String str) {
|
||||||
|
if (str == null || str.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return Hex.decode(str);
|
return Hex.decode(str);
|
||||||
} catch (Exception var3) {
|
} catch (Exception var3) {
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Wangjingcheng
|
||||||
|
* @Version 1.0.0
|
||||||
|
* @Date 2023/8/4 9:56
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AsymDecryptReq {
|
||||||
|
// 密钥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;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AsymDecryptResp {
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
private String plainData;
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AsymEncryptReq {
|
||||||
|
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AsymEncryptResp {
|
||||||
|
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// 密文C1C3C2,使用Base64编码
|
||||||
|
private String cipherData;
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AsymSignRawReq {
|
||||||
|
|
||||||
|
// 密钥ID
|
||||||
|
@NotNull(message = "密钥ID不能为空")
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// 是否进行预处理 默认是
|
||||||
|
private boolean preProcess = true;
|
||||||
|
|
||||||
|
// 预处理的userId,使用Base64编码
|
||||||
|
private String userId;
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AsymSignRawResp {
|
||||||
|
|
||||||
|
// 密钥ID
|
||||||
|
private Long keyId;
|
||||||
|
|
||||||
|
// 密钥索引
|
||||||
|
private String keyIndex;
|
||||||
|
|
||||||
|
// 签名值 Base64编码
|
||||||
|
private String signData;
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package com.sunyard.chsm.param;
|
||||||
|
|
||||||
|
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 AsymVerifyRawReq {
|
||||||
|
|
||||||
|
// 密钥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 signData;
|
||||||
|
|
||||||
|
// 明文,使用Base64编码
|
||||||
|
@NotBlank(message = "明文不能为空")
|
||||||
|
private String plainData;
|
||||||
|
|
||||||
|
// 是否进行预处理 默认是
|
||||||
|
private boolean preProcess = true;
|
||||||
|
|
||||||
|
// 预处理的userId,使用Base64编码
|
||||||
|
private String userId;
|
||||||
|
}
|
@ -12,8 +12,13 @@ public class HashReq {
|
|||||||
@NotBlank(message = "明文不能为空")
|
@NotBlank(message = "明文不能为空")
|
||||||
private String plainData;
|
private String plainData;
|
||||||
|
|
||||||
// 填充方式, 默认PCKS7
|
// Hash算法, 默认SM3
|
||||||
private HashAlg alg = HashAlg.SM3;
|
private HashAlg alg = HashAlg.SM3;
|
||||||
|
|
||||||
|
// 公钥,使用Base64编码
|
||||||
|
private String pubKey;
|
||||||
|
// userId,使用Base64编码
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ package com.sunyard.chsm.param;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class SymHmacCheckResp {
|
public class VerifyResp {
|
||||||
|
|
||||||
private Boolean valid;
|
private Boolean verified;
|
||||||
|
|
||||||
}
|
}
|
@ -90,7 +90,10 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) {
|
public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) {
|
||||||
return externalSignWithIdECC(privateKey, pucData, new byte[0]);
|
Assert.notNull(pucData, "待签名数据不能为空");
|
||||||
|
Assert.isTrue(pucData.length == 32, "待签名数据长度必须为32字节");
|
||||||
|
EccPriKey priKey = EccPriKey.fromBytes(privateKey);
|
||||||
|
return sdfApiAdapter.externalSignECC(sessionHandle, priKey, pucData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -123,7 +126,11 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) {
|
public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) {
|
||||||
return externalVerifyWithIdECC(publicKey, pubData, signData, new byte[0]);
|
Assert.notNull(pubData, "验签原文数据不能为空");
|
||||||
|
Assert.isTrue(pubData.length == 32, "验签原文数据长度必须为32字节");
|
||||||
|
EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey);
|
||||||
|
EccSignature eccSignature = EccSignature.fromBytes(signData);
|
||||||
|
return sdfApiAdapter.externalVerifyECC(sessionHandle, eccPublicKey, pubData, eccSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -154,6 +161,19 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
|||||||
return BCSM3Utils.hmac(key, srcData);
|
return BCSM3Utils.hmac(key, srcData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] hash(byte[] pucData, byte[] pubKey, byte[] userId) {
|
||||||
|
checkStatus();
|
||||||
|
EccPubKey eccPubKey = pubKey == null || pubKey.length == 0 ? null : EccPubKey.fromBytes(pubKey);
|
||||||
|
byte[] finalUserId = Objects.nonNull(pubKey) && Objects.isNull(userId) ? CryptoConst.USER_ID : userId;
|
||||||
|
String newSession = sdfApiAdapter.openSession(deviceHandle);
|
||||||
|
sdfApiAdapter.hashInit(newSession, AlgId.SGD_SM3, eccPubKey, finalUserId);
|
||||||
|
sdfApiAdapter.hashUpdate(newSession, pucData);
|
||||||
|
byte[] hash = sdfApiAdapter.hashFinish(newSession);
|
||||||
|
sdfApiAdapter.closeSession(newSession);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] hash(byte[] pucData) {
|
public byte[] hash(byte[] pucData) {
|
||||||
checkStatus();
|
checkStatus();
|
||||||
|
@ -130,11 +130,11 @@ public class SdfApiServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSm3() {
|
public void testSm3() {
|
||||||
byte[] sdfHash = sdfService.hash(plain.getBytes());
|
byte[] sdfHash = sdfService.hash(plain.getBytes(), null, null);
|
||||||
log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
|
log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
|
||||||
Assertions.assertEquals(32, sdfHash.length);
|
Assertions.assertEquals(32, sdfHash.length);
|
||||||
|
|
||||||
byte[] bcHash = bcService.hash(plain.getBytes());
|
byte[] bcHash = bcService.hash(plain.getBytes(),null, null);
|
||||||
log.info("bc hash: {}", CodecUtils.encodeHex(bcHash));
|
log.info("bc hash: {}", CodecUtils.encodeHex(bcHash));
|
||||||
Assertions.assertEquals(32, bcHash.length);
|
Assertions.assertEquals(32, bcHash.length);
|
||||||
|
|
||||||
|
@ -0,0 +1,89 @@
|
|||||||
|
package com.sunyard.chsm.controller;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.auth.AuthCode;
|
||||||
|
import com.sunyard.chsm.constant.AuthCodeConst;
|
||||||
|
import com.sunyard.chsm.model.R;
|
||||||
|
import com.sunyard.chsm.param.AsymDecryptReq;
|
||||||
|
import com.sunyard.chsm.param.AsymDecryptResp;
|
||||||
|
import com.sunyard.chsm.param.AsymEncryptReq;
|
||||||
|
import com.sunyard.chsm.param.AsymEncryptResp;
|
||||||
|
import com.sunyard.chsm.param.AsymSignRawReq;
|
||||||
|
import com.sunyard.chsm.param.AsymSignRawResp;
|
||||||
|
import com.sunyard.chsm.param.AsymVerifyRawReq;
|
||||||
|
import com.sunyard.chsm.param.VerifyResp;
|
||||||
|
import com.sunyard.chsm.service.AsymKeyService;
|
||||||
|
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/20
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/asym")
|
||||||
|
public class AsymKeyController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AsymKeyService asymKeyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非对称加密
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/encrypt")
|
||||||
|
@AuthCode(AuthCodeConst.asym_enc)
|
||||||
|
public R<AsymEncryptResp> encrypt(@Valid @RequestBody AsymEncryptReq req) {
|
||||||
|
AsymEncryptResp resp = asymKeyService.encrypt(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 非对称解密
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/encrypt")
|
||||||
|
@AuthCode(AuthCodeConst.asym_enc)
|
||||||
|
public R<AsymDecryptResp> decrypt(@Valid @RequestBody AsymDecryptReq req) {
|
||||||
|
AsymDecryptResp resp = asymKeyService.decrypt(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RAW签名
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/sign/raw")
|
||||||
|
@AuthCode(AuthCodeConst.sign_raw)
|
||||||
|
public R<AsymSignRawResp> signRaw(@Valid @RequestBody AsymSignRawReq req) {
|
||||||
|
AsymSignRawResp resp = asymKeyService.signRaw(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RAW验签
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/verify/raw")
|
||||||
|
@AuthCode(AuthCodeConst.verify_raw)
|
||||||
|
public R<VerifyResp> verifyRaw(@Valid @RequestBody AsymVerifyRawReq req) {
|
||||||
|
VerifyResp resp = asymKeyService.verifyRaw(req);
|
||||||
|
return R.data(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -36,7 +36,9 @@ public class HashController {
|
|||||||
@AuthCode(AuthCodeConst.cal_hash)
|
@AuthCode(AuthCodeConst.cal_hash)
|
||||||
public R<HashResp> hash(@Valid @RequestBody HashReq req) {
|
public R<HashResp> hash(@Valid @RequestBody HashReq req) {
|
||||||
byte[] bytes = CodecUtils.decodeBase64(req.getPlainData());
|
byte[] bytes = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
byte[] hash = sdfApiService.hash(bytes);
|
byte[] pubkey = CodecUtils.decodeBase64(req.getPubKey());
|
||||||
|
byte[] userId = CodecUtils.decodeBase64(req.getUserId());
|
||||||
|
byte[] hash = sdfApiService.hash(bytes, pubkey, userId);
|
||||||
HashResp resp = new HashResp();
|
HashResp resp = new HashResp();
|
||||||
resp.setHash(CodecUtils.encodeBase64(hash));
|
resp.setHash(CodecUtils.encodeBase64(hash));
|
||||||
return R.data(resp);
|
return R.data(resp);
|
||||||
|
@ -8,13 +8,12 @@ import com.sunyard.chsm.param.SymDecryptResp;
|
|||||||
import com.sunyard.chsm.param.SymEncryptReq;
|
import com.sunyard.chsm.param.SymEncryptReq;
|
||||||
import com.sunyard.chsm.param.SymEncryptResp;
|
import com.sunyard.chsm.param.SymEncryptResp;
|
||||||
import com.sunyard.chsm.param.SymHmacCheckReq;
|
import com.sunyard.chsm.param.SymHmacCheckReq;
|
||||||
import com.sunyard.chsm.param.SymHmacCheckResp;
|
|
||||||
import com.sunyard.chsm.param.SymHmacReq;
|
import com.sunyard.chsm.param.SymHmacReq;
|
||||||
import com.sunyard.chsm.param.SymHmacResp;
|
import com.sunyard.chsm.param.SymHmacResp;
|
||||||
import com.sunyard.chsm.param.SymMacCheckReq;
|
import com.sunyard.chsm.param.SymMacCheckReq;
|
||||||
import com.sunyard.chsm.param.SymMacCheckResp;
|
|
||||||
import com.sunyard.chsm.param.SymMacReq;
|
import com.sunyard.chsm.param.SymMacReq;
|
||||||
import com.sunyard.chsm.param.SymMacResp;
|
import com.sunyard.chsm.param.SymMacResp;
|
||||||
|
import com.sunyard.chsm.param.VerifyResp;
|
||||||
import com.sunyard.chsm.service.SymKeyService;
|
import com.sunyard.chsm.service.SymKeyService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@ -84,8 +83,8 @@ public class SymKeyController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/hmac/check")
|
@PostMapping("/hmac/check")
|
||||||
@AuthCode(AuthCodeConst.check_hmac)
|
@AuthCode(AuthCodeConst.check_hmac)
|
||||||
public R<SymHmacCheckResp> macCheck(@Valid @RequestBody SymHmacCheckReq req) {
|
public R<VerifyResp> macCheck(@Valid @RequestBody SymHmacCheckReq req) {
|
||||||
SymHmacCheckResp resp = symKeyService.hmacCheck(req);
|
VerifyResp resp = symKeyService.hmacCheck(req);
|
||||||
return R.data(resp);
|
return R.data(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,8 +109,8 @@ public class SymKeyController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/mac/check")
|
@PostMapping("/mac/check")
|
||||||
@AuthCode(AuthCodeConst.check_mac)
|
@AuthCode(AuthCodeConst.check_mac)
|
||||||
public R<SymMacCheckResp> macCheck(@Valid @RequestBody SymMacCheckReq req) {
|
public R<VerifyResp> macCheck(@Valid @RequestBody SymMacCheckReq req) {
|
||||||
SymMacCheckResp resp = symKeyService.macCheck(req);
|
VerifyResp resp = symKeyService.macCheck(req);
|
||||||
return R.data(resp);
|
return R.data(resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,7 +102,10 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) {
|
public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) {
|
||||||
return externalSignWithIdECC(privateKey, pucData, new byte[0]);
|
Assert.notNull(pucData, "待签名数据不能为空");
|
||||||
|
Assert.isTrue(pucData.length == 32, "待签名数据长度必须为32字节");
|
||||||
|
EccPriKey priKey = EccPriKey.fromBytes(privateKey);
|
||||||
|
return apply(s -> s.getSdfApiAdapter().externalSignECC(s.getSessionHandle(), priKey, pucData));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -136,7 +140,11 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) {
|
public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) {
|
||||||
return externalVerifyWithIdECC(publicKey, pubData, signData, new byte[0]);
|
Assert.notNull(pubData, "验签原文数据不能为空");
|
||||||
|
Assert.isTrue(pubData.length == 32, "验签原文数据长度必须为32字节");
|
||||||
|
EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey);
|
||||||
|
EccSignature eccSignature = EccSignature.fromBytes(signData);
|
||||||
|
return apply(s -> s.getSdfApiAdapter().externalVerifyECC(s.getSessionHandle(), eccPublicKey, pubData, eccSignature));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -169,10 +177,12 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] hash(byte[] pucData) {
|
public byte[] hash(byte[] pucData, byte[] pubKey, byte[] userId) {
|
||||||
|
EccPubKey eccPubKey = pubKey == null || pubKey.length == 0 ? null : EccPubKey.fromBytes(pubKey);
|
||||||
|
byte[] finalUserId = Objects.nonNull(pubKey) && Objects.isNull(userId) ? CryptoConst.USER_ID : userId;
|
||||||
return apply(s -> {
|
return apply(s -> {
|
||||||
String newSession = s.getSdfApiAdapter().openSession(s.getDeviceHandle());
|
String newSession = s.getSdfApiAdapter().openSession(s.getDeviceHandle());
|
||||||
s.getSdfApiAdapter().hashInit(newSession, AlgId.SGD_SM3, null, null);
|
s.getSdfApiAdapter().hashInit(newSession, AlgId.SGD_SM3, eccPubKey, finalUserId);
|
||||||
s.getSdfApiAdapter().hashUpdate(newSession, pucData);
|
s.getSdfApiAdapter().hashUpdate(newSession, pucData);
|
||||||
byte[] hash = s.getSdfApiAdapter().hashFinish(newSession);
|
byte[] hash = s.getSdfApiAdapter().hashFinish(newSession);
|
||||||
s.getSdfApiAdapter().closeSession(newSession);
|
s.getSdfApiAdapter().closeSession(newSession);
|
||||||
@ -180,6 +190,11 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] hash(byte[] pucData) {
|
||||||
|
return hash(pucData, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] encryptByTMK(byte[] data) {
|
public byte[] encryptByTMK(byte[] data) {
|
||||||
byte[] pad = PaddingUtil.PKCS7Padding(data);
|
byte[] pad = PaddingUtil.PKCS7Padding(data);
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
package com.sunyard.chsm.service;
|
||||||
|
|
||||||
|
import com.sunyard.chsm.auth.UserContext;
|
||||||
|
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.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.AsymDecryptReq;
|
||||||
|
import com.sunyard.chsm.param.AsymDecryptResp;
|
||||||
|
import com.sunyard.chsm.param.AsymEncryptReq;
|
||||||
|
import com.sunyard.chsm.param.AsymEncryptResp;
|
||||||
|
import com.sunyard.chsm.param.AsymSignRawReq;
|
||||||
|
import com.sunyard.chsm.param.AsymSignRawResp;
|
||||||
|
import com.sunyard.chsm.param.AsymVerifyRawReq;
|
||||||
|
import com.sunyard.chsm.param.VerifyResp;
|
||||||
|
import com.sunyard.chsm.sdf.SdfApiService;
|
||||||
|
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||||
|
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||||
|
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.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author liulu
|
||||||
|
* @since 2024/12/20
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class AsymKeyService {
|
||||||
|
|
||||||
|
private final KeyInfoMapper keyInfoMapper;
|
||||||
|
private final SpKeyRecordMapper spKeyRecordMapper;
|
||||||
|
private final SdfApiService sdfApiService;
|
||||||
|
|
||||||
|
|
||||||
|
public AsymEncryptResp encrypt(AsymEncryptReq req) {
|
||||||
|
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.ENCRYPT_DECRYPT);
|
||||||
|
KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg());
|
||||||
|
Assert.notNull(keyAlg, "数据异常");
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
byte[] cipherData;
|
||||||
|
switch (keyAlg) {
|
||||||
|
case SM2:
|
||||||
|
byte[] pub = CodecUtils.decodeHex(keyRecord.getPubKey());
|
||||||
|
EccCipher eccCipher = sdfApiService.externalEncryptECC(pub, plainData);
|
||||||
|
cipherData = eccCipher.getC1C3C2Bytes();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
AsymEncryptResp resp = new AsymEncryptResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setCipherData(CodecUtils.encodeBase64(cipherData));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsymDecryptResp decrypt(AsymDecryptReq req) {
|
||||||
|
byte[] cipherData = CodecUtils.decodeBase64(req.getCipherData());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.ENCRYPT_DECRYPT);
|
||||||
|
KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg());
|
||||||
|
Assert.notNull(keyAlg, "数据异常");
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex()));
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配");
|
||||||
|
byte[] plainData;
|
||||||
|
switch (keyAlg) {
|
||||||
|
case SM2:
|
||||||
|
byte[] encPri = CodecUtils.decodeHex(keyRecord.getKeyData());
|
||||||
|
byte[] pri = sdfApiService.decryptByTMK(encPri);
|
||||||
|
plainData = sdfApiService.externalDecryptECC(pri, cipherData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode());
|
||||||
|
}
|
||||||
|
AsymDecryptResp resp = new AsymDecryptResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setPlainData(CodecUtils.encodeBase64(plainData));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsymSignRawResp signRaw(AsymSignRawReq req) {
|
||||||
|
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
byte[] userId = CodecUtils.decodeBase64(req.getUserId());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.SIGN_VERIFY);
|
||||||
|
KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg());
|
||||||
|
Assert.notNull(keyAlg, "数据异常");
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectUsedByKeyId(keyInfo.getId());
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
byte[] signData;
|
||||||
|
switch (keyAlg) {
|
||||||
|
case SM2:
|
||||||
|
byte[] encPri = CodecUtils.decodeHex(keyRecord.getKeyData());
|
||||||
|
byte[] pri = sdfApiService.decryptByTMK(encPri);
|
||||||
|
EccSignature signature = req.isPreProcess()
|
||||||
|
? sdfApiService.externalSignWithIdECC(pri, plainData, userId)
|
||||||
|
: sdfApiService.externalSignECC(pri, plainData);
|
||||||
|
signData = signature.getRawSignBytes();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
AsymSignRawResp resp = new AsymSignRawResp();
|
||||||
|
resp.setKeyId(keyInfo.getId());
|
||||||
|
resp.setKeyIndex(keyRecord.getKeyIndex());
|
||||||
|
resp.setSignData(CodecUtils.encodeBase64(signData));
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public VerifyResp verifyRaw(AsymVerifyRawReq req) {
|
||||||
|
byte[] plainData = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
|
byte[] signData = CodecUtils.decodeBase64(req.getSignData());
|
||||||
|
byte[] userId = CodecUtils.decodeBase64(req.getUserId());
|
||||||
|
KeyInfo keyInfo = checkKey(req.getKeyId(), KeyUsage.SIGN_VERIFY);
|
||||||
|
KeyAlg keyAlg = KeyAlg.of(keyInfo.getKeyAlg());
|
||||||
|
Assert.notNull(keyAlg, "数据异常");
|
||||||
|
KeyRecord keyRecord = spKeyRecordMapper.selectById(Long.valueOf(req.getKeyIndex()));
|
||||||
|
Assert.notNull(keyRecord, "数据异常");
|
||||||
|
Assert.isTrue(Objects.equals(keyRecord.getKeyId(), keyInfo.getId()), "密钥Id和密钥索引不匹配");
|
||||||
|
boolean verified;
|
||||||
|
switch (keyAlg) {
|
||||||
|
case SM2:
|
||||||
|
byte[] pub = CodecUtils.decodeHex(keyRecord.getPubKey());
|
||||||
|
verified = req.isPreProcess()
|
||||||
|
? sdfApiService.externalVerifyWithIdECC(pub, plainData, signData, userId)
|
||||||
|
: sdfApiService.externalVerifyECC(pub, plainData, signData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new UnsupportedOperationException("不支持的密钥算法:" + keyAlg.getCode());
|
||||||
|
}
|
||||||
|
VerifyResp resp = new VerifyResp();
|
||||||
|
resp.setVerified(verified);
|
||||||
|
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.ASYM_KEY.getCode().equals(keyInfo.getKeyType()), "此密钥不是对称密钥");
|
||||||
|
|
||||||
|
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()), "此密钥不是启用状态, 无法操作");
|
||||||
|
Assert.isTrue(KeyUsage.hasUsage(keyInfo.getKeyUsage(), usage), "此密钥无权进行" + usage.getDesc() + "操作");
|
||||||
|
|
||||||
|
return keyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -16,13 +16,12 @@ import com.sunyard.chsm.param.SymDecryptResp;
|
|||||||
import com.sunyard.chsm.param.SymEncryptReq;
|
import com.sunyard.chsm.param.SymEncryptReq;
|
||||||
import com.sunyard.chsm.param.SymEncryptResp;
|
import com.sunyard.chsm.param.SymEncryptResp;
|
||||||
import com.sunyard.chsm.param.SymHmacCheckReq;
|
import com.sunyard.chsm.param.SymHmacCheckReq;
|
||||||
import com.sunyard.chsm.param.SymHmacCheckResp;
|
|
||||||
import com.sunyard.chsm.param.SymHmacReq;
|
import com.sunyard.chsm.param.SymHmacReq;
|
||||||
import com.sunyard.chsm.param.SymHmacResp;
|
import com.sunyard.chsm.param.SymHmacResp;
|
||||||
import com.sunyard.chsm.param.SymMacCheckReq;
|
import com.sunyard.chsm.param.SymMacCheckReq;
|
||||||
import com.sunyard.chsm.param.SymMacCheckResp;
|
|
||||||
import com.sunyard.chsm.param.SymMacReq;
|
import com.sunyard.chsm.param.SymMacReq;
|
||||||
import com.sunyard.chsm.param.SymMacResp;
|
import com.sunyard.chsm.param.SymMacResp;
|
||||||
|
import com.sunyard.chsm.param.VerifyResp;
|
||||||
import com.sunyard.chsm.sdf.SdfApiService;
|
import com.sunyard.chsm.sdf.SdfApiService;
|
||||||
import com.sunyard.chsm.sdf.context.AlgId;
|
import com.sunyard.chsm.sdf.context.AlgId;
|
||||||
import com.sunyard.chsm.utils.CodecUtils;
|
import com.sunyard.chsm.utils.CodecUtils;
|
||||||
@ -149,7 +148,7 @@ public class SymKeyService {
|
|||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SymHmacCheckResp hmacCheck(SymHmacCheckReq req) {
|
public VerifyResp hmacCheck(SymHmacCheckReq req) {
|
||||||
|
|
||||||
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
byte[] originHmac = CodecUtils.decodeBase64(req.getHmac());
|
byte[] originHmac = CodecUtils.decodeBase64(req.getHmac());
|
||||||
@ -161,8 +160,8 @@ public class SymKeyService {
|
|||||||
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
byte[] hmac = sdfApiService.hmac(symKey, plain);
|
byte[] hmac = sdfApiService.hmac(symKey, plain);
|
||||||
|
|
||||||
SymHmacCheckResp resp = new SymHmacCheckResp();
|
VerifyResp resp = new VerifyResp();
|
||||||
resp.setValid(Arrays.equals(hmac, originHmac));
|
resp.setVerified(Arrays.equals(hmac, originHmac));
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +187,7 @@ public class SymKeyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public SymMacCheckResp macCheck(SymMacCheckReq req) {
|
public VerifyResp macCheck(SymMacCheckReq req) {
|
||||||
|
|
||||||
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||||
byte[] iv = CodecUtils.decodeBase64(req.getIv());
|
byte[] iv = CodecUtils.decodeBase64(req.getIv());
|
||||||
@ -204,8 +203,8 @@ public class SymKeyService {
|
|||||||
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||||
byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain);
|
byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain);
|
||||||
|
|
||||||
SymMacCheckResp resp = new SymMacCheckResp();
|
VerifyResp resp = new VerifyResp();
|
||||||
resp.setValid(Arrays.equals(mac, originMac));
|
resp.setVerified(Arrays.equals(mac, originMac));
|
||||||
return resp;
|
return resp;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,11 @@ import com.sunyard.chsm.sdf.model.EccPriKey;
|
|||||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||||
import com.sunyard.chsm.utils.CodecUtils;
|
import com.sunyard.chsm.utils.CodecUtils;
|
||||||
|
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||||
|
import com.sunyard.chsm.utils.gm.SM2PreprocessSigner;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
|
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.Assertions;
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
@ -147,6 +151,30 @@ class SdfApiAdapterTest {
|
|||||||
Assertions.assertArrayEquals(hash, bcHash);
|
Assertions.assertArrayEquals(hash, bcHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashWithId() {
|
||||||
|
byte[] userId = "87123824awdqweqw".getBytes();
|
||||||
|
|
||||||
|
String newSession = sdfAdapter.openSession(hd);
|
||||||
|
EccKey eccKey = sdfAdapter.generateKeyPairECC(newSession, AlgId.SGD_SM2_1);
|
||||||
|
EccPubKey prePubKey = EccPubKey.fromBytes(eccKey.getPubKey());
|
||||||
|
sdfAdapter.hashInit(newSession, AlgId.SGD_SM3, prePubKey, userId);
|
||||||
|
sdfAdapter.hashUpdate(newSession, plain.getBytes());
|
||||||
|
byte[] hash = sdfAdapter.hashFinish(newSession);
|
||||||
|
log.info("sdf pubkey pre hash: {}", CodecUtils.encodeHex(hash));
|
||||||
|
sdfAdapter.closeSession(newSession);
|
||||||
|
|
||||||
|
|
||||||
|
CipherParameters parameters = BCECUtils.createECPublicKeyParameters(prePubKey.getX(), prePubKey.getY());
|
||||||
|
parameters = new ParametersWithID(parameters, userId);
|
||||||
|
SM2PreprocessSigner signer = new SM2PreprocessSigner();
|
||||||
|
signer.init(false, parameters);
|
||||||
|
byte[] bcPrehash = signer.preprocess( plain.getBytes());
|
||||||
|
log.info("bc preHash: {}", CodecUtils.encodeHex(bcPrehash));
|
||||||
|
|
||||||
|
Assertions.assertArrayEquals(hash, bcPrehash);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSm4Mac() {
|
public void testSm4Mac() {
|
||||||
String hk = sdfAdapter.importKey(hs, symKey);
|
String hk = sdfAdapter.importKey(hs, symKey);
|
||||||
|
@ -38,7 +38,7 @@ import java.util.UUID;
|
|||||||
public class SdfApiServiceTest {
|
public class SdfApiServiceTest {
|
||||||
|
|
||||||
private final static byte[] symKey = CodecUtils.decodeHex("a00f10444a727d09b94e2112cd662ea4");
|
private final static byte[] symKey = CodecUtils.decodeHex("a00f10444a727d09b94e2112cd662ea4");
|
||||||
private final static String plain = "hjsu234127qikqwndqqw13412as324";
|
private final static String plain = "hjsu234127qikqwndqqw13412as324qw";
|
||||||
// private final static String ip1 = "172.16.18.41";
|
// private final static String ip1 = "172.16.18.41";
|
||||||
// private final static int port = 8889;
|
// private final static int port = 8889;
|
||||||
private static final SdfApiService bcService = new SingleSdfApiService(SdfApiAdapterFactory.getBcAdapter());
|
private static final SdfApiService bcService = new SingleSdfApiService(SdfApiAdapterFactory.getBcAdapter());
|
||||||
@ -164,11 +164,11 @@ public class SdfApiServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSm3() {
|
public void testSm3() {
|
||||||
byte[] sdfHash = sdfService.hash(plain.getBytes());
|
byte[] sdfHash = sdfService.hash(plain.getBytes(), null, null);
|
||||||
log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
|
log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
|
||||||
Assertions.assertEquals(32, sdfHash.length);
|
Assertions.assertEquals(32, sdfHash.length);
|
||||||
|
|
||||||
byte[] bcHash = bcService.hash(plain.getBytes());
|
byte[] bcHash = bcService.hash(plain.getBytes(), null, null);
|
||||||
log.info("bc hash: {}", CodecUtils.encodeHex(bcHash));
|
log.info("bc hash: {}", CodecUtils.encodeHex(bcHash));
|
||||||
Assertions.assertEquals(32, bcHash.length);
|
Assertions.assertEquals(32, bcHash.length);
|
||||||
|
|
||||||
|
@ -1,36 +1,57 @@
|
|||||||
package sdf.adapter;
|
package sdf.adapter;
|
||||||
|
|
||||||
import com.sunyard.chsm.constant.CryptoConst;
|
import com.sunyard.chsm.constant.CryptoConst;
|
||||||
|
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
||||||
import com.sunyard.chsm.enums.Padding;
|
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;
|
||||||
import com.sunyard.chsm.sdf.SdfApiService;
|
import com.sunyard.chsm.sdf.SdfApiService;
|
||||||
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
|
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
|
||||||
|
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
|
||||||
import com.sunyard.chsm.sdf.context.AlgId;
|
import com.sunyard.chsm.sdf.context.AlgId;
|
||||||
|
import com.sunyard.chsm.sdf.model.DeviceInfo;
|
||||||
import com.sunyard.chsm.sdf.model.EccCipher;
|
import com.sunyard.chsm.sdf.model.EccCipher;
|
||||||
import com.sunyard.chsm.sdf.model.EccKey;
|
import com.sunyard.chsm.sdf.model.EccKey;
|
||||||
import com.sunyard.chsm.sdf.model.EccPriKey;
|
import com.sunyard.chsm.sdf.model.EccPriKey;
|
||||||
import com.sunyard.chsm.sdf.model.EccPubKey;
|
import com.sunyard.chsm.sdf.model.EccPubKey;
|
||||||
import com.sunyard.chsm.sdf.model.EccSignature;
|
import com.sunyard.chsm.sdf.model.EccSignature;
|
||||||
import com.sunyard.chsm.sdf.util.PaddingUtil;
|
import com.sunyard.chsm.sdf.util.PaddingUtil;
|
||||||
|
import com.sunyard.chsm.service.TmkService;
|
||||||
import com.sunyard.chsm.utils.gm.BCECUtils;
|
import com.sunyard.chsm.utils.gm.BCECUtils;
|
||||||
import com.sunyard.chsm.utils.gm.BCSM3Utils;
|
import com.sunyard.chsm.utils.gm.BCSM3Utils;
|
||||||
import com.sunyard.chsm.utils.gm.SM2PreprocessSigner;
|
import com.sunyard.chsm.utils.gm.SM2PreprocessSigner;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.bouncycastle.crypto.CipherParameters;
|
import org.bouncycastle.crypto.CipherParameters;
|
||||||
import org.bouncycastle.crypto.params.ParametersWithID;
|
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author liulu
|
* @author liulu
|
||||||
* @since 2024/12/11
|
* @since 2024/12/11
|
||||||
*/
|
*/
|
||||||
|
@Service
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
public class SingleSdfApiService implements SdfApiService {
|
public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
||||||
|
|
||||||
private String tmkHandle;
|
private String tmkHandle;
|
||||||
private String deviceHandle;
|
private String deviceHandle;
|
||||||
private String sessionHandle;
|
private String sessionHandle;
|
||||||
private SdfApiAdapter sdfApiAdapter;
|
private SdfApiAdapter sdfApiAdapter;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
public TmkService tmkService;
|
||||||
|
@Resource
|
||||||
|
private SpDeviceMapper spDeviceMapper;
|
||||||
|
|
||||||
public SingleSdfApiService(SdfApiAdapter sdfApiAdapter) {
|
public SingleSdfApiService(SdfApiAdapter sdfApiAdapter) {
|
||||||
this.sdfApiAdapter = sdfApiAdapter;
|
this.sdfApiAdapter = sdfApiAdapter;
|
||||||
this.deviceHandle = sdfApiAdapter.openDevice();
|
this.deviceHandle = sdfApiAdapter.openDevice();
|
||||||
@ -70,7 +91,10 @@ public class SingleSdfApiService implements SdfApiService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) {
|
public EccSignature externalSignECC(byte[] privateKey, byte[] pucData) {
|
||||||
return externalSignWithIdECC(privateKey, pucData, new byte[0]);
|
Assert.notNull(pucData, "待签名数据不能为空");
|
||||||
|
Assert.isTrue(pucData.length == 32, "待签名数据长度必须为32字节");
|
||||||
|
EccPriKey priKey = EccPriKey.fromBytes(privateKey);
|
||||||
|
return sdfApiAdapter.externalSignECC(sessionHandle, priKey, pucData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -103,7 +127,11 @@ public class SingleSdfApiService implements SdfApiService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) {
|
public boolean externalVerifyECC(byte[] publicKey, byte[] pubData, byte[] signData) {
|
||||||
return externalVerifyWithIdECC(publicKey, pubData, signData, new byte[0]);
|
Assert.notNull(pubData, "验签原文数据不能为空");
|
||||||
|
Assert.isTrue(pubData.length == 32, "验签原文数据长度必须为32字节");
|
||||||
|
EccPubKey eccPublicKey = EccPubKey.fromBytes(publicKey);
|
||||||
|
EccSignature eccSignature = EccSignature.fromBytes(signData);
|
||||||
|
return sdfApiAdapter.externalVerifyECC(sessionHandle, eccPublicKey, pubData, eccSignature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -134,6 +162,19 @@ public class SingleSdfApiService implements SdfApiService {
|
|||||||
return BCSM3Utils.hmac(key, srcData);
|
return BCSM3Utils.hmac(key, srcData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] hash(byte[] pucData, byte[] pubKey, byte[] userId) {
|
||||||
|
checkStatus();
|
||||||
|
EccPubKey eccPubKey = pubKey == null || pubKey.length == 0 ? null : EccPubKey.fromBytes(pubKey);
|
||||||
|
byte[] finalUserId = Objects.nonNull(pubKey) && Objects.isNull(userId) ? CryptoConst.USER_ID : userId;
|
||||||
|
String newSession = sdfApiAdapter.openSession(deviceHandle);
|
||||||
|
sdfApiAdapter.hashInit(newSession, AlgId.SGD_SM3, eccPubKey, finalUserId);
|
||||||
|
sdfApiAdapter.hashUpdate(newSession, pucData);
|
||||||
|
byte[] hash = sdfApiAdapter.hashFinish(newSession);
|
||||||
|
sdfApiAdapter.closeSession(newSession);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] hash(byte[] pucData) {
|
public byte[] hash(byte[] pucData) {
|
||||||
checkStatus();
|
checkStatus();
|
||||||
@ -167,4 +208,51 @@ public class SingleSdfApiService implements SdfApiService {
|
|||||||
checkStatus();
|
checkStatus();
|
||||||
Assert.hasText(tmkHandle, "没有可用的主密钥设备");
|
Assert.hasText(tmkHandle, "没有可用的主密钥设备");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void changeDevice(boolean tmkInit) {
|
||||||
|
DeviceTmkStatus status = tmkInit ? DeviceTmkStatus.finished : DeviceTmkStatus.available;
|
||||||
|
Device device = spDeviceMapper.selectOneByStatus(status);
|
||||||
|
if (Objects.nonNull(device)) {
|
||||||
|
DeviceCheckRes checkRes = tmkService.checkDevice(device);
|
||||||
|
if (!checkRes.isHasError() && checkRes.getStatus() == status) {
|
||||||
|
this.sdfApiAdapter = checkRes.getSdfApiAdapter();
|
||||||
|
this.deviceHandle = sdfApiAdapter.openDevice();
|
||||||
|
this.sessionHandle = sdfApiAdapter.openSession(deviceHandle);
|
||||||
|
if (tmkInit) {
|
||||||
|
sdfApiAdapter.getPrivateKeyAccessRight(sessionHandle, device.getEncKeyIdx(), device.getAccessCredentials().getBytes());
|
||||||
|
this.tmkHandle = sdfApiAdapter.importKeyWithISKECC(sessionHandle, device.getEncKeyIdx(), EccCipher.fromHex(device.getEncTmk()));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tmkService.isEnableSoftDevice()) {
|
||||||
|
this.sdfApiAdapter = SdfApiAdapterFactory.getBcAdapter();
|
||||||
|
this.sessionHandle = sdfApiAdapter.openSession("");
|
||||||
|
if (tmkInit) {
|
||||||
|
byte[] encTmk = tmkService.getSoftDeviceEncTmk();
|
||||||
|
this.tmkHandle = sdfApiAdapter.importKeyWithISKECC(sessionHandle, device.getEncKeyIdx(), EccCipher.fromBytes(encTmk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
Executors.newSingleThreadScheduledExecutor()
|
||||||
|
.scheduleWithFixedDelay(() -> {
|
||||||
|
boolean tmkInit = tmkService.isTmkInit();
|
||||||
|
if (sdfApiAdapter == null) {
|
||||||
|
changeDevice(tmkInit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tmkInit && StringUtils.isEmpty(tmkHandle)) {
|
||||||
|
changeDevice(tmkInit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
DeviceInfo deviceInfo = sdfApiAdapter.getDeviceInfo(sessionHandle);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
changeDevice(tmkInit);
|
||||||
|
}
|
||||||
|
}, 0L, 5L, TimeUnit.MINUTES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user