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加密解密編碼