比特幣、區塊鏈基礎技術簡介(含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。
相關文章
- 區塊鏈基礎之密碼學及安全技術區塊鏈密碼學
- 區塊鏈的密碼學基礎區塊鏈密碼學
- 區塊鏈-技術簡介區塊鏈
- 區塊鏈鼻祖比特幣之13:比特幣原始碼編譯詳解區塊鏈比特幣原始碼編譯
- 比特幣和區塊鏈(2):比特幣中區塊鏈的實現比特幣區塊鏈
- NodeJS實現簡易區塊鏈NodeJS區塊鏈
- 一個簡單的區塊鏈程式碼實現區塊鏈
- 密碼學中的RSA演算法與橢圓曲線演算法密碼學演算法
- 兄弟連區塊鏈教程Fabric1.0原始碼分析ECDSA橢圓曲線數字簽名演算法區塊鏈原始碼演算法
- 橢圓曲線公鑰密碼演算法原理入門密碼演算法
- 兄弟連區塊鏈教程區塊鏈資訊保安3橢圓曲線加解密及簽名演算法的技術原理二區塊鏈解密演算法
- 一個簡單的區塊鏈貨幣,python實現區塊鏈Python
- 區塊鏈鼻祖比特幣之6:詳解比特幣的密碼攻擊與分散式雙花攻擊區塊鏈比特幣密碼分散式
- HarmonyOS Next 橢圓曲線密碼學應用:ECC 與 SM2 深入剖析密碼學
- 2018最新區塊鏈技術,從入門到精通視訊教程(比特幣基礎技術)區塊鏈比特幣
- 區塊鏈鼻祖比特幣之7:區塊鏈在比特幣中的真正用意區塊鏈比特幣
- 區塊鏈背後的資訊保安(3)橢圓曲線加解密及簽名演算法的技術原理及其Go語言實現區塊鏈解密演算法Go
- 用Java程式碼實現區塊鏈技術Java區塊鏈
- Vue原始碼分析之實現一個簡易版的VueVue原始碼
- 區塊鏈簡介區塊鏈
- 比特幣學習筆記——————1、比特幣簡介比特幣筆記
- 區塊鏈鼻祖比特幣之11:比特幣困難度區塊鏈比特幣
- 區塊鏈及虛擬幣的發展簡介區塊鏈
- Unity引擎與C#指令碼簡介UnityC#指令碼
- 2018年區塊鏈與加密貨幣技術比特幣以太坊全套零基礎視訊教程區塊鏈加密比特幣
- 比特幣中的密碼學原理比特幣密碼學
- 關於區塊鏈密碼學區塊鏈密碼學
- 300行ABAP程式碼實現一個最簡單的區塊鏈原型區塊鏈原型
- C#基礎程式設計——簡介及基礎語法C#程式設計
- 區塊鏈鼻祖比特幣之1:比特幣的內涵與價值區塊鏈比特幣
- 學習下區塊鏈技術基礎知識區塊鏈
- C# 簡介C#
- C# 實現記住密碼功能C#密碼
- Fabric 1.0原始碼分析(46)ECDSA(橢圓曲線數字簽名演算法)原始碼演算法
- 用java實現一個簡單的區塊鏈Java區塊鏈
- c#實現簡單的俄羅斯方塊C#
- Django作者闡述比特幣和區塊鏈的主要技術主張Django比特幣區塊鏈
- 比特幣學習筆記——————7、區塊鏈比特幣筆記區塊鏈