比特幣、區塊鏈基礎技術簡介(含C#原始碼)(一) —— 橢圓曲線密碼學之secp256k1的C#簡易實現
轉載請在文章首尾註明原始出處CSDN
前言
原本這個文章的標題是《比特幣、區塊鏈基礎技術學習筆記》,想做個學習筆記,供有相同興趣的同學參考;但發到朋友圈後部分朋友反饋說看不懂,於是把這個筆記補充了簡介這一章節內容(其餘章節也有少量調整或改動),再把標題改成了《比特幣、區塊鏈基礎技術簡介》
簡介
文章的副標題是《橢圓曲線密碼學之secp256k1的C#簡易實現》,目的在於基於C#語言在兩百行左右的程式碼簡易實現了橢圓曲線密碼學secp256k1的根據私鑰計算公鑰的演算法。
那麼從《筆記》改為《簡介》之後,首先就要回答以下幾個問題:什麼是橢圓曲線密碼學?有什麼用?在比特幣、區塊鏈中又發揮了什麼作用?
橢圓曲線密碼學,是非對稱加密技術的一種。那麼什麼又是非對稱加密技術呢?又有什麼用呢?
簡單來說,非對稱加密技術基本上是用來解決類似於“證明我媽是我媽”這一類問題的。在比特幣系統中,就用來“證明我的比特幣就是我的比特幣(我才能花出去)”;而比特幣及大部分割槽塊鏈系統,都採用了橢圓曲線密碼學,並且通常用的是secp256k1。
順帶說一句,大家平常用的網銀的UKey,其中也用到了非對稱加密技術,作用也就是為了在銀行系統“證明我就是我”。
通常非對稱加密技術會給不同的人生成不同的私鑰,再根據私鑰生成公鑰;公鑰會公佈給大眾,而私鑰則需要妥善保管。
那麼非對稱加密技術怎麼來證明我的比特幣就是我的比特幣呢?其中的道理在於:用私鑰加密的資料,用公鑰才能解開;換句話說,用公鑰能解讀出正確的資料,就能證明你才是私鑰的擁有者,那麼你就是這些比特幣(或者銀行賬戶)的擁有者,你就能順理成章地支配這些比特幣。
目前常用的非對稱加密技術有RSA和橢圓曲線密碼學(ECC)。RSA目前普遍用於銀行、證書等系統;而比特幣、區塊鏈系統則基本採用了橢圓曲線密碼學。
在比特幣系統中,所有賬本是公開的;即使沒有私鑰,你也能清楚的看到,每個比特幣從哪裡來,到了哪裡去;比特幣錢包就是你的私鑰合集,如果不幸遺失了私鑰或錢包,那麼私鑰對應的比特幣將可能會被私鑰的拾獲者進行支配;如果確定遺失並沒有其他人拾獲,那麼那些比特幣,還是你的,只是你再也無法支付出去了(花錢需要私鑰)。
簡單地說,在比特幣系統中:收錢只需要公鑰,花錢需要私鑰;而橢圓曲線密碼學,則是這一切的基礎。
橢圓曲線密碼學之secp256k1的C#簡易實現
本文的內容偏向於C#
的簡易實現(兩百行程式碼左右),所以關於橢圓曲線密碼學的介紹只止步於能實現即可,並不完整。同時限於該知識是從搜尋引擎中來,加之個人理解能力有限,所以可能有錯漏之處,希望懂的朋友能留言指正。
橢圓曲線密碼學
對於橢圓曲線,我們用到的方程式是:
橢圓曲線密碼學,取某個大質數
當給定整數
取點集
定義加法
其中,
定義
根據上述定義的加法,
對於給定
secp256k1
在上述理論中取以下值即可:
單位點
(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8)
則
應用
在 1 ~
橢圓曲線密碼學在數學上,有這樣的特性:從
其中
C#簡易實現
根據私鑰來計算公鑰,實際上就是實現點G的標量乘法,從演算法到程式碼,這裡有兩個難點:一、需要一個大整數類:這一點還好,通過搜尋引擎,找到了C#自身已有的BigInteger類的完美實現,只需要引用 System.Numerics.dll就好了;二、計算公式中的斜率
上簡易程式碼(兩個檔案共兩百行左右):
BitcoinTools.cs
using System.Text;
using System.Globalization;
using System.Numerics;
public class BitcoinTools
{
public class ECPoint
{
public BigInteger x;
public BigInteger y;
}
public static readonly BigInteger P = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", NumberStyles.HexNumber);
public static readonly BigInteger N = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", NumberStyles.HexNumber);
public static readonly ECPoint G = new ECPoint()
{
x = BigInteger.Parse("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", NumberStyles.HexNumber),
y = BigInteger.Parse("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", NumberStyles.HexNumber)
};
/// <summary>
/// N - 1
/// </summary>
private static readonly BigInteger N_1 = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", NumberStyles.HexNumber);
/// <summary>
/// P - 2
/// </summary>
private static readonly BigInteger P_2 = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D", NumberStyles.HexNumber);
private static readonly int MaxBit = 256;
private static readonly ECPoint[] Power_G = new ECPoint[MaxBit];
/// <summary>
/// 為了提高運算效率,將G與2的n次方的積進行快取
/// </summary>
static BitcoinTools()
{
for (int i = 0; i < MaxBit; i++)
{
if (i == 0)
{
Power_G[0] = G;
}
else
{
Power_G[i] = Addition(Power_G[i - 1], Power_G[i - 1]);
}
}
}
/// <summary>
/// 判斷某點是否在橢圓曲線 secp256k1 上(mod P)
/// </summary>
public static bool IsOnCurve(ECPoint point)
{
BigInteger leftSide = BigInteger.Pow(point.y, 2) % P;
BigInteger rightSide = (BigInteger.Pow(point.x, 3) + 7) % P;
return leftSide == rightSide;
}
/// <summary>
/// 取得x的倒數(mod P)
/// 根據費爾瑪小定理
/// </summary>
private static BigInteger GetReciprocalModP(BigInteger x)
{
BigInteger[] array = new BigInteger[MaxBit];
BigInteger ret = 1;
BigInteger temp = P_2;
for (int i = 0; i < MaxBit; i++)
{
if (i == 0)
{
array[0] = x;
}
else
{
array[i] = BigInteger.Pow(array[i - 1], 2) % P;
}
if (!temp.IsEven)
{
ret *= array[i];
ret %= P;
}
temp >>= 1;
if (temp.IsZero)
break;
}
return ret;
}
/// <summary>
/// 兩個點相加
/// </summary>
public static ECPoint Addition(ECPoint a, ECPoint b)
{
if (a == null)
return b;
if (b == null)
return a;
BigInteger k;
if (a.x == b.x)
{
if ((a.y + b.y) % P == 0)
{
return null;
}
k = 3 * BigInteger.Pow(a.x, 2);
k *= GetReciprocalModP(2 * a.y);
k %= P;
}
else
{
k = (b.y + P - a.y) % P;
k *= GetReciprocalModP((b.x + P - a.x) % P);
k %= P;
}
ECPoint ret = new ECPoint();
ret.x = (k * k + P - a.x + P - b.x) % P;
ret.y = (k * (P + a.x - ret.x) + P - a.y) % P;
return ret;
}
/// <summary>
/// 對點G的標量乘法
/// </summary>
public static ECPoint Multiplication(BigInteger pri)
{
ECPoint ret = null;
for (int i = 0; i < MaxBit; i++)
{
if (!pri.IsEven)
{
if (ret == null)
{
ret = Power_G[i];
}
else
{
ret = Addition(ret, Power_G[i]);
}
}
pri >>= 1;
if (pri.IsZero)
break;
}
return ret;
}
/// <summary>
/// 將位元組陣列輸出為字串
/// </summary>
public static string BytesToString(byte[] bytes)
{
StringBuilder builder = new StringBuilder();
for (int i = bytes.Length - 1; i >= 0; i--)
{
builder.Append(bytes[i].ToString("X2"));
}
return builder.ToString();
}
}
Program.cs
using System;
using System.Numerics;
class Program
{
static void Main(string[] args)
{
for (int k = 1; k <= 20; k++)
{
var publicKey = BitcoinTools.Multiplication(k);
Console.WriteLine(" k = {0}", k);
Console.WriteLine(" x = {0}", BitcoinTools.BytesToString(publicKey.x.ToByteArray()));
Console.WriteLine(" y = {0}", BitcoinTools.BytesToString(publicKey.y.ToByteArray()));
Console.WriteLine();
}
string[] kArray =
{ "112233445566778899"
, "112233445566778899112233445566778899"
, "28948022309329048855892746252171976963209391069768726095651290785379540373584"
, "57896044618658097711785492504343953926418782139537452191302581570759080747168"
, "86844066927987146567678238756515930889628173209306178286953872356138621120752"
};
foreach (string k in kArray)
{
var publicKey = BitcoinTools.Multiplication(BigInteger.Parse(k));
Console.WriteLine(" k = {0}", k);
Console.WriteLine(" x = {0}", BitcoinTools.BytesToString(publicKey.x.ToByteArray()));
Console.WriteLine(" y = {0}", BitcoinTools.BytesToString(publicKey.y.ToByteArray()));
Console.WriteLine();
}
BigInteger kStart = BigInteger.Parse("115792089237316195423570985008687907852837564279074904382605163141518161494317");
BigInteger kEnd = BigInteger.Parse("115792089237316195423570985008687907852837564279074904382605163141518161494336");
for (BigInteger k = kStart; k <= kEnd; k++)
{
var publicKey = BitcoinTools.Multiplication(k);
Console.WriteLine(" k = {0}", k);
Console.WriteLine(" x = {0}", BitcoinTools.BytesToString(publicKey.x.ToByteArray()));
Console.WriteLine(" y = {0}", BitcoinTools.BytesToString(publicKey.y.ToByteArray()));
Console.WriteLine();
}
}
}
執行結果
執行結果和這個網址的內容能匹配上:
https://crypto.stackexchange.com/questions/784/are-there-any-secp256k1-ecdsa-test-examples-available
暫未發現Bug。結果的略有不同之處是在部分大整數(最高bit為1的大整數)之前額外多了兩個十六進位制的0。是因為C#實現的大整數,如果最高位(bit)是1的話,則表示是負數;因此正數且最高bit為1的話,會在之前再補充一個位元組0。
後記及下節內容預告
橢圓曲線密碼學,相信是部分初入門朋友的難點;我也不例外,為這點內容通過搜尋引擎搜了n久,然後寫了這些程式碼,整理成文,為的是讓別的初學者能快速、初步瞭解相關的技術。下次的內容是《比特幣私鑰、公鑰及錢包地址的C#簡易實現》
第一次在網路上發表文章,排版及錯漏之處在所難免,大家請多多見諒。
轉載請在文章首尾註明原始出處CSDN。
相關文章
- 區塊鏈基礎之密碼學及安全技術區塊鏈密碼學
- 區塊鏈的密碼學基礎區塊鏈密碼學
- 區塊鏈-技術簡介區塊鏈
- 區塊鏈技術簡介區塊鏈
- 兄弟連區塊鏈教程區塊鏈資訊保安3橢圓曲線加解密及簽名演算法的技術原理二區塊鏈解密演算法
- 兄弟連區塊鏈教程Fabric1.0原始碼分析ECDSA橢圓曲線數字簽名演算法區塊鏈原始碼演算法
- 密碼學中的RSA演算法與橢圓曲線演算法密碼學演算法
- 一個簡單的區塊鏈程式碼實現區塊鏈
- NodeJS實現簡易區塊鏈NodeJS區塊鏈
- 區塊鏈背後的資訊保安(3)橢圓曲線加解密及簽名演算法的技術原理及其Go語言實現區塊鏈解密演算法Go
- Vue原始碼分析之實現一個簡易版的VueVue原始碼
- 用Java程式碼實現區塊鏈技術Java區塊鏈
- 區塊鏈簡介區塊鏈
- 51行程式碼實現簡單的PHP區塊鏈行程PHP區塊鏈
- 【區塊鏈技術實現】區塊鏈
- 關於區塊鏈密碼學區塊鏈密碼學
- 學習下區塊鏈技術基礎知識區塊鏈
- 200 行程式碼實現一個簡單的區塊鏈應用行程區塊鏈
- 橢圓曲線加法原理計算
- C#實現聯通簡訊Sgip協議程式原始碼C#協議原始碼
- c#畫圖(橢圓和弧線)Craphics類C#
- Fabric 1.0原始碼分析(46)ECDSA(橢圓曲線數字簽名演算法)原始碼演算法
- Unity引擎與C#指令碼簡介UnityC#指令碼
- 區塊鏈技術實戰學習路線圖區塊鏈
- 用java實現一個簡單的區塊鏈Java區塊鏈
- c#實現簡單的俄羅斯方塊C#
- 轉貼:Ivanopulo說CloneCD用的是橢圓曲線密碼演算法 (1千字)密碼演算法
- 300行ABAP程式碼實現一個最簡單的區塊鏈原型區塊鏈原型
- 區塊鏈鼻祖比特幣之13:比特幣原始碼編譯詳解區塊鏈比特幣原始碼編譯
- 區塊鏈技術工坊 - 線下區塊鏈技術分享區塊鏈
- C#基礎程式設計——簡介及基礎語法C#程式設計
- 雲+區塊鏈 實現區塊鏈技術的普惠應用區塊鏈
- c#簡易 logC#
- c#簡易logC#
- 比特幣和區塊鏈(2):比特幣中區塊鏈的實現比特幣區塊鏈
- C#之簡易猜數字遊戲C#遊戲
- C# 簡介C#
- C# 實現記住密碼功能C#密碼