以太坊Web钱包: 账号Keystore文件导入导出

EtherWeb钱包: 账号Keystore文件导入导出

电报联系方式

如何导入Geth创建的账号?

如何进行导入操作呢?对于使用Geth的用户,他们可能已经了解,在Geth创建帐户时会生成一个相应的Keystore JSON文件。Keystore文件用于安全存储Private Key信息,因此,我们的任务是将该Keystore文件导入。通常情况下,Keystore文件位于区块链数据同步目录的”keystore”文件夹中,例如:~/.ethereum/keystore。

详细解读 Keystore 文件

为什么需要 Keystore 文件

私钥代表了一个账号,最简单的保管账号的方式就是直接把私钥保存起来,如果私钥文件被人盗取,我们的数字资产将洗劫一空。

Keystore文件是以Encryption方式存储密钥的一种文件。这种方法的好处在于,当要发起交易时,首先需要使用密码解密Keystore文件以获取私钥,然后才能签署交易。这种双重安全层确保了数字资产的安全性,因为黑客必须同时获取Keystore文件和密码才能夺走我们的数字资产。

Keystore 文件如何生成的

以太坊是使用对称加密算法来加密私钥生成Keystore文件,因此对称加密秘钥(注意它其实也是发起交易时需要的解密秘钥)的选择就非常关键,这个秘钥是使用KDF算法推导派生而出。

使用 KDF 生成秘钥

密码学KDF(key derivation functions),其作用是通过一个密码派生出一个或多个秘钥,即从 password 生成加密用的 key。

而在Keystore中,是用的是Scrypt算法,用一个公式来表示的话,派生的Key生成方程为:

DK = Scrypt(salt, dk_len, n, r, p)

其中的 salt 是一段随机的盐,dk_len 是输出的哈希值的长度。n 是 CPU/Memory 开销值,越高的开销值,计算就越困难。r 表示块大小,p 表示并行度。

Litecoin 就使用 scrypt 作为它的 POW 算法

对私钥进行对称加密

上面已经用KDF算法生成了一个秘钥,这个秘钥就是接着进行对称加密的秘钥,这里使用的对称加密算法是 aes-128-ctr,aes-128-ctr 加密算法还需要用到一个参数初始化向量 iv。

Keystore文件

{
“address”:”856e604698f79cef417aab…”,
“crypto”:{
“cipher”:”aes-128-ctr”,
“ciphertext”:”13a3ad2135bef1ff228e399dfc8d7757eb4bb1a81d1b31….”,
“cipherparams”:{
“iv”:”92e7468e8625653f85322fb3c…”
},
“kdf”:”scrypt”,
“kdfparams”:{
“dklen”:32,
“n”:262144,
“p”:1,
“r”:8,
“salt”:”3ca198ce53513ce01bd651aee54b16b6a….”
},
“mac”:”10423d837830594c18a91097d09b7f2316…”
},
“id”:”5346bac5-0a6f-4ac6-baba-e2f3ad464f3f”,
“version”:3
}

来解读一下各个字段:

在Keystore文件中,有以下重要字段:

  1. address: 这是账号的地址。
  2. version: Keystore文件的版本,目前为第3版,也称为V3 KeyStore。
  3. id: 这是一个唯一标识符(UUID),用于标记Keystore文件的唯一性。
  4. crypto: 这部分包括了与加密相关的配置信息。
    • cipher: 它是用于加密以太坊私钥的对称加密算法,具体使用的是aes-128-ctr算法。
    • cipherparams: 这包括了aes-128-ctr加密算法所需的参数,主要是初始化向量(IV)。
    • ciphertext: 这是加密算法输出的密文,也是在将来解密时所需的输入。
  5. kdf: 该字段指定了使用的密钥派生函数(Key Derivation Function)算法,这里使用的是scrypt。
  6. kdfparams: 这部分包括了scrypt函数所需的参数。
  7. mac: 用来验证密码正确性的消息认证码(MAC),计算方式是mac = sha3(DK[16:32], ciphertext)。

这些字段共同构成了Keystore文件,确保了私钥的安全存储和保护。

总结一下Keystore 文件的产生:

  1. 使用scrypt函数 (根据密码 和 相应的参数) 生成秘钥
  2. 使用上一步生成的秘钥 + 账号私钥 + 参数 进行对称加密。
  3. 把相关的参数 和 输出的密文 保存为以上格式的 JSON 文件

如何确保密码是对的?

当我们在使用Keystore文件来还原私钥时,依然是使用kdf生成一个秘钥,然后用秘钥对ciphertext进行解密以太坊Web钱包: 账号Keystore文件导入导出

正是在这一步,不论你使用什么密码,都将生成一个私钥。然而,最终生成的以太坊私钥的正确性却仍然是一个谜。

这就是Keystore文件中MAC(消息认证码)值的作用。MAC值是通过对KDF输出和密文进行SHA3-256哈希运算得到的结果。显然,不同的密码会产生不同的MAC值,因此MAC值用于验证密码的正确性。验证过程可以用下图表示:

以太坊Web钱包: 账号Keystore文件导入导出

用ethers.js 实现账号导出导入

ethers.js 直接提供了加载keystore JSON来创建钱包对象以及加密生成keystore文件的方法,方法如下:

// 导入keystore Json
ethers.Wallet.fromEncryptedJson(json, password, [progressCallback]).then(function(wallet) {
// wallet
});

// 使用钱包对象 导出keystore Json
wallet.encrypt(pwd, [progressCallback].then(function(json) {
// 保存json
});

现在结合界面来完整的实现账号导出及导入,UI图如下:

以太坊Web钱包: 账号Keystore文件导入导出

HTML Code如下:

<h3>KeyStore 导出:</h3>
<table>
<tr>
<th>密码:</th>
<td><input type="”text”" placeholder="”(password)”" id="”save-keystore-file-pwd”" /></td>
</tr>

<tr>
<td> </td>
<td>
<div id="”save-keystore”" class="”submit”">导出</div>
</td>
</tr>
</table>

上面主要定义了一个密码输入框和一个导出按钮,点击“导出”后,处理逻辑代码如下:

// “导出” 按钮,执行exportKeystore函数
$(‘#save-keystore’).click(exportKeystore);

exportKeystore: function() {
// 获取密码
var pwd = $(‘#save-keystore-file-pwd’);

// wallet 是上一篇文章中生成的钱包对象
wallet.encrypt(pwd.val()).then(function(json) {
var blob = new Blob([json], {type: “text/plain;charset=utf-8”});

// 使用了FileSaver.js 进行文件保存
saveAs(blob, “keystore.json”);

});
}

FileSaver.js 是可以用来在页面保存文件的一个库。

再来看看导入keystore 文件, UI图如下:

以太坊Web钱包: 账号Keystore文件导入导出

<h2>加载账号Keystore文件</h2>
<table>
<tr>
<th>Keystore:</th>
<td><div class="”file”" id="”select-wallet-drop”">把Json文件拖动到这里</div><input type="”file”" id="”select-wallet-file”" /></td>
</tr>
<tr>
<th>密码:</th>
<td><input type="”password”" placeholder="”(password)”" id="”select-wallet-password”" /></td>
</tr>
<tr>
<td> </td>
<td>
<div id="”select-submit-wallet”" class="”submit" disable”>解密</div>
</td>
</tr>
</table>

上述代码片段主要包含一个文件输入框、一个密码输入框和一个“解密”按钮。因此,它的主要逻辑分为两部分:一是文件读取,二是解析和加载账户。以下是关键代码:

// 使用FileReader读取文件,

var fileReader = new FileReader();
fileReader.onload = function(e) {
var json = e.target.result;

// 从加载
ethers.Wallet.fromEncryptedJson(json, password).then(function(wallet) {

}, function(error) {

});

};
fileReader.readAsText(inputFile.files[0]);

这部分就介绍到这里

开发联系:DEXDAO

 

© 版权声明

Related posts

No comments

No comments...