什么是以太坊私钥储存(Keystore)文件?

进入keystore管理以太坊私钥的障碍很大,主要是因为以太坊客户端在直接的命令行或图形界面下隐藏了大部分的密码复杂性。

例如,用geth:

$ geth account new

Your new account is locked with a password.

Please give a password. Do not forget this password.

Passphrase: Repeat passphrase:

Address: {008aeeda4d805471df9b2a5b0f38a0c3bcba786b}

$ geth account list Account #0: {8a1c4d573cc29a96547816522cfe0b266e88abac} keystore:~/.ethereum/keystore/UTC---- 008aeeda4d805471df9b2a5b0f38a0c3bcba786b

我只需要输入3个单词就能创建一个新账户。然后输入两遍密码,就这么简单!我的以太坊keystore文件就创建了。 你需要把那些非常珍贵的keystore文件备份、存储在一个或多个隐秘的位置,这样就只有你能获取这些文件并取到资金。 从经验来看,当我没有完全理解一个新概念的微妙之处,并且过分依赖抽象层和现有工具来让我的生活更轻松时,我很有可能忘记一些事情,采取不必要的捷径并且搞砸它。搞砸的时候,可能就是会以我辛苦挣来的以太币被永久锁定而告终(幸好它还没有发生!)。 幸运的是,作为一个以太坊用户,你能搞砸的方式并不多: 你丢失了你的keystore文件 你忘记了和文件关联的密码 或者以上两者你都搞砸了。

在本文中,我们将为你介绍以太坊私钥是如何从 keystore 文件中算出来的。我们将讨论加密函数(对称加密,密钥生成函数,SHA3 哈希算法),但我们会尽可能的保证简明直接地来解释上述问题。

什么是keystore文件?

以太坊的 keystore 文件(Linux 系统存储在 ~/.ethereum/keystore 或者 Windows 系统存储在 C:\Users\Appdata/Roaming/Ethereum/keystore)是你独有的、用于签署交易的以太坊私钥的加密文件。如果你丢失了这个文件,你就丢失了私钥,意味着你失去了签署交易的能力,意味着你的资金被永久的锁定在了你的账户里。 当然,你可以直接把你的以太坊私钥存储在一个加密文件里,但是这样你的私钥容易受到攻击,攻击者简单的读取你的文件、用你的私钥签署交易,把钱转到他们的账户中。你的币会在你意识到发生什么了之前的短时间内丢失。 这就是以太坊 keystore 文件被创建的原因:它允许你以加密的方式存储密钥。这是安全性(一个攻击者需要 keystore 文件和你的密码才能盗取你的资金)和可用性(你只需要keystore文件和密码就能用你的钱了)两者之间完美的权衡。 为了让你发送一些以太币,大多数的以太坊客户端会让你输入密码(与创建账户时密码相同)以解密你的以太坊私钥。一旦解密,客户端程序就得到私钥签署交易,允许你移动资金。

Keystore文件是什么样子的?

如果你打开一个你的账户文件,它看起来像这样(取自这里):

$ cat ~/.ethereum/keystore/UTC---- 008aeeda4d805471df9b2a5b0f38a0c3bcba786b

{ "crypto" :

    {

          "cipher" : "aes-128-ctr",

          "cipherparams" : {

              "iv" : "83dbcc02d8ccb40e466191a123791e0e" },

          "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",

          "kdf" : "scrypt",

          "kdfparams" : {

              "dklen" : 32,

              "n" : 262144,

              "r" : 1,

              "p" : 8,

              "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"

          },

          "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"

      },

      "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",

      "version" : 3

}

一个有许多神奇的参数的粗笨的 JSON 文件,似乎与复杂的加密操作相关。这绝不吸引人。 让我们深入理解一下 如果你看这个 keystore 文件的结构,你会看到大部分内容都是在“crypto”中的:

"crypto" : {

    "cipher" : "aes-128-ctr",

    "cipherparams" : {

        "iv" : "83dbcc02d8ccb40e466191a123791e0e"

    },

    "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",

    "kdf" : "scrypt",

    "kdfparams" : {

          "dklen" : 32,

          "n" : 262144,

          "r" : 1,

          "p" : 8,

          "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"

    },

    "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"

},

这包括:

cipher:对称 AES 算法的名称;

cipherparams:上述 cipher 算法需要的参数;

ciphertext:你的以太坊私钥使用上述 cipher 算法进行加密;

kdf:密钥生成函数,用于让你用密码加密 keystore 文件;

kdfparams:上述 kdf 算法需要的参数;

Mac:用于验证密码的代码。

让我们看看他们是如何协同工作的,如何在你的密码下保护 keystore 文件。

 加密你的私钥

就像之前提到的,一个以太坊账户就是用于加密签署交易的一个私钥 —公钥对。为了确保你的私钥没有在文件中明文存储(即任何人只要能得到这个文件就能读),使用强对称算法(cipher)对其加密至关重要。 这些对称算法使用密钥来加密数据。加密后的数据可以使用相同的方法和同样的密钥来解密,因此算法命名为对称算法。在本文中,我们称这个对称密钥为解密密钥,因为它将用于对我们的以太坊私钥进行解密。

以下是 cipher,cipherparams 和 ciphertext 对应的概念:

Cipher 是用于加密以太坊私钥的对称加密算法。此处cipher用的是 aes-128-ctr 加密模式

Cipherparams 是 aes-128-ctr 加密算法需要的参数。

         在这里,用到的唯一的参数 iv,是aes-128-ctr加密算法需要的初始化向量。

Ciphertext 密文是 aes-128-ctr 函数的加密输入。

所以,在这里,你已经有了进行解密以太坊私钥计算所需要的一切…等等。你需要首先取回你的解密密钥。


1 -ciphertex 密文的对称解密-

2. 用你的密码来保护它

要确保解锁你的账户很容易,你不需要记住你的每一个又长又非用户友好型的用于解密 ciphertext 密文解密密钥。相反,以太坊开发者选择了基于密码的保护,也就是说你只需要输入密码就能拿回解密密钥。

为了能做到这一点,以太坊用了一个密钥生成函数,输入密码和一系列参数就能计算解密密钥。 这就是 kdf 和 kdfparams 的用途:

kdf 是一个密钥生成函数,根据你的密码计算(或者取回)解密密钥。在这里,kdf 用的是scrypt算法。

kdfparams 是scrypt函数需要的参数。在这里,简单来说,dklen、n、r、p 和 salt 是 kdf 函数的参数。

更多关于 scrypt 函数的信息可以在这里找到。 在这里,用 kdfparams 参数对 scrypt 函数进行调整,反馈到我们的密码中,你就会得到解密密钥也就是密钥生成函数的输出。

2 -用密码生成密钥-

3. 确保你的密码是对的

我们描述了用密码和 keystore 文件生成以太坊私钥所需要的所有东西。然而,如果解锁账户的密码错误会发生什么?

根据迄今为止我们所看到的,所有操作(密码派生和解密)都会成功,但是最终计算的以太坊私钥不是正确的,这首先违背了密钥文件的使用初衷!

我们要保证输入解锁账户的密码是正确的,和最初创建 keystore 文件时一样(回想一下 geth 下创建新账户时两次输入的密码)。 这就是 keystore 文件中 mac 值起作用的地方。在密钥生成函数执行之后,它的输出(解密密钥)和 ciphertext 密文就被处理【注1】,并且和 mac(就像一种认可的印章)作比较。如果结果和 mac 相同,那么密码就是正确的,并且解密就可以开始了。

【注1】这里有点简略了。在和 mac 进行比较之前,解密密钥(左起第二字节开始的16字节)要和 ciphertext 密文连接在一起,并进行哈希散列(用SHA3-256的方法)。 更多信息请访问这里。

把所有的都放到一起考虑 唷!

如果你已经做到了这一点,那么恭喜你! 让我们回顾一下我们描述的3个函数。

首先,你输入了密码,这个密码作为 kdf 密钥生成函数的输入,来计算解密密钥。

然后,刚刚计算出的解密密钥和 ciphertext 密文连接并进行处理,和 mac 比较来确保密码是正确的。

最后,通过 cipher 对称函数用解密密钥对 ciphertext 密文解密。 瞧!解密的结果是你的以太坊私钥。

你可以在这里看看整个过程:

 就像你从图中可以看到的,整个过程可以看做一个黑盒(不过,图中是个灰盒),你的密码是惟一的输入,你的以太坊私钥是惟一的输出。所需的其他信息都可以在你的以太坊账户创建时生成的keystore文件中获得。 由于这个原因,请确保你的密码足够强(并且无论如何你要记住它!)才能保证即使攻击者偷到了你的keystore文件也不能轻易得到你的私钥。

测试数据

Details:

Address:008aeeda4d805471df9b2a5b0f38a0c3bcba786b

ICAP:XE542A5PZHH8PYIZUBEJEO0MFWRAPPIL67

UUID:3198bc9c-6672-5ab3-d9954942343ae5b6

Password:testpassword

Secret:7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d

PBKDF2-SHA-256

Test vector using AES-128-CTR and PBKDF2-SHA-256:

File contents of~/.web3/keystore/3198bc9c-6672-5ab3-d9954942343ae5b6.json:

{

    "crypto" : {

        "cipher" : "aes-128-ctr",

        "cipherparams" : {

            "iv" : "6087dab2f9fdbbfaddc31a909735c1e6"

        },

        "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46",

        "kdf" : "pbkdf2",

        "kdfparams" : {

            "c" : 262144,

            "dklen" : 32,

            "prf" : "hmac-sha256",

            "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd"

        },

        "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2"

    },

    "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",

    "version" : 3

}

Intermediates:

Derived key:f06d69cdc7da0faffb1008270bca38f5e31891a3a773950e6d0fea48a7188551

MAC Body:e31891a3a773950e6d0fea48a71885515318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46

MAC517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2

Cipher key:f06d69cdc7da0faffb1008270bca38f5

Scrypt

Test vector using AES-128-CTR and Scrypt:

{

    "crypto" : {

        "cipher" : "aes-128-ctr",

        "cipherparams" : {

            "iv" : "83dbcc02d8ccb40e466191a123791e0e"

        },

        "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c",

        "kdf" : "scrypt",

        "kdfparams" : {

            "dklen" : 32,

            "n" : 262144,

            "r" : 1,

            "p" : 8,

            "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19"

        },

        "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097"

    },

    "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6",

    "version" : 3

}

Intermediates:

Derived key:fac192ceb5fd772906bea3e118a69e8bbb5cc24229e20d8766fd298291bba6bd

MAC Bodybb5cc24229e20d8766fd298291bba6bdd172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c

MAC:2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097

Cipher key:fac192ceb5fd772906bea3e118a69e8b

从keystore提取Private Key的源代码

package main

import (

"encoding/hex" "flag" "fmt" "io/ioutil" "os" "github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/crypto")

func main() {

ksfilefullpath := "" kstype := "" kspassword := "" flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError)

flags.StringVar(&ksfilefullpath, "keystore-src", "", "`path` to keystore to be processed")

flags.StringVar(&kstype, "keystore-type", "", "keystore type: `pem`,`keystore`")

flags.StringVar(&kspassword, "keystore-password", "", "keystore password")

if err := flags.Parse(os.Args[1:]); err != nil {

fmt.Fprintln(os.Stderr, "Failed to parse flags:", err)

os.Exit(1)

}

keyjson, err := ioutil.ReadFile(ksfilefullpath)

if err != nil {

fmt.Println(err)

}

// Decrypt with the correct password key, err := keystore.DecryptKey(keyjson, kspassword)

if err != nil {

fmt.Println("test : json key failed to decrypt: %v", err)

}

fmt.Println("Private Key= " + hex.EncodeToString(crypto.FromECDSA(key.PrivateKey)))

}

编译

运行

提取的Private Key:  ab979c2c4d092212af817802f31313bd6f4144b4b9cb289f659c29f5cabbd428

https://www.myetherwallet.com/#view-wallet-info,上传keystore

看看结果

Hola, 一模一样

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容