基础名词
Neo是个区块链工程,地址,公钥,私钥,地址脚本,base58,sha256,ripemd160,ECCsa,secp256k1,secp25r1这些词都是区块链技术相关的,或是新东西或者有应用到
通过学习后,记录下自己的理解,如果是新接触的话,需要找基础的区块链入门资料学习.
参考资料https://www.cnblogs.com/crazylights/p/8166690.html
ECCurve类文档https://docs.microsoft.com/zh-cn/dotnet/api/system.security.cryptography.eccurve?view=netcore-2.2
Neo区块链
一个区块链开源项目,git:https://github.com/neo-project 区块链的应用之一是数字加密货币.
私钥
私钥是一个大整数,换成二进制是一个256位的数.私钥类似于银行账户,失去私钥等于失去了资产.私钥非常重要!
例:用十六进制表示的私钥 0d005d28cf3d62ec461770746ba42d4a30a638c73c6fbfa90fb3359dc7f72e6b
这个数是有限大的,取值范围由位数和椭圆曲线有关.取256位的话,肯定不会大于2^256-1.也和使用的椭圆曲线有关,但没研究.
公钥
公钥通过私钥计算而来,NEO的计算办法叫ECCsa,使用的是secp256r1曲线,固定的起始G点,经过私钥次数的相加而得到.简单公式描述为 公钥=私钥*G
例: 022b03a16c8d16226618a7327226b4a073daef9ae259d7d884afce47353b51ee29
ecc加密算法有关的知识很多,如果要搞清楚,不太容易.尤其在数学基础很弱的情况下更困难.此次学习也没有懂,了解一些词如下:
私钥到公钥算法相关知识
算法基于被证明过的数学理论,从一条在射影平面上的ecc椭圆曲线上的点开始,不停的做加法,做够n次时,得到的点的坐标是公钥.这个点位于曲线上,n次值就是私钥
射影平面
射影平面上,两条平行线相交于无穷远点.与中学时学的平面几何(欧氏几何)不同,这个平面是另一门几何,它们基于的公理不完全一样.例如,平行线不相交这点就不同
射影平面上,所有直线都有一个无穷远点,任何两条直线都有1个交点.如果两行直线平行,那么它们的交点是无穷远点.如果不平行,则它们有各自的无穷远点
一个射影平面上有且只有一条无穷远直线,平面上所有的无穷远点都在此条直线上.
射影平面是一个有限平面,并且是单面的,从平面上一点出发,可以走到平面上任何一点,不存在平面的另一面.这点,可以参考默比乌斯带特点
或者想象球面,不同于长方体有6个面,要走完6个面,必须跨过面与面之间的边界,球只有一个面,不存在从一面到另一面要跨过边界的情况.
ecc曲线加法
ecc椭圆曲线位于射影平面上,有一些数学计算规则.这个规则就是ecc加密用到的算法之一.对比下图,算法大至逻辑如下:
加法定义:曲线上一点与另一点之和,等于这两点所在直线与曲线的交点的X轴对称点.如果这个点是同一点,那么和等于该点所在切线与曲线交点的X轴对称点.
例如图上G点,它的和就是2G,2G正是G点切线与曲线交点G的X轴对称点.继续做加法,从2G点开始得到4G...
做N次加法之后,得到一个点Q,这个点就是公钥,而N就是私钥.
算法特点
采用这种算法计算有个特点,G是固定的,私钥(N)是个随机大整数,公钥(Q)是一个坐标(X,Y).那么,给定N时,通过将G相加N次,就得到了公钥Q.
那么反过来,给定Q时,却很难计算出N,也就是说,从G点开始,不知道是相加了多少次而得到Q的.那么,有私钥好计算公钥,而反过来则不行.
如果将ecc曲线加法换成简单的加法,也能得出相似的结论.例如2+2+2=6,一眼看出,是2相加3次得到的.那么公钥是6,私钥是3,基点是2
那么,2+2+.?.+2=2744584774030027284458040387221349021599708342405734667783110002634902555648 这个人工算要很久,要靠计算机算.
如果以现有计算能力,破解一个密码要很久,比如几百年.那么这个加密手段肯定是有效的,因为人已经死了几个世纪了,你却还不能破解密码.
地址脚本
脚本是一段程序代码,地址脚本也是程序代码.由公钥计算得到地址脚本.
例如这个地址脚本: cc8e9da86d4c81663d52b5baf9d1c2a2d935013f 计算方法如下
公钥的形式
上文讲述曲线时提到,公钥是一个坐标,是一个椭圆曲线上的点,它有X,Y两个值.十六进制表示格式如:04XY,看起来有些误解.它是04开头接着X坐标和Y坐标的十六进制值
X,Y都是一个32字节的数,那么整个O4XY一共是65个字节.这种表示形式是非压缩的.压缩的形式如下
022b03a16c8d16226618a7327226b4a073daef9ae259d7d884afce47353b51ee29 这是个33字节的数据,开始的02表示Y为偶数,如果Y是奇数,那么是03开头.后面是X值
选用压缩形式表示公钥,因为短.由于椭圆曲线的特点,只需要知道X坐标和Y坐标的奇偶性,就能推算出Y坐标.所以能这样干.至于这特点,并未深入研究,所以没懂.
压缩公钥算出地址脚本
在公钥前面加0x21,后面加oxac,然后做一次sha256,再做一次ripemd160,最后得到20字节的数据,就是地址脚本
21022b03a16c8d16226618a7327226b4a073daef9ae259d7d884afce47353b51ee29ac 0x21=33,而压缩公钥的长度是33字节.
地址
地址由地址脚本计算得到,计算办法如下:
将地址脚本,前面加17变成21个字节,然后将其做两次sha256,取得到的数据前4字节,加到这21个字节后面 简单的讲,地址是地址脚本加了摘要,可以验证.
17cc8e9da86d4c81663d52b5baf9d1c2a2d935013f1234abcd 类似这样,然后用base58编码成如下形式
AXdWU5vYe3Ja9n778RpgJrrCUjAsfQgT1r
区块链的数字账户,通过此地址发生交易.类似于银行账号,转账时需要双方的银行卡号.有了对方地址,就能向对方转加密货币.
小结
转换过程: 私钥 ==> 公钥 ==> 地址脚本 ==> 地址
账户相关这些都源于私钥.WIF是一种私钥形式,由私钥到WIF的计算如下:
私钥前面加0x80后面加0x01,一共34字节,将这34字节做两次SHA256,取结果前4字节,放到34个字节后面,变成38字节.然后base58编码得到WIF.和地址类似,能验证
080d005d28cf3d62ec461770746ba42d4a30a638c73c6fbfa90fb3359dc7f72e6b011234abcd
写代码时,需要用到的一些库:
摘要相关: System.Security.Cryptography命名空间下, SHA256 , RIPEMD160(这个在.netcore上还没有,framework上有)
ecc曲线相关: System.Security.Cryptography命名空间下 ECCurve ECDsa ECParameters
大整数: System.Numerics命名空间下 BigInteger
base58,这个没有库,但是比较简单,可以自己实现.算法类同于将2进制转为16进制.58编码就是将2进制转为58进制
其它
Neo ECC曲线参数
NeoSecp256r1 = new ECCurve { // 选择曲线 y^2 = x^ + ax + b CurveType = ECCurve.ECCurveType.PrimeShortWeierstrass, A = hex2ByteArray("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"), B = hex2ByteArray("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"), // 所谓"阶",没搞明白 Prime = hex2ByteArray("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"), // 私钥必须小于n n对应是Order这个属性 Order = hex2ByteArray("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"), Cofactor = hex2ByteArray("01"), // 加法起点G G=new ECPoint { X=hex2ByteArray("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"), Y=hex2ByteArray("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5") } }
ECCurve
用c#的这个类,可以得到一对私钥和公钥.如下
// secp2561曲线,相关参数是neo特定的 var secp256r1 = NeoSecp256r1; ECDsa sa = ECDsa.Create(); // 随机生成密钥对,私钥和公钥 sa.GenerateKey(secp256r1); // 导出的对象里包含私钥和公钥 ECParameters ecpE = sa.ExportParameters(true); // 如果不异常,则生成成功 ecpE.Validate(); // 私钥 ecpE.D // 公钥 ecpE.Q
在这个类中,没有找到给定私钥求公钥的方法.NEO源码中有ECC加密的代码,有这样的方法,G*私钥=公钥,可以实现.