Elliptic curve cryptography,椭圆曲线密码学,即ECC。是一种创建公开密钥加密的算法,基于椭圆曲线数学。椭圆曲线在密码学中的使用是在1985年由Neal Koblitz和Victor Miller分别独立提出的。详情连接http://www.javashuo.com/article/p-sbgydvcd-mr.html。html
若是你可以坚持看完上面的博客,而且可以看懂里面的内容,我十分佩服,由于我实在是没看下去。不过或多或少了解了一些基本的概念好比椭圆曲线函数并非说真的就是一个函数来生成椭圆上的两个点这么简单,函数的几何形状也并不真的是一个椭圆。(真的就看懂了这么点东西..)因此有机会仍是但愿多多阅读一下大神的博客。git
在go语言中crypto/elliptic包是声明椭圆曲线模型的包
注意:对于go语言自带的ECC函数来讲,数字越大对应的ECC的公私钥的长度就越长,对应的加密等级就越高,固然也就越安全,那么对应的执行效率也就会相对下降。
crypto/ecdsa包则是go中用于椭圆曲线数字签名的包。
github
1、生成公私钥
生成密钥-->将生成的私钥进行x509序列化为ASN.1标准的DER二进制编码--> 构建pem.Block数据块-->pem编码【公钥同理】golang
2、ecdsa私钥生成数字签名
读取本地私钥pem文件-->pem解析pem数据块-->x509解析der字符串,得到私钥-->计算hash-->ecdsa签名
3、ecdsa签名验证
读取本地公钥pem文件-->pem解析pem数据块-->x509解析der字符串,得到公钥-->计算hash-->ecdsa签名验证
算法
package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/sha256" "crypto/x509" "encoding/pem" "fmt" "math/big" "os" ) // 初始化建立ecc密钥 func generateECDSAKey() { // 生成ecc算法的密钥 privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader) if err != nil { panic(err) } // 将私钥本地化,使用x509进行序列化 privateDerBytes, err := x509.MarshalECPrivateKey(privateKey) if err != nil { panic(err) } // 再将它转换成pem的格式编码 privatePemBlock := pem.Block{ Type: "Ecc PrivateKey", // 简介使用算法类型rsa/ecc Bytes: privateDerBytes, } // 在本地建立pem文件 privateFile, err := os.Create("ECDSAPrivateKey.pem") if err != nil { panic(err) } defer privateFile.Close() // 进行pem编码 err = pem.Encode(privateFile, &privatePemBlock) if err != nil { panic(err) } // 公钥同理 publicKey := privateKey.PublicKey publicDerBytes, err := x509.MarshalPKIXPublicKey(&publicKey) if err != nil { panic(err) } publicPemBlock := pem.Block{ Type: "Ecc PublicKey", Bytes: publicDerBytes, } publicFile, err := os.Create("ECDSAPublicKey.pem") if err != nil { panic(err) } defer publicFile.Close() pem.Encode(publicFile, &publicPemBlock) } // 私钥签名 func privateKeySignature(data []byte, privateKeyPemFileName string) (rText, sText []byte) { // 读取私钥 privateFile, err := os.Open(privateKeyPemFileName) if err != nil { panic(err) } defer privateFile.Close() // 读取文件源信息 fileInfo, err := privateFile.Stat() if err != nil { panic(err) } buffer := make([]byte, fileInfo.Size()) privateFile.Read(buffer) // pem解码 block, _ := pem.Decode(buffer) // x509 DER解码 privateKey, err := x509.ParseECPrivateKey(block.Bytes) if err != nil { panic(err) } // 进行签名 r, s, err := ecdsa.Sign(rand.Reader, privateKey, getHash(data)) // 将big的数据转换成[]byte rText, _ = r.MarshalText() sText, _ = s.MarshalText() return } // 获取哈希 func getHash(data []byte) []byte { hash256 := sha256.New() hash256.Write(data) return hash256.Sum(nil) } // 公钥验证 func publicKeyVerify(data, rText, sText []byte, publicKeyPemFileName string) bool { // 读取公钥 publicFile, err := os.Open(publicKeyPemFileName) if err != nil { panic(err) } defer publicFile.Close() fileInfo, err := publicFile.Stat() if err != nil { panic(err) } buffer := make([]byte, fileInfo.Size()) publicFile.Read(buffer) block, _ := pem.Decode(buffer) publicKeyType, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { panic(err) } // 获取到publicKey的类型,要进行断言判断rsa/ecc publicKey := publicKeyType.(*ecdsa.PublicKey) // 对[]byte签名数据转换成big数据 var r, s big.Int r.UnmarshalJSON(rText) s.UnmarshalJSON(sText) if !ecdsa.Verify(publicKey, getHash(data), &r, &s) { return false } else { return true } } func main() { data := []byte("我向某用户转帐10元") generateECDSAKey() rText, sText := privateKeySignature(data, "ECDSAPrivateKey.pem") fmt.Println(publicKeyVerify(data, rText, sText, "ECDSAPublicKey.pem")) }
该模块分为两个部分一个是实现sha3,一个是实现secp256k1(这也是比特币中使用的签名算法). 须要说明的是secp256k1有两种实现方式,一种是依赖libsecp256k1,须要cgo,另一种是依赖github.com/btcsuite/btcd,这是一个使用go语言实现的比特币的客户端.安全
这个模块实际上能够认为就是一个功能计算sha3-256,用法也很简单,就是调用crypto中的Keccak256,输出是一个32字节的hash结果函数
hash := crypto.Keccak256Hash([]byte("hello")) //hash值:4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45
这个模块比较复杂,若是要细度源码,须要对密码学有比较深刻的理解,可是使用起来其实比较简单.
主要就是签名,验证,以及公钥与以太坊地址转换
1.签名
secp256k1的私钥地址长度是32字节256位,公钥地址长度是65字节,而以太坊的地址长度是20字节.
2.验证
验证签名是否正确,须要公钥,hash(对message进行hash的结果),以及签名. 这里面真正校验的是第三步,也就是公钥是否和个人相同,而不像普通工RSA签名验证同样.固然咱们能够封装成和RSA签名验证同样形式的func VerifySignature(pubKey,msg,sig []byte) error
3.公钥与地址的转换
以太坊中并无直接拿公钥当作帐户地址,而是进行了一个简单的转换,具体来讲就是hash(公钥)的后20位,这里的hash算法是sha3-256,能够用一行代码来表示学习
crypto.Keccak256(pubKey)[12:]
package main import ( "bytes" "crypto/ecdsa" "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/ecies" "strconv" ) // 获取私钥 func getKey() (*ecdsa.PrivateKey, error) { privateKey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader) if err != nil { return privateKey, err } return privateKey, nil } // 加密 func ECCEncrypt(publicKey ecies.PublicKey, data []byte) ([]byte, error) { ct, err := ecies.Encrypt(rand.Reader, &publicKey, data, nil, nil) return ct, err } // 解密 func ECCDecrypt(privateKey ecies.PrivateKey, ct []byte) ([]byte, error) { m, err := privateKey.Decrypt(ct, nil, nil) return m, err } // 获取哈希 func getHash2(data []byte, nonce int) string { hashBytes := sha256.Sum256([]byte(string(data) + strconv.Itoa(nonce))) return hex.EncodeToString(hashBytes[:]) } // 获取挖矿等级需求来进行判断hash是否知足 func getMineDiff(diff int) (str string) { for i := 0; i < diff; i++ { str = str + "0" } return } // 开始挖矿[链的挖矿难度] func calculationHash(diff int, data []byte) string { strDiff := getMineDiff(diff) var nonce int for { if getHash2(data, nonce)[:diff] == strDiff { return getHash2(data, nonce) } nonce++ } } // 签名 func signature(hash []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) { signature, err := crypto.Sign(hash, privateKey) if err != nil { return signature, err } return signature, nil } // 验证 func validate(recoveredPub, recoveredPubBytes []byte) bool { if !bytes.Equal(recoveredPubBytes, recoveredPub) { return false } return true } func main() { privateKeyECDSA, _ := getKey() // 将ecdsa的私钥转换成以太坊的私钥 privateKey := ecies.ImportECDSA(privateKeyECDSA) publicKey := privateKey.PublicKey // 公钥加密 data := []byte("我向某用户转帐10元") hash := calculationHash(4, data) fmt.Println("哈希散列为", hash) encryptData, err := ECCEncrypt(publicKey, []byte(hash)) if err != nil { panic(err) } fmt.Println("公钥加密后为", hex.EncodeToString(encryptData)) // 私钥解密 decryptData, err := ECCDecrypt(*privateKey, encryptData) if err != nil { panic(err) } fmt.Println("私钥解密后为", string(decryptData)) // 进行签名 signData, _ := signature(crypto.Keccak256([]byte(hash)), privateKey.ExportECDSA()) fmt.Println("签名为", hex.EncodeToString(signData)) // 验证 recoveredPub, _ := crypto.Ecrecover(crypto.Keccak256([]byte(hash)), signData) recoveredPubBytes := crypto.FromECDSAPub(&privateKeyECDSA.PublicKey) fmt.Println(validate(recoveredPub, recoveredPubBytes)) }
借鉴参考:
https://blog.csdn.net/u013792921/article/details/85057646#1.ECC 密码学07--数字签名之go中的椭圆曲线数字签名
http://www.javashuo.com/article/p-oordgxcv-gt.html 以太坊系列之三: 以太坊的crypto模块--以太坊源码学习
https://blog.csdn.net/reigns_/article/details/83069118 golang实现ecc加密解密编码