1. 程式人生 > 其它 >從零開發區塊鏈應用(十一)--以太坊地址生成

從零開發區塊鏈應用(十一)--以太坊地址生成

目錄

一、生成以太坊地址私鑰

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)
}
當你老了,回顧一生,就會發覺:什麼時候出國讀書,什麼時候決定做第一份職業、何時選定了物件而戀愛、什麼時候結婚,其實都是命運的鉅變。只是當時站在三岔路口,眼見風雲千檣,你作出選擇的那一日,在日記上,相當沉悶和平凡,當時還以為是生命中普通的一天。