從零開發區塊鏈應用(十一)--以太坊地址生成
一、生成以太坊地址私鑰
1.1 生成以太坊地址私鑰函式程式碼
- 建立常量
const ( BitcoinSeed = "Bitcoin seed" Mnemonic = "search crime session tag file joke leaf express interest slender file hawk" )
- 建立生成地址函式
func NewAccount(password string) string { seed := pbkdf2.Key([]byte(Mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New) hmac := hmac.New(sha512.New, []byte(BitcoinSeed)) _, err := hmac.Write([]byte(seed)) if err != nil { return "" } intermediary := hmac.Sum(nil) keyBytes := intermediary[:32] // 私鑰 _, pub := ecdsa.PrivKeyFromBytes(ecdsa.S256(), keyBytes) return pub.ToAddress().String() }
1.2 pbkdf2.Key() 生成祕鑰函式
PBKDF2(Password-Based Key Derivation Function)
是一個用來匯出金鑰的函式,常用於生成加密的密碼。
它的基本原理是通過一個偽隨機函式(例如HMAC函式、sha512等),把明文(password)和一個鹽值(salt)作為一個輸入引數,然後重複進行運算,並最終產生祕鑰。
如果重複的次數足夠大,破解的成本就會變得很高。而鹽值的新增也會增加“彩虹表”攻擊的難度。
使用者密碼採用PBKDF2演算法儲存,比較安全。
PBKDF2函式的語法定義
DK = PBKDF2(PRF, Password, Salt, c, dkLen ,Hash algorithm)
- PRF是一個偽隨機函式,例如HASH_HMAC函式,它會輸出長度為hLen的結果。
- Password是用來生成金鑰的原文密碼。
- Salt是一個加密用的鹽值。
- c是進行重複計算的次數。
- dkLen是期望得到的金鑰的長度。
- DK是最後產生的金鑰。
以下為使用助記詞生成私鑰的程式碼
package pbkdf2
import (
"crypto/rand"
"crypto/sha512"
"golang.org/x/crypto/pbkdf2"
)
const (
Mnemonic = "search crime conversation tag directory joke leaf express interest password = ""
)
func encryptPwdWithSalt(password string) (*ecdsa.PrivateKey, *ecdsa.PublicKey) {
seed := pbkdf2.Key([]byte(Mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New)
}
// []byte(Mnemonic):助記詞
// []byte("mnemonic"+password) :salt鹽值
// 2048:重複計算的次數
// 64:返回的祕鑰長度
// sha512.New:雜湊演算法
1.3 HMAC 生成摘要演算法
HMAC演算法中文名稱叫雜湊訊息認證碼,英文全稱是Hash-based Message Authentication Code。它的演算法是基於某個雜湊雜湊函式(主要是SHA系列和MD系列),以一個金鑰和一個訊息為輸入,生成一個訊息摘要作為輸出。HMAC演算法與其他雜湊雜湊演算法最大區別就是需要有金鑰。它的演算法函式是利用分組密碼來建立的一個單向Hash函式。
下表顯示具體的演算法對應輸出摘要的長度。
演算法 | 摘要長度(位)|備註
---|---|---|---
HmacMD5 |128| BouncyCastle實現
HmacSHA1| 160|(20個位元組) BouncyCastle實現
HmacSHA256| 256 |BouncyCastle實現
HmacSHA384| 384| BouncyCastle實現
HmacSHA512| 512| JAVA6實現
HmacMD2 |128| BouncyCastle實現
HmacMD4 |128| BouncyCastle實現
HmacSHA224| 224| BouncyCastle實現
HMAC的金鑰可以是任何長度,如果金鑰的長度超過了摘要演算法資訊分組的長度,則首先使用摘要演算法計算金鑰的摘要作為新的金鑰。一般不建議使用太短的金鑰,因為金鑰的長度與安全強度是相關的。通常選取金鑰長度不小於所選用摘要演算法輸出的資訊摘要的長度。
HMAC演算法golang封裝的程式碼詳細解析
//建立運算物件,HMAC需要兩個引數:hash函式和key
hmac := hmac.New(sha512.New, []byte(BitcoinSeed))
//將明文寫入到hmac中
_, err := hmac.Write([]byte(seed))
if err != nil {
return nil, nil
}
//hmac物件對寫入資料的運算,生成的引數為位元組
intermediary := hmac.Sum(nil)
用golang使用HMAC演算法的距舉例
package main
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"fmt"
)
func Md5(data string) string {
md5 := md5.New()
md5.Write([]byte(data))
md5Data := md5.Sum([]byte(""))
return hex.EncodeToString(md5Data)
}
func Hmac(key, data string) string {
hmac := hmac.New(md5.New, []byte(key))
hmac.Write([]byte(data))
return hex.EncodeToString(hmac.Sum(nil)
}
func Sha1(data string) string {
sha1 := sha1.New()
sha1.Write([]byte(data))
return hex.EncodeToString(sha1.Sum(nil))
}
func main() {
fmt.Println(Md5("hello"))
fmt.Println(Hmac("key2", "hello"))
fmt.Println(Sha1("hello"))
}
二、根據私鑰建立公私鑰
2.1 根據私鑰建立公私鑰函式程式碼
- 定義私鑰結構體,根據私鑰返回對應公鑰的結構體
// ToPubKey 返回與此私鑰對應的公鑰
func (p *PrivateKey) ToPubKey() *PublicKey {
return (*PublicKey)(&p.PublicKey)
}
- 根據私鑰建立公鑰的函式程式碼
// PrivKeyFromBytes 根據私鑰隨機數D返回公私鑰
func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey, *PublicKey) {
// 根據位元組pk,返回x,y
x, y := curve.ScalarBaseMult(pk)
// 定義一個私鑰結構體,結構體中包含:公鑰結構體
priv := &PrivateKey{
PublicKey: e.PublicKey{
Curve: curve,
X: x,
Y: y,
},
D: new(big.Int).SetBytes(pk),
}
return priv, (*PublicKey)(&priv.PublicKey)
}
接收引數:
- curve elliptic.Curve:橢圓曲線加密演算法
- pk []byte:私鑰位元組
返回引數:
- PrivateKey:ECDSA私鑰
- PublicKey:ECDSA公鑰
2.2 PrivKeyFromBytes 建立私鑰、公鑰對
根據作為引數作為位元組切片傳遞的私鑰返回“曲線”的私鑰和公鑰。
我們應該知道,可以從私鑰生成公鑰。所以擁有私鑰相當於擁有整個金鑰對。
*ecdsa.PrivateKey 是 PublicKey 和 PrivateKey 的結構。這也是從原始位元組 PrivateKey 檢索金鑰對的函式。
三、根據公鑰轉地址
3.1 主函式程式碼
- 定義結構體
// Address 表示20位元組地址
type Address [AddressLength]byte
- 主函式PubkeyToAddress()
// PubkeyToAddress 公鑰轉地址方法
func (p *PublicKey) ToAddress() Address {
pubBytes := p.FromECDSAPub()
i := sha3.Keccak256(pubBytes[1:])[12:]
return BytesToAddress(i)
}
3.2 子函式程式碼
- 子函式FromECDSAPub()
// FromECDSAPub 橢圓加密公鑰轉座標
func (p *PublicKey) FromECDSAPub() []byte {
if p == nil || p.X == nil || p.Y == nil {
return nil
}
return elliptic.Marshal(S256(), p.X, p.Y)
}
// S256()是特定的橢圓曲線,在程式啟動的時候進行初始化,後來呼叫只是獲取其引用而已。
// elliptic.Marshal(...)為標準庫函式,按照非壓縮形式得到相應的[]byte
func Marshal(curve Curve, x, y *big.Int) []byte {
byteLen := (curve.Params().BitSize + 7) / 8
ret := make([]byte, 1+2*byteLen)
ret[0] = 4 // uncompressed point
x.FillBytes(ret[1 : 1+byteLen])
y.FillBytes(ret[1+byteLen : 1+2*byteLen])
return ret
}
- 子函式Keccak256()
// Keccak256 使用sha3 256加密內容
func Keccak256(data ...[]byte) []byte {
d := NewKeccak256()
for _, b := range data {
d.Write(b)
}
return d.Sum(nil)
}
對除了符號位(第一個位元組)的其他位元組陣列進行sha3處理.
// sha3的結果共有32位元組。
// 取sha3結果的最後20位元組,生成地址。在以太坊中,地址為: type Address [AddressLength]byte
- 子函式BytesToAddress()
// BytesToAddress其實就是位元組拷貝
// BytesToAddress byte轉address
func BytesToAddress(b []byte) Address {
var a Address
a.SetBytes(b)
return a
}
// SetBytes 將地址設定為b的值。如果b大於len(a),會宕機
// SetBytes()考慮到了位元組數量不匹配的情況
func (a *Address) SetBytes(b []byte) {
if len(b) > len(a) {
b = b[len(b)-AddressLength:]
}
copy(a[AddressLength-len(b):], b)
}
當你老了,回顧一生,就會發覺:什麼時候出國讀書,什麼時候決定做第一份職業、何時選定了物件而戀愛、什麼時候結婚,其實都是命運的鉅變。只是當時站在三岔路口,眼見風雲千檣,你作出選擇的那一日,在日記上,相當沉悶和平凡,當時還以為是生命中普通的一天。