摘要:如果傳的是,就會(huì)在內(nèi)部使用默認(rèn)的隨機(jī)數(shù)生成器生成隨機(jī)數(shù)并生成密鑰。使用的是,生成的是一個(gè)形如這樣的全球唯一的隨機(jī)數(shù)把密鑰以文件形式保存在硬盤上。
作者:freewind
比原項(xiàng)目倉(cāng)庫(kù):
Github地址:https://github.com/Bytom/bytom
Gitee地址:https://gitee.com/BytomBlockc...
在前一篇,我們探討了從瀏覽器的dashboard中進(jìn)行注冊(cè)的時(shí)候,密鑰、帳戶的別名以及密碼,是如何從前端傳到了后端。在這一篇,我們就要看一下,當(dāng)比原后臺(tái)收到了創(chuàng)建密鑰的請(qǐng)求之后,將會(huì)如何創(chuàng)建。
由于本文的問(wèn)題比較具體,所以就不需要再細(xì)分,我們直接從代碼開始。
還記得在前一篇中,對(duì)應(yīng)創(chuàng)建密鑰的web api的功能點(diǎn)的配置是什么樣的嗎?
在API.buildHandler方法中:
api/api.go#L164-L244
func (a *API) buildHandler() { // ... if a.wallet != nil { // ... m.Handle("/create-key", jsonHandler(a.pseudohsmCreateKey)) // ...
可見,其路徑為/create-key,而相應(yīng)的handler是a.pseudohsmCreateKey(外面套著的jsonHandler在之前已經(jīng)討論過(guò),這里不提):
api/hsm.go#L23-L32
func (a *API) pseudohsmCreateKey(ctx context.Context, in struct { Alias string `json:"alias"` Password string `json:"password"` }) Response { xpub, err := a.wallet.Hsm.XCreate(in.Alias, in.Password) if err != nil { return NewErrorResponse(err) } return NewSuccessResponse(xpub) }
它主要是調(diào)用了a.wallet.Hsm.XCreate,讓我們跟進(jìn)去:
blockchain/pseudohsm/pseudohsm.go#L50-L66
// XCreate produces a new random xprv and stores it in the db. func (h *HSM) XCreate(alias string, auth string) (*XPub, error) { // ... // 1. normalizedAlias := strings.ToLower(strings.TrimSpace(alias)) // 2. if ok := h.cache.hasAlias(normalizedAlias); ok { return nil, ErrDuplicateKeyAlias } // 3. xpub, _, err := h.createChainKDKey(auth, normalizedAlias, false) if err != nil { return nil, err } // 4. h.cache.add(*xpub) return xpub, err }
其中出現(xiàn)了HSM這個(gè)詞,它是指Hardware-Security-Module,原來(lái)比原還預(yù)留了跟硬件相關(guān)的模塊(暫不討論)。
上面的代碼分成了4部分,分別是:
首先對(duì)傳進(jìn)來(lái)的alias參數(shù)進(jìn)行標(biāo)準(zhǔn)化操作,即去兩邊空白,并且轉(zhuǎn)換成小寫
檢查cache中有沒有,有的話就直接返回并報(bào)個(gè)相應(yīng)的錯(cuò),不會(huì)重復(fù)生成,因?yàn)樗借€和別名是一一對(duì)應(yīng)的。在前端可以根據(jù)這個(gè)錯(cuò)誤提醒用戶檢查或者換一個(gè)新的別名。
調(diào)用createChainKDKey生成相應(yīng)的密鑰,并拿到返回的公鑰xpub
把公鑰放入cache中??雌饋?lái)公鑰和別名并不是同一個(gè)東西,那前面為什么可以查詢alias呢?
所以我們進(jìn)入h.cache.hasAlias看看:
blockchain/pseudohsm/keycache.go#L76-L84
func (kc *keyCache) hasAlias(alias string) bool { xpubs := kc.keys() for _, xpub := range xpubs { if xpub.Alias == alias { return true } } return false }
通過(guò)xpub.Alias我們可以了解到,原來(lái)別名跟公鑰是綁定的,alias可以看作是公鑰的一個(gè)屬性(當(dāng)然也屬于相應(yīng)的私鑰)。所以前面把公鑰放進(jìn)cache,之后就可以查詢別名了。
那么第3步中的createChainKDKey又是如何生成密鑰的呢?
blockchain/pseudohsm/pseudohsm.go#L68-L86
func (h *HSM) createChainKDKey(auth string, alias string, get bool) (*XPub, bool, error) { // 1. xprv, xpub, err := chainkd.NewXKeys(nil) if err != nil { return nil, false, err } // 2. id := uuid.NewRandom() key := &XKey{ ID: id, KeyType: "bytom_kd", XPub: xpub, XPrv: xprv, Alias: alias, } // 3. file := h.keyStore.JoinPath(keyFileName(key.ID.String())) if err := h.keyStore.StoreKey(file, key, auth); err != nil { return nil, false, errors.Wrap(err, "storing keys") } // 4. return &XPub{XPub: xpub, Alias: alias, File: file}, true, nil }
這塊代碼內(nèi)容比較清晰,我們可以把它分成4步,分別是:
調(diào)用chainkd.NewXKeys生成密鑰。其中chainkd對(duì)應(yīng)的是比原代碼庫(kù)中的另一個(gè)包"crypto/ed25519/chainkd",從名稱上來(lái)看,使用的是ed25519算法。如果對(duì)前面文章“如何連上一個(gè)比原節(jié)點(diǎn)”還有印象的話,會(huì)記得比原在有新節(jié)點(diǎn)連上的時(shí)候,就會(huì)使用該算法生成一對(duì)密鑰,用于當(dāng)次連接進(jìn)行加密通信。不過(guò)需要注意的是,雖然兩者都是ed25519算法,但是上次使用的代碼卻是來(lái)自第三方庫(kù)"github.com/tendermint/go-crypto"的。它跟這次的算法在細(xì)節(jié)上究竟有哪些不同,目前還不清楚,留待以后合適的機(jī)會(huì)研究。然后是傳入chainkd.NewXKeys(nil)的參數(shù)nil,對(duì)應(yīng)的是“隨機(jī)數(shù)生成器”。如果傳的是nil,NewXKeys就會(huì)在內(nèi)部使用默認(rèn)的隨機(jī)數(shù)生成器生成隨機(jī)數(shù)并生成密鑰。關(guān)于密鑰算法相關(guān)的內(nèi)容,在本文中并不探討。
給當(dāng)前密鑰生成一個(gè)唯一的id,在后面用于生成文件名,保存在硬盤上。id使用的是uuid,生成的是一個(gè)形如62bc9340-f6a7-4d16-86f0-4be61920a06e這樣的全球唯一的隨機(jī)數(shù)
把密鑰以文件形式保存在硬盤上。這塊內(nèi)容比較多,下面詳細(xì)講。
把公鑰相關(guān)信息組合在一起,供調(diào)用者使用。
我們?cè)僭敿?xì)講一下第3步,把密鑰保存成文件。首先是生成文件名,keyFileName函數(shù)對(duì)應(yīng)的代碼如下:
blockchain/pseudohsm/key.go#L96-L101
// keyFileName implements the naming convention for keyfiles: // UTC--- func keyFileName(keyAlias string) string { ts := time.Now().UTC() return fmt.Sprintf("UTC--%s--%s", toISO8601(ts), keyAlias) }
注意這里的參數(shù)keyAlias實(shí)際上應(yīng)該是keyID,就是前面生成的uuid。寫成alias有點(diǎn)誤導(dǎo),已經(jīng)提交PR#922。最后生成的文件名,形如:UTC--2018-05-07T06-20-46.270917000Z--62bc9340-f6a7-4d16-86f0-4be61920a06e
生成文件名之后,會(huì)通過(guò)h.keyStore.JoinPath把它放在合適的目錄下。通常來(lái)說(shuō),這個(gè)目錄是本機(jī)數(shù)據(jù)目錄下的keystore,如果你是OSX系統(tǒng),它應(yīng)該在你的~/Library/Bytom/keystore,如果是別的,你可以通過(guò)下面的代碼來(lái)確定DefaultDataDir()
關(guān)于上面的保存密鑰文件的目錄,到底是怎么確定的,在代碼中其實(shí)是有點(diǎn)繞的。不過(guò)如果你對(duì)這感興趣的話,我相信你應(yīng)該能自行找到,這里就不列出來(lái)了。如果找不到的話,可以試試以下關(guān)鍵字:pseudohsm.New(config.KeysDir()), os.ExpandEnv(config.DefaultDataDir()), DefaultDataDir(),DefaultBaseConfig()
在第3步的最后,會(huì)調(diào)用keyStore.StoreKey方法,把它保存成文件。該方法代碼如下:
blockchain/pseudohsm/keystore_passphrase.go#L67-L73
func (ks keyStorePassphrase) StoreKey(filename string, key *XKey, auth string) error { keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP) if err != nil { return err } return writeKeyFile(filename, keyjson) }
EncryptKey里做了很多事情,把傳進(jìn)來(lái)的密鑰及其它信息利用起來(lái)生成了JSON格式的信息,然后通過(guò)writeKeyFile把它保存硬盤上。所以在你的keystore目錄下,會(huì)看到屬于你的密鑰文件。它們很重要,千萬(wàn)別誤刪了。
a.wallet.Hsm.XCreate看完了,讓我們回到a.pseudohsmCreateKey方法的最后一部分??梢钥吹?,當(dāng)成功生成key之后,會(huì)返回一個(gè)NewSuccessResponse(xpub),把與公鑰相關(guān)的信息返回給前端。它會(huì)被jsonHandler自動(dòng)轉(zhuǎn)換成JSON格式,通過(guò)http返回過(guò)去。
在這次的問(wèn)題中,我們主要研究的是比原在通過(guò)web api接口/create-key接收到請(qǐng)求后,在內(nèi)部做了哪些事,以及把密鑰文件放在了哪里。其中涉及到密鑰的算法(如ed25519)會(huì)在以后的文章中,進(jìn)行詳細(xì)的討論。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24182.html
摘要:下一步,將進(jìn)入比原的節(jié)點(diǎn)也就是后端。它具體是怎么創(chuàng)建密鑰的,這在以后的文章中將詳細(xì)討論。當(dāng)我們清楚了在本文中,前后端數(shù)據(jù)是如何交互的,就很容易推廣到更多的情景。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前面一篇文章,我們粗略...
摘要:而本文將繼續(xù)討論,比原是如何通過(guò)接口來(lái)創(chuàng)建帳戶的。把各信息打包在一起,稱之為另外,在第處還是一個(gè)需要注意的。比原在代碼中使用它保存各種數(shù)據(jù),比如區(qū)塊帳戶等。到這里,我們已經(jīng)差不多清楚了比原的是如何根據(jù)用戶提交的參數(shù)來(lái)創(chuàng)建帳戶的。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://git...
摘要:繼續(xù)看生成地址的方法由于這個(gè)方法里傳過(guò)來(lái)的是而不是對(duì)象,所以還需要再用查一遍,然后,再調(diào)用這個(gè)私有方法創(chuàng)建地址該方法可以分成部分在第塊中主要關(guān)注的是返回值。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在比原的dashboard中...
摘要:所以本文本來(lái)是想去研究一下,當(dāng)別的節(jié)點(diǎn)把區(qū)塊數(shù)據(jù)發(fā)給我們之后,我們應(yīng)該怎么處理,現(xiàn)在換成研究比原的是怎么做出來(lái)的。進(jìn)去后會(huì)看到大量的與相關(guān)的配置。它的功能主要是為了在訪問(wèn)與的函數(shù)之間增加了一層轉(zhuǎn)換。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlo...
摘要:?jiǎn)?dòng)直到進(jìn)入所以我們首先需要知道,比原在源代碼中是如何啟動(dòng),并且一步步走進(jìn)了的世界。后面省略了一些代碼,主要是用來(lái)獲取當(dāng)前監(jiān)聽的實(shí)際以及外網(wǎng),并記錄在日志中。 比原是如何監(jiān)聽p2p端口的 我們知道,在使用bytomd init --chain_id mainnet/testnet/solonet初始化比原的時(shí)候,它會(huì)根據(jù)給定的chain_id的不同,使用不同的端口(參看config/t...
閱讀 1699·2021-11-24 09:39
閱讀 3161·2021-11-22 15:24
閱讀 3104·2021-10-26 09:51
閱讀 3293·2021-10-19 11:46
閱讀 2901·2019-08-30 15:44
閱讀 2228·2019-08-29 15:30
閱讀 2548·2019-08-29 15:05
閱讀 788·2019-08-29 10:55