Neo中hash算法,加密算法使用介绍
深圳林妙可
发表于 2023-1-5 00:08:26
93
0
0
关于区块链中密码学的介绍,yeasy大牛的文章已经介绍的非常好,下文主要通过和Neo结合,加上一些自己的理解,去讲述一下加密算法的使用方法。6 q' o( P$ R! O; r
0 w- y$ q2 Q- _. A* t# o/ a i
Hash 算法
6 _8 p% \. R# X# Y# a6 V$ c
Hash (哈希或散列)算法是信息技术领域非常基础也非常重要的技术。它能任意长度的二进制值(明文)映射为较短的固定长度的二进制值(Hash 值),并且不同的明文很难映射为相同的 Hash 值。7 k+ R9 U) d7 U; S- ?9 \% N
. X1 y# b5 g: V D, b9 i2 v+ [
hash函数的作用
注意上一篇文章说明了如何将hash后的字符串保存到Neo的UInt256类型,其中一个前提就是结果集合在[0-15]之间。9 R# J: X* ^) Q1 I- u9 Z' X
0 d( M/ n1 ?7 ]0 K( d K
哈希完全不等于加密,很多时候开发人员都对用户表中的密码进行哈希后保存,实际上不叫做加密,只是相当于把密码的“特征指纹”保存下来,而对非法攻击者来说,在不知道真实的“密码”的情况下,得到有相同指纹的密码是极为困难的。* T$ u) L6 `0 L- q
/ r2 a, v, g' f
一个优秀的 hash 算法,将能实现:
; H/ g1 d/ S6 S$ R# q+ \7 X3 `& _
正向快速:给定明文和 hash 算法,在有限时间和有限资源内能计算出 hash 值。) G' s3 U4 u7 k# N; q
# P7 `: h. l% Q
逆向困难:给定(若干) hash 值,在有限时间内很难(基本不可能)逆推出明文。* c, \" Y3 {( R. j
输入敏感:原始输入信息修改一点信息,产生的 hash 值看起来应该都有很大不同。) S. L5 |# T; L% [
/ U4 G3 G0 h7 Q. B1 U
冲突避免:很难找到两段内容不同的明文,使得它们的 hash 值一致(发生冲突)。
目前,一般认为 MD5 和 SHA1 已经不够安全,推荐至少使用 SHA2-256 算法。! B: _) _: |8 o, x
( G4 A2 X3 j; a; N6 i5 C
一般的,Hash 算法都是算力敏感型,意味着计算资源是瓶颈,主频越高的 CPU 进行 Hash 的速度也越快。
0 k' g# j {# t
也有一些 Hash 算法不是算力敏感的,例如 scrypt,需要大量的内存资源,节点不能通过简单的增加更多 CPU 来获得 hash 性能的提升。4 y- g$ j$ Z/ h: |2 M! }- D9 G
2 T2 G) x! z/ X& }4 c# M
Neo中的hash算法2 V' w% Z+ a9 T0 Y5 h, Y3 q
scrypt [% B, [+ K$ ? w# W, h
scrpyt算法是由著名的FreeBSD黑客 Colin Percival为他的备份服务 Tarsnap开发的,当初的设计是为了降低CPU负荷,尽量少的依赖cpu计算,利用CPU闲置时间进行计算,因此scrypt不仅计算所需时间长,而且占用的内存也多,使得并行计算多个摘要异常困难,因此利用rainbow table进行暴力攻击更加困难。scrypt没有在生产环境中大规模应用,并且缺乏仔细的审察和广泛的函数库支持。所以scrpyt一直没有推广开,但是由于其内存依赖的设计特别符合当时对抗专业矿机的设计,成为数字货币算法发展的一个主要应用方向。+ ]6 l p5 P0 L$ C) B; n% C
* J' v! p0 V k
scrypt的参数 \4 J6 @5 }0 y: h
$ z* C8 D$ ^9 S& p ?0 u
https://stackoverflow.com/questi ... scrypt-work-factors
! E9 i" T% n% M
Cpercival mentioned in his slides from 2009 something around+ x0 ~; I) D! [) t" ?
' r, ~( O; P' G) j) f, @
(N = 2^14, r = 8, p = 1) for
9 H; W( V, V* A3 S5 K, e
scrypt特点* @. M0 F) `2 r7 O
3 m$ b$ e1 l; c! c. u
scrpyt的出名主要是因为莱特币为了抵抗比特币矿机采用的一个算法,可以指定内存和cpu的使用量,可以用参数确定hash的时间。
. Q3 H) j8 W4 Y- K" L V
Neo中如何使用scrypt+ R% m- g% [+ m0 ~. w& c0 X* N
// in NEP6Account7 O, x& ]$ z, I! Q
public NEP6Account(NEP6Wallet wallet,6 Z8 [% `8 w$ T/ ^$ h! Y
0 R/ O! k$ V" ^2 K( H/ e8 o1 ]$ u
UInt160 scriptHash, KeyPair key, string password)% I# U* y7 {4 \( p8 ^9 |
- J" T- }, b4 g$ L S! H
: this(wallet,
scriptHash,
: x* H$ v2 x4 o; y/ y
key.Export(password, wallet.Scrypt.N, wallet.Scrypt.R, wallet.Scrypt.P))
{+ G7 Z" `# b+ w/ `2 _& v* b; u
$ z" L, R; x, Q, [0 i
this.key = key;
}
6 Y8 ?+ k- `9 Y' B4 H& H
// in class KeyPair
+ }( w/ X2 W$ l
public string Export(string passphrase, int N = 16384, int r = 8, int p = 8)! p' u& k. M% a9 e, K! {
E$ a6 w+ d! O r
{
using (Decrypt())
0 u& Q+ N s: i+ ?2 U! m2 v+ E. }
{7 G* j: d. ]! z
! R; A1 z$ f1 f: Y
UInt160 script_hash = Contract.
: |7 u7 n1 |+ c9 T( u
CreateSignatureRedeemScript(PublicKey).ToScriptHash();
' ?; L! W* D$ _1 |+ a& P
0 ]+ ]: S* H! _$ |7 j1 G7 ^, f- L: X
1 j& t1 f( D' G' C Z
string address = Wallet.ToAddress(script_hash);" C7 e) \6 V# a# Q4 r: M. m3 b
) E1 U: y* F4 L9 {
byte[] addresshash = Encoding.ASCII.GetBytes(address)
.Sha256().Sha256().Take(4).ToArray();
; C0 s' p! T8 i$ x/ |
* D7 p% J& \, k- N3 g; ]2 t' }
# d% W8 F) a8 O6 \# z" Z5 x
byte[] derivedkey = SCrypt.DeriveKey(
8 G5 ]2 M$ G) J0 C) N) `8 J! I
Encoding.UTF8.GetBytes(passphrase), addresshash, N, r, p, 64);
5 O5 I2 g- y& Y; Y+ y8 k
byte[] derivedhalf1 = derivedkey.Take(32).ToArray();, I! |% V% [" ^0 d; B: L3 e0 t- B7 v
byte[] derivedhalf2 = derivedkey.Skip(32).ToArray();
" ?0 a/ a& \# E6 X! E3 b
byte[] encryptedkey = XOR(PrivateKey, derivedhalf1)
.AES256Encrypt(derivedhalf2);: V! \1 R" q N' r* ?
9 h3 J$ Q. v. \1 b
byte[] buffer = new byte[39];
" q8 f4 \) Y1 m( [4 N2 J
buffer[0] = 0x01;
0 |) C. M) W$ j$ \7 ^
buffer[1] = 0x42;
buffer[2] = 0xe0;
- z% i4 i3 H4 `; Y
Buffer.BlockCopy(addresshash, 0, buffer, 3, addresshash.Length);/ `" U; z8 E! c. E) v
Buffer.BlockCopy(encryptedkey, 0, buffer, 7, encryptedkey.Length);
! y) I& b* B1 M/ Z6 R
return buffer.Base58CheckEncode();
}5 M& o7 e2 k- ?
5 U: t. y9 y3 B9 q k: K: {6 E
}
, h8 t0 a! e# A+ T
可见SCrypt.DeriveKey方法参与了加密密钥的生成过程。后面解密也必然使用到了这个hash算法。所以该hash算法参与了加密过程,而加密密钥用AES256Encrypt生成。可以确定的是,使用该算法的逆过程,可以解密出密钥来,这个比WIF要安全。1 r2 I9 _, v( C
. G# m0 z( v8 W Y- c. M
Murmur3! K/ T n: [& n. b o$ b
" U; H W. P0 A8 j: y& z( i
MurmurHash 是一种非加密型哈希函数,适用于一般的哈希检索操作。[1][2][3]由Austin Appleby在2008年发明,[4][5] 并出现了多个变种,[6] 都已经发布到了公有领域(public domain)。与其它流行的哈希函数相比,对于规律性较强的key,MurmurHash的随机分布特征表现更良好。[7]: M' Z% Q1 U+ d5 `
Murmur3特点% B' C: S# w( W W
1 ^7 Z" `; \ F
1.碰撞率低/ G5 i; G/ h& @- d
* ~& W5 y* w6 A A8 l, B* }$ Q) N1 b
2.计算速度快) m& e; {5 t" p8 x, ?
2 n* A; G. o8 @, s: @
3.擅长大文件的hash) U7 r/ }, O( |
Neo中如何使用Murmur3* W* I; r: t6 g/ `
; P5 A% w5 a! _$ L; W5 ?2 W& W
Neo中Murmur3
0 `8 }4 n8 @% L5 V
Murmur3的具体算法,以后再研究,现在大致知道,Neo用Murmur3生成key,也在BloomFilter中使用了。
; a/ C9 ]5 m! F: O$ ~
RIPEMD-160
" g' i, b$ v: m7 z+ `, ^
Neo中用这个算法来生成短一点的hash值,script hash就是用了这个算法。0 d, M/ G; F9 |4 {
// in neo-compiler/neo/neo/Core/Helper.cs9 F2 E% {( m3 c; r4 I
public static UInt160 ToScriptHash(this byte[] script)8 Q+ f0 d+ F2 p y# x
: \3 f( p- }2 S& }* V
{
return new UInt160(Crypto.Default.Hash160(script));0 P- j# K; C0 C& H6 p: Z0 X; s
" \9 N" c; B: H- B- z; d2 u" k! k3 f0 |
}
* L9 o! ?4 W. j- y5 M9 S. T) h
RIPEMD-160算法的特点
, V) {2 |6 ^: a: a: Z3 p
RIPEMD-160能表现出理想的 雪崩效应 (例如将 d 改成 c,即微小的变化就能产生一个完全不同的哈希值):
加密算法体系
现代加密算法的典型组件包括:加解密算法、加密密钥、解密密钥。其中,加解密算法自身是固定不变的,一般是公开可见的;密钥则往往每次不同,并且需要保护起来,一般来说,对同一种算法,密钥长度越长,则加密强度越大。
7 B; T# d/ T6 |- X
加密过程中,通过加密算法和加密密钥,对明文进行加密,获得密文。; `& X7 M/ u: ^* E# U0 d8 Z$ v, V3 C
% O7 S5 r0 z. h3 {
解密过程中,通过解密算法和解密密钥,对密文进行解密,获得明文。) y, D: v% a x5 l) P, a
2 T$ x; C' h: B7 G- B/ x
根据加解密的密钥是否相同,算法可以分为对称加密(symmetric cryptography,又称公共密钥加密,common-key cryptography)和非对称加密(asymmetric cryptography,又称公钥加密,public-key cryptography)。两种模式适用于不同的需求,恰好形成互补,很多时候也可以组合使用,形成混合加密机制。* k/ A, O. `. g5 P
并非所有加密算法的强度都可以从数学上进行证明。公认的高强度加密算法是在经过长时间各方面实践论证后,被大家所认可,不代表其不存在漏洞。但任何时候,自行发明加密算法都是一种不太明智的行为。) s1 O ` w5 P5 M% t4 H0 \0 x- r& }
对称加密2 _& |2 [6 \6 A% d' D7 F& h5 z
; t' y% a7 m& i7 Q
顾名思义,加解密的密钥是相同的。8 s' }6 S* e8 @# ~8 x9 W
对称加密优缺点
优点是加解密效率高(速度快,空间占用小),加密强度高。
缺点是参与多方都需要持有密钥,一旦有人泄露则安全性被破坏;另外如何在不安全通道下分发密钥也是个问题。
0 L8 i# V5 |0 m) }
适用于大量数据的加解密;不能用于签名场景;需要提前分发密钥。
对称加密实现
( t- M' o8 \+ H3 P& X( X
对称密码从实现原理上可以分为两种:分组密码和序列密码。前者将明文切分为定长数据块作为加密单位,应用最为广泛。后者则只对一个字节进行加密,且密码不断变化,只用在一些特定领域,如数字媒介的加密等。
6 j: I+ _7 D% f
代表算法包括 DES、3DES、AES、IDEA 等。) @. o; X5 p0 r* P( ^
& n3 c* Z" o: P$ D5 e
DES(Data Encryption Standard):经典的分组加密算法,1977 年由美国联邦信息处理标准(FIPS)所采用 FIPS-46-3,将 64 位明文加密为 64 位的密文,其密钥长度为 56 位 + 8 位校验。现在已经很容易被暴力破解。
9 M* h A: Z1 O! o3 U7 i0 t
3DES:三重 DES 操作:加密 –> 解密 –> 加密,处理过程和加密强度优于 DES,但现在也被认为不够安全。1 x3 B) E+ n4 N+ d, @
AES(Advanced Encryption Standard):美国国家标准研究所(NIST)采用取代 DES 成为对称加密实现的标准,1997~2000 年 NIST 从 15 个候选算法中评选 Rijndael 算法(由比利时密码学家 Joan Daemon 和 Vincent Rijmen 发明)作为 AES,标准为 FIPS-197。AES 也是分组算法,分组长度为 128、192、256 位三种。AES 的优势在于处理速度快,整个过程可以数学化描述,目前尚未有有效的破解手段。& X4 o3 h2 A+ N+ N" J1 I
注:分组加密每次只能处理固定长度的明文,因此过长的内容需要采用一定模式进行加密,《实用密码学》中推荐使用 密文分组链接(Cipher Block Chain,CBC)、计数器(Counter,CTR)模式。, p9 p" M- q2 H" |" V
4 R3 t' y3 _$ w7 r
Neo中的AES
在钱包的加解密中,使用了该算法。
; X; c, t! K9 z
下图的代码在/neo/Wallets/Wallet.cs中,NEP是neo enhancement proposal的意思。参数nep2就是符合这个格式的一个Neo钱包文件。
拿到私钥
具体的过程,后面再仔细研究分享出来。( ^! T* I) T, v& M0 ~
* }2 }- k, S, @7 d; V! Y7 S
非对称加密
非对称加密是现代密码学历史上最为伟大的发明,可以很好的解决对称加密需要的提前分发密钥问题。顾名思义,加密密钥和解密密钥是不同的,分别称为公钥和私钥。公钥一般是公开的,人人可获取的,私钥一般是个人自己持有,不能被他人获取。8 a: @ d+ [, s* P7 }
% Y i4 ` p/ }' u
非对称加密优缺点8 _; l N- r0 K8 n
; s% V' s8 y* W
优点是公私钥分开,不安全通道也可使用。
7 S4 C" |, b9 P/ c
缺点是加解密速度慢,一般比对称加解密算法慢两到三个数量级;同时加密强度相比对称加密要差。
. {0 s& z$ u7 G0 s# r# D3 f) n
非对称加密代表算法! N* J" W2 I4 C* i: L
9 ?' a+ I0 V# m! Z4 L3 V! d
非对称加密算法的安全性往往需要基于数学问题来保障,目前主要有基于大数质因子分解、离散对数、椭圆曲线等几种思路。' |( m! i- o) ^5 a6 x1 l
代表算法包括:RSA、ElGamal、椭圆曲线(Elliptic Curve Crytosystems,ECC)系列算法。
RSA:经典的公钥算法,1978 年由 Ron Rivest、Adi Shamir、Leonard Adleman 共同提出,三人于 2002 年获得图灵奖。算法利用了对大数进行质因子分解困难的特性,但目前还没有数学证明两者难度等价,或许存在未知算法在不进行大数分解的前提下解密。7 G K* ~$ d& k0 [) j. }
+ X/ ?9 z, b8 q* \' b
Diffie-Hellman 密钥交换:基于离散对数无法快速求解,可以在不安全的通道上,双方协商一个公共密钥。- t# H8 \ a5 {9 O7 o# R
ElGamal:由 Taher ElGamal 设计,利用了模运算下求离散对数困难的特性。被应用在 PGP 等安全工具中。/ L9 w( h; s' y; d% H
椭圆曲线算法(Elliptic curve cryptography,ECC):现代备受关注的算法系列,基于对椭圆曲线上特定点进行特殊乘法逆运算难以计算的特性。最早在 1985 年由 Neal Koblitz 和 Victor Miller 分别独立提出。ECC 系列算法一般被认为具备较高的安全性,但加解密计算过程往往比较费时。一般适用于签名场景或密钥协商,不适于大量数据的加解密。7 |& } R7 }: U$ t4 ]2 s0 R
2 f+ `8 y1 w! ]: A- U. Z
RSA 算法等已被认为不够安全,一般推荐采用椭圆曲线系列算法。
+ a- a0 q, ]1 L; N; J
Neo中的数字签名算法
在Neo中,也使用了非对称加密算法,我们通过代码来看看是如何使用的。8 p7 y: L- r$ |# W+ }
( `; x4 q& ^: y- R& ^4 a2 A* C+ H
public virtual WalletAccount Import(X509Certificate2 cert)
- L( U5 x/ \, S8 }8 @" y# l: ?
{
byte[] privateKey;4 }8 r' n4 ?, ^; x7 r4 O8 a9 q
using (ECDsa ecdsa = cert.GetECDsaPrivateKey())" O6 ]/ B% o0 V; i. A; @5 ^
- B# ~5 o. b2 S% Q; s
{
' W6 O: d$ L0 v) Y4 ~. f! m1 r; Q5 `
privateKey = ecdsa.ExportParameters(true).D;
}
WalletAccount account = CreateAccount(privateKey);
+ M& U* ]9 |8 h9 a
Array.Clear(privateKey, 0, privateKey.Length);
( C5 o8 ^: K, v$ l9 m7 p7 \0 O
return account;
7 `& V3 @" O% r1 [" s( e3 D6 e
}5 L+ r7 k9 p o& m; U$ V$ T
X509Certificate2是数字证书,和我们在https里面使用的是一样的,从里面拿出私钥后,创建钱包。
总结* ~9 e3 c6 {& c; m
$ J: `6 ]: u1 n! ?4 S% y
目前只是简单的介绍了一下Neo中加密算法的使用情况,这些加密算法的原理和实现也是很有意思的,后面看看怎么实现的,再分享出来。
成为第一个吐槽的人