Skip to content

Commit 295a0f3

Browse files
Copilotbinarywang
andcommitted
修复同城配送接口RSA签名payload格式错误(错误码40234)
根据微信官方API签名文档,待签名串格式应为: urlpath\nappid\ntimestamp\npostdata 原代码错误地在payload中包含了rsaKeySn,导致签名验证失败(40234错误)。 修复:从postWithSignature方法的签名payload中移除rsaKeySn字段。 新增:WxMaSignaturePayloadTest单元测试验证签名格式正确性。 Co-authored-by: binarywang <1343140+binarywang@users.noreply.github.com>
1 parent 752e43f commit 295a0f3

File tree

2 files changed

+114
-2
lines changed

2 files changed

+114
-2
lines changed

weixin-java-miniapp/src/main/java/cn/binarywang/wx/miniapp/api/impl/BaseWxMaServiceImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -962,8 +962,8 @@ public String postWithSignature(String url, JsonObject jsonObject) throws WxErro
962962
reqData.addProperty("authtag", base64Encode(authTag));
963963
String requestJson = reqData.toString();
964964

965-
// 计算签名 RSA
966-
String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + rsaKeySn + "\n" + requestJson;
965+
// 计算签名 RSA,待签名串格式:urlpath\nappid\ntimestamp\npostdata
966+
String payload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
967967
byte[] dataBuffer = payload.getBytes(StandardCharsets.UTF_8);
968968
RSAPrivateKey priKey;
969969
try {
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package cn.binarywang.wx.miniapp.api.impl;
2+
3+
import static org.testng.Assert.assertFalse;
4+
import static org.testng.Assert.assertTrue;
5+
6+
import java.nio.charset.StandardCharsets;
7+
import java.security.KeyPair;
8+
import java.security.KeyPairGenerator;
9+
import java.security.Signature;
10+
import java.security.interfaces.RSAPrivateKey;
11+
import java.security.interfaces.RSAPublicKey;
12+
import java.security.spec.MGF1ParameterSpec;
13+
import java.security.spec.PSSParameterSpec;
14+
import java.util.Base64;
15+
import org.testng.annotations.Test;
16+
17+
/**
18+
* 验证同城配送 API 签名 payload 格式的单元测试。
19+
*
20+
* <p>根据微信官方文档,待签名串格式为:<br>
21+
* {@code urlpath\nappid\ntimestamp\npostdata}<br>
22+
* 字段之间使用换行符 {@code \n} 分隔,末尾无额外回车符。
23+
*
24+
* @author GitHub Copilot
25+
* @see <a
26+
* href="https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/getting_started/api_signature.html">
27+
* 微信服务端API签名指南</a>
28+
*/
29+
public class WxMaSignaturePayloadTest {
30+
31+
/**
32+
* 验证正确的签名 payload 格式(不含 rsaKeySn)可以通过签名验证,
33+
* 即格式为:urlpath\nappid\ntimestamp\npostdata
34+
*/
35+
@Test
36+
public void testCorrectSignaturePayloadFormat() throws Exception {
37+
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
38+
keyGen.initialize(2048);
39+
KeyPair keyPair = keyGen.generateKeyPair();
40+
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
41+
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
42+
43+
String urlPath = "https://api.weixin.qq.com/cgi-bin/express/intracity/createstore";
44+
String appId = "wx1234567890abcdef";
45+
long timestamp = 1700000000L;
46+
String requestJson = "{\"iv\":\"abc\",\"data\":\"xyz\",\"authtag\":\"tag\"}";
47+
48+
// 正确格式:urlpath\nappid\ntimestamp\npostdata(不含 rsaKeySn)
49+
String correctPayload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
50+
byte[] dataBuffer = correctPayload.getBytes(StandardCharsets.UTF_8);
51+
52+
Signature signer = Signature.getInstance("RSASSA-PSS");
53+
PSSParameterSpec pssSpec = new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
54+
signer.setParameter(pssSpec);
55+
signer.initSign(privateKey);
56+
signer.update(dataBuffer);
57+
byte[] sigBytes = signer.sign();
58+
String signatureStr = Base64.getEncoder().encodeToString(sigBytes);
59+
60+
// 使用公钥验证签名
61+
Signature verifier = Signature.getInstance("RSASSA-PSS");
62+
verifier.setParameter(pssSpec);
63+
verifier.initVerify(publicKey);
64+
verifier.update(dataBuffer);
65+
assertTrue(verifier.verify(Base64.getDecoder().decode(signatureStr)),
66+
"正确格式的签名应该能通过验证");
67+
}
68+
69+
/**
70+
* 验证错误的签名 payload(含 rsaKeySn)签名后,用正确 payload 验证会失败。
71+
* 这证明了原来代码中将 rsaKeySn 加入 payload 是错误的。
72+
*/
73+
@Test
74+
public void testIncorrectPayloadWithRsaKeySnFails() throws Exception {
75+
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
76+
keyGen.initialize(2048);
77+
KeyPair keyPair = keyGen.generateKeyPair();
78+
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
79+
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
80+
81+
String urlPath = "https://api.weixin.qq.com/cgi-bin/express/intracity/createstore";
82+
String appId = "wx1234567890abcdef";
83+
long timestamp = 1700000000L;
84+
String rsaKeySn = "some_serial_number";
85+
String requestJson = "{\"iv\":\"abc\",\"data\":\"xyz\",\"authtag\":\"tag\"}";
86+
87+
// 错误格式:payload 中包含了 rsaKeySn(修复前的代码逻辑)
88+
String incorrectPayload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + rsaKeySn + "\n" + requestJson;
89+
byte[] incorrectData = incorrectPayload.getBytes(StandardCharsets.UTF_8);
90+
91+
Signature signer = Signature.getInstance("RSASSA-PSS");
92+
PSSParameterSpec pssSpec = new PSSParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 32, 1);
93+
signer.setParameter(pssSpec);
94+
signer.initSign(privateKey);
95+
signer.update(incorrectData);
96+
byte[] sigBytes = signer.sign();
97+
String signatureStr = Base64.getEncoder().encodeToString(sigBytes);
98+
99+
// 用正确格式的 payload 去验证签名,应该失败
100+
String correctPayload = urlPath + "\n" + appId + "\n" + timestamp + "\n" + requestJson;
101+
byte[] correctData = correctPayload.getBytes(StandardCharsets.UTF_8);
102+
103+
Signature verifier = Signature.getInstance("RSASSA-PSS");
104+
verifier.setParameter(pssSpec);
105+
verifier.initVerify(publicKey);
106+
verifier.update(correctData);
107+
108+
boolean verified = verifier.verify(Base64.getDecoder().decode(signatureStr));
109+
assertFalse(verified, "用错误 payload 生成的签名不应该通过正确 payload 的验证,"
110+
+ "说明 rsaKeySn 不应该包含在签名 payload 中");
111+
}
112+
}

0 commit comments

Comments
 (0)