一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2327
0
0
见 Account.swift 类:( Z5 K5 ^8 X3 [4 l
public init?() {
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {) S$ @- ?8 ?3 ?3 J
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}* R3 n0 i d" A
if result != errSecSuccess {
fatalError()7 b4 w: u' \, H- V! d
}7 M, {& E8 T2 r% h+ t- j
1 V6 f& z: ]% ^$ G
var error: NSError?/ m8 h' K' y" T
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }5 N+ |- Q) g; L3 ~5 J' i4 }
self.wif = wallet.wif()0 c; y. e1 c; L9 G+ ~
self.publicKey = wallet.publicKey() S4 {' e$ H' i: t$ p' f
self.privateKey = pkeyData
self.address = wallet.address()
self.hashedSignature = wallet.hashedSignature()
//default to mainnet+ M2 e n2 w# l. x
self.neoClient = NeoClient.sharedMain
}$ o* u- ~4 u5 x+ l$ [% B
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:, o2 X9 k, ~$ V4 B
/*!
@function SecRandomCopyBytes
@abstract Return count random bytes in *bytes, allocated by the caller.. A8 l8 C$ y! H! z' w
It is critical to check the return value for error& {% P( m8 f8 L6 s, g5 X
@result Return 0 on success, any other value on failure.
*/* y3 |& w6 C$ V( j+ {
@available(iOS 2.0, *)$ j) L, q' }7 K/ @' q% n7 I3 S
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32+ m, ?+ X& A& s9 j; t4 `" k
随机生成一个32字节的 Data 数据,即 privatekeyData:
var pkeyData = Data(count: 32)$ C* Y& C8 ]; Y; g: y2 W7 O
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0). l5 f. A6 Y% n/ O
}8 I. X' m9 N7 L$ ~
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:- ~2 x" Y* ?2 w7 Q
var error: NSError?* f/ L r0 O2 Z6 u, F
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }( }6 k- W! z" G
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {# B) U5 Z4 M; `. G! ^
pb := hex2bytes(privateKey): R0 z3 b" ~6 t) M9 z$ H' h) v
var priv btckey.PrivateKey7 \ K* ^, ?* ^/ g0 a" [( M
err := priv.FromBytes(pb)! i$ w2 k% Z2 I$ Q
if err != nil {- W# W& y5 C0 ^4 D5 a
return &Wallet{}, err1 D: L! X& d; F; f
}4 ~% x1 d, o& k+ c }
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),% O' ~0 q# e% ]8 }
HashedSignature: priv.ToNeoSignature(),
}
return wallet, nil
}
公钥是怎么来的? B+ d% M/ t& @/ w& ^& V# C$ z
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。6 U5 I; N# U, u/ V
见neowallet.go 和 btckey.go:
// Generate a wallet from a private key6 U9 g4 K2 Y* U
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)( R8 `: U" u& E8 F& \
var priv btckey.PrivateKey: Y* o1 E/ t. }; W# ^7 n
err := priv.FromBytes(pb)9 J% r8 J9 a( U% m" f* w1 G
if err != nil {
return &Wallet{}, err
}
wallet := &Wallet{' u `9 O3 \) k6 u
PublicKey: priv.PublicKey.ToBytes(),3 {- D& x0 j) l. z3 x& ^) j
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),; L( E; ]8 L- P3 Z* f* {$ g
WIF: priv.ToWIFC(),) J, B1 C* t; k. c1 i; X
HashedSignature: priv.ToNeoSignature(),
}4 `# f' x( j3 J4 P/ T5 o5 d0 ]) w
return wallet, nil! }8 t9 u' K! D' q0 X& G
}
// derive derives a Bitcoin public key from a Bitcoin private key.
func (priv *PrivateKey) derive() (pub *PublicKey) {+ O7 m. {5 y9 F; W; u9 \; E3 d+ K
/* See Certicom's SEC1 3.2.1, pg.23 */3 ~! ]! g; |$ y5 ]6 r* w! @
/* Derive public key from Q = d*G */
Q := secp256r1.ScalarBaseMult(priv.D)9 ^/ L2 N5 u, l. {
/* Check that Q is on the curve */0 V: v5 n6 x! o- j' g
if !secp256r1.IsOnCurve(Q) {
panic("Catastrophic math logic failure in public key derivation.")
}
priv.X = Q.X
priv.Y = Q.Y7 j& \! W+ E3 P
return &priv.PublicKey3 W+ H: _3 Q: g9 x9 _
}- k7 ~5 V# |$ F* q2 Z3 W5 X
地址脚本是怎么来的?
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:' u7 G4 W5 [ c6 I4 h9 Z( y
前面是:0x21后面是:0xAC
见btckey.go:
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)! ~3 ?6 G# b" m( y
地址ScriptHash是怎么来的?8 C2 {! u# D6 r# {2 \
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()7 p1 y7 E; A* r
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */) P9 u# w) H- A" d8 W9 ]
ripemd160_h := ripemd160.New()
ripemd160_h.Reset()- \, x* q) I8 O
ripemd160_h.Write(pub_hash_1)5 h7 X6 n3 c8 x
pub_hash_2 := ripemd160_h.Sum(nil); M. e' e# J, F& Q# K
program_hash := pub_hash_25 E0 V) b. Q* y' [% r4 ^
地址是怎么来的?
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:& _; w- R3 L0 ~' k( H* _( X: I
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码
: }/ v1 e+ y' ?# I! k4 K9 Q- f
见 btckey.go 完整的由公钥生成地址的代码:" M( n; \! g3 N* f$ s2 ^2 d
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
func (pub *PublicKey) ToNeoAddress() (address string) {
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */
/* Convert the public key to bytes */$ M0 X0 I+ b, Y% Y' ~2 [
pub_bytes := pub.ToBytes()- c' t& s6 o: N% q5 R- B8 l
pub_bytes = append([]byte{0x21}, pub_bytes...)1 V0 c+ N% Q) c: b' w% Q
pub_bytes = append(pub_bytes, 0xAC)7 }1 {& j1 l; r: g6 P
/* SHA256 Hash */2 L* l' S0 l- g9 N% j0 `9 k+ P
sha256_h := sha256.New()" U$ w0 _. S( S9 |1 F
sha256_h.Reset()
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil) u2 r: ~ r b
/* RIPEMD-160 Hash */3 n8 j% Z: V& B. J5 |( k* p" T
ripemd160_h := ripemd160.New()' D9 y, V# ]: _4 |
ripemd160_h.Reset(). k3 [+ s/ r# V1 r$ g
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)! ]" c" A/ h! N
program_hash := pub_hash_2
//wallet version( a: @6 m2 [+ X+ X" G- E1 ~% T
//program_hash = append([]byte{0x17}, program_hash...)
// doublesha := sha256Bytes(sha256Bytes(program_hash))/ B- C( S& V# }
// checksum := doublesha[0:4]# P1 b6 l' F U; ]
// result := append(program_hash, checksum...)$ c; [( ~) N1 `. W( @5 L
/* Convert hash bytes to base58 check encoded sequence */* X) j1 P8 J3 W0 w6 s+ S7 @
address = b58checkencodeNEO(0x17, program_hash)
return address
}( W! X( M2 \! b `* m+ n! n
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.& o6 V7 U, L+ A) m. K* r- K
func b58checkencodeNEO(ver uint8, b []byte) (s string) {
/* Prepend version */
bcpy := append([]byte{ver}, b...)$ A% p, H* `( t' Y% K% ]5 G
/* Create a new SHA256 context */
sha256_h := sha256.New()* [3 d0 J; O6 `3 L8 }- R
/* SHA256 Hash #1 */& Y1 X. d0 e* U" g" Q; _
sha256_h.Reset()" | ~( l& e7 A! e. ` G
sha256_h.Write(bcpy)
hash1 := sha256_h.Sum(nil)0 ?( _# l6 e8 s, c2 c, A6 y
/* SHA256 Hash #2 */2 ]$ p* ?; _9 t+ c+ ~
sha256_h.Reset(). u! [3 w9 Q; q
sha256_h.Write(hash1): O }: }* q2 [0 ^% U
hash2 := sha256_h.Sum(nil)+ _7 h* U8 g) D0 s$ H8 D- {
/* Append first four bytes of hash */; ]7 m8 r, Q% Q6 p+ j( O! k
bcpy = append(bcpy, hash2[0:4]...)
/* Encode base58 string */& U7 R. J( A+ L' w( x, A I* B
s = b58encode(bcpy)
// /* For number of leading 0's in bytes, prepend 1 */3 G. V7 i" }$ z4 H' |) K6 Y- P a" f
// for _, v := range bcpy {
// if v != 0 {- z9 [ m9 X; ^$ W8 |
// break/ H' b6 @6 G1 Y; t `
// }3 E, d; [- u& }, q; `
// s = "1" + s
// }
return s
}3 e4 a7 S J/ W" j4 M6 ]
WIF 是怎么来的?5 O, i. B1 h0 J5 a9 R' i% ^1 _# Y
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码6 H# U b5 Z) x6 N. Y5 D/ c
成为第一个吐槽的人