Eth Keystore
Overview
这是一个以太坊 keystore 文件的内容,我刚刚拿工具生成的:
在了解每个字段含义之前,后面不会再解释的基础名词如下:
-
私钥(private key): 长度为
256 bit = 32 bytes
的随机的密钥,一切加密的源头 -
公钥(public key): 和私钥对应的公钥, 长度为
512 bit = 64 bytes
, 由 私钥 + 椭圆曲线加密 计算出来。可以由私钥推出公钥,无法从公钥推出私钥。具体的椭圆曲线算法参数secp256k1
和比特币一样。 -
账户地址(address): 由公钥进行
keccak256
hash 运算后取后20个字节,以太坊给账号作标记的地址。 -
密码(password): 用于加密
keystore
文件的密码,可以是任意长度的字符串。保护 keystore 文件不那么容易被破解。
再来解释上面 JSON 文件里每个字段的含义:
整个 keystore 的加密,通过两类加密模式(cipher + kdf)来加密密码和私钥。从而达到的效果是:只有知道密码的用户才能利用 keystore 文件解出私钥,而想要暴力破解 keystore 也比较困难。
其中涉及到的加密算法 WIKI 链接:也许未来会进一步学习下啊巴啊巴…
Generate Keystore
来看看生成 keystore 的流程,目标是一个 keystore 文件和我们心中的密码,比如"some_password"
,它蕴含了一个32字节的私钥。私钥是生成 keystore 的数据源头,所以是整个算法过程中的参数。
除此之外,加上上面 JSON 文件里的众多参数里,可以分为三类:
随机的输入参数,由随机器/用户定义生成的内容
- 用户定义,不在文件里的私钥
- 用户定义,不在文件里的密码
-
随机生成:
cipherparams.iv
cipher 初始向量 -
随机生成:
kdfparams.salt
kdf 盐值
算法需要的参数或固定参数,控制了加密的强度或者其他标记
- kdfparams.dklen/n/p/r
- version
- id
算法的计算结果,由上面两类数据生成,写入文件为了校验/使用
- address: 对应了公钥
- ciphertext: 私钥加密结果
- mac: 验证码,用于在解密时验证密码是否正确
除掉相对固定的参数,整个方法可以认为是 \(F(prikey, password, iv, salt) \rightarrow mac, ciphertext\)
另外有 \(G(prikey) \rightarrow pubkey, address\) ,单独就可以生成。
Generate 算法步骤
私钥推出公钥和地址
\[G(prikey) \rightarrow pubkey, address\]
password + salt
经过 kdf 算法(Scrypt)
得到中间计算结果 key (256bits 32bytes)
\[Scrypt(password, salt, kdfparams) \rightarrow key\]
prikey
作为初始数据,经过对称加密 cipher 算法(AES128)
,应用数据:key
的前16字节,iv
的前16字节,得到加密后的 ciphertext
\[AES128(prikey, key[0..16], iv[0..16]) \rightarrow ciphertext\]
key[16..32] + ciphertext
合起来,经过 keccak256算法
作 hash256,得到结果 mac
\[keccak256(key[16..32] + ciphertext) \rightarrow mac\]
经过这四步,即把私钥和密码加密出来,计算出了校验用的 ciphertext
和 mac
Decrypt Keystore
解码 Keystore 需要 keystore 文件和密码,密码正确的结果是解出来的密钥,密码错误会出错。
Decrypt 算法步骤
根据输入的 password'
计算 key'
,此步骤算法同上面的 #2,因为输入和中间结果 key
不一定正确,后面加了一个 '
做区分
\[Scrypt(password', salt, kdfparams) \rightarrow key'\]
key'[16..32] + ciphertext
合起来,经过 keccak256算法
作 hash256,得到结果 mac'
, 和上面的步骤4一样,计算出 mac'
校验密码是否正确。
\[keccak256(key'[16..32] + ciphertext) \rightarrow mac'\]
验证 mac'
是否等于 mac
\[(mac'==mac)?continue:exit\]
ciphertext
作为初始数据,经过对称加密 cipher 算法(AES128)
,应用数据:key'
的前16字节,iv
的前16字节,恢复出私钥
\[AES128(ciphertext, key'[0..16], iv[0..16]) \rightarrow prikey\]
实现
加密和解密的方法,拿 Rust
实现了一下: 仓库链接
总结
完整的思维导图
推荐拓展阅读:
- https://julien-maffre.medium.com/what-is-an-ethereum-keystore-file-86c8c5917b97
- https://betterprogramming.pub/understanding-ethereum-cryptography-3ef7429eddce
- https://cryptobook.nakov.com/symmetric-key-ciphers/