1. 程式人生 > >區塊鏈100講:能夠證明你是你的數字簽名和多重簽名

區塊鏈100講:能夠證明你是你的數字簽名和多重簽名

image

隨著區塊鏈相關技術的創新和突破,很多有形或無形資產都將實現去中心化,數字資產將無處不在。要保護數字出版物版權,實現去中心化,解決業界多年來版權保護不力的難題。無論數字資產,還是數字出版版權,都是有明確所有權的,當前實現數字資產所屬的技術手段就是本期《區塊鏈100講》要介紹的數字簽名。而多重簽名是對數字簽名的擴充套件使用,給數字資產轉移提供了安全保障和技術手段。本期內容從基本概念入手,詳細瞭解數字簽名和多重簽名的作用和程式碼實現。

1

數字簽名

什麼是數字簽名?

很多人的第一反應是用筆簽名。生活中我們經常需要用筆簽名:借條要簽名,合同要簽名,去銀行辦理業務也要簽名。簽下名字,就表示對這件事負責。人的筆跡是很個性的,幾乎不可能複製,即便是專業模仿也可以通過技術鑑別出來,因此簽名就具有唯一性的特點

,並被法律認可。數字簽名的原理跟用筆簽名類似,用於確認身份。

數字簽名有兩個作用:

  • 一是能確定訊息確實是由傳送方簽名併發出來的;

  • 二是數字簽名能確定訊息的完整性。

然而,在沒有筆跡驗證的情況下,數字簽名如何保持唯一性和可驗證性呢?

簽名的驗證。如果,你拿著一張支票去銀行兌換,銀行職員會對支票上的簽名和印章仔細比對,確保印章大小、樣式,以及付款人簽名等,與銀行留存的資訊一致,才會給你兌付,這就是簽名驗證。

簡單地講,就是加密技術代替人的筆跡,數字簽名涉及一個雜湊函式、傳送者的公鑰、傳送者的私鑰。

其操作方法是:

在區塊鏈系統中傳送一條廣播時,傳送方用一個雜湊函式從廣播文字中生成摘要,然後用自己的私鑰對摘要進行加密,加密後的摘要將作為廣播的數字簽名和廣播一起傳送給接收方,接收方首先用與傳送方一樣的雜湊函式從接收到的原始廣播中計算出報文摘要,接著再用傳送方的公鑰來對廣播附加的數字簽名進行解密。如果這兩個摘要相同,那麼接收方就能確認該數字簽名是傳送方的。

比特幣客戶端簽名功能

數字資產需要簽名。類比人類簽名,比特幣也有簽名功能。如果瞭解比特幣錢包(客戶端軟體),就會發現它提供了一個簽名訊息的功能,可以用來對其他使用者通過比特幣網路之外的資訊進行簽名和驗證。我個人使用的是 比特幣官方網站 提供的比太錢包,如圖:

image

這個功能幹什麼用的呢?有好多小夥伴不清楚,這裡舉個簡單的例子解釋一下,具體使用的時候絕不限於這些應用。

Alice開了一個網店,但沒有直接接入比特幣網路,不能自動確認和驗證支付者。客戶Imfly購買了她的產品,並用比特幣支付了全部貨款。因為比特幣地址和交易都是公開匿名的,為了防止冒充冒領,Alice需要確認Imfly提供的那個付款地址確實是imfly本人的,否則不能發貨。這時候,就需要Imfly先把支付貨款的比特幣地址和相關交易簽名信息(如圖),然後通過QQ或郵件傳給Alice,Alice使用客戶端驗證資訊簽名,才能確認交易確實是Imfly的。

想象一下,如果沒有簽名功能會怎麼樣呢?因為比特幣僅是一個匿名、安全的支付手段,但卻無法確認支付方或收款方是誰,資訊的不確定性,將使得比特幣網路之外的交易無法達成。在中心化的世界裡,這個問題是通過運營平臺這個第三方達成的,比如支付寶等,雙方的全部資訊,平臺都掌握,任何一方出現欺詐,都需要通過向平臺投訴來解決。使用者需要對第三方平臺絕對信任,並通過犧牲個人資訊保安獲得交易的基本保障。

電子簽名

通過上述分析,可以理解的是,簽名的作用是確定資產所屬,其特徵是簡單、安全、可驗證。把這個概念抽象出來,應用到計算機系統裡,為了確定數字資產所屬,也需要進行簽名,這就是大家經常看到的“電子簽名”的概念。在網路世界裡,簽名可以對任何需要確認的數字資產進行處理,比如比特幣地址、電子書版權等,並以此來宣告重要資產的所屬,這讓無需監管的去中心化交易成為可能。

具體開發設計中,就是用加密技術代替人的筆跡,不然任何簽名方法都會被模仿,而且模仿的成本極低,相反,驗證的成本卻很高。這也是當前數字出版行業版權保護不力的原因之一,各大平臺僅僅通過使用者許可權來限制使用者使用數字出版物,並沒有對出版物本身採取數字簽名等加密措施,一旦被盜版,驗證和取證工作耗費很多人力物力。下面那某個專案為例:

支付密碼

簽名方法在modules/signatures.js檔案裡,類圖如下:

image

我們還是從Api開始,程式碼如下:

// modules/signatures.js檔案

router.map(shared, {

"get /fee": "getFee",

"put /": "addSignature"

});

library.network.app.use('/api/signatures', router);

通過上面的程式碼,可以瞭解簽名提供了兩個簡單的公共介面:

get /api/signatures/fee -> shared.getFee

put /api/signatures/ -> shared.addSignature //簽名操作

顯然,最核心的方法也就是shared.addSignature,程式碼:

shared.addSignature = function (req, cb) {

...

library.scheme.validate(body, {

properties: {

...

},

required: ["secret", "secondSecret"] },

function (err) {

...

library.balancesSequence.add(function (cb) {

if (body.multisigAccountPublicKey && body.multisigAccountPublicKey != keypair.publicKey.toString('hex')) { modules.accounts.getAccount({publicKey: body.multisigAccountPublicKey}, function (err, account) {

...

try {

var transaction = library.logic.transaction.create({

type: TransactionTypes.SIGNATURE,

sender: account,

keypair: keypair,

requester: keypair,

secondKeypair: secondKeypair,

});

} catch (e) {

return cb(e.toString());

}

... }

毫無疑問,支付密碼也是一個簡單的交易(交易型別TransactionTypes.SIGNATURE)。基於此,我們不難想象,新增類似比特幣的簽名功能也是件非常簡單的事情。

再簡單一點:

image

傳送方把hello kitty的資訊進行雙重處理。首先是通過接收方公鑰來進行加密。

為什麼要接收方的公鑰來加密? 因為只有接收方的私鑰可以解開接受方公鑰加過的密,所以只有接受方可以解密。

然後hello kitty還要通過雜湊值得到摘要,接要再經過傳送方私匙進行簽名,簽名後得出的原文密文和傳送方簽名一起發給接受方。

接收方用自己的私匙解開密文,得到hello kitty.

然後通過雜湊得到摘要。

另外則是通過傳送方的公鑰解開發送方簽名,得到摘要,並且通過解密原文密文的摘要和解密傳送方簽名的摘要進行對比,最後的摘要一致,則認為摘要是對的。

通過這樣的方式,接受方完成了對傳送方簽名過的hello kitty資訊的認證。

這裡要記一個口訣,公鑰加密,私鑰解密,私鑰簽名,公鑰解密。

再短一點,則是公鑰加,私鑰解,私鑰籤,公鑰解。

有個問題,為什麼用接收方的公鑰加密hello kitty?因為通過這種方式,只有接受方的私鑰才能解開。

為什麼要用傳送方的私鑰簽名,不用其它人的私鑰簽名?因為這種方式,才能讓接收方確認這條資訊是傳送方發出來的。只有傳送方的公鑰才能解開發送方的簽名。

2

多重簽名

什麼是多重簽名

比特幣的匿名性,使交易處於不可信之中,最終導致使用者不敢交易。有了簽名功能,就有了確認雙方資訊的有效手段。那麼,什麼是多重簽名呢?

多重簽名,可以簡單地理解為一個數字資產的多個簽名。多重簽名預示著數字資產可由多人支配和管理。多重簽名,就表示動用這筆資金需要多個私鑰簽名,通常這筆資金或數字資產會儲存在一個多重簽名的地址或賬號裡。類似於生活中有一份檔案需要多個部門簽署才能生效一樣。

多重簽名是數字簽名的升級,它讓區塊鏈相關技術應用到各行各業成為可能。在實際的操作過程中,一個多重簽名地址可以關聯n個私鑰,在需要轉賬等操作時,只要其中的m個私鑰簽名就可以把資金轉移了,其中m要小於等於n,也就是說m/n小於1,可以是2/3, 3/5等等,是要在建立這個多重簽名地址的時候確定好的。

例如,電子商務、財產分割、資金監管等。例如,一對夫妻要儲備一筆資金,供孩子上大學使用,在這之前誰都不能動,那麼把簽名模式改為2/2,不僅限制了夫妻雙方,也給黑客攻擊增加了難度。多重簽名的設計,讓各種業務去中心化充滿無限可能。

工作原理

數字資產在某種情況下,需要多人支配。換句話說,在某些特定條件下,數字資產如果無法確認歸屬某個特定的人,那麼最好讓相關人共同簽署它的所有權。

仍然舉上面的例子,在Alice發貨之後,Imfly收到貨之前,這筆錢應該由第三方信用比較高的中介暫時儲存,這個階段,這筆錢要麼是Alice的,要麼是Imfly的,最終的歸屬要看Imfly是否收到貨。所以,這個第三方,無論如何都是應該有的,不然Imfly就要承擔大部分風險(因為比特幣的單向不可逆,Imfly傳送之後就沒有辦法收回了)

這樣一來,這筆錢的所屬關係,在交易過程中涉及到Alice、Imfly和平臺第三方(雖然不屬於它,但它有權裁定資金去向),那麼就應該由他們三方簽名,因此網上購物就是典型的多重簽名的例子。其多重簽名模型就是2/3,也就是說只要他們中的兩個簽名,資金就可以被轉移。

具體到這個例子,Imfly把錢打給一個關聯三方私鑰的多重簽名地址,如果整個交易過程順利,只要Alice和Imfly兩個簽名,這筆錢就會順利到達Alice手裡。如果不順利,他們任何一人提出仲裁,平臺第三方調查之後,通過簽名就能把這筆錢轉給Alice或退回Imfly。這非常類似淘寶和京東的模式,但是比他們更加便捷和安全,至少不用擔心第三方倒閉、挪用資金或攜款跑路。

應用場景

很顯然,多重簽名給了加密貨幣騰飛的翅膀,讓它單一單項支付的能力更具吸引力,讓加密貨幣技術應用到各行各業成為可能。這裡簡單的羅列幾個應用場景,供探索和思考:

電子商務。比較常見的是2/3的模式。上面電子商務網站的例子,就是最典型的場景之一,目前已經有成功的案例了。延伸一下,這類應用本質就是中介,所以還可用在各類中介機構性質的服務上。

財產分割。比如夫妻雙方共有財產,可以使用1/2的模式,一個賬戶誰都可以使用,跟各自擁有帳號一樣,好處是系統忠實記錄了每個人的花銷,鬧掰的時候很容易清算。擴充套件到公司合夥經營,可以使用1/n模式,n個人合夥人,都可以直接支配共有資金,具體清算時,一目瞭然。

資金監管。其實,這是多重簽名的最直接作用,一筆錢需要多個人簽名才能使用,任何一個人都無法直接動用資金,這在生活中太常見了,只要靈活設定多重簽名的比重模式,就能解決生活中很多問題。比如,接著上面夫妻的例子,夫妻要儲備一筆資金,供孩子上大學使用,在這之前誰都不能動,那麼把模式改為2/2,不僅限制了夫妻雙方,也給黑客攻擊增加了難度.

多重簽名的設計,讓各種業務去中心化充滿無限可能。

多重簽名

多重簽名方法在modules/multisignatures.js檔案裡,類圖如下:

image

實現Api的程式碼如下:

router.map(shared, {

"get /pending": "pending",

// Get pending

transactions "post /sign": "sign",

// Sign transaction

"put /": "addMultisignature",

// Add multisignature

"get /accounts": "getAccounts" });

library.network.app.use('/api/multisignatures', router);

解析一下,最後產生的Api如下:

get /api/multisignatures/pending -> shared.pending // 查詢等待中的交易

post /api/multisignatures/sign -> shared.sign // 簽名交易

put /api/multisignatures/ -> shared.addMultisignature // 建立多重簽名帳號

get /api/multisignatures/accounts -> shared.getAccounts // 獲得關聯的帳號(對應者使用者私鑰)

提供的功能很顯然,包括:待交易查詢、關聯帳號列表查詢,使用者簽名交易,建立多重簽名帳號等4個核心功能。我們先從建立多重簽名帳號開始,這個Api使用的是http的put方法,對應的自然是更新操作,不檢視程式碼也可以猜想到,該功能應該是在已有帳號基礎上的操作,從客戶端錢包設定選單裡,可以看到如圖操作:

image

看看shared.addMultisignature的原始碼如下:

// modules/multisignatures.js檔案

shared.addMultisignature = function (req, cb) {

var body = req.body;

library.scheme.validate(body, {

... required: ['min', 'lifetime', 'keysgroup', 'secret'] },

function (err) {

...

library.balancesSequence.add(function (cb) {

modules.accounts.getAccount({publicKey: keypair.publicKey.toString('hex')}, function (err, account) {

...

try {

var transaction = library.logic.transaction.create({

type: TransactionTypes.MULTI,

sender: account,

keypair: keypair,

secondKeypair: secondKeypair,

min: body.min,

keysgroup: body.keysgroup,

lifetime: body.lifetime

});

} catch (e) {

return cb(e.toString());

}

... };

建立一個多重簽名,必須’min’, ‘lifetime’, ‘keysgroup’, ‘secret’這四引數(其實,一個預設引數就是當前帳號),min代表上面講到的m值,即需要確認的人數;lifetime代表生命週期;keysgroup包含多重簽名關聯的全部帳號,它是陣列型別,包含的元素個數就是n,secret是使用者密碼,與使用者私鑰對應。

經過一系列的驗證之後,作為一個交易(交易型別TransactionTypes.MULTI)儲存到資料庫(區塊鏈)裡。建立成功的帳號,可以顯示多重帳號選單,對交易進行操作。接下來,自然應該能夠檢視全部關聯的帳號(請看shared.getAccounts方法),檢視待確認的交易(請看shared.pending方法),這兩個方法僅僅是簡單的查詢,沒什麼難度,這裡不再浪費篇幅。

如果使用者同意交易,就可以對待確認的交易進行簽名(shared.sign方法),這個方法的原始碼如下:

shared.sign = function (req, cb) {

var body = req.body;

library.scheme.validate(body, {

...

required: ['transactionId', 'secret'] },

function (err) {

...

function done(cb) {

library.balancesSequence.add(function (cb) {

var transaction = modules.transactions.getUnconfirmedTransaction(body.transactionId);

if (!transaction) {

return cb("Transaction not found"); }

transaction.signatures = transaction.signatures || [];

transaction.signatures.push(sign);

library.bus.message('signature', {

signature: sign,

transaction: transaction.id

}, true);

cb();

}, function (err) {

if (err) {

return cb(err.toString());

}

cb(null, {transactionId: transaction.id});

});

}

... };

這個方法,相比單獨的簽名方法,不同的是單獨的簽名方法相當於一個新建交易,而這裡的多重簽名的使用者簽名,顯然僅僅是對未確認交易進行簽名確認(640行維護了一個簽名陣列,push方法把使用者簽名寫入陣列)。

內容來源:圖書–機械工業出版社華章分社《連結未來:迎接區塊鏈與數字資產的新時代》;公眾號–區塊鏈研究實驗室、鏈社群

作者:許子敬、鏈三豐、社長等

整理:Cynthia

以下是我們的社群介紹,歡迎各種合作、交流、學習:)

image