package osais import ( "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "testing" ) // 生成测试密钥对(PKCS8 私钥 / PKIX 公钥,Base64) func testKeyPair(t *testing.T) (priB64, pubB64 string) { t.Helper() pri, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("生成密钥失败: %v", err) } priDER, err := x509.MarshalPKCS8PrivateKey(pri) if err != nil { t.Fatalf("编码私钥失败: %v", err) } priB64 = base64.StdEncoding.EncodeToString(priDER) pubDER, err := x509.MarshalPKIXPublicKey(&pri.PublicKey) if err != nil { t.Fatalf("编码公钥失败: %v", err) } pubB64 = base64.StdEncoding.EncodeToString(pubDER) return priB64, pubB64 } func TestSignVerify(t *testing.T) { priB64, pubB64 := testKeyPair(t) data := []byte("appid=1j0m408660091832&bizContent={\"bizUserId\":\"demo-user-001\"}&method=osais.wxbizpay.auth×tamp=1504147887642&version=1.0") sig, err := Sign(data, priB64) if err != nil { t.Fatalf("签名失败: %v", err) } ok, err := Verify(data, sig, pubB64) if err != nil { t.Fatalf("验签出错: %v", err) } if !ok { t.Fatal("验签失败:签名不匹配") } // 篡改数据后验签必须失败 ok, _ = Verify([]byte(string(data[:len(data)-2])+"99"), sig, pubB64) if ok { t.Fatal("篡改数据后验签仍通过,不安全") } } func TestVerifyPKCS1PublicKey(t *testing.T) { // 兼容 PKCS#1 公钥格式(对接方公钥为该格式,mock 场景) pri, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { t.Fatalf("生成密钥失败: %v", err) } priDER, _ := x509.MarshalPKCS8PrivateKey(pri) priB64 := base64.StdEncoding.EncodeToString(priDER) pubB64 := base64.StdEncoding.EncodeToString(x509.MarshalPKCS1PublicKey(&pri.PublicKey)) data := []byte("code=0&data={}&msg=success") sig, err := Sign(data, priB64) if err != nil { t.Fatalf("签名失败: %v", err) } ok, err := Verify(data, sig, pubB64) if err != nil { t.Fatalf("PKCS1 公钥验签出错: %v", err) } if !ok { t.Fatal("PKCS1 公钥验签失败") } } func TestBuildContent(t *testing.T) { params := map[string]any{ "version": "1.0", "appid": "abc", "timestamp": "1504147887642", "bizContent": "{\"a\":1}", "nilField": nil, // null 不参与拼接 "empty": "", // 空字符串参与拼接 } content, err := BuildContent(params) if err != nil { t.Fatalf("BuildContent 失败: %v", err) } want := "appid=abc&bizContent={\"a\":1}&empty=×tamp=1504147887642&version=1.0" if content != want { t.Fatalf("拼接结果不符:\n got: %s\nwant: %s", content, want) } } func TestSortJSONKeys(t *testing.T) { // 乱序 + null 字段:键按字典序重排,null 剔除 in := []byte(`{"mpQuery":null,"authUrl":"https://a.com/x","expireTime":"2026-09-01 12:00:00"}`) got := string(SortJSONKeys(in)) want := `{"authUrl":"https://a.com/x","expireTime":"2026-09-01 12:00:00"}` if got != want { t.Fatalf("排序结果不符:\n got: %s\nwant: %s", got, want) } // 嵌套对象递归排序 in2 := []byte(`{"b":{"d":1,"c":2},"a":3}`) got2 := string(SortJSONKeys(in2)) want2 := `{"a":3,"b":{"c":2,"d":1}}` if got2 != want2 { t.Fatalf("嵌套排序结果不符:\n got: %s\nwant: %s", got2, want2) } // 数组元素递归排序 in3 := []byte(`[{"z":1,"a":2},{"q":3}]`) got3 := string(SortJSONKeys(in3)) want3 := `[{"a":2,"z":1},{"q":3}]` if got3 != want3 { t.Fatalf("数组排序结果不符:\n got: %s\nwant: %s", got3, want3) } }