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 则验签失败。