使用golang实现对请求签名和验签

概要

签名的作用是识别请求的发起者,通常对未通过签名验证的发起者不做业务处理。基础知识可以参考 -- https://zhuanlan.zhihu.com/p/384595092

整个流程分为两部:请求的发起者按照一定算法进行签名,服务器接收到请求后会根据请求体中所带参数以及对应的签名算法进行验签。

通常使用golang实现请求签名有两种方法,一是rsa签名,二是md5签名。Rsa签名是一种非对称的签名方式,相反md5签名是一种对称签名的方式。

一般来说使用rsa签名的保密性会更高(通过后面介绍的实现得知)。

rsa签名

生成公钥私钥对

用标准库 crypto/rsa 来生成秘钥,用 crypto/rand 库来生成随机数

privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
    panic(err)
}

// The public key is a part of the *rsa.PrivateKey struct
publicKey := privateKey.PublicKey

读取私钥和公钥

func getPrivRSA() *rsa.PrivateKey {
	block, _ := pem.Decode([]byte(privateKey))

	if prk, err := x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
		return nil // 获取失败
	} else {
		return prk // 读取成功
	}
}

func getPubRSA(pubKey string) *rsa.PublicKey {
	block, _ := pem.Decode([]byte(pubKey))

	if pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes); err != nil {
		return nil. // 获取失败
	} else {
		return pubInterface.(*rsa.PublicKey) // 读取成功
	}
}

签名

func Sign(params map[string]string, priv *rsa.PrivateKey) string {
        // 对请求参数按照字母顺序进行排序并组合
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	var signature string = ""
	i := 0
	for _, k := range keys {
		v := params[k]
		if i != 0 {
			signature += "&"
		}
		signature += k
		signature += "="
		signature += v
		i++
	}
        // 将排序后的signature进行hash操作
	h := sha256.New()
	h.Write([]byte(s))
	Sha256Code := h.Sum(nil)
        // 使用rsa算法进行签名
// 第一个参数是一个随机数参数器,确保每次相同输入产生不同的签名
// 第二个参数是密钥
// 第三个参数是我们上面使用的hash函数
// 第四个参数是被hash函数处理过的原始输入
	signatureAfter, err := rsa.SignPKCS1v15(cry_rand.Reader, priv, crypto.SHA256, Sha256Code)
// 返回base64编码的字符串
	return base64.StdEncoding.EncodeToString(signatureAfter)
}

验签

func Verify(params map[string]string, pub *rsa.PublicKey, sign string) (err error) {
        // 和签名步骤相同,对收到的请求参数按照字母顺序进行排序
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	var signature string = ""
	i := 0
	for _, k := range keys {
		v := params[k]
		if i != 0 {
			signature += "&"
		}
		signature += k
		signature += "="
		signature += v
		i++
	}
        // 和签名步骤相同,将排序后的signature进行hash操作
	h := sha256.New()
	h.Write([]byte(s))
	Sha256Code := h.Sum(nil)
        // 对签名进行base64解码
	decodeSignature, err := base64.StdEncoding.DecodeString (sign)
	// 使用rsa验签函数
        // 第一个参数是公钥
        // 第二个参数是hash函数
        // 第三个参数是被hash函数处理过的原始输入
        // 第四个参数是被处理过的签名
	err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, Sha256Code, decodeSignature)
	if err != nil {  // 验证失败
		return err
	}
	return nil // 验证成功
}

Md5

唯一key

由一方生成key后,交有双方共同保密保存

签名和验签的实现完全相同

func MD5Params(params map[string]string, key string, filter string) string {
    // 将请求参数的key提取出来,并排好序
    newKeys := make([]string, 0)
    for k,_ := range params {
        //需要过滤的签名
        if k == filter {
            continue
        }
        newKeys = append(newKeys, k)
    }
    sort.Strings(newKeys)
var originStr string
// 将输入进行标准化的处理
    for _,v := range newKeys {
        originStr += fmt.Sprintf("%v=%v&", v, params[v]) 
    }
originStr += fmt.Sprintf("key=%v", key)
// 使用md5算法进行处理
    sign := MD5(originStr)
    return sign
}

服务端拿到请求参数后,按照相同的逻辑计算签名,和输入相同则表示验签通过。

小结

  1. 使用rsa和md5算法均可以对请求体进行签名和验签;
  2. 两者有部分共通的处理逻辑;
  3. Md5的实现较简单,rsa保密性更好(随机数,不同的hash函数);
上一篇:实现H5连接分享给好友或朋友圈自定义分享内容(标题、图片、简介)的方法代码000


下一篇:jwt