From 752e43f9297a5043946b3cd8e5222bf0a54d55e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 02:31:36 +0000 Subject: [PATCH 1/3] Initial plan From 295a0f32715bee3a30064ef2377da178f5f74f21 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 02:38:45 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=8C=E5=9F=8E?= =?UTF-8?q?=E9=85=8D=E9=80=81=E6=8E=A5=E5=8F=A3RSA=E7=AD=BE=E5=90=8Dpayloa?= =?UTF-8?q?d=E6=A0=BC=E5=BC=8F=E9=94=99=E8=AF=AF=EF=BC=88=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E7=A0=8140234=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根据微信官方API签名文档,待签名串格式应为: urlpath\nappid\ntimestamp\npostdata 原代码错误地在payload中包含了rsaKeySn,导致签名验证失败(40234错误)。 修复:从postWithSignature方法的签名payload中移除rsaKeySn字段。 新增:WxMaSignaturePayloadTest单元测试验证签名格式正确性。 Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com> --- .../miniapp/api/impl/BaseWxMaServiceImpl.java | 4 +- .../api/impl/WxMaSignaturePayloadTest.java | 112 ++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java index 47ce08bef..6db89aa9a 100644 --- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java +++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java @@ -962,8 +962,8 @@ public String postWithSignature(String url, JsonObject jsonObject) throws WxErro reqData.addProperty("authtag", base64Encode(authTag)); String requestJson = reqData.toString(); - // 计算签名 RSA - String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + rsaKeySn + "\n" + requestJson; + // 计算签名 RSA,待签名串格式:urlpath\nappid\ntimestamp\npostdata + String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson; byte[] dataBuffer = payload.getBytes(StandardCharsets.UTF_8); RSAPrivateKey priKey; try { diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java new file mode 100644 index 000000000..5c7531b0b --- /dev/null +++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java @@ -0,0 +1,112 @@ +package cn.binarywang.wx.miniapp.api.impl; + +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Signature; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.MGF1ParameterSpec; +import java.security.spec.PSSParameterSpec; +import java.util.Base64; +import org.testng.annotations.Test; + +/** + * 验证同城配送 API 签名 payload 格式的单元测试。 + * + *
根据微信官方文档,待签名串格式为:
+ * {@code urlpath\nappid\ntimestamp\npostdata}
+ * 字段之间使用换行符 {@code \n} 分隔,末尾无额外回车符。
+ *
+ * @author GitHub Copilot
+ * @see
+ * 微信服务端API签名指南
+ */
+public class WxMaSignaturePayloadTest {
+
+ /**
+ * 验证正确的签名 payload 格式(不含 rsaKeySn)可以通过签名验证,
+ * 即格式为:urlpath\nappid\ntimestamp\npostdata
+ */
+ @Test
+ public void testCorrectSignaturePayloadFormat() throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ KeyPair keyPair = keyGen.generateKeyPair();
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+
+ String urlPath = "https://api.weixin.qq.com/cgi-bin/express/intracity/createstore";
+ String appId = "wx1234567890abcdef";
+ long timestamp = 1700000000L;
+ String requestJson = "{\"iv\":\"abc\",\"data\":\"xyz\",\"authtag\":\"tag\"}";
+
+ // 正确格式:urlpath\nappid\ntimestamp\npostdata(不含 rsaKeySn)
+ String correctPayload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
+ byte[] dataBuffer = correctPayload.getBytes(StandardCharsets.UTF_8);
+
+ Signature signer = Signature.getInstance("RSASSA-PSS");
+ PSSParameterSpec pssSpec = new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
+ signer.setParameter(pssSpec);
+ signer.initSign(privateKey);
+ signer.update(dataBuffer);
+ byte[] sigBytes = signer.sign();
+ String signatureStr = Base64.getEncoder().encodeToString(sigBytes);
+
+ // 使用公钥验证签名
+ Signature verifier = Signature.getInstance("RSASSA-PSS");
+ verifier.setParameter(pssSpec);
+ verifier.initVerify(publicKey);
+ verifier.update(dataBuffer);
+ assertTrue(verifier.verify(Base64.getDecoder().decode(signatureStr)),
+ "正确格式的签名应该能通过验证");
+ }
+
+ /**
+ * 验证错误的签名 payload(含 rsaKeySn)签名后,用正确 payload 验证会失败。
+ * 这证明了原来代码中将 rsaKeySn 加入 payload 是错误的。
+ */
+ @Test
+ public void testIncorrectPayloadWithRsaKeySnFails() throws Exception {
+ KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+ keyGen.initialize(2048);
+ KeyPair keyPair = keyGen.generateKeyPair();
+ RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+ RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+
+ String urlPath = "https://api.weixin.qq.com/cgi-bin/express/intracity/createstore";
+ String appId = "wx1234567890abcdef";
+ long timestamp = 1700000000L;
+ String rsaKeySn = "some_serial_number";
+ String requestJson = "{\"iv\":\"abc\",\"data\":\"xyz\",\"authtag\":\"tag\"}";
+
+ // 错误格式:payload 中包含了 rsaKeySn(修复前的代码逻辑)
+ String incorrectPayload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + rsaKeySn + "\n" + requestJson;
+ byte[] incorrectData = incorrectPayload.getBytes(StandardCharsets.UTF_8);
+
+ Signature signer = Signature.getInstance("RSASSA-PSS");
+ PSSParameterSpec pssSpec = new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
+ signer.setParameter(pssSpec);
+ signer.initSign(privateKey);
+ signer.update(incorrectData);
+ byte[] sigBytes = signer.sign();
+ String signatureStr = Base64.getEncoder().encodeToString(sigBytes);
+
+ // 用正确格式的 payload 去验证签名,应该失败
+ String correctPayload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
+ byte[] correctData = correctPayload.getBytes(StandardCharsets.UTF_8);
+
+ Signature verifier = Signature.getInstance("RSASSA-PSS");
+ verifier.setParameter(pssSpec);
+ verifier.initVerify(publicKey);
+ verifier.update(correctData);
+
+ boolean verified = verifier.verify(Base64.getDecoder().decode(signatureStr));
+ assertFalse(verified, "用错误 payload 生成的签名不应该通过正确 payload 的验证,"
+ + "说明 rsaKeySn 不应该包含在签名 payload 中");
+ }
+}
From 2ad43b3646299452e5b835b33303d979502f84b1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 10 Mar 2026 03:20:01 +0000
Subject: [PATCH 3/3] =?UTF-8?q?=E9=87=8D=E6=9E=84=EF=BC=9A=E5=B0=86?=
=?UTF-8?q?=E7=AD=BE=E5=90=8Dpayload=E6=9E=84=E9=80=A0=E9=80=BB=E8=BE=91?=
=?UTF-8?q?=E6=8A=BD=E5=8F=96=E4=B8=BA=E5=8F=AF=E6=B5=8B=E8=AF=95=E7=9A=84?=
=?UTF-8?q?=E5=8C=85=E5=8F=AF=E8=A7=81=E6=96=B9=E6=B3=95=EF=BC=8C=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E6=B5=8B=E8=AF=95=E6=97=A0=E6=B3=95=E9=98=B2=E6=AD=A2?=
=?UTF-8?q?=E5=9B=9E=E5=BD=92=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 postWithSignature 中的 payload 拼接逻辑提取为包可见静态方法 buildSignaturePayload
- 重写 WxMaSignaturePayloadTest,直接测试生产代码的 payload 格式
- 新测试断言:恰好4个字段、不含rsaKeySn、格式与官方规范完全一致
- 如果将来有人误将 rsaKeySn 加回 payload,测试将立即失败
Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
---
.../miniapp/api/impl/BaseWxMaServiceImpl.java | 19 ++-
.../api/impl/WxMaSignaturePayloadTest.java | 119 ++++++------------
2 files changed, 57 insertions(+), 81 deletions(-)
diff --git a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
index 6db89aa9a..13b4248e2 100644
--- a/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
+++ b/weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java
@@ -908,6 +908,23 @@ private String base64Encode(byte[] data) {
return Base64.getEncoder().encodeToString(data);
}
+ /**
+ * 构造 RSA 待签名串。
+ *
+ *
根据微信官方 API 签名规范,待签名串格式为:
+ * {@code urlpath\nappid\ntimestamp\npostdata}
+ * 字段之间使用换行符 {@code \n} 分隔,共 4 个字段,末尾无额外回车符。
+ *
+ * @param urlPath 当前请求 API 的 URL,不含 Query 参数
+ * @param appId 小程序 AppId
+ * @param timestamp 签名时的时间戳
+ * @param postData 加密后的请求 POST 数据(JSON 字符串)
+ * @return 拼接好的待签名串
+ */
+ static String buildSignaturePayload(String urlPath, String appId, long timestamp, String postData) {
+ return urlPath + "\n" + appId + "\n" + timestamp + "\n" + postData;
+ }
+
@Override
public String postWithSignature(String url, JsonObject jsonObject) throws WxErrorException {
long timestamp = System.currentTimeMillis() / 1000;
@@ -963,7 +980,7 @@ public String postWithSignature(String url, JsonObject jsonObject) throws WxErro
String requestJson = reqData.toString();
// 计算签名 RSA,待签名串格式:urlpath\nappid\ntimestamp\npostdata
- String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
+ String payload = buildSignaturePayload(urlPath, appId, timestamp, requestJson);
byte[] dataBuffer = payload.getBytes(StandardCharsets.UTF_8);
RSAPrivateKey priKey;
try {
diff --git a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java
index 5c7531b0b..82f436fe5 100644
--- a/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java
+++ b/weixin-java-miniapp/src/test/java/cn/binarywang/wx/miniapp/api/impl/WxMaSignaturePayloadTest.java
@@ -1,25 +1,17 @@
package cn.binarywang.wx.miniapp.api.impl;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertTrue;
-import java.nio.charset.StandardCharsets;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.Signature;
-import java.security.interfaces.RSAPrivateKey;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.MGF1ParameterSpec;
-import java.security.spec.PSSParameterSpec;
-import java.util.Base64;
import org.testng.annotations.Test;
/**
* 验证同城配送 API 签名 payload 格式的单元测试。
*
- *
根据微信官方文档,待签名串格式为:
+ *
直接测试 {@link BaseWxMaServiceImpl#buildSignaturePayload} 生产方法,
+ * 确保待签名串格式符合微信官方规范:
* {@code urlpath\nappid\ntimestamp\npostdata}
- * 字段之间使用换行符 {@code \n} 分隔,末尾无额外回车符。
+ * 共 4 个字段,字段间以换行符 {@code \n} 分隔,末尾无额外回车符。
*
* @author GitHub Copilot
* @see