PHP如何驗證以太坊簽名

以太坊有一個很是強大的JavaScript生態系統。有一些很棒的開源項目,好比ethereumjs-util,它提供了一個用以太坊賬戶簽名的即插即用功能。php

JavaScript的一個缺點是,在許多領域,它帶來了安全問題。一個這樣的安全風險是顯而易見,這是因爲我努力在EthTools.com上實現持久性認證(仍然是一個正在進行的被警告了的工做)。html

利用開源項目(如ethereumjs-util)來簽署任意的數據消息是至關容易的。然而,不容易的是告訴服務器有人已經成功地驗證了某賬號的全部權。node

固然這也不是絕對正確的,你也能夠很容易作到這一點。簡單地構建一個簡單的API端點,並在成功認證後向其發出請求。git

真正的問題是,建立一個「假」請求並將其發送到上述(易於識別 - 只需在控制檯中查看)端點很是容易。 我能夠輕鬆發出請求,說我已經驗證了任何賬戶的全部權。github

憑藉尖端技術……特別是處理「真實價值」的技術,尤爲重要的是安全性體現出了它應有的重要性並被重視,特別是在歷史上出現各類被利用的攻擊的狀況下。web

並且即便是在初期,以太坊也吸引了最優秀的人——那些知道在作什麼的人。若是有安全漏洞,有人會找到它。api

如今。。雖然能夠確保AJAX請求和僞造更難,但幾乎不可能使交易100%的安全。我須要另外一種方式。數組

我最終解決的方法是選擇最簡單的服務器端認證方式。緩存

對每一個人交易都是可視化的

在客戶端中與區塊鏈交互的一件大事是,在任何狀況下,任何人都能清楚地看到你在作什麼。他們能夠自信地知道你沒有把他們的私人鑰匙發送給別人。怎樣?他們能夠查看控制檯並查看每個發出的請求。安全

若是一個服務在任何地方POSTing個人私鑰,我會很是擔憂。

在咱們實現的認證流中,用戶能夠看到咱們沒有在任何地方發送任何數據——全部的東西都是在客戶端中完成的。

遺憾的是,個人身份驗證方案中確實須要POSTing數據。但也不用擔憂(有些人可能不一樣意)。

咱們POST身份驗證的公鑰到咱們的API端點。雖然你不能用服務器上的公鑰來驗證咱們所作的事情,但咱們並無用你的公鑰作任何惡做劇——這就是爲何它是公開的。

在服務器上,咱們使用提交的公鑰來驗證提交的簽名是由具備相應私鑰信息的人建立的。這裏要明確指出,咱們不知道你的私鑰,但橢圓曲線加密容許咱們經過簡單地使用公鑰來驗證簽名是不是使用它建立的。

在ethereumjs-util和solidity中,ecrecover方法是前提,除非這些工做分別在客戶端和區塊鏈中。

以太坊論壇上,chriseth給出了ecrecover的如下有用解釋:

ecrecover的思想是,能夠計算對應於用於建立ECDSA簽名的私鑰的公鑰,這兩個額外的字節一般是由簽名提供的。簽名自己是橢圓曲線點R和S的兩個(編碼),而V是恢復公鑰所需的兩個附加位。

這也解釋了爲何返回類型是地址:它返回對應於恢復的公鑰(即其sha3/keccak的哈希)的地址。這意味着要實際驗證簽名,檢查返回的地址是否等於相應的私鑰應該已經簽署哈希的那個地址。

咱們但願在服務器上有相同的功能。

注意:Solidity的ecrecover返回一個地址,而ethereumjs-utils的ecrecover返回一個公鑰。

注:研究期間,我發現了一些有趣的StackExchange的問題主題。這些內容以下:

web3.js API文檔還提供了一些關於ecrecover的參數的看法:

After the hex prefix, characters correspond to ECDSA values like this:

r = signature[0:64]
s = signature[64:128]
v = signature[128:130]

Note that if you are using ecrecover, v will be either "00" or "01". As a result, in order to use this value, you will have to parse it to an integer and then add 27. This will result in either a 27 or a 28.
複製代碼

PHP中怎麼作

[EthTools.com]是創建在Phalcon PHP框架之上的。

沒有真正意義上的以太坊PHP社區,PHP在處理數值表示方面有其缺點。

固然,橢圓曲線密碼的問題是極其複雜的,而我對它也缺少已經掌握的可用知識。

在大量的資料查詢研究和大量的開發調試以後,我成功地實現了PHP中的ecrecover功能。

雖然我知道如何作到這一點,我寫了一些「筆記」,我整理和包含在下面的內容,但願能幫助別人瞭解正確的方向。

個人行動邏輯是使用ethereumjs-util,使用已知的以太坊私鑰簽署交易。而後,我會模仿PHP中的ecrecover方法的代碼路徑,而後像宏播放同樣執行,直到從簽名返回的輸出公共密鑰與原始簽名賬戶匹配。

因此…

在Node中,緩存 Buffers 是無符號8位整數的數組。 digits 是它們的10進制(十進制)表示。

8位就會有2^8=255個十進制選項。這些整數是來自UTF-8字符集的字符的數字編碼表示。

Node利用這些緩存來進行這些計算所需的排序的數據操做。

在服務器上,咱們有不一樣的字符串(消息哈希和簽名),可是PHP不知道這些字符串中的字節是base 16 numerical表示(十六進制)。

每一個字符都是一個「小寫」,它須要4個字節來表示(容許十六進制字符是0—9和A—F)。

這樣,8位數據是兩個十六進制字符。

在Node中,將字符串「61BF09」轉換爲一個buffer,經過將兩個小寫的集合轉換成它的十進制形式。

  • 61轉成97
  • bf轉成191
  • 09轉成9

要在PHP中執行等效,咱們執行以下的操做:

$r_byte_array = unpack('C*', hex2bin($r));
複製代碼

咱們調用hex2bin,它將十六進制字符串(不含0x)轉換爲二進制表示(base 2)。經過調用這個方法,咱們隱式地說明初始格式是十六進制。

unpack而後將字符串轉換爲代碼中的數組——咱們的Buffer等價物。

最初PHP只是認爲字符串是UTF-8。若是咱們不先調用hex2bin,第一個int是54,效果是這樣:

這是由於unpack只是將UTF8中的第一個字節(54)轉換成二進制代碼(6),64個字符=64個代碼點。

當咱們告訴unpack咱們處理十六進制時,它將每一個兩個字節的十六進制集合(每一個表明4位數據的字符)轉換爲它的十進制表示。61(0x61)變爲97。咱們的64個字節十六進制字符串變成32個8位整數,效果是這樣:

你能夠經過使用這個轉換器來看這些不一樣的表示。

如今,你就有一個符合要求而且已經格式化了的消息哈希和簽名表示,「你能夠做弊了」。

我比較懶和喜歡自作聰明。也就是說,讓我試圖充分理解、欣賞和實施secp256k1橢圓曲線是根本不會發生的。此外…何苦?這是一個不須要從新發明的輪子。

我發現了一些與secp256k1有關的php庫。例如:

我最終使用了全部三個庫的組合,我喜歡知道我在使用什麼,而且基本上(至少)理解我正在向服務器推送什麼。上面的庫是至關豐富的、複雜的,我只是簡單提取我須要的相對簡單的功能。

在花了大量的時間來了解我正在作的事情以後,我終於成功地實現了我想要達到的目標——我已經成功地驗證了以太坊客戶端中建立的簽名是來自個人一個特定的私鑰。

當我第一次爬進這個rabbit hole的時候,我會繼續實施我所想到的功能。

注意事項。2018年又我寫了第二篇文章,詳細介紹了我如何驗證PHP先前簽署的消息的有效性。(注:後面也會翻譯給你們)

若是你們在學習用php開發以太坊那咱們推薦這個教程:

php以太坊,主要是介紹使用php進行智能合約開發交互,進行帳號建立、交易、轉帳、代幣開發以及過濾器和事件等內容。

匯智網原創翻譯,轉載請標明出處。這裏是原文

相關文章
相關標籤/搜索