go语言 椭圆数字签名及其验证算法

淺唱寂寞╮ 提交于 2020-01-29 12:18:25
package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "log"
    "math/big"
)

func main() {
    // 1、对需要签名的文件进行hash运算
    data := "from xiaoxiao to maomao 100 btc"
    hashInstance := sha256.New()
    hashInstance.Write([]byte(data))
    hashed := hashInstance.Sum(nil)
    // 2、生成私钥和公钥字节
    privateKey, publicKeyBytes := NewKeyPair()
    // 3、生成签名的der编码格式
    derSignString := ECDSASign(hashed, privateKey)
    fmt.Printf("签名信息为:%s\n", derSignString)
    // 4、验证签名
    flag := ECDSAVerify(publicKeyBytes, hashed, derSignString)
    fmt.Println("签名验证结果:", flag)
}

// NewKeyPair 生成私钥和公钥,生成的私钥为结构体ecdsa.PrivateKey的指针
func NewKeyPair() (ecdsa.PrivateKey, []byte) {
    // 1、生成椭圆曲线对象
    curve := elliptic.P256()
    // 2、生成秘钥对,返回私钥对象(ecdsa.PrivateKey指针)
    privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
    if err != nil {
        log.Panic(err)
    }
    // 3、编码生成公钥字节数组,参数是椭圆曲线对象、x坐标、y坐标
    publicKeyBytes := elliptic.Marshal(curve, privateKey.PublicKey.X, privateKey.Y)
    fmt.Printf("私钥:%x\n", *privateKey)
    fmt.Printf("公钥:%x\n", publicKeyBytes)
    return *privateKey, publicKeyBytes
}

// ECDSASign ECDSA数字签名
func ECDSASign(hashed []byte, privateKey ecdsa.PrivateKey) string {
    // 1、数字签名生成r、s的big.Int对象,参数是随机数、私钥、签名文件的哈希串
    r, s, err := ecdsa.Sign(rand.Reader, &privateKey, hashed)
    if err != nil {
        return ""
    }
    fmt.Println("r结果:", r)
    fmt.Println("s结果:", s)
    // 2、将r、s转成r/s字符串
    strSignR := fmt.Sprintf("%x", r)
    strSignS := fmt.Sprintf("%x", s)
    if len(strSignR) == 63 {
        strSignR = "0" + strSignR
    }
    if len(strSignS) == 63 {
        strSignS = "0" + strSignS
    }
    fmt.Printf("r的16进制为:%s,长度为:%d\n", strSignR, len(strSignR))
    fmt.Printf("s的16进制为:%s,长度为:%d\n", strSignS, len(strSignS))
    // 3、r和s字符串拼接,形成数字签名的der格式
    derString := MakeDERSignString(strSignR, strSignS)
    return derString
}

// MakeDERSignString 生成数字签名的DER编码格式
func MakeDERSignString(strR, strS string) string {
    // 1、获取R和S的长度
    lenSignR := len(strR) / 2
    lenSignS := len(strS) / 2
    // 2、计算DER序列的总长度
    len := lenSignR + lenSignS + 4
    fmt.Printf("lenSignR为:%d,lenSignS为:%d,len为:%d\n", lenSignR, lenSignS, len)
    // 3、将10进制长度转16进制字符串
    strLenSignR := fmt.Sprintf("%x", int64(lenSignR))
    strLenSignS := fmt.Sprintf("%x", int64(lenSignS))
    strLen := fmt.Sprintf("%x", int64(len))
    fmt.Printf("strLenSignR为:%s,strLenSignS为:%s,strLen为:%s\n", strLenSignR, strLenSignS, strLen)
    // 4、拼接DER编码格式
    derString := "30" + strLen
    derString += "02" + strLenSignR + strR
    derString += "02" + strLenSignS + strS
    derString += "01"
    return derString
}

// ECDSAVerify ECDSA验证签名 (比特币系统中公钥具有0x04前缀)
func ECDSAVerify(publicKeyBytes, hashed []byte, derSignString string) bool {
    // 公钥长度
    keyLen := len(publicKeyBytes)
    if keyLen != 65 {
        return false
    }
    // 1、生成椭圆曲线对象
    curve := elliptic.P256()
    // 2、根据公钥字节数字,获取公钥中的x和y
    // 公钥字节中的前一半为x轴坐标,再将字节数组转成big.Int类型
    publicKeyBytes = publicKeyBytes[1:]
    // x := big.NewInt(0).SetBytes(publicKeyBytes[:32])
    x := new(big.Int).SetBytes(publicKeyBytes[:32])
    y := new(big.Int).SetBytes(publicKeyBytes[32:])
    // 3、生成公钥对象
    publicKey := ecdsa.PublicKey{Curve: curve, X: x, Y: y}
    // 4、对der格式的签名进行解析,获取r/s字节数组后转成big.Int类型
    rBytes, sBytes := ParseDERSignString(derSignString)
    r := new(big.Int).SetBytes(rBytes)
    s := new(big.Int).SetBytes(sBytes)
    return ecdsa.Verify(&publicKey, hashed, r, s)
}

// ParseDERSignString 对der格式的签名进行解析
func ParseDERSignString(derString string) (rBytes, sBytes []byte) {
    fmt.Println("derString:", derString)
    derBytes, _ := hex.DecodeString(derString)
    fmt.Println("derBytes", derBytes)
    rBytes = derBytes[4:36]
    sBytes = derBytes[len(derBytes)-33 : len(derBytes)-1]
    return
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!