GO语言-区块链离线钱包开发之如何存储私钥

398 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

如何存储私钥

在确保私钥安全的情况下,为了更好的体验,我们需要让钱包把私钥存储起来。给用户更好的体验感。Geth是将私钥通过加密技术转换为json格式的文件,这个文件虽然是明文的,但是解析它的时候需要密码,否则将无法解密。

在Geth中,使用personal.newAccount("password"),password就是密码。在keystore的文件中,我们可以看到一些关键元素。

  • Address:账户地址信息
  • Crypto:加密算法部分
    1、Cipher:对称加密
    2、Kdf:密钥生成函数
    3、Mac:验证密码的代码
    
  • ID:uuid,系统内的唯一标识
  • Version:版本号

定义hdkeystore包和结构

自定义一个HDkeystore结构体

//HDkeystore使用的并非是私钥,它也是一个结构体,内部包含私钥
type HDkeyStore struct {
	keysDirPath string       //文件所在路径
	scryptN     int          //生成加密文件的参数N
	scryptP     int          //生成加密文件的参数P
	Key         keystore.Key //keystore对应的key
}
type Key struct{
 Id uuid.UUID
 Address common.Address
 PrivateKey *ecdsa.PrivateKey
}

生成UUID

借助rand加密包,生成uuid

type UUID []byte

//全局加密随机阅读器
var rander = rand.Reader

//生成UUID
func NewRandom() UUID {
	uuid := make([]byte, 16)
	io.ReadFull(rand.Reader, uuid)
	//版本4规范处理与变形
	uuid[6] = (uuid[6] & 0x0f) | 0x40
	uuid[8] = (uuid[8] & 0x3f) | 0x80
	return uuid
}

编写HDkeystore构造函数

面向对象编程的通用思想

//给出一个生成HDkeyStore对象的方法,通过privatekey生成
func NewHDkeyStore(path string, privateKey *ecdsa.PrivateKey) *HDkeyStore {
	//获得UUID
	uuid := []byte(NewRandom())
	key := keystore.Key{
		Id:         uuid,
		Address:    crypto.PubkeyToAddress(privateKey.PublicKey), //地址信息
		PrivateKey: privateKey,                                   //私钥信息
	}
	return &HDkeyStore{
		keysDirPath: path,
		scryptN:     keystore.LightScryptN, //固定参数
		scryptP:     keystore.LightScryptP, //固定参数
		Key:         key,
	}
}

写入文件实现

Keystore实际上是以太坊的一个接口,内部定义的三个方法都需要实现

实现StoreKey方法

存储key值

//存储key为keystore文件
//StoreKey
func (ks HDkeyStore) StoreKey(filename string, key *keystore.Key, auth string) error {

    //编码key为json

    keyjson, err := keystore.EncryptKey(key, auth, ks.scryptN, ks.scryptP)

    if err != nil {
        return err
    }

    //写入文件

    return WriteKeyFile(filename, keyjson)

}

实现JoinPath方法

用于路径和文件拼接

func (ks HDkeyStore) JoinPath(filename string) string {
	//如果filename是绝对路径,则直接返回
	if filepath.IsAbs(filename) {
		return filename
	}
	//将路径与文件拼接
	return filepath.Join(ks.keysDirPath, filename)
}

实现GetKey方法

keystore文件解析,形成私钥信息


//解析key
func (ks *HDkeyStore) GetKey(addr common.Address, filename, auth string) (*keystore.Key, error) {
	//读取文件内容

	keyjson, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	//利用以太坊DecryptKey解码json文件
	key, err := keystore.DecryptKey(keyjson, auth)
	if err != nil {
		return nil, err
	}
	// 如果地址不同代表解析失败
	if key.Address != addr {
		return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
	}
	ks.Key = *key
	return key, nil
}