124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
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)
|
||
}
|
||
}
|