从零开发区块链应用(十一)--以太坊地址生成

2022-02-22 17:59:15 浏览数 (1)

本文作者:杰哥的技术杂货铺[1]

一、生成以太坊地址私钥

1.1 生成以太坊地址私钥函数代码

  • 创建常量
代码语言:javascript复制
const (
 BitcoinSeed = "Bitcoin seed"
 Mnemonic    = "search crime session tag file joke leaf express interest slender file hawk"
)
  • 创建生成地址函数
代码语言:javascript复制
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 函数的语法定义

代码语言:javascript复制
DK = PBKDF2(PRF, Password, Salt, c, dkLen ,Hash algorithm)
  • PRF 是一个伪随机函数,例如 HASH_HMAC 函数,它会输出长度为 hLen 的结果。
  • Password 是用来生成密钥的原文密码。
  • Salt 是一个加密用的盐值。
  • c 是进行重复计算的次数。
  • dkLen 是期望得到的密钥的长度。
  • DK 是最后产生的密钥。

以下为使用助记词生成私钥的代码

代码语言:javascript复制
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 封装的代码详细解析

代码语言:javascript复制
//创建运算对象,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 算法的距举例

代码语言:javascript复制
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 根据私钥创建公私钥函数代码

  • 定义私钥结构体,根据私钥返回对应公钥的结构体
代码语言:javascript复制
// ToPubKey 返回与此私钥对应的公钥
func (p *PrivateKey) ToPubKey() *PublicKey {
 return (*PublicKey)(&p.PublicKey)
}
  • 根据私钥创建公钥的函数代码
代码语言:javascript复制
// 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 主函数代码

  • 定义结构体
代码语言:javascript复制
// Address 表示20字节地址
type Address [AddressLength]byte
  • 主函数 PubkeyToAddress()
代码语言:javascript复制
// PubkeyToAddress 公钥转地址方法
func (p *PublicKey) ToAddress() Address {
 pubBytes := p.FromECDSAPub()
 i := sha3.Keccak256(pubBytes[1:])[12:]
 return BytesToAddress(i)
}

3.2 子函数代码

  • 子函数 FromECDSAPub()
代码语言:javascript复制
// 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()
代码语言:javascript复制
// 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()
代码语言:javascript复制
// 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)
}

本系列文章:

从零开发区块链应用(一)--golang 配置文件管理工具 viper[2]

从零开发区块链应用(二)--mysql 安装及数据库表的安装创建[3]

从零开发区块链应用(三)--mysql 初始化及 gorm 框架使用[4]

从零开发区块链应用(四)--自定义业务错误信息[5]

从零开发区块链应用(五)--golang 网络请求[6]

从零开发区块链应用(六)--gin 框架使用[7]

从零开发区块链应用(七)--gin 框架参数获取[8]

从零开发区块链应用(八)--结构体初识[9]

从零开发区块链应用(九)--区块链结构体创建[10]

从零开发区块链应用(十)--golang 协程使用[11]

从零开发区块链应用(十一)--以太坊地址生成[12]

参考资料

[1]

杰哥的技术杂货铺: https://learnblockchain.cn/people/3835

[2]

从零开发区块链应用(一)--golang配置文件管理工具viper: https://learnblockchain.cn/article/3446

[3]

从零开发区块链应用(二)--mysql安装及数据库表的安装创建: https://learnblockchain.cn/article/3447

[4]

从零开发区块链应用(三)--mysql初始化及gorm框架使用: https://learnblockchain.cn/article/3448

[5]

从零开发区块链应用(四)--自定义业务错误信息: https://learnblockchain.cn/article/3449

[6]

从零开发区块链应用(五)--golang网络请求: https://learnblockchain.cn/article/3457

[7]

从零开发区块链应用(六)--gin框架使用: https://learnblockchain.cn/article/3480

[8]

从零开发区块链应用(七)--gin框架参数获取: https://learnblockchain.cn/article/3481

[9]

从零开发区块链应用(八)--结构体初识: https://learnblockchain.cn/article/3482

[10]

从零开发区块链应用(九)--区块链结构体创建: https://learnblockchain.cn/article/3483

[11]

从零开发区块链应用(十)--golang协程使用: https://learnblockchain.cn/article/3484

[12]

从零开发区块链应用(十一)--以太坊地址生成: https://learnblockchain.cn/article/3485

0 人点赞