From d1c8201f98c248eefc21035a01c0efd223fff85d Mon Sep 17 00:00:00 2001 From: cheney Date: Fri, 29 May 2026 16:44:04 +0800 Subject: [PATCH] =?UTF-8?q?fix=20=E9=95=BF=E5=BA=A6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/sunyard/util/ParamChecker.java | 2 +- .../java/racal/sunyard/main/SydApi4j.java | 135 +++++++++++++++++- 2 files changed, 132 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/sunyard/util/ParamChecker.java b/src/main/java/com/sunyard/util/ParamChecker.java index 72ee0db..2b2f5b8 100644 --- a/src/main/java/com/sunyard/util/ParamChecker.java +++ b/src/main/java/com/sunyard/util/ParamChecker.java @@ -91,7 +91,7 @@ public final class ParamChecker { * @throws IllegalArgumentException 如果 str 为 null 或仅包含空白字符 */ public static String checkNotBlank(String str, String paramName) { - if (str == null || str.isBlank()) { + if (str == null || !str.trim().isEmpty()) { throw new IllegalArgumentException("参数 '" + paramName + "' 不能为 null、空字符串或仅包含空白字符"); } return str; diff --git a/src/main/java/racal/sunyard/main/SydApi4j.java b/src/main/java/racal/sunyard/main/SydApi4j.java index 9e5d7de..1ae4b6d 100644 --- a/src/main/java/racal/sunyard/main/SydApi4j.java +++ b/src/main/java/racal/sunyard/main/SydApi4j.java @@ -6791,6 +6791,8 @@ public class SydApi4j implements SydApi { } + + @Override public String SM2EncryptOrder(byte[] publicKey, byte[] pOrgData) { Proto77 proto = new Proto77(); @@ -6917,10 +6919,8 @@ public class SydApi4j implements SydApi { // 重新计算长度 int len = bb.position(); len -= 2; - byte[] lenArray = new byte[2]; - lenArray[0] = (byte) ((len >> 8) & 0xFF); - lenArray[1] = (byte) (len & 0xFF); - bb.put(lenArray); + bb.put(0, (byte) ((len >> 8) & 0xFF)); // 覆盖开头的两个占位字节 + bb.put(1, (byte) (len & 0xFF)); // 覆盖开头的两个占位字节 // 通信 synchronized (this) { @@ -6955,6 +6955,133 @@ public class SydApi4j implements SydApi { + + /** + * SM2 解密(支持大包分段) + * @param privateKey 私钥字节数组 + * @param pCipherData 密文数据 + * @return 解密后的明文 + */ + public byte[] SYD_SM2Decrypt(byte[] privateKey, byte[] pCipherData) { + ParamChecker.checkNotNull(privateKey, "privateKey"); + ParamChecker.checkNotNull(pCipherData, "pCipherData"); + + final int CHUNK_SIZE = 63 * 1024; // 63 KB,与加密保持一致 + int dataLen = pCipherData.length; + int offset = 0; + + // 1. 分块传入密文 + while (offset < dataLen) { + int len = Math.min(CHUNK_SIZE, dataLen - offset); + SYD_SM2Decrypt(1, privateKey, ByteBuffer.wrap(pCipherData, offset, len), offset); + offset += len; + } + + // 2. 传入私钥,触发解密处理 + SYD_SM2Decrypt(2, privateKey, null, 0); + + // 3. 分块取回明文 + int idx = 0; + List chunks = new ArrayList<>(); + int totalLen = 0; + int isLast = 0; + + while (isLast != 1) { + MutiReturn7 ret = SYD_SM2Decrypt(3, null, null, idx); + ByteBuffer bb = ret.getData(); + isLast = ret.getIsLast(); + + if (bb != null && bb.remaining() > 0) { + chunks.add(bb); + totalLen += bb.remaining(); + idx += bb.remaining(); + } else if (isLast != 1) { + throw new RuntimeException("Empty plain chunk with isLast=0"); + } + } + + // 合并明文分片 + byte[] result = new byte[totalLen]; + int offset2 = 0; + for (ByteBuffer chunk : chunks) { + int len = chunk.remaining(); + chunk.get(result, offset2, len); + offset2 += len; + } + return result; + } + + /** + * 底层解密通信(与加密结构对称,魔法数改为 "7G") + * @param dataType 1-传入密文块,2-传入私钥,3-取明文块 + * @param privateKey 私钥(dataType=2时使用) + * @param pCipherData 密文块(dataType=1时使用) + * @param idx 偏移量索引 + * @return MutiReturn7 包含返回码、isLast标志及数据片 + */ + private MutiReturn7 SYD_SM2Decrypt(int dataType, byte[] privateKey, ByteBuffer pCipherData, int idx) { + ParamChecker.checkInRange(dataType, "dataType", 1, 3); + + // 估算缓存大小 + int buffLen = 4096; + if (dataType == 1) { + buffLen = pCipherData.limit() > 4096 ? 64 * 1024 : 5 * 1024; + } + + ByteBuffer bb = ByteBuffer.allocate(buffLen); + PacketSN sn = PacketSN.gen(); + bb.put(new byte[2]); // 占位,后续填总长度 + bb.put(sn.getSn().array()); + bb.put("7G".getBytes()); // 魔法数改为 "7G" + bb.put((byte) dataType); + + if (dataType == 2) { // 私钥 + bb.put(ByteUtil.shortToBytes((short) privateKey.length)); + bb.put(privateKey); + } + if (dataType == 1) { // 密文块 + bb.put(ByteUtil.shortToBytes((short) pCipherData.limit())); + bb.put(pCipherData); + bb.put(ByteUtil.shortToBytes((short) idx)); + } + if (dataType == 3) { // 请求明文块 + bb.put(ByteUtil.shortToBytes((short) (63 * 1024))); + bb.put(ByteUtil.shortToBytes((short) idx)); + } + + // 填写总长度(长度字段本身不包含在内) + int len = bb.position() - 2; + bb.put(0, (byte) ((len >> 8) & 0xFF)); // 覆盖开头的两个占位字节 + bb.put(1, (byte) (len & 0xFF)); // 覆盖开头的两个占位字节 + + // 通信 + synchronized (this) { + bb = syncRead(syncSend(bb)); + } + + bb.flip(); + bb.position(12); // 跳过前12字节(头部固定) + byte[] code = new byte[2]; + bb.get(code); + int retCode = Integer.parseInt(new String(code)); + if (retCode != 0) { + throw new SydApiException(retCode); + } + + // 仅 dataType=3 时有数据返回 + if (dataType == 3) { + int isLast = bb.get(); + byte[] dataLenArr = new byte[2]; + bb.get(dataLenArr); + int dataLen = ByteUtil.bytesToInt(dataLenArr); + ByteBuffer slice = ByteBufferUtil.sliceAndConsume(bb, dataLen); + return new MutiReturn7(retCode, isLast, slice); + } else { + return new MutiReturn7(retCode, 0, null); + } + } + + @Override public String SM2Encrypt(byte[] publicKey, byte[] pOrgData) { Proto77 proto = new Proto77();