本文作者:杰哥的技术杂货铺[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 函数的语法定义
代码语言: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 根据私钥创建公私钥函数代码
- 定义私钥结构体,根据私钥返回对应公钥的结构体
// 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)
}
本系列文章:
从零开发区块链应用(一)--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