一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2329
0
0
见 Account.swift 类:
public init?() {' ^) h+ A$ L( {% u
var pkeyData = Data(count: 32)8 d8 Y. [* A$ ~/ u. l4 f% V( m! n
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}
if result != errSecSuccess {
fatalError()3 w7 w8 i% a9 D( a. {( W: r( l
}) i1 h7 y2 }( y: _
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
self.wif = wallet.wif()- T, P5 U+ T# Y6 R, r: X
self.publicKey = wallet.publicKey()! K! N* E1 B# k Z9 o
self.privateKey = pkeyData
self.address = wallet.address()
self.hashedSignature = wallet.hashedSignature()# {: ]" q0 n& G
//default to mainnet0 ^8 x" S3 R$ Y9 m2 b. H
self.neoClient = NeoClient.sharedMain
}
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
/*!& T$ u J! m* s4 Z2 x a
@function SecRandomCopyBytes
@abstract Return count random bytes in *bytes, allocated by the caller.
It is critical to check the return value for error/ Z5 g9 K+ U+ J% i
@result Return 0 on success, any other value on failure.
*/2 p* U2 f& z4 Q" w, u$ l; L" ?
@available(iOS 2.0, *)! m& Q7 b+ f, m" I% ]" N7 F" W
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int321 v% S: n9 G( Q: a
随机生成一个32字节的 Data 数据,即 privatekeyData:8 n6 |5 m- k6 U# h1 y
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0); f4 k4 _( q& t
}
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private key; p+ u- G3 n" @0 D" q4 c, X
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey): Y5 B' C2 c1 B% ]$ w# h8 D- d0 a
var priv btckey.PrivateKey
err := priv.FromBytes(pb)
if err != nil {% z" a; t ]$ d4 C1 n3 X
return &Wallet{}, err4 A: y: G; ?# t$ u
}$ h/ _: k+ p1 l. ?
wallet := &Wallet{% E2 r, g2 a( W
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),! {/ T4 j4 G% i9 X
WIF: priv.ToWIFC(),5 x* T$ J. Q9 Y
HashedSignature: priv.ToNeoSignature(),. B3 B, R" H; v+ m% w
}: J% c; F: c2 U. \1 n- V$ ]
return wallet, nil
}5 O% r" b2 ^( V; h
公钥是怎么来的?# ]4 v D* I$ O+ l. r5 l
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。; `( a0 N }4 Y! y
见neowallet.go 和 btckey.go:
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {0 H, a q- ^& P& W# n3 O
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey
err := priv.FromBytes(pb)
if err != nil {% M$ n. W& r" S) e! X* D4 P
return &Wallet{}, err
}% s$ n; Y2 ^5 \1 u( ]+ ^
wallet := &Wallet{( @" F( T" t8 v2 ?9 k, O( D* Q6 l2 ~" Z
PublicKey: priv.PublicKey.ToBytes(),+ n6 s1 Z7 a# Y' e I: U4 c
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),; b4 y7 O5 U! S% a
HashedSignature: priv.ToNeoSignature(),
}
return wallet, nil
}
// derive derives a Bitcoin public key from a Bitcoin private key.. I3 d) S) U, @& n2 K
func (priv *PrivateKey) derive() (pub *PublicKey) {( H( o( d- n# M
/* See Certicom's SEC1 3.2.1, pg.23 */
/* Derive public key from Q = d*G */
Q := secp256r1.ScalarBaseMult(priv.D)
/* Check that Q is on the curve */
if !secp256r1.IsOnCurve(Q) {
panic("Catastrophic math logic failure in public key derivation.")
}
priv.X = Q.X
priv.Y = Q.Y
return &priv.PublicKey( B, B- s2 X9 Q4 e8 r
}' T Q% Q5 z6 S3 f8 A
地址脚本是怎么来的?; u' D9 r3 h9 y# d2 A/ ? g R
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:
前面是:0x21后面是:0xAC
见btckey.go:
/* Convert the public key to bytes */7 g+ Q; e" K2 |5 N
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC); g( u* j+ w6 D8 p# C* o
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:+ X$ R/ R5 h9 T. o8 q2 K% t
/* SHA256 Hash */6 Q0 ^. s9 ~$ t' ]5 e
sha256_h := sha256.New()
sha256_h.Reset()
sha256_h.Write(pub_bytes)% q4 ~& J6 `9 v' r* \% K& v8 R! g
pub_hash_1 := sha256_h.Sum(nil)& Q5 K3 V& T2 |: l7 z
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()
ripemd160_h.Reset(); M( B" h0 O/ ^- a! |1 J6 y
ripemd160_h.Write(pub_hash_1): t; [# S Z. D' H" t% M
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2% c- ]9 |' G( x, J" T" k6 e
地址是怎么来的?$ w I& k: p9 w: [* K
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码- N; W! P0 [4 z& G @& D/ }. B$ y3 q
: k; S3 m1 t- V& j
见 btckey.go 完整的由公钥生成地址的代码:# H! e/ t: {7 X0 [$ ~
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.- o7 E% X" P% J( E Q, e, ]
func (pub *PublicKey) ToNeoAddress() (address string) {7 D$ n+ Y% D7 ~- l) n
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */# ~! J" s' V. v: D
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)
/* SHA256 Hash */0 \! x4 v+ U# m% t/ @0 _" A: Q' ]
sha256_h := sha256.New()
sha256_h.Reset()
sha256_h.Write(pub_bytes)3 g# P( A* S: u1 ]3 V
pub_hash_1 := sha256_h.Sum(nil)! |/ i m9 [% u1 W) l/ j
/* RIPEMD-160 Hash */0 V1 B$ v4 k% Z1 T# U4 h- E7 {% N
ripemd160_h := ripemd160.New()' R/ ]2 k) D- w
ripemd160_h.Reset()4 Z7 P4 G0 {. f
ripemd160_h.Write(pub_hash_1). k* ?* W( z: ]' V( i. q! q' o
pub_hash_2 := ripemd160_h.Sum(nil)& T8 O3 C# I% S5 [! x; D C0 D0 X
program_hash := pub_hash_2
//wallet version
//program_hash = append([]byte{0x17}, program_hash...)$ M3 i+ d" e, s- H; |1 F) ?
// doublesha := sha256Bytes(sha256Bytes(program_hash))2 K8 B8 J/ V; [4 k
// checksum := doublesha[0:4]% k! J/ @# L! k6 G
// result := append(program_hash, checksum...)7 s6 h7 b9 D* W( n W' F
/* Convert hash bytes to base58 check encoded sequence */9 a3 G, Y# a! J. P( c/ q% v
address = b58checkencodeNEO(0x17, program_hash)
return address3 z# {% \! o* [. @4 B9 W( P& X1 J
}" b$ C+ E# @/ L5 G& k$ \! n
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.& B3 G) A! N3 X' e
func b58checkencodeNEO(ver uint8, b []byte) (s string) {( `3 r' w0 F3 E& b5 g( v0 r" d: c
/* Prepend version */9 d& U8 S( p+ A" x0 }
bcpy := append([]byte{ver}, b...)
/* Create a new SHA256 context */
sha256_h := sha256.New()
/* SHA256 Hash #1 */
sha256_h.Reset()
sha256_h.Write(bcpy)1 R8 x. w8 M1 t8 m* t
hash1 := sha256_h.Sum(nil)' |4 a6 I1 H7 x; M
/* SHA256 Hash #2 */
sha256_h.Reset()
sha256_h.Write(hash1)" X# Y# l7 x0 y4 v8 M
hash2 := sha256_h.Sum(nil)
/* Append first four bytes of hash */: J2 M* C, _6 ]5 h$ U8 { `
bcpy = append(bcpy, hash2[0:4]...)& X f2 g* V# I: g6 L
/* Encode base58 string */
s = b58encode(bcpy)$ H/ @; V" o& f* r/ Z) i) v4 l
// /* For number of leading 0's in bytes, prepend 1 */
// for _, v := range bcpy {8 c* M$ j1 s2 f! p
// if v != 0 {+ e4 r. W* I/ _& `4 y! n
// break
// }
// s = "1" + s; H& O$ G% u1 C6 p" V/ Y7 [' c2 Q
// }7 q# j: ?( p* u3 x
return s0 U, n; K* [# h! Z6 Z) e3 C
}
WIF 是怎么来的?
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到: k2 q2 D7 X. D
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码: d4 Z' {4 D6 q& w
成为第一个吐槽的人