Files
zt-closet-backend/pkg/wechat/msgencrypt_test.go
T
2026-09-14 02:39:00 +08:00

125 lines
4.1 KiB
Go

package wechat
import (
"encoding/base64"
"strings"
"testing"
)
// testEncodingAESKey 由固定 32 字节密钥生成合法的 43 位 EncodingAESKey。
func testEncodingAESKey() string {
key := []byte("0123456789abcdef0123456789abcdef")
return strings.TrimRight(base64.StdEncoding.EncodeToString(key), "=")
}
// TestSortedSHA1HexOfficialVectors 用官方文档示例校验签名算法:
// 参与项按字典序(字节序)排序后拼接,取 sha1 小写 hex。
// 官方「安全模式接收 msg_signature」示例中的 Encrypt 是占位符而非真实密文,无法复现,故不纳入。
func TestSortedSHA1HexOfficialVectors(t *testing.T) {
cases := []struct {
name string
parts []string
want string
}{
{
name: "GET URL 验证 signature",
parts: []string{"AAAAA", "1514711492", "1714036504"},
want: "f464b24fc39322e44b38aa78f5edd27bd1441696",
},
{
name: "明文模式接收 signature",
parts: []string{"AAAAA", "1714037059", "486452656"},
want: "899cf89e464efb63f54ddac96b0a0a235f53aa78",
},
{
name: "安全模式回包 MsgSignature",
parts: []string{
"AAAAA", "1713424427", "415670741",
"ELGduP2YcVatjqIS+eZbp80MNLoAUWvzzyJxgGzxZO/5sAvd070Bs6qrLARC9nVHm48Y4hyRbtzve1L32tmxSQ==",
},
want: "1b9339964ed2e271e7c7b6ff2b0ef902fc94dea1",
},
}
for _, c := range cases {
if got := sortedSHA1Hex(c.parts...); got != c.want {
t.Fatalf("%s: got %s, want %s", c.name, got, c.want)
}
}
}
// TestVirtualPayMsgCryptRoundTrip 校验安全模式加解密闭环:明文 → 加密 → 解密应完全还原,
// 且密文中携带的 appid 可被正确切出(用于防伪造校验)。
func TestVirtualPayMsgCryptRoundTrip(t *testing.T) {
key, err := decodeEncodingAESKey(testEncodingAESKey())
if err != nil {
t.Fatalf("decodeEncodingAESKey: %v", err)
}
c := &virtualPayClient{token: "test-token", aesKey: key, appID: "wxfc03c59fc1bb4de5"}
const plain = `{"ErrCode":0,"ErrMsg":"success"}`
encrypt, err := c.encrypt(plain)
if err != nil {
t.Fatalf("encrypt: %v", err)
}
msg, appID, err := c.decrypt(encrypt)
if err != nil {
t.Fatalf("decrypt: %v", err)
}
if msg != plain {
t.Fatalf("round trip msg = %q, want %q", msg, plain)
}
if appID != c.appID {
t.Fatalf("round trip appid = %q, want %q", appID, c.appID)
}
}
// TestVirtualPayMsgCryptSignature 校验接收签名:只有 token/timestamp/nonce/encrypt 全对才通过。
func TestVirtualPayMsgCryptSignature(t *testing.T) {
c := &virtualPayClient{token: "test-token"}
const (
timestamp = "1714112445"
nonce = "415670741"
encrypt = "abc123"
)
good := sortedSHA1Hex(c.token, timestamp, nonce, encrypt)
if !c.checkMsgSignature(timestamp, nonce, encrypt, good) {
t.Fatal("valid msg_signature should pass")
}
if c.checkMsgSignature(timestamp, nonce, encrypt, "deadbeef") {
t.Fatal("wrong msg_signature should fail")
}
if c.checkMsgSignature(timestamp, nonce, "tampered", good) {
t.Fatal("tampered encrypt should fail")
}
if c.checkMsgSignature(timestamp, nonce, encrypt, "") {
t.Fatal("empty msg_signature should fail")
}
}
// TestDecodeEncodingAESKey 校验密钥解码:空值=明文模式,长度非法报错,合法值解出 32 字节。
func TestDecodeEncodingAESKey(t *testing.T) {
if key, err := decodeEncodingAESKey(""); err != nil || key != nil {
t.Fatalf("empty key: key=%v err=%v, want nil/nil", key, err)
}
if _, err := decodeEncodingAESKey("tooshort"); err == nil {
t.Fatal("short key should fail")
}
key, err := decodeEncodingAESKey(testEncodingAESKey())
if err != nil || len(key) != 32 {
t.Fatalf("valid key: len=%d err=%v, want 32/nil", len(key), err)
}
}
// TestExtractEncryptField 校验安全模式信封解析与 XML 明确提示。
func TestExtractEncryptField(t *testing.T) {
if _, err := extractEncryptField([]byte(`{"ToUserName":"gh_x","Encrypt":"abc"}`)); err != nil {
t.Fatalf("json envelope: %v", err)
}
if _, err := extractEncryptField([]byte(`{"ToUserName":"gh_x"}`)); err == nil {
t.Fatal("missing Encrypt should fail")
}
if _, err := extractEncryptField([]byte(`<xml><Encrypt>abc</Encrypt></xml>`)); err == nil {
t.Fatal("xml envelope should fail with a clear hint")
}
}