Go语言签名与验签

Golang 加验签

测试用例

func TestSignAndVerify(t *testing.T) {
	person := Person{
		Name: "张三",
		Age:  18,
		Sons: []Person{
			{Name: "李四", Age: 19},
			{Name: "王五", Age: 20},
		},
		Index: []int{1, 2, 3, 4, 5},
	}
	queryString := ToSortQueryString(person)
	fmt.Println("签名串:", string(queryString))

	edPubKey, edPriKey, _ := ed25519.GenerateKey(nil)
	rsaPriKey, _ := rsa.GenerateKey(rand.Reader, 2048)
	
	//edSigned, _ := Sign(ED25519, edPriKey, queryString)
	edSigned, _ := SignStruct(ED25519, edPriKey, person)
	fmt.Println("ed25519签名结果:", edSigned)
	err := VerifyStruct(ED25519, edPubKey, edSigned, person)
	if err != nil {
		t.Fatal(err)
		return
	}
	fmt.Println("ed25519验证签名成功")

	rsaSigned, err := SignStruct(RSA256, rsaPriKey, person)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println("rsa签名结果:", rsaSigned)
	err = VerifyStruct(RSA256, &rsaPriKey.PublicKey, rsaSigned, person)
	if err != nil {
		t.Fatal(err)
		return
	}
	fmt.Println("rsa验证签名成功")
}

输出:

=== RUN TestSignAndVerify
签名串: age=18&index=[1,2,3,4,5]&name=张三&son=[age=19&name=李四,age=20&name=王五]
ed25519签名结果: X6IKI1udBpa4thfTcAQV6fnNwatPyPXnFmBwjhzzJAN/rTSjwt2N/JgnVPutg3rkwoVx3r3X9s6rAgCvSw5ZDA==
ed25519验证签名成功
rsa签名结果: Q5vP1ESuOoDEpCjxpDrzYeVVU0fU9hW8nXxvik665JUnHEhHE9hIXRzfneDE8CxtcQDtjVV15bAzDLRUHNzwVdPvvLuSXjsu+cJVeCIdTuXckzUsb758XfbTkj+0JbXSwrBO5xfyYNyndIJlX3j6jrpjXitmaYwbfod7ZORQgo23EnQPxAbxDR5u+5U/90XDYO/TzX4R96qBGhlfMEQZTQx6KbiCQpGf4BrnElh07Uf4My9VD7FYCEbXuWiVqp1ztfJsuPu9+zJg/wa9o2fiA55SqvaH/hqawKFa+GxpADeVPeaRR2LmsmcvmdB1LgrmO88r8vRfmpMhF6kDWdEbmg==
rsa验证签名成功
— PASS: TestSignAndVerify (0.11s)

1.现将业务字段转换成字符串

签名字符串一般为URL参数格式,如:age=18&name=张三

import (
	"bytes"
	"fmt"
	"net/url"
	"reflect"
	"sort"
	"strings"
)

func flatten(v reflect.Value, prefix string, values url.Values) {
	switch v.Kind() {
	case reflect.Ptr, reflect.Interface:
		if v.IsNil() {
			return
		}
		flatten(v.Elem(), prefix, values)
	case reflect.Struct:
		typ := v.Type()
		for i := 0; i < v.NumField(); i++ {
			field := typ.Field(i)
			val := v.Field(i)
			// 排除零值
			zero := reflect.Zero(val.Type()).Interface()
			if reflect.DeepEqual(val.Interface(), zero) {
				continue
			}
			// 读取标签
			tag := field.Tag.Get("json")
			tag = strings.Split(tag, ",")[0]
			if prefix != "" && tag != "" {
				tag = prefix + "." + tag
			}
			flatten(val, tag, values)
		}

	case reflect.Map:
		for _, key := range v.MapKeys() {
			if v.IsNil() {
				continue
			}
			k := key.String()
			if prefix != "" {
				k = prefix + "." + k
			}
			flatten(v.MapIndex(key), k, values)
		}
	case reflect.Slice:
		s := make([]string, 0, v.Len())
		for i := 0; i < v.Len(); i++ {
			switch v.Index(i).Kind() {
			case reflect.Ptr, reflect.Interface, reflect.Struct, reflect.Map:
				s = append(s, string(ToSortQueryString(v.Index(i).Interface())))
			default:
				s = append(s, fmt.Sprintf("%v", v.Index(i)))
			}
		}

		values.Add(prefix, "["+strings.Join(s, ",")+"]")
	default:
		values.Add(prefix, fmt.Sprintf("%v", v))
	}
}

func ToSortQueryString(v interface{}) []byte {
	values := url.Values{}
	val := reflect.ValueOf(v)
	// 递归处理结构体
	flatten(val, "", values)
	// 获取所有键名
	keys := make([]string, 0, len(values))
	for k := range values {
		keys = append(keys, k)
	}
	// 对键名排序
	sort.Strings(keys)
	// 按排序后的顺序构造查询字符串
	var buf bytes.Buffer
	for _, k := range keys {
		vs := values[k]
		prefix := k + "="
		for _, v := range vs {
			if buf.Len() > 0 {
				buf.WriteByte('&')
			}
			buf.WriteString(prefix)
			buf.WriteString(v)
		}
	}
	return buf.Bytes()
}

2.签名

func SignStruct(st SignType, priKey interface{}, data interface{}) (signed string, err error) {
	qs := ToSortQueryString(data)
	signed, err = Sign(st, priKey, qs)
	return
}

func Sign(st SignType, priKey interface{}, body []byte) (signed string, err error) {
	var signature []byte
	switch st {
	case ED25519:
		pk, ok := priKey.(ed25519.PrivateKey)
		if !ok {
			err = errors.New("wrong type of key")
			return
		}
		signature = ed25519.Sign(pk, body)
	case RSA256:
		hash := sha256.Sum256(body)
		pk, ok := priKey.(*rsa.PrivateKey)
		if !ok {
			err = errors.New("wrong type of key")
			return
		}
		signature, err = rsa.SignPKCS1v15(rand.Reader, pk, crypto.SHA256, hash[:])
		if err != nil {
			return
		}
	default:
		err = errors.New("wrong type of key")
		return
	}
	signed = base64.StdEncoding.EncodeToString(signature)
	return
}

使用SignStruct将私钥传入即签名

3.验签

func VerifyStruct(st SignType, pubKey interface{}, signed string, data interface{}) (err error) {
	qs := ToSortQueryString(data)
	err = Verify(st, pubKey, signed, qs)
	return
}

func Verify(st SignType, pubKey interface{}, signed string, body []byte) (err error) {
	signature, err := base64.StdEncoding.DecodeString(signed)
	if err != nil {
		return err
	}
	switch st {
	case ED25519:
		pk, ok := pubKey.(ed25519.PublicKey)
		if !ok {
			err = errors.New("wrong type of key")
			return
		}
		if ok := ed25519.Verify(pk, body, signature); !ok {
			return errors.New("invalid signature")
		}
	case RSA256:
		hash := sha256.Sum256(body)
		pk, ok := pubKey.(*rsa.PublicKey)
		if !ok {
			err = errors.New("wrong type of key")
			return
		}
		err = rsa.VerifyPKCS1v15(pk, crypto.SHA256, hash[:], signature)
	default:
		err = errors.New("wrong type of key")
	}
	return
}

使用VerifyStruct将公钥传入验证签名。 err!=nil 则验签失败。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值