This commit is contained in:
liulu 2024-11-08 08:57:51 +08:00
parent 1cf14e0884
commit 0acb061756
13 changed files with 479 additions and 13 deletions

View File

@ -49,6 +49,11 @@
<groupId>net.java.dev.jna</groupId> <groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId> <artifactId>jna</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.2.1-jre</version>
</dependency>
</dependencies> </dependencies>

View File

@ -6,9 +6,12 @@ import com.sunyard.chsm.sdf.lib.SdfLibrary;
import com.sunyard.chsm.sdf.model.DeviceInfo; import com.sunyard.chsm.sdf.model.DeviceInfo;
import com.sunyard.chsm.sdf.model.EccKey; import com.sunyard.chsm.sdf.model.EccKey;
import com.sunyard.chsm.sdf.model.EccPubKey; import com.sunyard.chsm.sdf.model.EccPubKey;
import com.sunyard.chsm.sdf.model.SDF_DeviceInfo;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -17,6 +20,7 @@ import java.util.concurrent.ConcurrentHashMap;
* @author liulu * @author liulu
* @since 2024/11/4 * @since 2024/11/4
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public abstract class JnaSdfAdaptor implements SdfApiAdapter { public abstract class JnaSdfAdaptor implements SdfApiAdapter {
@ -87,7 +91,21 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter {
@Override @Override
public DeviceInfo getDeviceInfo(String sessionHandle) { public DeviceInfo getDeviceInfo(String sessionHandle) {
return null;
SDF_DeviceInfo sdfInfo = new SDF_DeviceInfo();
sdfLibrary.SDF_GetDeviceInfo(getSessionHandle(sessionHandle), sdfInfo);
DeviceInfo deviceInfo = new DeviceInfo();
deviceInfo.setIssuerName(new String(sdfInfo.IssuerName));
deviceInfo.setDeviceName(new String(sdfInfo.DeviceName));
deviceInfo.setDeviceSerial(new String(sdfInfo.DeviceSerial));
deviceInfo.setDeviceVersion(sdfInfo.DeviceVersion);
deviceInfo.setStandardVersion(sdfInfo.StandardVersion);
deviceInfo.setAsymAlgAbility(sdfInfo.AsymAlgAbility);
deviceInfo.setSymAlgAbility(sdfInfo.SymAlgAbility);
deviceInfo.setHashAlgAbility(sdfInfo.HashAlgAbility);
deviceInfo.setBufferSize(sdfInfo.BufferSize);
return deviceInfo;
} }
@Override @Override
@ -100,7 +118,10 @@ public abstract class JnaSdfAdaptor implements SdfApiAdapter {
@Override @Override
public EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) { public EccPubKey exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
return null; byte[] pubKey = new byte[132];
Pointer hSessionHandle = getSessionHandle(sessionHandle);
sdfLibrary.SDF_ExportEncPublicKey_ECC(hSessionHandle, uiKeyIndex, pubKey);
return new EccPubKey(256, Arrays.copyOfRange(pubKey, 36, 68), Arrays.copyOfRange(pubKey, 100, 132));
} }
@Override @Override

View File

@ -27,7 +27,7 @@ public class SdfApiAdapterFactory {
case enc001: case enc001:
return new SunyardJnaSdfAdaptor(device.getServiceIp(), device.getServicePort()); return new SunyardJnaSdfAdaptor(device.getServiceIp(), device.getServicePort());
default: default:
return null; throw new UnsupportedOperationException("不支持的设备型号: " + device.getManufacturerModel());
} }
} }

View File

@ -24,7 +24,7 @@ public class SunyardJnaSdfAdaptor extends JnaSdfAdaptor {
private final Integer dealTimeout; private final Integer dealTimeout;
public SunyardJnaSdfAdaptor(String ip, int port) { public SunyardJnaSdfAdaptor(String ip, int port) {
this(ip, port, "libsdf"); this(ip, port, "sdf");
} }
public SunyardJnaSdfAdaptor(String ip, int port, String libName) { public SunyardJnaSdfAdaptor(String ip, int port, String libName) {

View File

@ -2,7 +2,9 @@ package com.sunyard.chsm.sdf.lib;
import com.sun.jna.Library; import com.sun.jna.Library;
import com.sun.jna.Pointer; import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.ptr.PointerByReference; import com.sun.jna.ptr.PointerByReference;
import com.sunyard.chsm.sdf.model.SDF_DeviceInfo;
/** /**
* @author liulu * @author liulu
@ -50,7 +52,7 @@ public interface SdfLibrary extends Library {
* @param pstDeviceInfo 设备能力描述信息内容及格式见设备信息定义 * @param pstDeviceInfo 设备能力描述信息内容及格式见设备信息定义
* @return 0 成功; 非0 失败,返回错误代码 * @return 0 成功; 非0 失败,返回错误代码
*/ */
int SDF_GetDeviceInfo(Pointer hSessionHandle, byte[] pstDeviceInfo); int SDF_GetDeviceInfo(Pointer hSessionHandle, SDF_DeviceInfo pstDeviceInfo);
/** /**
* 产生随机数 * 产生随机数
@ -63,4 +65,237 @@ public interface SdfLibrary extends Library {
int SDF_GenerateRandom(Pointer hSessionHandle, int uiLength, byte[] pucRandom); int SDF_GenerateRandom(Pointer hSessionHandle, int uiLength, byte[] pucRandom);
/**
* 生成ECC密钥对
*
* @param phSessionHandle 会话句柄
* @param uiAlgID 算法标识
* @param uiKeyBits 密钥长度
* @param pucPublicKey 输出的ECC公钥结构
* @param pucPrivateKey 输出的ECC私钥结构
* @return int 响应码
*/
int SDF_GenerateKeyPair_ECC(
Pointer phSessionHandle,
int uiAlgID,
int uiKeyBits,
byte[] pucPublicKey,
byte[] pucPrivateKey
);
/**
* 导出签名公钥
*
* @param phSessionHandle
* @param uiKeyIndex
* @param pucPublicKey
* @return
*/
int SDF_ExportSignPublicKey_ECC(Pointer phSessionHandle,
int uiKeyIndex,
byte[] pucPublicKey);
/**
* 导出加密公钥
*
* @param phSessionHandle
* @param uiKeyIndex
* @param pucPublicKey
* @return
*/
int SDF_ExportEncPublicKey_ECC(Pointer phSessionHandle,
int uiKeyIndex,
byte[] pucPublicKey);
/**
* 密钥加密
*
* @param phSessionHandle 会话句柄
* @param uiAlgID 算法标识
* @param pucPublicKey ECC公钥结构
* @param pucData 输入的数据明文
* @param pucDatalength 输入的数据明文长度
* @param pucEncData 输出的密文数据
* @return int 响应码
*/
int SDF_ExternalEncrypt_ECC(Pointer phSessionHandle, int uiAlgID, byte[] pucPublicKey, byte[] pucData, int pucDatalength, byte[] pucEncData);
/**
* 密钥解密
*
* @param phSessionHandle 会话句柄
* @param uiAlgID 算法标识
* @param pucPrivateKey ECC私钥结构
* @param pucEncData 输入的密文数据
* @param pucDataOut 输出的数据明文
* @param pucDatalength 输出的数据明文长度
* @return int 响应码
*/
int SDF_ExternalDecrypt_ECC(Pointer phSessionHandle, int uiAlgID, byte[] pucPrivateKey, byte[] pucEncData, byte[] pucDataOut, IntByReference pucDatalength);
/**
* 外部ECC签名
*
* @param phSessionHandle 会话句柄
* @param uiAlgID 算法标识
* @param pucPublicKey ECC公钥结构
* @param pucData 输入的数据明文
* @param pucDataLength 输入的数据明文长度
* @param pucECCSignature 输出的签名值数据
* @return int 响应码
*/
int SDF_ExternalSign_ECC(
Pointer phSessionHandle,
int uiAlgID,
byte[] pucPublicKey,
byte[] pucData,
int pucDataLength,
byte[] pucECCSignature
);
/**
* 外部ECC验签
*
* @param phSessionHandle 会话句柄
* @param uiAlgID 算法标识
* @param pucPublicKey ECC公钥结构
* @param pucData 输入的数据明文
* @param pucDataLength 输入的数据明文长度
* @param pucECCSignature 输入的签名值数据
* @return int 响应码
*/
int SDF_ExternalVerify_ECC(Pointer phSessionHandle, int uiAlgID, byte[] pucPublicKey, byte[] pucData, int pucDataLength, byte[] pucECCSignature);
/**
* 基于ECC算法的数字信封转换
*
* @param hSessionHandle 与设备建立的会话句柄
* @param uiKeyIndex 密码设备内部存储ECC密钥对索引值
* @param uiAlgID 外部ECC公钥的算法标识 SGD_SM2_1
* @param pucPublicKey 外部ECC公钥结构
* @param pucEncDateIn 缓冲区指针用于存放输入的会话密钥密文
* @param pucEncDateOut 缓冲区指针用于存放输出的会话密钥密文
* @return 0 成功; 非0 失败,返回错误代码
*/
int SDF_ExchangeDigitEnvelopeBaseOnECC(Pointer hSessionHandle, int uiKeyIndex, int uiAlgID, byte[] pucPublicKey, byte[] pucEncDateIn, byte[] pucEncDateOut);
/**
* 导入会话密钥并用内部ECC私钥解密
*
* @param phSessionHandle
* @param uiISKIndex
* @param puckey
* @param phKeyhandle
* @return
*/
int SDF_ImportKeyWithISK_ECC(Pointer phSessionHandle,
int uiISKIndex,
byte[] puckey,
PointerByReference phKeyhandle);
/**
* 销毁会话密钥
*
* @param phSessionHandle
* @param phKeyhandle
* @return
*/
int SDF_DestroyKey(Pointer phSessionHandle, PointerByReference phKeyhandle);
/**
* 对称加密
*
* @param phSessionHandle 会话句柄
* @param hKeyHandle 密钥句柄
* @param uiAlgID 算法标识
* @param pucIV IV数据
* @param pucData 待加密数据
* @param pucDataLength 待加密数据长度
* @param pucEncData 存放密文容器
* @param pucEncDataLength 密文长度
* @return int 响应码
*/
int SDF_Encrypt(
Pointer phSessionHandle,
Pointer hKeyHandle,
int uiAlgID,
byte[] pucIV,
byte[] pucData,
int pucDataLength,
byte[] pucEncData,
IntByReference pucEncDataLength
);
/**
* 对称解密
*
* @param phSessionHandle 会话句柄
* @param hKeyHandle 密钥句柄
* @param uiAlgID 算法标识
* @param pucIV IV数据
* @param purEncData 指向密文数据的指针
* @param encDataLength 密文数据长度
* @param pucData 输出明文数据
* @param pucDataLength 输出明文数据长度
* @return int 响应码
*/
int SDF_Decrypt(
Pointer phSessionHandle,
Pointer hKeyHandle,
int uiAlgID,
byte[] pucIV,
byte[] purEncData,
int encDataLength,
byte[] pucData,
IntByReference pucDataLength
);
/**
* 杂凑运算初始化
*
* @param phSessionHandle 会话句柄
* @param uiAlgID 算法标识
* @param pucPublicKey ECC公钥结构
* @param pucID 签名者的ID值
* @param pucIDlength 签名者ID的长度
* @return int 响应码
*/
int SDF_HashInit(Pointer phSessionHandle, int uiAlgID, byte[] pucPublicKey, String pucID, int pucIDlength);
/**
* 多包杂凑运算
*
* @param phSessionHandle 会话句柄
* @param pucData 输入的数据明文
* @param uiDataLength 输入的数据明文长度
* @return int 响应码
*/
int SDF_HashUpdate(
Pointer phSessionHandle, // 使用IntByReference因为SessionHandle可能是引用类型的
byte[] pucData, // 输入的明文数据作为字节数组
int uiDataLength // 明文数据长度
);
/**
* 杂凑运算结束
*
* @param phSessionHandle 会话句柄
* @param pucHash 杂凑结果
* @param pucHashLength 杂凑结果长度
* @return int 响应码
*/
int SDF_HashFinal(Pointer phSessionHandle, byte[] pucHash, IntByReference pucHashLength);
int SDF_CalculateMAC(Pointer phSessionHandle,
Pointer hKeyHadnle,
int uiAlgID,
byte[] pucIV,
byte[] purData,
int pucDatalength,
byte[] pucMAC,
IntByReference pucMACLength);
} }

View File

@ -0,0 +1,40 @@
package com.sunyard.chsm.sdf.model;
import com.sun.jna.Structure;
/**
* @author liulu
* @version V1.0
* @since 2022/10/11
*/
@Structure.FieldOrder({"IssuerName", "DeviceName", "DeviceSerial", "DeviceVersion", "StandardVersion", "AsymAlgAbility", "SymAlgAbility", "HashAlgAbility", "BufferSize"})
public class SDF_DeviceInfo extends Structure {
// 设备 生产 厂商名称
public byte[] IssuerName = new byte[40];
// 设备 型号
public byte[] DeviceName = new byte[16];
// : (8 ) (3 ) (5 )
public byte[] DeviceSerial = new byte[16];
// 密码 设备 内部软件的版本号
public int DeviceVersion;
// 密码 设备 支持的接口规范版本号
public int StandardVersion;
// 前4字节表示支持的算法表示方法为非对称算法标识 按位或的结果;后4字节表示算法的最大模长表示方法 为支持的模长按位或的结果
public int[] AsymAlgAbility = new int[2];
// 所有支持的对称算法表示方法为对称算法标识按位或 运算结果
public int SymAlgAbility;
// 所有支持的杂凑算法表示方法为杂凑算法标识按位或 运算结果
public int HashAlgAbility;
// 支持 的最 大文件存储空间(单位字节)
public int BufferSize;
}

View File

@ -0,0 +1,59 @@
package com.sunyard.chsm.sdf.util;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* @author liulu
* @version V1.0
* @since 2024/11/06
*/
public abstract class LangUtils {
public static byte[] merge(byte[]... bytes) {
int newLen = Arrays.stream(bytes).mapToInt(it -> it.length).sum();
byte[] res = new byte[newLen];
int mergedLen = 0;
for (byte[] item : bytes) {
System.arraycopy(item, 0, res, mergedLen, item.length);
mergedLen += item.length;
}
return res;
}
public static byte[] toByteArray(int i) {
byte[] result = new byte[4];
result[0] = (byte) ((i >> 24) & 0xFF);
result[1] = (byte) ((i >> 16) & 0xFF);
result[2] = (byte) ((i >> 8) & 0xFF);
result[3] = (byte) (i & 0xFF);
return result;
}
public static byte[] toByteArray(long i) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(i);
return buffer.array();
}
public static int toInt(byte[] bytes) {
if (bytes == null || bytes.length != Integer.BYTES) {
throw new IllegalArgumentException("转int需要4个字节");
}
int result = 0;
for (int i = 0; i < 4; i++) {
int shift = (3 - i) * 8;
result += (bytes[i] & 0xFF) << shift;
}
return result;
}
public static long toLong(byte[] bytes) {
if (bytes == null || bytes.length != Long.BYTES) {
throw new IllegalArgumentException("转long需要8个字节");
}
ByteBuffer buffer = ByteBuffer.wrap(bytes);
return buffer.getLong();
}
}

View File

@ -0,0 +1,65 @@
package com.sunyard.chsm.sdf.util;
/**
* @author liulu
*/
public abstract class PaddingUtil {
public static byte[] PKCS7Padding(byte[] content, int blockSize) {
if (content == null || blockSize < 8)
throw new IllegalStateException("parameter error");
int len = content.length;
int remain = len % blockSize;
int NUM = blockSize - remain;
int Len = blockSize * (len / blockSize + 1);
byte[] padded = new byte[Len];
System.arraycopy(content, 0, padded, 0, len);
for (int i = 0; i < NUM; i++)
padded[len + i] = (byte) NUM;
return padded;
}
public static byte[] PKCS7Padding(byte[] content) {
try {
return PKCS7Padding(content, 16);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static byte[] PKCS5Padding(byte[] content) throws Exception {
return PKCS7Padding(content, 8);
}
public static byte[] PKCS7Unpadding(byte[] content, int blockSize)
throws Exception {
if (blockSize < 8 || content == null || content.length % blockSize != 0)
throw new Exception("parameter error");
return PKCS7Unpadding(content);
}
public static byte[] PKCS7Unpadding(byte[] content) {
if (content == null || content.length < 8)
throw new IllegalStateException("parameter error");
int len = content.length;
int NUM = content[len - 1];
if (NUM > len)
throw new IllegalStateException("invalid padding");
for (int i = 0; i < NUM; i++)
if (content[len - i - 1] != NUM)
throw new IllegalStateException("invalid padding");
byte[] unpadded = new byte[len - NUM];
System.arraycopy(content, 0, unpadded, 0, len - NUM);
return unpadded;
}
public static byte[] PKCS5Unpadding(byte[] content) throws Exception {
return PKCS7Unpadding(content, 8);
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -117,10 +117,19 @@
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/**</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -4,6 +4,13 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.sunyard.chsm.mapper.SpDeviceMapper; import com.sunyard.chsm.mapper.SpDeviceMapper;
import com.sunyard.chsm.model.entity.Device; import com.sunyard.chsm.model.entity.Device;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapter;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
import com.sunyard.chsm.sdf.context.DeviceContext;
import com.sunyard.chsm.sdf.model.DeviceInfo;
import com.sunyard.chsm.sdf.model.EccPubKey;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@ -12,12 +19,14 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
/** /**
* @author liulu * @author liulu
* @since 2024/11/4 * @since 2024/11/4
*/ */
@Slf4j
@Component @Component
public class DeviceTask implements InitializingBean { public class DeviceTask implements InitializingBean {
@ -34,7 +43,7 @@ public class DeviceTask implements InitializingBean {
for (int i = 1; i < 500; i++) { for (int i = 1; i < 500; i++) {
Page<Device> devicePage = spDeviceMapper.selectPage( Page<Device> devicePage = spDeviceMapper.selectPage(
new Page<>(i, 100L), new Page<>(i, 20L),
new LambdaQueryWrapper<Device>().orderByAsc(Device::getId) new LambdaQueryWrapper<Device>().orderByAsc(Device::getId)
); );
List<Device> records = devicePage.getRecords(); List<Device> records = devicePage.getRecords();
@ -43,15 +52,38 @@ public class DeviceTask implements InitializingBean {
} }
for (Device record : records) { for (Device record : records) {
DeviceContext context = new DeviceContext();
context.setServiceIp(record.getServiceIp());
context.setServicePort(record.getServicePort());
context.setManufacturer(record.getManufacturer());
context.setManufacturerModel(record.getManufacturerModel());
boolean connected = false;
try {
SdfApiAdapter sdfApiAdapter = SdfApiAdapterFactory.newInstance(context);
String dh = sdfApiAdapter.openDevice();
String sh = sdfApiAdapter.openSession(dh);
DeviceInfo info = sdfApiAdapter.getDeviceInfo(sh);
log.info("get DeviceInfo: {}", info);
EccPubKey eccPubKey = sdfApiAdapter.exportEncPublicKeyECC(sh, 2);
log.info("exportEncPublicKeyECC: {}", Hex.toHexString(eccPubKey.getPubKeyBytes()));
sdfApiAdapter.closeSession(sh);
sdfApiAdapter.closeDevice(dh);
connected = true;
} catch (Exception ex) {
log.warn("设备 {}:{} 连接异常", record.getServiceIp(), record.getServicePort(), ex);
}
Device up = new Device();
up.setId(record.getId());
up.setConnected(connected);
LocalDateTime now = LocalDateTime.now();
if (connected) {
up.setLastConnectedTime(now);
}
up.setLastCheckTime(now);
spDeviceMapper.updateById(up);
} }
} }
} }
@Override @Override