一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2386
0
0
见 Account.swift 类:
public init?() {$ g8 N* T% _3 D5 a- ?" d& d
var pkeyData = Data(count: 32) {; u- Y* p6 S, I8 e
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}, S' u* g7 b, f& K& b% A/ |$ F4 A0 ^
+ U: M: [( P" C) T. r9 y; J( F% U
if result != errSecSuccess {$ ~2 N( P' r( I
fatalError()
}2 z0 S- @( }4 b7 B7 ~/ T0 j1 w1 O
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
self.wif = wallet.wif(). {) c" y6 u7 M/ Q# f
self.publicKey = wallet.publicKey()& C6 j3 D n1 M! i! A
self.privateKey = pkeyData
self.address = wallet.address()
self.hashedSignature = wallet.hashedSignature()8 Q( ]$ }5 y; L$ o) x: V
//default to mainnet
self.neoClient = NeoClient.sharedMain& d) J) U# f$ R. \$ [& K
}
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:8 V- ?; d: i# V% i& \ D
/*!0 G. c/ r, z$ T1 b A) R
@function SecRandomCopyBytes
@abstract Return count random bytes in *bytes, allocated by the caller.
It is critical to check the return value for error
@result Return 0 on success, any other value on failure./ }8 v! U. D, V) x" k8 Y
*/ B1 g7 r0 } \ k& o0 s; i
@available(iOS 2.0, *)
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
随机生成一个32字节的 Data 数据,即 privatekeyData:8 Y& q( ?8 T! a5 q3 [$ q9 b. Y6 z$ x" ?3 k
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0) X$ I b1 S0 s
}
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private key9 B; ?1 ]' M0 I2 \% u" v
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)6 |* I: w& Q/ F, b$ j
var priv btckey.PrivateKey
err := priv.FromBytes(pb)7 n( e3 I! f2 a; {
if err != nil {4 B2 p$ e. w: r3 F# [" ]8 B
return &Wallet{}, err
}
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(), c2 M# n* ?1 U, }1 p) Z6 _7 q+ i
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),( `7 U7 e( s6 X- f7 P. n
WIF: priv.ToWIFC(),4 M0 N% u- v9 q2 O
HashedSignature: priv.ToNeoSignature(),
}% @/ W% {2 r* i" o8 ^1 Y& @
return wallet, nil8 ?2 v7 `. G; x4 I1 m* e
}
公钥是怎么来的?
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。% x" { B! v9 K! X
见neowallet.go 和 btckey.go:8 ^: Z/ {; y+ J N$ p. b
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {1 I. { G, m5 U" C
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey& j: a& _% D# C+ ~+ ~( i! k
err := priv.FromBytes(pb)
if err != nil {
return &Wallet{}, err6 g( Y$ u, X7 V* e
}0 m! \+ w) M. [% k, ~. q
wallet := &Wallet{; n* W: ]7 L6 L/ l
PublicKey: priv.PublicKey.ToBytes(),' M. q! M; B1 A C4 y' ~
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),2 A( y2 {- D' I1 T- e6 G4 d% {2 v" V
WIF: priv.ToWIFC()," T# {% `- v/ \) }6 d& w+ Y4 x
HashedSignature: priv.ToNeoSignature(),
}8 O; b; r* b O# _, i+ ^
return wallet, nil! [ P) o* _( g) y5 R+ n
}/ @% ^$ k5 r+ a# M+ u
// derive derives a Bitcoin public key from a Bitcoin private key.
func (priv *PrivateKey) derive() (pub *PublicKey) {
/* See Certicom's SEC1 3.2.1, pg.23 */+ P% }7 b( j; R
/* Derive public key from Q = d*G */4 k& W2 z' e; K2 C
Q := secp256r1.ScalarBaseMult(priv.D)
/* Check that Q is on the curve */9 P, h2 H8 ~+ q/ c; l' H
if !secp256r1.IsOnCurve(Q) {4 x7 s0 C, s ^# J5 p
panic("Catastrophic math logic failure in public key derivation.")
}
priv.X = Q.X- n* S" j: Q$ P+ p7 V
priv.Y = Q.Y+ s% q: c2 c/ I) }2 ~ D; O
return &priv.PublicKey4 t2 \2 D" g3 i0 O s5 q
}, ` B3 t% A8 ]
地址脚本是怎么来的?
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:% ^2 t- n' z f" e# i
前面是:0x21后面是:0xAC8 i. Z) Y( J% q$ T
9 G3 z) m+ Q4 c# E! I
见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)0 E: z6 T& w( F+ w2 H: m2 ?) B8 z
地址ScriptHash是怎么来的?4 v- J+ H+ I9 u
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */# Z. @) o0 A. A* w# h
ripemd160_h := ripemd160.New()
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)3 E4 c$ n i; @" Z9 x' o
program_hash := pub_hash_2
地址是怎么来的?
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:$ R) x8 T9 Z2 V
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码8 G9 u( N6 F" `
! b" O( A- v3 D6 D
见 btckey.go 完整的由公钥生成地址的代码:
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string./ f8 m: X# P5 J1 s# ~ ]
func (pub *PublicKey) ToNeoAddress() (address string) {
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */
/* Convert the public key to bytes */3 I# [; m; V0 H' \* {
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()
sha256_h.Write(pub_bytes)* f. ^* X) x' o# d6 i' N9 ?3 N# m* L( o
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */8 Q7 r' o2 v' q& q$ C- z0 X, R
ripemd160_h := ripemd160.New()4 Q, S m% q% @
ripemd160_h.Reset()- H+ \5 n" n1 d3 V2 X- p! W
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)% F/ h4 r+ {) F2 e) P$ f9 L" @/ a
program_hash := pub_hash_2
//wallet version0 {2 T& L- r, H ]7 U; U
//program_hash = append([]byte{0x17}, program_hash...)/ o1 T4 O2 ]! H' u6 h* c7 g/ [
// doublesha := sha256Bytes(sha256Bytes(program_hash))5 }* ~ \# J% _. O' J
// checksum := doublesha[0:4]
// result := append(program_hash, checksum...)
/* Convert hash bytes to base58 check encoded sequence */2 k: f$ C! _# D- t$ ^
address = b58checkencodeNEO(0x17, program_hash)
return address
}
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.% b) |. Q; G/ o9 a
func b58checkencodeNEO(ver uint8, b []byte) (s string) {- i( c% u7 ?$ H% p- b
/* Prepend version */3 |8 `) E" e _. e7 d
bcpy := append([]byte{ver}, b...)
/* Create a new SHA256 context */
sha256_h := sha256.New()
/* SHA256 Hash #1 */
sha256_h.Reset()
sha256_h.Write(bcpy)+ T* ]; r! K! m+ R/ m* ~
hash1 := sha256_h.Sum(nil)
/* SHA256 Hash #2 */
sha256_h.Reset()
sha256_h.Write(hash1), q; D @8 k1 d, E! |
hash2 := sha256_h.Sum(nil)
/* Append first four bytes of hash */" _+ P$ I1 a1 A% P( E ~ U
bcpy = append(bcpy, hash2[0:4]...)
/* Encode base58 string */0 b& U: w3 @4 n
s = b58encode(bcpy)0 P% b# K; b& S& e4 I
// /* For number of leading 0's in bytes, prepend 1 */
// for _, v := range bcpy {( l3 ]# M4 z }7 ^7 Z
// if v != 0 {
// break
// }
// s = "1" + s' ~6 Y) s% D8 u& r, a, q
// }
return s0 P: ?0 ^# o& u/ ^3 [# v
}
WIF 是怎么来的?
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码. S7 s# N3 X: g$ d X
成为第一个吐槽的人