非对称加解密
This commit is contained in:
parent
0ba56c7c25
commit
7fe6482983
@ -8,6 +8,8 @@ import com.sunyard.chsm.utils.CodecUtils;
|
||||
*/
|
||||
public interface CryptoConst {
|
||||
|
||||
byte[] USER_ID = CodecUtils.decodeHex("31323334353637383132333435363738");
|
||||
|
||||
static byte[] iv() {
|
||||
return CodecUtils.decodeHex("30303030303030303030303030303030");
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ public interface SdfApiService {
|
||||
/**
|
||||
* 计算MAC
|
||||
*
|
||||
* @param algId algId
|
||||
* @param algId algId
|
||||
* @param padding padding
|
||||
* @param symKey 用户指定的密钥
|
||||
* @param pucIv 缓冲区指针,用于存放输入和返回的IV数据
|
||||
@ -136,8 +136,12 @@ public interface SdfApiService {
|
||||
* 杂凑运算
|
||||
*
|
||||
* @param pucData 缓冲区指针,用于存放输入的数据明文
|
||||
* @param pubKey 执行预处理的公钥
|
||||
* @param userId 执行预处理的userId
|
||||
* @return hash值
|
||||
*/
|
||||
byte[] hash(byte[] pucData, byte[] pubKey, byte[] userId);
|
||||
|
||||
byte[] hash(byte[] pucData);
|
||||
|
||||
byte[] encryptByTMK(byte[] data);
|
||||
|
@ -15,6 +15,9 @@ public abstract class CodecUtils {
|
||||
}
|
||||
|
||||
public static byte[] decodeBase64(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Base64.getDecoder().decode(str);
|
||||
} catch (Exception var3) {
|
||||
@ -35,6 +38,9 @@ public abstract class CodecUtils {
|
||||
}
|
||||
|
||||
public static byte[] decodeHex(String str) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return Hex.decode(str);
|
||||
} 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 = "明文不能为空")
|
||||
private String plainData;
|
||||
|
||||
// 填充方式, 默认PCKS7
|
||||
// Hash算法, 默认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;
|
||||
|
||||
@Data
|
||||
public class SymHmacCheckResp {
|
||||
public class VerifyResp {
|
||||
|
||||
private Boolean valid;
|
||||
private Boolean verified;
|
||||
|
||||
}
|
@ -90,7 +90,10 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -123,7 +126,11 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -154,6 +161,19 @@ public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
||||
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
|
||||
public byte[] hash(byte[] pucData) {
|
||||
checkStatus();
|
||||
|
@ -130,11 +130,11 @@ public class SdfApiServiceTest {
|
||||
|
||||
@Test
|
||||
public void testSm3() {
|
||||
byte[] sdfHash = sdfService.hash(plain.getBytes());
|
||||
byte[] sdfHash = sdfService.hash(plain.getBytes(), null, null);
|
||||
log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
|
||||
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));
|
||||
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)
|
||||
public R<HashResp> hash(@Valid @RequestBody HashReq req) {
|
||||
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();
|
||||
resp.setHash(CodecUtils.encodeBase64(hash));
|
||||
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.SymEncryptResp;
|
||||
import com.sunyard.chsm.param.SymHmacCheckReq;
|
||||
import com.sunyard.chsm.param.SymHmacCheckResp;
|
||||
import com.sunyard.chsm.param.SymHmacReq;
|
||||
import com.sunyard.chsm.param.SymHmacResp;
|
||||
import com.sunyard.chsm.param.SymMacCheckReq;
|
||||
import com.sunyard.chsm.param.SymMacCheckResp;
|
||||
import com.sunyard.chsm.param.SymMacReq;
|
||||
import com.sunyard.chsm.param.SymMacResp;
|
||||
import com.sunyard.chsm.param.VerifyResp;
|
||||
import com.sunyard.chsm.service.SymKeyService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -84,8 +83,8 @@ public class SymKeyController {
|
||||
*/
|
||||
@PostMapping("/hmac/check")
|
||||
@AuthCode(AuthCodeConst.check_hmac)
|
||||
public R<SymHmacCheckResp> macCheck(@Valid @RequestBody SymHmacCheckReq req) {
|
||||
SymHmacCheckResp resp = symKeyService.hmacCheck(req);
|
||||
public R<VerifyResp> macCheck(@Valid @RequestBody SymHmacCheckReq req) {
|
||||
VerifyResp resp = symKeyService.hmacCheck(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
@ -110,8 +109,8 @@ public class SymKeyController {
|
||||
*/
|
||||
@PostMapping("/mac/check")
|
||||
@AuthCode(AuthCodeConst.check_mac)
|
||||
public R<SymMacCheckResp> macCheck(@Valid @RequestBody SymMacCheckReq req) {
|
||||
SymMacCheckResp resp = symKeyService.macCheck(req);
|
||||
public R<VerifyResp> macCheck(@Valid @RequestBody SymMacCheckReq req) {
|
||||
VerifyResp resp = symKeyService.macCheck(req);
|
||||
return R.data(resp);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
@ -101,7 +102,10 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -136,7 +140,11 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -169,10 +177,12 @@ public class LoadBalancedSdfApiService implements SdfApiService {
|
||||
}
|
||||
|
||||
@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 -> {
|
||||
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);
|
||||
byte[] hash = s.getSdfApiAdapter().hashFinish(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
|
||||
public byte[] encryptByTMK(byte[] 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.SymEncryptResp;
|
||||
import com.sunyard.chsm.param.SymHmacCheckReq;
|
||||
import com.sunyard.chsm.param.SymHmacCheckResp;
|
||||
import com.sunyard.chsm.param.SymHmacReq;
|
||||
import com.sunyard.chsm.param.SymHmacResp;
|
||||
import com.sunyard.chsm.param.SymMacCheckReq;
|
||||
import com.sunyard.chsm.param.SymMacCheckResp;
|
||||
import com.sunyard.chsm.param.SymMacReq;
|
||||
import com.sunyard.chsm.param.SymMacResp;
|
||||
import com.sunyard.chsm.param.VerifyResp;
|
||||
import com.sunyard.chsm.sdf.SdfApiService;
|
||||
import com.sunyard.chsm.sdf.context.AlgId;
|
||||
import com.sunyard.chsm.utils.CodecUtils;
|
||||
@ -149,7 +148,7 @@ public class SymKeyService {
|
||||
return resp;
|
||||
}
|
||||
|
||||
public SymHmacCheckResp hmacCheck(SymHmacCheckReq req) {
|
||||
public VerifyResp hmacCheck(SymHmacCheckReq req) {
|
||||
|
||||
byte[] plain = CodecUtils.decodeBase64(req.getPlainData());
|
||||
byte[] originHmac = CodecUtils.decodeBase64(req.getHmac());
|
||||
@ -161,8 +160,8 @@ public class SymKeyService {
|
||||
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||
byte[] hmac = sdfApiService.hmac(symKey, plain);
|
||||
|
||||
SymHmacCheckResp resp = new SymHmacCheckResp();
|
||||
resp.setValid(Arrays.equals(hmac, originHmac));
|
||||
VerifyResp resp = new VerifyResp();
|
||||
resp.setVerified(Arrays.equals(hmac, originHmac));
|
||||
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[] iv = CodecUtils.decodeBase64(req.getIv());
|
||||
@ -204,8 +203,8 @@ public class SymKeyService {
|
||||
byte[] symKey = sdfApiService.decryptByTMK(CodecUtils.decodeHex(keyRecord.getKeyData()));
|
||||
byte[] mac = sdfApiService.calculateMAC(AlgId.SGD_SM4_MAC, req.getPadding(), symKey, iv, plain);
|
||||
|
||||
SymMacCheckResp resp = new SymMacCheckResp();
|
||||
resp.setValid(Arrays.equals(mac, originMac));
|
||||
VerifyResp resp = new VerifyResp();
|
||||
resp.setVerified(Arrays.equals(mac, originMac));
|
||||
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.EccSignature;
|
||||
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 org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@ -147,6 +151,30 @@ class SdfApiAdapterTest {
|
||||
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
|
||||
public void testSm4Mac() {
|
||||
String hk = sdfAdapter.importKey(hs, symKey);
|
||||
|
@ -38,7 +38,7 @@ import java.util.UUID;
|
||||
public class SdfApiServiceTest {
|
||||
|
||||
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 int port = 8889;
|
||||
private static final SdfApiService bcService = new SingleSdfApiService(SdfApiAdapterFactory.getBcAdapter());
|
||||
@ -164,11 +164,11 @@ public class SdfApiServiceTest {
|
||||
|
||||
@Test
|
||||
public void testSm3() {
|
||||
byte[] sdfHash = sdfService.hash(plain.getBytes());
|
||||
byte[] sdfHash = sdfService.hash(plain.getBytes(), null, null);
|
||||
log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
|
||||
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));
|
||||
Assertions.assertEquals(32, bcHash.length);
|
||||
|
||||
|
@ -1,36 +1,57 @@
|
||||
package sdf.adapter;
|
||||
|
||||
import com.sunyard.chsm.constant.CryptoConst;
|
||||
import com.sunyard.chsm.enums.DeviceTmkStatus;
|
||||
import com.sunyard.chsm.enums.Padding;
|
||||
import com.sunyard.chsm.mapper.SpDeviceMapper;
|
||||
import com.sunyard.chsm.model.dto.DeviceCheckRes;
|
||||
import com.sunyard.chsm.model.entity.Device;
|
||||
import com.sunyard.chsm.sdf.SdfApiService;
|
||||
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.model.DeviceInfo;
|
||||
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.service.TmkService;
|
||||
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.apache.commons.lang3.StringUtils;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.params.ParametersWithID;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
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
|
||||
* @since 2024/12/11
|
||||
*/
|
||||
@Service
|
||||
@NoArgsConstructor
|
||||
public class SingleSdfApiService implements SdfApiService {
|
||||
public class SingleSdfApiService implements SdfApiService, InitializingBean {
|
||||
|
||||
private String tmkHandle;
|
||||
private String deviceHandle;
|
||||
private String sessionHandle;
|
||||
private SdfApiAdapter sdfApiAdapter;
|
||||
|
||||
@Resource
|
||||
public TmkService tmkService;
|
||||
@Resource
|
||||
private SpDeviceMapper spDeviceMapper;
|
||||
|
||||
public SingleSdfApiService(SdfApiAdapter sdfApiAdapter) {
|
||||
this.sdfApiAdapter = sdfApiAdapter;
|
||||
this.deviceHandle = sdfApiAdapter.openDevice();
|
||||
@ -70,7 +91,10 @@ public class SingleSdfApiService implements SdfApiService {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -103,7 +127,11 @@ public class SingleSdfApiService implements SdfApiService {
|
||||
|
||||
@Override
|
||||
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
|
||||
@ -134,6 +162,19 @@ public class SingleSdfApiService implements SdfApiService {
|
||||
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
|
||||
public byte[] hash(byte[] pucData) {
|
||||
checkStatus();
|
||||
@ -167,4 +208,51 @@ public class SingleSdfApiService implements SdfApiService {
|
||||
checkStatus();
|
||||
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