相关文章:
加密算法
对称加密
概念:即加密,解密的密钥相同。
优点:是加解密效率高(速度快,空间占用小),加密强度高。
缺点:是参与多方都需要持有密钥,一旦有人泄露则安全性被破坏;另外如何在不安全通道下分发密钥也是个问题。
代表算法:包括DES、3DES、AES、IDEA等。
非对称加密
概念:加密密钥和解密密钥是不同的,分别称为公钥和私钥。公钥一般是公开的,人人可获取的,私钥一般是个人自己持有,不能被他人获取。
优点:是公私钥分开,不安全通道也可以使用。
缺点:是加解密速度慢,一般比对称加解密算法慢2到3个数量级;同时加密强度相比对称加密要差。
非对称加密算法的安全性往往需要基于数学问题来保障,目前主要有基于大数质因子分解、离散对数、椭圆曲线等几种思路。
代表算法:RSA、ElGamal、椭圆曲线(Elliptic Curve Crytosystems,ECC)系列算法。
混合机密机制
既先用计算复杂度高的非对称机密协商一个临时的加密密钥(会话密钥,一般相对内容来说要短得多),然后双方再通过对称加密对传递的大量数据进行加解密处理。
典型的场景是现在大家常用的HTTPS机制。
即:先用较短的非对称加密
确认权限,再用对称加密
对传递的大量数据进行加解密。
区块链椭圆曲线加密(Elliptic Curve Crytosystems,ECC)简介
导入
假设这个世界上所有的人都会乘法,却没有人会除法。
有一天张三挑出了两个数字,123,456。
由于张三会乘法,于是乎张三计算出了:
123 * 456 = 56088
于是张三告诉你:
123 * ??? =56088
你是个天资卓绝的人,但是没办法,上天不允许你会除法,因此你没法知道张三说的???是什么。
当然了,别人也不知道,因为张三没有告诉其他人???是啥。
有一天,你打算告诉张三一个秘密,67。但是你又不想别人知道。于是聪明绝顶的你随手选了一个数字222。
计算出:
123 * 222 = 27306
56088 * 222 + 67 = 12451603
然后你对张三说:
123 * ??? = 27306
56088 * ??? + x = 12451603
当然,你聪明绝顶,张三聪明秃顶,于是张三一寻思:
123 * ??? * 456 = 56088 * ???
这上下一减,
x = 12451603 - 27306 * 456 = 67
哎妈呀,这x就这么被传递过来了。如果我们把上面的过程写成数学公式的话,
G * k = K (G,K公开,k保密)
c1 = G * Mc2 = K * M + x (M随机选取,x为要加密的数字,M和x都保密)
x = c2 - c1 * k
= K * M + x - G * M * k = G * M * k + x - G * M * k (消除左右两侧的G * M * k)
= x
总结:
所以我们想要实现加密容易,校验容易,但解密很难,
其实就是要找到一组运算加,减,乘很简单,除却很难,这个事情并不容易。
因为加,减,乘就可以实现加密和校验,但除我们希望他很难,难到几乎无法得到解。
而这个符合这个条件的运算之一,就是:椭圆曲线方程
,其加减乘除是指域
上的加减乘除,域的概念是从我们的有理数,实数的运算中抽象出来的,严格的定义请参考近世代数方面的数。简单的说,域中的元素同有理数一样,有自己得加法、乘法、除法、单位元(1),零元(0),并满足交换率、分配率。
椭圆曲线方程
包括ECC、ECDH或者ECDSA。ECC是Elliptic Curve Cryptography的缩写,就是椭圆加密算法,ECDH和ECDSA是ECC的不同实现。
椭圆加密算法的应用范围很广,主要的三个技术 TLS、PGP以及SSH都在使用它,更别提比特币以及其他加密数字货币了。
椭圆曲线方程:(可以暂时简单的理解为描述了特定点的集合的公式)
y² = x³ + ax + b
椭圆曲线几何学上的加法
任意取椭圆曲线上两点P、Q(若P、Q两点重合,则作P点的切线),作直线交于椭圆曲线的另一点R',过R'做y轴的平行线交于R,定义P+Q=R。这样,加法的和也在椭圆曲线上,并同样具备加法的交换律、结合律。
椭圆曲线上的加密/解密
公开密钥算法总是要基于一个数学上的难题。比如RSA 依据的是:给定两个素数p、q 很容易相乘得到n,而对n进行因式分解却相对困难。那椭圆曲线上有什么难题呢?
考虑如下等式:
K=kG [其中 K,G为Ep(a,b)上的点,k为小于n(n是点G的阶)的整数]
不难发现,给定k和G,根据加法法则,计算K很容易;但给定K和G,求k就相对困难了。
这就是椭圆曲线加密算法采用的难题。
我们把点G称为基点(base point),
k(k<n,n为基点g的阶)称为私有密钥(privte key),
k称为公开密钥(public="" key)。<="" p="">
现在我们描述一个利用椭圆曲线进行加密通信的过程:
1、用户A选定一条椭圆曲线Ep(a,b),并取椭圆曲线上一点,作为基点G。
2、用户A选择一个私有密钥k,并生成公开密钥K=kG。
3、用户A将Ep(a,b)和点K,G传给用户B。
4、用户B接到信息后 ,将待传输的明文编码到Ep(a,b)上一点M(编码方法很多,这里不作讨论),并产生一个随机整数r(r<n)。
5、用户B计算点C1=M+rK;C2=rG。
6、用户B将C1、C2传给用户A。
7、用户A接到信息后,计算C1-kC2,结果就是点M。
因为C1-kC2=M+rK-k(rG)=M+rK-r(kG)=M再对点M进行解码就可以得到明文。
在这个加密通信中,如果有一个偷窥者H ,他只能看到Ep(a,b)、K、G、C1、C2 而通过K、G 求k 或通过C2、G求r 都是相对困难的。因此,H无法得到A、B间传送的明文信息。
总结:
设私钥、公钥分别为k、K,即K = kG,其中G为G点。
公钥加密:
选择随机数r,将消息M生成密文C,该密文是一个点对,即:
C = {rG, M+rK},其中K为公钥
私钥解密:
M + rK - k(rG) = M + r(kG) - k(rG) = M
其中k、K分别为私钥、公钥。
椭圆曲线签名与验证签名
椭圆曲线签名算法,即ECDSA。
设私钥、公钥分别为k、K,即K = kG,其中G为G点。
私钥签名:
1、选择随机数r,计算点rG(x, y)。
2、根据随机数r、消息M的哈希h、私钥k,计算s = (h + kx)/r。
3、将消息M、和签名{rG, s}发给接收方。
公钥验证签名:
1、接收方收到消息M、以及签名{rG=(x,y), s}。
2、根据消息求哈希h。
3、使用发送方公钥K计算:hG/s + xK/s,并与rG比较,如相等即验签成功。
原理如下:
hG/s + xK/s = hG/s + x(kG)/s = (h+xk)G/s = r(h+xk)G / (h+kx) = rG
比特币私钥、公钥、钱包地址的来历和关系
比特币系统选用的secp256k1
比特币钱包地址示例:1QCXRuoxWo5Bya9NxHaVBArBQYhatHJrU7
那这个钱包地址是如何生成的?
从比特币私钥得到我们日常转账所用的比特币钱包地址总共需要9个步骤,中间用到了SHA256加密、RIPEMD160加密和BASE58编码。
1. 生成随机私钥
私钥是一个随机数,随机选取一个32字节的数,这个数的范围大小是介于1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141之间的一个数,为了方便后面的计算,我们随机生成一个合法的私钥:
8F72F6B29E6E225A36B68DFE333C7CE5E55D83249D3D2CD6332671FA445C4DD3
2. 椭圆曲线算公钥
生成了私钥之后,我们使用椭圆曲线加密算法(ECDSA-secp256k1)计算私钥所对应的非压缩公钥,生成的公钥共65字节, 其中一个字节是0x04,其中32个字节是X坐标,另外32个字节是Y坐标:
公钥P.X:06CCAE7536386DA2C5ADD428B099C7658814CA837F94FADE365D0EC6B1519385
公钥P.Y:FF83EC5F2C0C8F016A32134589F7B9E97ACBFEFD2EF12A91FA622B38A1449EEB
3.计算公钥的SHA-256哈希值
将上述公钥地址拼合,得到标准地址:
0406CCAE7536386DA2C5ADD428B099C7658814CA837F94FADE365D0EC6B1519385FF83EC5F2C0C8F016A32134589F7B9E97ACBFEFD2EF12A91FA622B38A1449EEB
对齐进行SHA-256哈希计算,得到结果:
2572e5f4a8e77ddf5bb35b9e61c61f66455a4a24bcfd6cb190a8e8ff48fc097d
4.计算 RIPEMD-160哈希值
取上一步结果,进行RIPEMD-160计算,得到结果:
0b14f003d63ab31aef5fedde2b504699547dd1f6
5.加入地址版本号(比特币主网版本号“0x00”)
取上一步结果,在前面加上16进制的00,即:
000b14f003d63ab31aef5fedde2b504699547dd1f6
6.计算 SHA-256 哈希值
取上一步结果,进行SHA-256计算,可得:
ddc2270f93cc84cc6869dd373f3c340bbf5cb9a8f5559297cc9e5d947aab2536
然后,对以上结果再次计算 SHA-256 哈希值,得到:
869ac57b83ccf75ca9da8895823562fffb611e3c297d9c2d4612aeeb32850078
7.取上一步结果的前4个字节(8位十六进制)
869ac57b
8.把这4个字节加在步骤5的结果后面
作为校验位,将这4个字节加载第五步的结果后面,这就是比特币地址的16进制形态了:
869ac57b000b14f003d63ab31aef5fedde2b504699547dd1f6
9.用Base58编码变换一下地址
对上一步的结果进行Base58编码,得到:
1QCXRuoxWo5Bya9NxHaVBArBQYhatHJrU7
这就是我们经常看到的传统意义上的比特币钱包地址了。
Delos区块链使用的椭圆加密
delos区块链上使用了两种不同的椭圆加密实现,都是go语言编写
ed25519 gitlab.zhonganonline.com/ann/ann-module/lib/ed25519
secp256k1 github.com/btcsuite/btcd/btcec
delos链生成私钥
privkey crypto.PrivKeyEd25519
delos/api项目中调用的是以太坊
的delos/eth/crypto的ECDSA椭圆加密方法生成私钥和地址
生成account私钥和地址
result.Privkey = ethcmn.Bytes2Hex(crypto.FromECDSA(privkey))
result.Address = ethcmn.Bytes2Hex(crypto.FromECDSAPubCompressed(&privkey.PublicKey))
Breadwallet-android使用的椭圆加密
Breadwallet-android使用的是secp256k1,都是c语言编写
在app/src/main/secp/secp256k1下
生成私钥
app\src\main\jni\breadwallet-core\Java\root\com\breadwallet\core\BRCoreKey.java
private static native long createJniCoreKey ();
调用的是 jni c库
/*
* Class: com_breadwallet_core_BRCoreKey
* Method: createJniCoreKey
* Signature: ()J
*/
JNIEXPORT jlong JNICALL
Java_com_breadwallet_core_BRCoreKey_createJniCoreKey
(JNIEnv *env, jclass thisClass) {
BRKey *key = (BRKey *) calloc (1, sizeof(BRKey));
return (jlong) key;
}
生成公钥
可用助记词或序列
src\main\jni\breadwallet-core\Java\root\com\breadwallet\core\BRCoreMasterPubKey.java
private static native long createJniCoreMasterPubKeyFromPhrase (byte[] phrase);
private static native long createJniCoreMasterPubKeyFromSerialization(byte[] pubKey);
调用的是 jni c库
/*
* Class: com_breadwallet_core_BRCoreMasterPubKey
* Method: createJniCoreMasterPubKeyFromPubKey
* Signature: ([B)J
*/
JNIEXPORT jlong JNICALL
Java_com_breadwallet_core_BRCoreMasterPubKey_createJniCoreMasterPubKeyFromSerialization
(JNIEnv *env, jclass thisClass,
jbyteArray serialization) {
jsize serializationLength = (*env)->GetArrayLength (env, serialization);
jbyte *serializationBytes = (*env)->GetByteArrayElements (env, serialization, 0);
assert (serializationLength == sizeof(BRMasterPubKey));
BRMasterPubKey *key = (BRMasterPubKey *) calloc (1, sizeof (BRMasterPubKey));
memcpy(key, serializationBytes, sizeof(BRMasterPubKey));
return (jlong) key;
}