This commit is contained in:
liulu
2024-12-13 14:32:58 +08:00
parent f2720fdb83
commit 8f997d2a00
14 changed files with 230 additions and 132 deletions
@@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -34,6 +35,7 @@ import java.util.stream.Collectors;
* @since 2024/12/2
*/
@Slf4j
@Component
public class IpFilter extends OncePerRequestFilter implements ApplicationRunner {
public static List<String> whiteIps = new ArrayList<>();
@@ -75,7 +77,7 @@ public class IpFilter extends OncePerRequestFilter implements ApplicationRunner
if (enableWhiteIp) {
syncWhiteIps();
}
}, 0L, 5L, TimeUnit.MINUTES);
}, 1L, 5L, TimeUnit.MINUTES);
}
@@ -12,13 +12,11 @@ import com.sunyard.chsm.utils.gm.BCECUtils;
import com.sunyard.chsm.utils.gm.BCSM2Utils;
import com.sunyard.chsm.utils.gm.BCSM4Utils;
import lombok.SneakyThrows;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.NullDigest;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;
@@ -29,6 +27,7 @@ import java.math.BigInteger;
import java.security.KeyPair;
import java.security.SecureRandom;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@@ -135,28 +134,69 @@ public class BcSdfApiAdaptor implements SdfApiAdapter {
}
}
private static final SecureRandom RANDOM = new SecureRandom();
@Override
public EccSignature externalSignECC(String sessionHandle, EccPriKey privateKey, byte[] pucData) {
ECPrivateKeyParameters pri = BCECUtils.createECPrivateKeyParameters(privateKey.getD());
try {
SM2Signer signer = new SM2Signer(new NullDigest());
signer.init(true, pri);
signer.update(pucData, 0, pucData.length);
byte[] signature = signer.generateSignature();
return EccSignature.fromBytes(signature);
} catch (CryptoException e) {
throw new IllegalArgumentException(e);
}
BigInteger n = pri.getParameters().getN();
BigInteger d = pri.getD();
BigInteger e = new BigInteger(1, pucData);
BigInteger r, s = null;
do {
BigInteger k;
do {
k = new BigInteger(n.bitLength(), RANDOM);
} while (k.compareTo(BigInteger.ONE) < 0 || k.compareTo(n) >= 0);
ECPoint kG = pri.getParameters().getG().multiply(k).normalize();
BigInteger x1 = kG.getAffineXCoord().toBigInteger();
r = e.add(x1).mod(n);
if (r.equals(BigInteger.ZERO) || r.add(k).equals(n)) {
continue;
}
BigInteger dPlus1Inv = d.add(BigInteger.ONE).modInverse(n);
s = dPlus1Inv.multiply(k.subtract(r.multiply(d))).mod(n);
} while (Objects.equals(s, BigInteger.ZERO));
return new EccSignature(BigIntegers.asUnsignedByteArray(32, r),
BigIntegers.asUnsignedByteArray(32, s));
}
@Override
public boolean externalVerifyECC(String sessionHandle, EccPubKey publicKey, byte[] pucData, EccSignature pucSignature) {
ECPublicKeyParameters pub = BCECUtils.createECPublicKeyParameters(publicKey.getX(), publicKey.getY());
SM2Signer verifier = new SM2Signer(new NullDigest());
verifier.init(false, pub);
// 验证数据
verifier.update(pucData, 0, pucData.length);
return verifier.verifySignature(pucSignature.getRawSignBytes());
if (pucData.length != 32) {
throw new IllegalArgumentException("Hash length must be 32 bytes");
}
ECDomainParameters domainParams = pub.getParameters();
ECPoint G = domainParams.getG();
BigInteger n = domainParams.getN();
ECPoint Q = pub.getQ();
BigInteger e = new BigInteger(1, pucData);
BigInteger r = new BigInteger(1, pucSignature.getR());
BigInteger s = new BigInteger(1, pucSignature.getS());
if (r.compareTo(BigIntegers.ONE) < 0 || r.compareTo(n) >= 0 || s.compareTo(BigIntegers.ONE) < 0 || s.compareTo(n) >= 0) {
return false;
}
BigInteger t = r.add(s).mod(n);
if (t.equals(BigInteger.ZERO)) {
return false;
}
ECPoint point = G.multiply(s).add(Q.multiply(t)).normalize();
BigInteger R = e.add(point.getAffineXCoord().toBigInteger()).mod(n);
return R.equals(r);
}
@Override
@@ -0,0 +1,11 @@
package com.sunyard.chsm.sdf.adapter;
/**
* @author liulu
* @since 2024/12/12
*/
public interface RpcSdfAdapter extends SdfApiAdapter {
String openDevice(String ip, int port, int connTimeout, int dealTimeout, int ipMode);
}
@@ -1,15 +1,26 @@
package com.sunyard.chsm.sdf.adapter;
import com.googlecode.jsonrpc4j.JsonRpcHttpClient;
import com.googlecode.jsonrpc4j.ProxyUtil;
import com.sun.jna.Platform;
import com.sunyard.chsm.enums.ManufacturerModelEnum;
import com.sunyard.chsm.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.Objects;
/**
* @author liulu
* @since 2024/11/5
*/
@Slf4j
public class SdfApiAdapterFactory {
@@ -28,11 +39,36 @@ public class SdfApiAdapterFactory {
}
switch (modelEnum) {
case enc001:
return new SunyardJnaSdfAdaptor(ip, port);
return Platform.isMac() ? getProxyRcpAdapter(ip, port) : new SunyardJnaSdfAdaptor(ip, port);
default:
throw new UnsupportedOperationException("不支持的设备型号: " + model);
}
}
private static final RpcSdfAdapter rpcSdfAdapter;
static {
try {
JsonRpcHttpClient client = new JsonRpcHttpClient(JsonUtils.objectMapper(),
new URL("http://172.16.18.46:9989/sdf/adapter"), Collections.emptyMap());
rpcSdfAdapter = ProxyUtil.createClientProxy(
ClassUtils.getDefaultClassLoader(), RpcSdfAdapter.class, client);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
public static SdfApiAdapter getProxyRcpAdapter(String ip, Integer port) {
return (SdfApiAdapter) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(),
new Class[]{SdfApiAdapter.class},
(proxy, method, args) -> {
if (Objects.equals(method.getName(), "openDevice")) {
return rpcSdfAdapter.openDevice(ip, port, 3000, 3000, 0);
}
return method.invoke(rpcSdfAdapter, args);
}
);
}
}
@@ -1,22 +1,17 @@
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;
import com.sunyard.chsm.sdf.context.SunyardAlgId;
import com.sunyard.chsm.sdf.lib.SdfLibrary;
import com.sunyard.chsm.sdf.lib.SunyardSdfLibrary;
import lombok.RequiredArgsConstructor;
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
@@ -25,64 +20,48 @@ import java.util.concurrent.ConcurrentHashMap;
@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 String libName;
private final Integer connTimeout;
private final Integer dealTimeout;
public SunyardJnaSdfAdaptor(String ip) {
this(ip, 8889, "sdf");
}
public SunyardJnaSdfAdaptor(String ip, int port) {
this(ip, port, "sdf");
this(ip, port, 3000, 3000);
}
public SunyardJnaSdfAdaptor(String ip, int port, String libName) {
this(ip, port, libName, 3000, 3000);
}
public SunyardJnaSdfAdaptor(String ip, int port, String libName, int connTimeout, int dealTimeout) {
public SunyardJnaSdfAdaptor(String ip, int port, int connTimeout, int dealTimeout) {
super((SdfLibrary) Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(),
new Class[]{SdfLibrary.class},
new InvocationHandler() {
private final SunyardSdfLibrary sunyardSdfLibrary;
{
sunyardSdfLibrary = SDF_LIB_MAP.computeIfAbsent(libName, k -> Native.load(libName, SunyardSdfLibrary.class));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object res = method.invoke(sunyardSdfLibrary, args);
if (method.getName().startsWith("SDF_") && res instanceof Integer) {
int resInt = (Integer) res;
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));
}
}
(proxy, method, args) -> {
Object res = method.invoke(SunyardSdfLibrary.INSTANCE, 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;
}
));
this.ip = ip;
this.port = port;
this.libName = libName;
this.connTimeout = connTimeout;
this.dealTimeout = dealTimeout;
}
@Override
public String openDevice() {
SunyardSdfLibrary sunyardSdfLibrary = SDF_LIB_MAP.computeIfAbsent(libName, k -> Native.load(libName, SunyardSdfLibrary.class));
PointerByReference phDeviceHandle = new PointerByReference();
sunyardSdfLibrary.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0);
SunyardSdfLibrary.INSTANCE.SDF_OpenDevice(phDeviceHandle, safeStringBytes(ip), port, connTimeout, dealTimeout, 0);
String key = UUID.randomUUID().toString();
DEVICE_HANDLE_CONTEXT.put(key, phDeviceHandle.getValue());
return key;
@@ -104,15 +83,4 @@ public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor {
return ret;
}
@RequiredArgsConstructor
public static class CheckProxy implements InvocationHandler {
private final SdfLibrary sdfLibrary;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}
}
@@ -1,5 +1,6 @@
package com.sunyard.chsm.sdf.lib;
import com.sun.jna.Native;
import com.sun.jna.ptr.PointerByReference;
/**
@@ -8,6 +9,7 @@ import com.sun.jna.ptr.PointerByReference;
*/
public interface SunyardSdfLibrary extends SdfLibrary {
SunyardSdfLibrary INSTANCE = Native.load("sdf", SunyardSdfLibrary.class);
/**
* 打开设备
@@ -1,9 +1,12 @@
package com.sunyard.chsm.sdf.model;
import com.sunyard.chsm.utils.CodecUtils;
import com.sunyard.chsm.utils.gm.BCSM2Utils;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.IOException;
import java.util.Arrays;
/**
@@ -22,6 +25,19 @@ public class EccSignature {
// 签名的 s 部分
private byte[] s;
public String getRawSignHex() {
return CodecUtils.encodeHex(getRawSignBytes());
}
public byte[] getDerSignBytes() {
try {
return BCSM2Utils.encodeSM2SignToDER(getRawSignBytes());
} catch (IOException e) {
throw new IllegalArgumentException("ECC签名数据格式错误");
}
}
public byte[] getRawSignBytes() {
byte[] signData = new byte[r.length + s.length];
System.arraycopy(r, 0, signData, 0, r.length);
@@ -38,8 +54,12 @@ public class EccSignature {
public static EccSignature fromBytes(byte[] sign) {
if (sign == null || sign.length == 0) {
return null;
throw new IllegalArgumentException("ECC签名数据格式错误");
}
if (sign.length >= 70 && sign.length <= 75) {
sign = BCSM2Utils.decodeDERSM2Sign(sign);
}
if (sign.length == 64) {
byte[] r = Arrays.copyOfRange(sign, 0, 32);
byte[] s = Arrays.copyOfRange(sign, 32, 64);
@@ -174,11 +174,10 @@ public class TmkService {
public void checkSoftDeviceTmk() {
if (!isTmkInit() || !isEnableSoftDevice()) {
if (Objects.nonNull(getSoftDeviceEncTmk())) {
return;
}
byte[] softTmk = getSoftDeviceEncTmk();
if (Objects.nonNull(softTmk)) {
if (!isTmkInit() || !isEnableSoftDevice()) {
return;
}
log.warn("enabled soft device but no tmk in soft");
@@ -220,9 +219,17 @@ public class TmkService {
return null;
}
public boolean isTmkInit() {
private boolean tmkInit;
private boolean enableSoftDevice;
private byte[] softEncTmk;
public synchronized boolean isTmkInit() {
if (tmkInit) {
return true;
}
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.TMK_INIT);
return conf != null && String.valueOf(true).equals(conf.getValue());
tmkInit = conf != null && String.valueOf(true).equals(conf.getValue());
return tmkInit;
}
public boolean isEnableSoftDevice() {
@@ -244,16 +251,20 @@ public class TmkService {
}
}
public byte[] getSoftDeviceEncTmk() {
public synchronized byte[] getSoftDeviceEncTmk() {
boolean tmkInit = isTmkInit();
Assert.isTrue(tmkInit, "主密钥未初始化");
boolean enabled = isEnableSoftDevice();
Assert.isTrue(enabled, "未启用软设备");
if (softEncTmk != null) {
return softEncTmk;
}
ParamConf conf = paramConfMapper.selectByKey(ParamConfKeyConstant.SOFT_ENC_TMK);
if (conf == null || ObjectUtils.isEmpty(conf.getValue())) {
return null;
}
return CodecUtils.decodeBase64(conf.getValue());
softEncTmk = CodecUtils.decodeBase64(conf.getValue());
return softEncTmk;
}
private void updateSoftDeviceEncTmk(byte[] encTmk) {