第4章 數字簽名(連載)

本系列連載自《重新創造比特幣》,它是一本比特幣入門書籍,通過一個虛擬故事,讓讀者體驗從零開始創造比特幣的過程,從而理解比特幣為什麼如此設計。

重新創造比特幣 | 第4章 數字簽名(連載)

0. 前言

前篇說到了對稱加密,並且我們發現沒必要將交易的消息全部加密,因為交易數據不怕見光。只需要加密一小部分即可,究竟應該加密哪部分呢?

1. 簽名

既然我們的根本目的並不是防止別人看到交易數據的明文,而只是要用密文證明“我是我”。那麼我的要求就是儘量加密更少的文字,即,加密付款者的名字。

這是為什麼呢?

啟發往往來自於現實生活的例子:

趙四想買臺拖拉機,但是錢不夠,向劉能借了1萬塊錢,劉能要求趙四寫個借條。

趙四當然不願意寫啊,趙四就說自己不會寫字。

劉能當然不能放過他啊,劉能就幫趙四寫了欠條:“趙四欠劉能壹萬元整”。
但是這個欠條並不具有法律效果,因為趙四可以說是劉能自己瞎寫的。那麼為了讓這個借條成為證據,趙四最少也要簽上自己的名字。因為趙四的筆跡是全世界唯一的,所以趙四簽上了自己的名字,就代表著這個名字的本人認可這張借條的內容。如果趙四籤的不是自己的名字,這張借條也不成立,也就是說趙四的唯一的筆跡,只在寫自己的名字的時候借條生效。(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

籤 名

趙四的寫字方式就是私鑰,因為全世界就趙四能用這種方式寫字。

“趙四”這倆個標準漢字就是公鑰。

趙四的私鑰(寫字方式)只有在加密自己的公鑰(“趙四”)的時候有法律效應,加密別人的公鑰(籤別人的名字)就無效。

反之,法官看到趙四的簽名(那個寫得亂七八糟的簽字,等價於加密後的密文),可以用公鑰來解密簽名,因為公鑰是“趙四”。

所以法官可以調取法院保存的簽名數據庫,找到趙四過往的簽名來對比。

也可以傳喚趙四本人出庭,讓他辨認(解密)一下這個簽名(密文)是不是自己簽署(加密)的。

現實中我們管借條上的名字叫做簽名,簽名就是證明“我是我”的最少文字。
根據上面得到的靈感,借鑑到Bitcoin交易系統中,Alice用私鑰加密自己的名字“Alice”即可作為證明,加密後的密文就稱為:數字簽名。(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

利用數字簽名來證明Alice是Alice

2. 簽名用戶名的漏洞

中本聰一遍一遍的在腦子裡模擬著系統的運行,忽然發現了一個漏洞。

這個漏洞就是由於服務器沒有存儲用戶名和公鑰的對映關係,所以就不知道Alice的公鑰是什麼。如果有人用自己的公鑰簽署了別人的名字,法律上就等同於李四在借條上籤了謝大腳的名字,是無效的。但是現在的設計中服務端沒有能力判斷,因為服務器沒有類似法院的簽名數據庫。

如果Carol想要竊取Alice的Bitcoin:

1.Carol創建一條虛假的交易數據:“Alice to Carol 40”。

2.Carol用自己的私鑰加密Alice的用戶名,得到一個偽造的簽名:“806535be3e“。

3.然後將Carol的公鑰+偽造的簽名+偽造的交易數據,放入消息中,發送給Bitcoin服務端。

4.服務端收到消息,解析得到三部分:公鑰、簽名、交易數據。

5.服務端利用Carol的公鑰來解密Carol偽造的簽名,得到明文的用戶名:“Alice”。

6.服務器只會驗證簽名明文“Alice”是否等於交易數據中的付款方“Alice”。

問題就出在這裡,服務端沒有辦法驗證消息中收到的公鑰是否是Alice的,而本例子中的公鑰是Carol的,服務器端因為沒有存儲公鑰和用戶的對映關係,所以無法判斷。

7.服務器驗證通過,交易寫入賬本,交易生效!Carol成功的冒充Alice給自己轉了40個Bitcoin。(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

使用用戶名作為簽名的漏洞

3. 公鑰替代用戶名

那麼這個漏洞的關鍵點在哪裡呢?

關鍵點就在於,雖然服務器可以解密出簽名的明文用戶名,但是服務端不知道用戶名和消息中的公鑰是不是屬於同一個人,也就是說服務端不知道Alice的公鑰是什麼,當然也不知道Carol的公鑰是什麼?所以Carol可以用自己的私鑰來簽署用戶名為“Alice的簽名。服務端用Carol的公鑰來解密這個簽名自然可以解開。

所以選擇用戶名來簽名是行不通的。

如何繞過這個漏洞呢?

中本聰和Gilfoyle開始思考起來。

如果我們讓服務器來存儲用戶名和公鑰的對應關係,就又走回賬戶模型的老路了。

那麼根本的解決思路是什麼呢?

中本聰忽然有了靈感:“根本的解決的辦法就是捨棄用戶名!乾脆用公鑰來代替用戶名!”

同樣的場景:如果Carol想偽造一條Alice轉賬給自己的交易數據,就由上一個例子中的“Alice to Carol 40”變成了“公鑰A to 公鑰C 40”,其中我們用公鑰A代表Alice的公鑰,公鑰C代表Carol的公鑰。

整個流程是這樣的:

1.Carol創建一條虛假的交易數據:“公鑰A to 公鑰C 40”,意思就是Alice的公鑰轉賬給Carol的公鑰40個Bitcoin。

2.Carol用自己的私鑰加密Alice的公鑰A,得到一個偽造的簽名:“f9293ued3“。

3.然後將公鑰C+偽造的簽名+偽造的交易數據,放入消息中,通過網絡發送給Bitcoin服務端。

4.服務端收到消息,解析後得到三個部分:公鑰、簽名、交易數據。

5.服務端利用Carol的公鑰來解密Carol偽造的簽名,得到明文的用戶名:“公鑰A”(即,Alice的公鑰)。

6.服務器開始驗證邏輯:比較消息中的公鑰C和簽名中解密出來的公鑰A是否相等,顯然這倆個公鑰不相等,所以可以認定這筆交易不合法。

7.服務端拒絕繼續處理,交易無效。
這樣就解決了上面的漏洞,只需要將簽名由加密用戶名,改為加密公鑰。(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

公鑰替代用戶名

4. 將簽名加入到交易模型中

中本聰在腦子裡又模擬運行了幾遍系統,忽然又出現了一個靈感:“現在的消息由三個部分組成:公鑰,簽名,交易數據。這裡可以優化一下,首先可以去掉消息中的公鑰參數,因為它和交易數據中的付款方公鑰重複了。另外消息中的簽名參數也可以去掉,我們將簽名放入到交易數據中,變更下交易的模型,增加一個簽名字段,這樣簽名就作為交易數據的一部分,可以被寫入賬本中,永遠可查。”

Gilfoyle說:“這樣的改動十分合理!交易模型中增加簽名字段是很必要的,因為每筆交易的簽名應該和交易的業務數據一起存儲,並且永不分開。就好比欠條中的描述文字和簽名不可分開一樣,如果分開就等於欠條被撕成了兩半,不再具備法律效應。”(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

消息的改造

改造之後的完整交易是這個樣子(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

改造交易模型之後的交易

“到此為止,用戶名就沒有任何用處了,因為公鑰可以替代用戶名,user.txt也就沒有存在的意義了,終於可以徹底捨棄賬戶模型了!”中本聰終於把用戶註冊的問題給根本性的解決了。

Gilfoyle說:“我們可以將用戶的公鑰理解成收款的地址,而不僅僅看成是用戶名,雖然本質上他們是一個意思”。

“這個認知很棒!就像我訂報紙的時候,不必告訴送報紙的人我的真名叫什麼,我只要告訴他我的郵寄地址就可以啦。將公鑰理解成收款地址而非用戶名,的確更符合日常的生活經驗。”

“我準備開始編碼了,升級到只有賬本模型的新版本”,中本聰一邊說著一邊打開電腦,迅速的敲打起來。

Gilfoyle慢慢悠悠的說:“我可以幫忙嗎?”。
中本聰說:“太好了!你來改數據部分,我來搞程序部分,我把服務器的密碼告訴你”。
Gilfoyle按照剛才的討論,進入服務器,開始修改數據模型。
首先要將user.txt刪除。
然後將transaction.txt中的用戶名統統改成用戶的公鑰。
最後將簽名字段加入到交易模型中。(見下圖)

重新創造比特幣 | 第4章 數字簽名(連載)

數據模型的改造

5. 後記

故事中的Bitcoin系統雖然引入了數字簽名,並最終捨棄了賬戶模型。

但是,當前的設計是存在設計漏洞的,因為每次用戶的簽名不變,所以存在被重複使用的漏洞。例如Alice付款給Carol了50個Bitcoin,那麼Carol就有動機,將這筆交易數據截獲,並重復多次的向服務端發起相同的請求,服務端就會誤以為Alice自願支付多次50Bitcoin給Carol,直到Alice的餘額減少到小於50bitcion。

這個漏洞將在後面的故事中修復。

轉自:何巖 BSVOfficial


分享到:


相關文章: