一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2390
0
0
见 Account.swift 类:
public init?() {
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {* q3 X9 Z9 ?' y/ e
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}
if result != errSecSuccess {
fatalError()
}" B. X0 D" L( m+ K% @; u- u
# ~( u, T; E* C
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
self.wif = wallet.wif()
self.publicKey = wallet.publicKey()
self.privateKey = pkeyData5 b1 Q( E; X7 q$ T) ]
self.address = wallet.address()
self.hashedSignature = wallet.hashedSignature(); M+ P/ J* E5 D
//default to mainnet1 c1 ]) U: G2 A0 Y+ G4 r
self.neoClient = NeoClient.sharedMain
}4 x, ^0 {5 r8 G }2 N
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节: U9 x5 G2 D8 R! E1 s# e) u4 F
/*!, x. l8 A1 Q. {) | |
@function SecRandomCopyBytes( i6 b! s7 ?7 I! d3 B) h( P
@abstract Return count random bytes in *bytes, allocated by the caller.( P9 k7 T' h9 r+ m9 G
It is critical to check the return value for error! L" G6 [' x( q& n$ S
@result Return 0 on success, any other value on failure.' m% \- _0 n5 i; W3 y3 _0 _
*/- K X& \0 j0 a, ]. Z! ?" n; N& Z* n
@available(iOS 2.0, *)5 ~7 \ v t, X1 [- r8 |' _
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
随机生成一个32字节的 Data 数据,即 privatekeyData:
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {+ L* x8 j- @. K+ F m
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}4 m; t2 M" Q, t& Q0 n
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:. I* m8 ~) f" V8 H3 b
var error: NSError?+ N P* _; o' Z/ M D' _
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }, r6 _# J6 k( r% u1 P
// Generate a wallet from a private key9 {' G* V' R+ P0 L0 o( R
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {7 o$ D4 M/ j1 [0 l' r; _3 u
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey! g( Y5 X/ c; l5 l5 L7 N( X
err := priv.FromBytes(pb)
if err != nil {. G1 N% |' G& Y: ^. F
return &Wallet{}, err
}' s( s' M, H5 ^0 z/ }
wallet := &Wallet{+ w" {+ W. p; s* X4 h! n
PublicKey: priv.PublicKey.ToBytes(),2 }' { T4 S9 y$ N: j1 m
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),& A' K. K' S8 v8 g$ ]7 \" h& E
HashedSignature: priv.ToNeoSignature(),
}
return wallet, nil
}! Q0 I3 s+ S2 S5 ]
公钥是怎么来的?
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。
见neowallet.go 和 btckey.go:- t) A! N( G0 T. a
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {& E$ o+ W' u& H! b+ K- \4 Y0 \
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey
err := priv.FromBytes(pb)8 {- @5 ^& L' G& s' ?/ K& y
if err != nil {" z( D3 w8 k9 H& a
return &Wallet{}, err; ]) d: K/ r$ \6 k& k% X9 x! z
}* O$ j. f$ m' |4 V4 ]
wallet := &Wallet{6 T3 `/ J$ y0 K3 r6 [* w6 S
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),4 y) V. {# o0 r2 A9 h4 o6 D
HashedSignature: priv.ToNeoSignature(),
}8 l1 e7 L3 T# ~, F4 W1 ]6 N/ r$ m
return wallet, nil) e: i- q0 v$ s% X, K7 T4 C
}
// derive derives a Bitcoin public key from a Bitcoin private key./ t. Q2 l, y+ `( C; B9 p, ~8 E2 r
func (priv *PrivateKey) derive() (pub *PublicKey) {
/* See Certicom's SEC1 3.2.1, pg.23 */' G) S1 s% J T/ c& u5 V* T
/* Derive public key from Q = d*G */
Q := secp256r1.ScalarBaseMult(priv.D)
/* Check that Q is on the curve */, Q( J' X8 l3 Y! w8 u
if !secp256r1.IsOnCurve(Q) {
panic("Catastrophic math logic failure in public key derivation.")
}
priv.X = Q.X& V. u# Z$ G5 G1 t, b7 S/ i
priv.Y = Q.Y
return &priv.PublicKey
}
地址脚本是怎么来的?
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:! p- n9 n, V7 j
前面是:0x21后面是:0xAC
2 M: ]" e. [5 z; k5 ~+ i! {+ ^
见btckey.go:
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes(), I* w9 O' \8 o8 A0 t( L
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC). K0 A( b8 y" B; B$ V
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:1 L9 S$ h1 _; c, l
见btckey.go:' L- q6 m) d9 S1 o8 I! W
/* SHA256 Hash */- V! `: g: e( ~! G1 c
sha256_h := sha256.New()
sha256_h.Reset()9 c& r$ v# t$ y
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */3 _3 h5 s: w# w, b' Z z! ]/ e. i
ripemd160_h := ripemd160.New()
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)" N$ `' {# K! _) Y
pub_hash_2 := ripemd160_h.Sum(nil)0 N& r; g- P- U/ w0 d. @. P1 N; Z
program_hash := pub_hash_2
地址是怎么来的?
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:" N: L L/ [" l# ]( g( N( D
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码4 ~8 w! E6 f" f0 w1 j, }
8 g4 q1 G( |2 l7 {& V
见 btckey.go 完整的由公钥生成地址的代码:0 X- W' f% }; }$ N; F: P
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.3 q' L$ Q% B/ r! d* |" O8 x4 S
func (pub *PublicKey) ToNeoAddress() (address string) {
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */" ~" z2 z+ l( X+ X! c
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...), R3 `# N- \$ W- P; K8 O: A9 O
pub_bytes = append(pub_bytes, 0xAC)1 W% u9 K* K/ I" O
/* SHA256 Hash */8 f' e, ?( j+ O/ o0 Y i/ z
sha256_h := sha256.New()
sha256_h.Reset()
sha256_h.Write(pub_bytes)0 L1 J. x! H) u* c5 H7 x. U- e
pub_hash_1 := sha256_h.Sum(nil)) U' U Q& m$ `% K
/* RIPEMD-160 Hash */' ^3 s, s2 H d. f7 O
ripemd160_h := ripemd160.New() b& S6 A6 g4 d
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)$ h% U5 V2 B M
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
//wallet version2 L/ ~6 u4 p) O2 L0 v
//program_hash = append([]byte{0x17}, program_hash...)# Q! w! |3 ]% T
// doublesha := sha256Bytes(sha256Bytes(program_hash))) {. \2 j( I: x3 @! i
// checksum := doublesha[0:4]
// result := append(program_hash, checksum...)) f4 ?# K ?+ `( q; P
/* Convert hash bytes to base58 check encoded sequence */2 Y: G3 t- ]1 o: F" U, ~' x
address = b58checkencodeNEO(0x17, program_hash)8 h7 R) J0 a# k
return address
}
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string., { M4 X( l5 \5 I+ f( Q+ @3 |! |
func b58checkencodeNEO(ver uint8, b []byte) (s string) {
/* Prepend version */
bcpy := append([]byte{ver}, b...)( l# Y1 G7 W( D5 A* _; p3 j
/* Create a new SHA256 context */6 l: y- g9 c$ H7 U9 W
sha256_h := sha256.New()
/* SHA256 Hash #1 */
sha256_h.Reset()
sha256_h.Write(bcpy)+ @# l" ~9 @9 m, V% f- T
hash1 := sha256_h.Sum(nil)) z5 [/ I L* t: L4 O) u
/* SHA256 Hash #2 */
sha256_h.Reset()$ N( H$ Y$ J" j4 z/ F
sha256_h.Write(hash1)
hash2 := sha256_h.Sum(nil)1 J; {; U" }+ u; f2 h; |" {% w
/* Append first four bytes of hash */3 c9 S8 M; I3 x/ n* q- G" i- U2 Q
bcpy = append(bcpy, hash2[0:4]...)
/* Encode base58 string */
s = b58encode(bcpy)
// /* For number of leading 0's in bytes, prepend 1 */* }& S; w/ c: R8 o7 F1 ], R
// for _, v := range bcpy {$ Z! i8 q) i1 [3 d/ j3 R+ ~
// if v != 0 {# c6 E% G" _- n% \% T2 D
// break0 [$ r3 x& \) d/ H" i
// }
// s = "1" + s4 Z8 U1 { L+ X# p( D. x J
// }
return s; o+ `3 H" A7 {7 T/ O8 s) k
}
WIF 是怎么来的?0 P& \! z X, g" x
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:! \& b9 o8 T2 c2 P
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码- P2 b# g# ?0 M5 Q7 b& R0 f
成为第一个吐槽的人