一、ECDSA概述
椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。它基于椭圆曲线离散对数问题(ECDLP)的难解性,具有密钥短、速度快和安全性高的优点。与传统的离散对数问题(DLP)和大数分解问题(IFP)不同,椭圆曲线离散对数问题没有亚指数时间的解决方法,因此椭圆曲线密码的安全性更高。
二、ECDSA工作原理
ECDSA的签名过程包括以下步骤:
- 选择一条椭圆曲线Ep(a,b)和基点G;
- 选择私有密钥k(k<n,n为G的阶),利用基点G计算公开密钥K=kG;
- 产生一个随机整数r(r<n),计算点R=rG;
- 将原数据和点R的坐标值x,y作为参数,计算SHA1做为hash,即Hash=SHA1(原数据,x,y);
- 计算s≡r−Hash∗k(modn);
- r和s做为签名值,如果r和s其中一个为0,重新从第3步开始执行。
验证过程如下:
- 接受方在收到消息m和签名值r,s后,进行以下运算;
- 计算sG+H(m)P=(x1,y1),r1≡x1modp;
- 验证等式:r1≡rmodp;
- 如果等式成立,接受签名,否则签名无效。
三、Go实现
1. 生成私钥和公钥,生成的私钥为结构体ecdsa.PrivateKey的指针
func NewKeyPair()(ecdsa.PrivateKey,[]byte){
//生成secp256椭圆曲线
curve := elliptic.P256()
//产生的是一个结构体指针,结构体类型为ecdsa.PrivateKey
private,err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil{
log.Panic(err)
}
fmt.Printf("私钥:%x\n", private)
fmt.Printf("私钥X:%x\n", private.X.Bytes())
fmt.Printf("私钥Y:%x\n", private.Y.Bytes())
fmt.Printf("私钥D:%x\n", private.D.Bytes())
//x坐标与y坐标拼接在一起生成公钥
pubKey := append(private.X.Bytes(),private.Y.Bytes()...)
//打印公钥,公钥用16进制打印出来长度为128,包含了x轴坐标与y轴坐标。
fmt.Printf("公钥:%x \n", pubKey)
return *private, pubKey
}
2.生成签名的DER格式
func MakeSignatureDerString(r,s string)string{
//获取R和S的长度
lenSigR := len(r)/2
lenSigS := len(s)/2
//计算DER序列的总长度
lenSequence := lenSigR + lenSigS + 4
//将10进制长度转16进制字符串
strLenSigR := DecimalToHex(int64(lenSigR))
strLenSigS := DecimalToHex(int64(lenSigS))
strLenSequence := DecimalToHex(int64(lenSequence))
// 拼凑DER编码
derString := "30" + strLenSequence
derString = derString + "02" + strLenSigR + r
derString = derString + "02" + strLenSigS + s
derString = derString + "01"
return derString
}
3.验证签名1
func VerifySignature1(pubKey,message []byte, r,s *big.Int)bool{
curve := elliptic.P256()
//公钥的长度
keyLen := len(pubKey)
//前一半为x轴坐标,后一半为y轴坐标
x := big.Int{}
y := big.Int{}
x.SetBytes(pubKey[:(keyLen / 2)])
y.SetBytes(pubKey[:(keyLen / 2)])
rawPubKey := ecdsa.PublicKey{curve,&x,&y}
//根据交易哈希、公钥、数字签名验证成功。ecdsa.Verify func Verify(pub *PublicKey,
//hash []byte, r *big.Int, s *big.Int) bool
res := ecdsa.Verify(&rawPubKey, message, r, s)
return res
}
4.验证签名2
func VerifySignature2(pubKey, message []byte, r, s string) bool {
curve := elliptic.P256()
//公钥的长度
keyLen := len(pubKey)
//前一半为x轴坐标,后一半为y轴坐标
x := big.Int{}
y := big.Int{}
x.SetBytes(pubKey[:(keyLen / 2)])
y.SetBytes(pubKey[(keyLen / 2):])
rawPubKey := ecdsa.PublicKey{curve, &x, &y}
//根据交易哈希、公钥、数字签名验证成功。ecdsa.Verify func Verify(pub *PublicKey,
//hash []byte, r *big.Int, s *big.Int) bool
rint := big.Int{}
sint := big.Int{}
rByte, _ := hex.DecodeString(r)
sByte, _ := hex.DecodeString(s)
rint.SetBytes(rByte)
sint.SetBytes(sByte)
//fmt.Println("------", rint.SetBytes(rByte))
//fmt.Println("------", sint.SetBytes(sByte))
res := ecdsa.Verify(&rawPubKey, message, &rint, &sint)
return res
}
func DecimalToHex(n int64) string {
if n < 0 {
log.Println("Decimal to hexadecimal error: the argument must be greater than zero.")
return ""
}
if n == 0 {
return "0"
}
hex := map[int64]int64{10: 65, 11: 66, 12: 67, 13: 68, 14: 69, 15: 70}
s := ""
for q := n; q > 0; q = q / 16 {
m := q % 16
if m > 9 && m < 16 {
m = hex[m]
s = fmt.Sprintf("%v%v", string(m), s)
continue
}
s = fmt.Sprintf("%v%v", m, s)
}
return s
}
5.验证过程(完整代码)
//椭圆曲线数字签名算法ECDSA
package main
import(
"fmt"
"log"
"math/big"
"encoding/hex"
"crypto/sha256"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
)
//生成私钥和公钥,生成的私钥为结构体ecdsa.PrivateKey的指针
func NewKeyPair()(ecdsa.PrivateKey,[]byte){
//生成secp256椭圆曲线
curve := elliptic.P256()
//产生的是一个结构体指针,结构体类型为ecdsa.PrivateKey
private,err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil{
log.Panic(err)
}
fmt.Printf("私钥:%x\n", private)
fmt.Printf("私钥X:%x\n", private.X.Bytes())
fmt.Printf("私钥Y:%x\n", private.Y.Bytes())
fmt.Printf("私钥D:%x\n", private.D.Bytes())
//x坐标与y坐标拼接在一起生成公钥
pubKey := append(private.X.Bytes(),private.Y.Bytes()...)
//打印公钥,公钥用16进制打印出来长度为128,包含了x轴坐标与y轴坐标。
fmt.Printf("公钥:%x \n", pubKey)
return *private, pubKey
}
//生成签名的DER格式
func MakeSignatureDerString(r,s string)string{
//获取R和S的长度
lenSigR := len(r)/2
lenSigS := len(s)/2
//计算DER序列的总长度
lenSequence := lenSigR + lenSigS + 4
//将10进制长度转16进制字符串
strLenSigR := DecimalToHex(int64(lenSigR))
strLenSigS := DecimalToHex(int64(lenSigS))
strLenSequence := DecimalToHex(int64(lenSequence))
// 拼凑DER编码
derString := "30" + strLenSequence
derString = derString + "02" + strLenSigR + r
derString = derString + "02" + strLenSigS + s
derString = derString + "01"
return derString
}
//验证签名1
func VerifySignature1(pubKey,message []byte, r,s *big.Int)bool{
curve := elliptic.P256()
//公钥的长度
keyLen := len(pubKey)
//前一半为x轴坐标,后一半为y轴坐标
x := big.Int{}
y := big.Int{}
x.SetBytes(pubKey[:(keyLen / 2)])
y.SetBytes(pubKey[:(keyLen / 2)])
rawPubKey := ecdsa.PublicKey{curve,&x,&y}
//根据交易哈希、公钥、数字签名验证成功。ecdsa.Verify func Verify(pub *PublicKey,
//hash []byte, r *big.Int, s *big.Int) bool
res := ecdsa.Verify(&rawPubKey, message, r, s)
return res
}
//验证签名2
func VerifySignature2(pubKey, message []byte, r, s string) bool {
curve := elliptic.P256()
//公钥的长度
keyLen := len(pubKey)
//前一半为x轴坐标,后一半为y轴坐标
x := big.Int{}
y := big.Int{}
x.SetBytes(pubKey[:(keyLen / 2)])
y.SetBytes(pubKey[(keyLen / 2):])
rawPubKey := ecdsa.PublicKey{curve, &x, &y}
//根据交易哈希、公钥、数字签名验证成功。ecdsa.Verify func Verify(pub *PublicKey,
//hash []byte, r *big.Int, s *big.Int) bool
rint := big.Int{}
sint := big.Int{}
rByte, _ := hex.DecodeString(r)
sByte, _ := hex.DecodeString(s)
rint.SetBytes(rByte)
sint.SetBytes(sByte)
//fmt.Println("------", rint.SetBytes(rByte))
//fmt.Println("------", sint.SetBytes(sByte))
res := ecdsa.Verify(&rawPubKey, message, &rint, &sint)
return res
}
func DecimalToHex(n int64) string {
if n < 0 {
log.Println("Decimal to hexadecimal error: the argument must be greater than zero.")
return ""
}
if n == 0 {
return "0"
}
hex := map[int64]int64{10: 65, 11: 66, 12: 67, 13: 68, 14: 69, 15: 70}
s := ""
for q := n; q > 0; q = q / 16 {
m := q % 16
if m > 9 && m < 16 {
m = hex[m]
s = fmt.Sprintf("%v%v", string(m), s)
continue
}
s = fmt.Sprintf("%v%v", m, s)
}
return s
}
//验证过程
func main(){
//1、生成签名
fmt.Println("1、生成签名--------------------------")
//调用函数生成私钥与公钥
privKey,pubKey := NewKeyPair()
//信息的哈希
//msg := sha256.Sum256([]byte("hello world"))
msg := sha256.Sum256([]byte("我爱Go语音,好好学习天天向上!"))
//根据私钥和信息的哈希进行数字签名,产生r和s
r,s,_ := ecdsa.Sign(rand.Reader,&privKey,msg[:])
//生成r,s字符串
fmt.Println("-----------------------------------")
strSigR := fmt.Sprintf("%x",r)
strSigS := fmt.Sprintf("%x",s)
fmt.Println("r,s的10进制分别为:",r,s)
fmt.Println("r,s的16进制分别为:",strSigR,strSigS)
//r和s拼接在一起,形成数字签名的der格式
signatureDer := MakeSignatureDerString(strSigR,strSigS)
//打印数字签名的16进制显示
fmt.Println("数字签名DER格式为:", signatureDer)
fmt.Println()
//2、签名验证过程
fmt.Println("2、签名验证过程-------------------------------")
res := VerifySignature1(pubKey, msg[:], r, s)
fmt.Println("签名验证结果:" , res)
res = VerifySignature2(pubKey, msg[:], strSigR, strSigS)
fmt.Println("签名验证结果:" , res)
}
go run ecdsa.go
1、生成签名--------------------------
私钥:&{{{c0000540c0} 1da18c552d6ffd475afd08105fed0dfe2d54e5004348c03d8d3f2a52085ad58b c3b7be357933971eadfb6c0b7c386ceb4fa247b8de7bec7cf640aaaeedc3c5a5} 5a21d20e0965dc45685527d2455850513fa33af4e5f4f38c63ccdbdddf531c4a}
私钥X:1da18c552d6ffd475afd08105fed0dfe2d54e5004348c03d8d3f2a52085ad58b
私钥Y:c3b7be357933971eadfb6c0b7c386ceb4fa247b8de7bec7cf640aaaeedc3c5a5
私钥D:5a21d20e0965dc45685527d2455850513fa33af4e5f4f38c63ccdbdddf531c4a
公钥:1da18c552d6ffd475afd08105fed0dfe2d54e5004348c03d8d3f2a52085ad58bc3b7be357933971eadfb6c0b7c386ceb4fa247b8de7bec7cf640aaaeedc3c5a5
-----------------------------------
r,s的10进制分别为: 52728191750877498499034582730183912673898761759395273410353363098123716352490 93144752452882650126322260169752623811471464347533043453453550360746976309351
r,s的16进制分别为: 7493195391dec9a32cb1319ea7ca36d42227fe2a331ca4a6848c9eb2a0dd59ea cdee0fc7065f968e6a917600cfb15201602215ad84e42e3b591d8486edeb7067
数字签名DER格式为: 304402207493195391dec9a32cb1319ea7ca36d42227fe2a331ca4a6848c9eb2a0dd59ea0220cdee0fc7065f968e6a917600cfb15201602215ad84e42e3b591d8486edeb706701
2、签名验证过程-------------------------------
签名验证结果: false
签名验证结果: true