1. 程式人生 > >比特幣從地址逆向計算私鑰

比特幣從地址逆向計算私鑰

區塊鏈

區塊鏈簡介

說到比特幣,就不得不提區塊鏈。那什麼是區塊鏈呢?

區塊鏈本質是一個數據集,只不過資料的組織採用了比較特殊的方式,就是把資料拆分為一塊一塊的小資料集。

為什麼要進行資料拆分呢?因為區塊鏈的作用是做為開放式的分散式的人人都可以修改的賬本。這就需要防止惡意修改。

分成小的資料塊之後,我們可以給每一個數據塊加一個鎖,要修改資料就得先找到鑰匙,找鑰匙的過程是一個需要大量計算過程,比特幣使用的是工作量證明機制(PoW)。

區塊鏈的鏈體現在什麼地方呢?在於每一個小的資料庫的鑰匙都依賴於前一個塊的鑰匙,這樣就形成了一個鏈式結構。這樣做的目的是增加篡改資料的難度,降低篡改資料的可能性,因為篡改一個塊的資料,就得修改這個塊資料後的所有資料。

打包區塊的計算

找鑰匙的過程比較複雜,這裡我們簡化一下,首先看一下區塊鏈的每一個塊中包含的屬性:

  1. Index:第幾區塊(創世區塊的索引為0)。
  2. Hash:當前區塊的Hash值。
  3. Previous Hash:上一個區塊的Hash值。
  4. Timestamp:當前區塊建立時的時間戳。
  5. Data:儲存在當前區塊上的交易資訊。
  6. Nonce:參與hash運算的數值,使區塊的hash值滿足指定條件
function isValidHash(hash, difficulty) {
  for (var i = 0, b = hash.length; i < b; i ++) {
      if (hash[i] !== '0') {
          break;
      }
  }
  return i >= difficulty;
}
let nonce = 0;
let hash;
let input;
let difficulty=4;
while(!isValidHash(hash,difficulty)) {     
  nonce = nonce + 1;
  input = index + previousHash + timestamp + data + nonce;
  hash = SHA256(input)
}

鑰匙就是使得就算結果滿足特定條件的nonce值,難度difficuty一般值的是字首有多少個0,比如說difficulty等於4,就是要求調整nonce的值,一般演算法是從0遞增,使得通過sha256之類的hash演算法的結果的字首至少包含4個0。

根據hash演算法不可逆的特性我們知道不可能通過一個值來逆向計算nonce,所以大家都只能老實的做搬磚式的迭代計算。

有很多同學可能會有疑問:既然大家都只能搬磚,那肯定是力氣大的先搬完啊!也就是計算能力強的計算節點會先計算出結果。

主要2點:

  1. 演算法可以不同,比如有的人喜歡nonce每次加1,也可以每次增加其他的值,比如2
  2. data不同,data一般是交易,一般會有一個交易池,每一個人選取不同的交易構成data資料

通過地址逆向計算比特幣私鑰

橢圓曲線演算法

要逆向計算比特幣私鑰,首先得知道什麼是比特幣私鑰,什麼是比特幣的公鑰。說到公鑰私鑰那肯定得涉及到非對稱加密演算法,比如RSA,橢圓曲線演算法等。

比特幣選擇的是橢圓曲線演算法:

y^2=x^3+ax+b
4a^3+27b^2!=0

比特幣選用的是Secp256K1引數:

a=0
b=7
p=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
X: 79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Y: 483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8

其中X\Y是生成點的橫縱座標。

橢圓曲線演算法不是通過座標解方程這麼簡單,而是利用橢圓曲線定義了一個群,橢圓曲線方程的作用是把幾何運算轉換為代數運算。

這裡沒有圖不太好說,橢圓曲線演算法這裡就不詳細的介紹了,可以自己搜尋引擎搜尋相關資料,後面介紹了一本書裡面詳細的介紹了橢圓曲線演算法,有興趣可以看一下。

當然頁可以直接下載《以太坊智慧合約開發實戰》這本書的比特幣章節的pdf檔案,下載連結為: 以太坊智慧合約開發實戰

提取碼為:wb7k

這裡簡單的說一些就是使用CSPRNG生成一個隨機數做為私鑰,私鑰做橢圓曲線乘法運算乘以生成點,轉換為橢圓曲線方程的代數運算,計算得到一個橢圓曲線上的點為公鑰。

私鑰是一個隨機的大整數,而公鑰是一個點

私鑰到地址

要想暴力逆向計算私鑰,當然首先得了解私鑰是怎麼來的。

比特幣的使用可以通過CSPRNG生成,當然也可以自定義,只要範圍在1到n-1之間就可以了,其中n是橢圓曲線引數secp256k1中的n值。有了私鑰privateKey,計算公鑰就非常容易了,使用橢圓曲線乘法:

publicKey(px,py) = privateKey * G(x,y)

其中*表示橢圓曲線乘法,G是橢圓曲線引數secp256k1中指定的生成點G。

私鑰生成

那麼怎樣計算地址呢?這個過程就要稍微複雜一點了,如上圖所示,首先通過CSPRNG生成一個隨機數作為私鑰,然後通過私鑰計算公鑰,我們知道公鑰是一個點,不好計算Hash值,所以需要做一點轉換,怎樣轉換呢,首先拼接上一個04字首,然後拼接公鑰的橫座標,再拼接上公鑰的縱座標得到data。

然後計算data的sha256的值得到R,計算R的ripemd160得到一個extendR,extendR拼接上00字首,然後對extendR進行2次sha256雜湊計算取前4個位元組,然後用extendR拼接上前面計算得到的4個位元組得到data,最後對data做一次base58check編碼就得到了最後的地址了。

公鑰的壓縮

壓縮公鑰是為了減少比特幣交易的位元組數,從而可以節省那些執行區塊鏈資料庫的節點磁碟空間。公鑰是橢圓曲線上的一個點,把點轉換為數字使用的方式是加上04字首,拼接上點的橫座標x,然後再拼接上點的縱座標y,所以公鑰轉換為數字有520位,其中十六進位制字首04佔一個位元組8位,x座標256位,y座標256位。 公鑰是怎樣壓縮的呢?對於比特幣來說,橢圓曲線的引數確定了,也就意味著了橢圓曲線,所以我們可以通過橫座標x就算得到縱座標的值y。我們只需要儲存點x的值就可以了,當需要縱座標y的值的時候做一次計算就可以了。如圖3.8所示,是公鑰的格式獲取流程圖,非壓縮格式加上04字首主要是為了區分公鑰有沒有被壓縮。

公鑰壓縮

判斷縱座標的奇偶性,是因為橢圓曲線的y值計算有開方運算,所以要區分y的正負值,如果y是正數則新增02字首,如果y是負數則新增03字首。奇偶性判斷和正負判斷有什麼關聯呢?我們知道橢圓曲線的運算都是被限定在有限域內的,在p階有限域內,奇偶性和正負性相關。其中p是橢圓曲線引數中指定的,比特幣使用secp256k1的p值為: FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F 2^256 − 2^32 − 2^9 − 2^8 − 2^7 − 2^6 − 2^4 − 1

私鑰的格式

私鑰有多種格式,最常見的就是十六進位制格式,例如: 18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 私鑰還有兩種格式,一種是WIF(Wallet import format),另一種是WIF-compressed。WIF格式和WIF-compressed格式差不多,我們先來看怎樣通過私鑰計算WIF,相比於從私鑰到地址,私鑰轉換為WIF格式就平易近人的多了。

私鑰格式

首先私鑰新增一個十六進位制字首80,然後計算2次sha256作為一箇中間值H,再在data的最後拼接上H的前4個位元組作為校驗碼,得到data,然後對data計算base58check就獲取到了地址的WIF格式了。WIF-compressed格式和WIF格式的計算流程基本一致,只是先為私鑰新增01字尾在計算。例如要把私鑰: 18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 轉換為WIF-compressed格式,首先新增一個01字尾,變為: 18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A20632172501 然後再執行和WIF同樣的流程,獲取到的就是WIF-compressed格式。因為基本和WIF流程一樣,這裡就不給流程圖了。

逆向計算私鑰示例

const bitcoin = require('bitcoinjs-lib')
//找幾個比特幣較多的地址
constrichAddrs=['16ftSEQ4ctQFDtVZiUBusQUjRrGhM3JYwe','16rCmCmbuWDhPjWTrpQGaU3EPdZF7MTdUk','183hmJGRuTEi2YDCWy5iozY8rZtFwVgahM']
const N = 1000
// 使用prototype方式給Array物件新增一個contains函式,用於檢查是否包含指定元素
Array.prototype.contains = function (obj) {
    var index = this.length
    while (index--) {
        if (this[index] === obj) {
            return true
        }
    }
    return false
}
function conclusion(){
    for(let i=0;i<N;i++){
//使用bitcoinjs-lib 函式生成公鑰私鑰
        var keyPair = bitcoin.ECPair.makeRandom()
//通過公鑰計算地址
        var { address } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey })
//檢查地址是否包含在要碰撞的比特幣地址中,如果包含則打印出私鑰
        if(richAddrs.contains(address)){
            console.log(address)
console.log(keyPair.privateKey.toString('hex'))
        }
    }
    console.log("conclusion " + N + " times done!")
}
conclusion()

書籍推薦

這裡推薦一本書《以太坊智慧合約開發實戰》,對區塊鏈、智慧合約感興趣的同學可以看一下,書中有很多原理性的知識還是值得一看的。

以太坊智慧合約開發實戰

書中介紹了很多東西比如P2P網路、橢圓曲線演算法、智慧合約等,瞭解P2P網路就能理解區塊鏈的資料傳遞,瞭解橢圓曲線演算法就能理解為什麼計算私鑰基本是不可能的。

當然也包括很多智慧合約的知識,智慧合約是比特幣中沒有的,以太坊基本是參考比特幣實現的,但是最大的進步就體現在智慧合約上,瞭解智慧合約就會發現區塊鏈真的會有很多有前景的使用場景,只需要等待技術更加成熟了就