一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2359
0
0
见 Account.swift 类:
public init?() {
var pkeyData = Data(count: 32)6 `5 F5 C; n9 y) R2 Q
let result = pkeyData.withUnsafeMutableBytes {1 d! P/ [6 E% X. d
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)$ R, P0 ]$ G9 ~9 ~2 N3 Z- }
}
if result != errSecSuccess {8 L- D3 x6 r- X# m$ R- }
fatalError()7 v! r4 F/ K: \6 n
}
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
self.wif = wallet.wif()# \2 h; k1 ?0 t
self.publicKey = wallet.publicKey()0 U3 j! g9 t% w, @! M+ A; Q
self.privateKey = pkeyData9 N* @3 {! m) ]1 I- ^
self.address = wallet.address()
self.hashedSignature = wallet.hashedSignature()8 W& t, V" |7 y" ]/ w& `0 ~
//default to mainnet
self.neoClient = NeoClient.sharedMain' Y6 e/ z6 i0 K9 E/ H! K. _
}* M0 K4 l7 @+ R% f9 N2 i; Z6 Q4 j
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
/*!
@function SecRandomCopyBytes# ^& w3 x2 `, S0 V5 B
@abstract Return count random bytes in *bytes, allocated by the caller.
It is critical to check the return value for error6 x. Z' X6 E* S/ v% H8 h% v
@result Return 0 on success, any other value on failure.* N" D+ q, g& ]' t; ^ F$ @& b H* M
*/
@available(iOS 2.0, *), h! N+ \& x2 w* g
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
随机生成一个32字节的 Data 数据,即 privatekeyData:- y& I7 K8 m4 h4 G2 j
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)- W9 s+ G, ~+ l2 V& W
}0 v1 [- ~! J+ I8 a5 x; h) F
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:: i3 b3 W! f3 c2 ^$ a* {
var error: NSError?& D- ]5 t. I$ r- ]
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private key* Q: p! o& f2 S& c4 y
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)) o3 |1 N% \: s# \( x
var priv btckey.PrivateKey
err := priv.FromBytes(pb)" C+ ~0 \& d* l0 P
if err != nil {& A6 N& o* r! e8 @ @
return &Wallet{}, err
}
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),) }/ y$ l. ?9 ~
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),9 s- A# j+ a- l) _
}1 c( f8 S6 a0 S, p! f5 ]
return wallet, nil' o: m& e# j6 I, o% w/ m! p
}
公钥是怎么来的?0 c$ J* @3 M. ?2 z3 B* [+ x4 V
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。" c/ v: S- ~" _, M! h4 R
见neowallet.go 和 btckey.go:; M5 Q( M' w# E8 s5 B$ ~( Q
// Generate a wallet from a private key( M- f/ h/ t! k, c) o! ~
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)9 l9 ?# ]% B, ^9 j6 g
var priv btckey.PrivateKey
err := priv.FromBytes(pb)' D4 P; S) x$ S) G- |1 ]( j
if err != nil {
return &Wallet{}, err
}9 ~$ n0 g6 }" v" q$ U( g
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),- c9 B" l) D# W& g
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),
}4 g: [5 h. H% y! }8 P0 S# o
return wallet, nil
}
// derive derives a Bitcoin public key from a Bitcoin private key.
func (priv *PrivateKey) derive() (pub *PublicKey) {( e R* S7 b' W
/* See Certicom's SEC1 3.2.1, pg.23 */
/* Derive public key from Q = d*G */& u; v" W; n" R. E- ?! n& B
Q := secp256r1.ScalarBaseMult(priv.D)/ q: T, l) r. ]9 Q' m
/* Check that Q is on the curve */' w- H% m" ^2 F$ T3 d: D
if !secp256r1.IsOnCurve(Q) {4 m: R6 t0 k) s# a: \( |
panic("Catastrophic math logic failure in public key derivation.")
}/ ~) V, |- o g& `# }% N
priv.X = Q.X
priv.Y = Q.Y
return &priv.PublicKey+ ]3 i8 t/ G' n1 v" Z9 f
}
地址脚本是怎么来的?; \8 R5 i- y, A5 D
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:
前面是:0x21后面是:0xAC5 v6 G7 y; y6 b2 E6 g5 t g _
见btckey.go:+ s4 }/ c1 T0 l1 r* M5 ^( y
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)1 v3 P0 X( m4 E2 b) l! Q# v% w" _8 y, W
pub_bytes = append(pub_bytes, 0xAC)" D! ?$ ~" I5 H. B8 u! ~
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:' Z# O* g* W' x# [) h$ o
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()* U5 v" {- S6 o0 `5 H
sha256_h.Write(pub_bytes)/ U: m' I% l$ ?: H% J( y
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */" e& E. R3 j3 L' E4 a8 T* a
ripemd160_h := ripemd160.New(). D& I8 ]4 q2 M; i% `; X/ F
ripemd160_h.Reset() F1 t- @7 n9 w8 _) r& a
ripemd160_h.Write(pub_hash_1)# F) o/ ?$ c3 k0 X
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
地址是怎么来的?
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:1 L" Y; {3 c/ Y a) Z
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码# f, _9 ^. u0 b- ?6 D0 `/ Y$ J
( C z( f2 T; k4 ?* |
见 btckey.go 完整的由公钥生成地址的代码:: D9 l5 o8 F( W# f* d( X6 O7 a
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
func (pub *PublicKey) ToNeoAddress() (address string) {! o- b+ }0 B+ u6 x' s; x8 A) V
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */1 e9 q2 D6 x6 w# V
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)5 ~. F/ _) s5 _' l* t+ q! ^: u4 B0 Z
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()# f- f3 }5 g+ _- @+ g0 q7 E# W
sha256_h.Write(pub_bytes)5 U. S. ]7 }( m; T+ Y+ B V8 F
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()& O0 S' a5 ? a2 s
ripemd160_h.Reset()/ y, R+ S3 y9 C! z8 I
ripemd160_h.Write(pub_hash_1)8 i/ c3 \) c6 U0 B- d: a
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
//wallet version
//program_hash = append([]byte{0x17}, program_hash...), N( T3 L5 c4 M4 [" p a& o
// doublesha := sha256Bytes(sha256Bytes(program_hash))4 g% n/ q# ~2 J' ` M" J1 b, a
// checksum := doublesha[0:4]& p1 C' W5 Q" [! c0 c( _: n
// result := append(program_hash, checksum...)
/* Convert hash bytes to base58 check encoded sequence */: s2 @: K% X r8 g6 L
address = b58checkencodeNEO(0x17, program_hash)0 j$ e( f; X9 Z1 l8 P
return address) T! s$ w: Y5 ^* D3 f6 ~/ ]: k9 O* W
}( n5 w3 G# G$ R2 N
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
func b58checkencodeNEO(ver uint8, b []byte) (s string) {
/* Prepend version */, I3 @' A7 `- b; c, X3 V6 e
bcpy := append([]byte{ver}, b...)
/* Create a new SHA256 context */
sha256_h := sha256.New()
/* SHA256 Hash #1 */
sha256_h.Reset()$ ?8 p7 d9 s1 u$ b5 X
sha256_h.Write(bcpy)* m. N' _( X4 A- ?+ j
hash1 := sha256_h.Sum(nil)$ z! ]" N8 m8 c* @6 B2 Y
/* SHA256 Hash #2 */
sha256_h.Reset()
sha256_h.Write(hash1)
hash2 := sha256_h.Sum(nil)
/* Append first four bytes of hash */- T: w- [' @) ?
bcpy = append(bcpy, hash2[0:4]...)
/* Encode base58 string *// i `9 C/ @* Z. `+ Q$ B
s = b58encode(bcpy)
// /* For number of leading 0's in bytes, prepend 1 */; ^7 k: ]* n1 q `$ s1 B7 G
// for _, v := range bcpy {2 l A3 `# U) d. q3 [
// if v != 0 {
// break/ P& @6 @1 U' R6 I7 _$ h
// }# P* m+ g# c$ f' }5 Z4 p( @' \
// s = "1" + s
// }. F9 x. E" \" B' x) G% t
return s. b# G. F, A% @" F- C6 d
}
WIF 是怎么来的?
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码
成为第一个吐槽的人