根据私钥来计算公钥,实际上就是实现点G的标量乘法,从算法到代码,这里有两个难点:一、需要一个大整数类:这一点还好,通过搜索引擎,找到了C#自身已有的BigInteger类的完美实现,只需要引用 System.Numerics.dll就好了;二、计算公式中的斜率 k 值,用到了费尔玛小定理来实现,不太了解的同学可自行通过搜索引擎进行学习。
上简易代码(两个文件共两百行左右):
BitcoinTools.cs
using System.Text;
using System.Globalization;
using System.Numerics;
publicclass BitcoinTools
{
publicclass ECPoint
{
public BigInteger x;
public BigInteger y;
}
publicstaticreadonly BigInteger P = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", NumberStyles.HexNumber);
publicstaticreadonly BigInteger N = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", NumberStyles.HexNumber);
publicstaticreadonly ECPoint G = new ECPoint()
{
x = BigInteger.Parse("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", NumberStyles.HexNumber),
y = BigInteger.Parse("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", NumberStyles.HexNumber)
};
///<summary>/// N - 1 ///</summary>privatestaticreadonly BigInteger N_1 = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", NumberStyles.HexNumber);
///<summary>/// P - 2///</summary>privatestaticreadonly BigInteger P_2 = BigInteger.Parse("0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2D", NumberStyles.HexNumber);
privatestaticreadonlyint MaxBit = 256;
privatestaticreadonly 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>publicstaticboolIsOnCurve(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>privatestatic 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>publicstatic 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)
{
returnnull;
}
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>publicstatic 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>publicstaticstringBytesToString(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();
}
}
}