一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2332
0
0
见 Account.swift 类:
public init?() {1 k3 u0 D7 Y1 G% i) e
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {3 A8 P+ _6 X2 @$ U
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}; r t' ^3 a9 K
e: z, a5 w* B
if result != errSecSuccess {
fatalError()
}* _, V) e5 y+ G. Y' I
. A5 I u E& k
var error: NSError?( |& C4 D2 Z. D& k$ E
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }. D$ Z+ P0 i. A2 {$ q d
self.wif = wallet.wif()
self.publicKey = wallet.publicKey(): ^8 T0 a7 {" a7 F! u
self.privateKey = pkeyData2 T- M: t9 ^% W) a8 F; F4 Y7 b, Q
self.address = wallet.address()
self.hashedSignature = wallet.hashedSignature()# Y$ u: ]9 f+ o2 f$ Y9 l
//default to mainnet! F6 E# s: _2 `3 E
self.neoClient = NeoClient.sharedMain
}
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
/*! y8 ?. A$ @3 n7 o' x7 W( o9 Y a
@function SecRandomCopyBytes: Q; `4 d* m- f* x$ F0 @: ?
@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.7 l9 @* @0 |: l7 N# V: J$ |
*/4 [/ G& M8 U3 H
@available(iOS 2.0, *)
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32: a) n, ~ ~" J# c5 F( w. m, Y
随机生成一个32字节的 Data 数据,即 privatekeyData:
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {7 t$ l5 l( P" B+ i
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private key# u6 F' B/ I6 H4 I5 x
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {, y# o0 g) b! j* R U
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey
err := priv.FromBytes(pb)# U2 D& t; ^6 s. [; S
if err != nil {
return &Wallet{}, err
}
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),9 n1 C& t* S, J# m9 j% |: [
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),% l% `/ U, i- U8 E: {
HashedSignature: priv.ToNeoSignature(),# c, Y4 I, i3 f6 y) `0 S+ ?8 R
}- K8 i/ o, @: e8 v5 k" L! n
return wallet, nil- Q0 w1 y8 X: w* v0 Z
}2 T. \! e/ _3 k5 L V
公钥是怎么来的?) K( a* Y/ A' ]7 [ @
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。! ?+ _( A2 `6 v1 }
见neowallet.go 和 btckey.go:
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {9 Q/ u7 X8 V9 I. n% N
pb := hex2bytes(privateKey)$ Z$ ?" i; G& l1 p+ P# P
var priv btckey.PrivateKey. ?2 b9 z* y( S2 H6 o
err := priv.FromBytes(pb)
if err != nil {8 Z. Z. \: x8 x# @& r6 [5 x
return &Wallet{}, err# J* r, D/ B% `" |
}
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),2 [ Z0 {5 x! V/ k
PrivateKey: priv.ToBytes(),: H% B, @9 N f: V: A4 S$ T+ _
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),
}
return wallet, nil
}9 m7 I6 M+ ~- F
// derive derives a Bitcoin public key from a Bitcoin private key." v8 K4 a2 W( y- g! E
func (priv *PrivateKey) derive() (pub *PublicKey) {
/* See Certicom's SEC1 3.2.1, pg.23 */% ]3 j- F: ?2 t: P( @3 b# {0 N
/* Derive public key from Q = d*G */
Q := secp256r1.ScalarBaseMult(priv.D)
/* Check that Q is on the curve */
if !secp256r1.IsOnCurve(Q) {7 x- J- [' ]! m# k
panic("Catastrophic math logic failure in public key derivation.")
}/ j5 e) V4 {# W3 c) ]
priv.X = Q.X& Q) h, G1 T3 d- n- Q
priv.Y = Q.Y
return &priv.PublicKey* d: N3 R- P; I; C# h" q9 ?
}
地址脚本是怎么来的?+ \6 e7 Z. O! P; j ?8 R
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:5 v: ^( s! Z0 J. Q8 {
前面是:0x21后面是:0xAC( h* j' d& O, q. D8 T) n( ?% H
6 q8 z* ?/ k6 Q3 s
见btckey.go:
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()+ R4 [( M& ]2 D& H$ d* c
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)0 c5 j. A3 d! D" H$ ~" w
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:
/* SHA256 Hash */& O8 y3 c/ G1 i: H4 V" c& _
sha256_h := sha256.New()
sha256_h.Reset()
sha256_h.Write(pub_bytes); F$ F/ v5 ]; v5 y' J/ l# w$ H5 S
pub_hash_1 := sha256_h.Sum(nil)" Y! {9 T/ ^- a" x
/* RIPEMD-160 Hash */7 c" ]0 h" o' s9 {
ripemd160_h := ripemd160.New()
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1). @3 i9 c! I( ]% v/ }! A& Z1 H
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_22 u; R2 D* _, B7 A* N8 H$ r
地址是怎么来的?+ L5 E, `0 C2 V8 Q; H4 k O/ x" G
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码+ l- m2 X$ H- s0 W
; k3 Q& Q2 Y3 C+ r9 j- H/ }5 \
见 btckey.go 完整的由公钥生成地址的代码:
// 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 */; I4 X" U' ?8 r* S: A
/* Convert the public key to bytes */4 H, H/ q+ W, a
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)& Q; A# ~, w3 m, ?! p. |( Z2 {- v
pub_bytes = append(pub_bytes, 0xAC)$ Q1 k- [+ Q8 X
/* SHA256 Hash */
sha256_h := sha256.New()6 H U% |7 o+ I0 B# B
sha256_h.Reset()/ A8 Z2 g7 U; a. G- T I4 L+ X
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil); W' Q1 C! S& O4 ~* Q
/* RIPEMD-160 Hash */. g0 z" D9 ^9 o9 |. i1 l- R# A
ripemd160_h := ripemd160.New()1 L! i* ~' @$ G K$ w
ripemd160_h.Reset()) z; d2 D8 B0 P. `+ @8 y. z9 |9 h
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2& N. } E2 g& g# m' [
//wallet version
//program_hash = append([]byte{0x17}, program_hash...)
// doublesha := sha256Bytes(sha256Bytes(program_hash))
// checksum := doublesha[0:4]8 j" O3 f1 O. A4 c ?8 S
// result := append(program_hash, checksum...)
/* Convert hash bytes to base58 check encoded sequence */
address = b58checkencodeNEO(0x17, program_hash)
return address
}/ `0 a: E [& B7 m3 Y5 _
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
func b58checkencodeNEO(ver uint8, b []byte) (s string) {0 u0 ]: r/ Q5 L- d2 i
/* Prepend version */' f( N. X! q. k
bcpy := append([]byte{ver}, b...)
/* Create a new SHA256 context */
sha256_h := sha256.New()* L* [3 C4 P7 L2 @
/* SHA256 Hash #1 */
sha256_h.Reset()- e1 W9 D3 }( `2 U6 ?$ o" G( L p
sha256_h.Write(bcpy)+ h8 _. J+ @! N/ r
hash1 := sha256_h.Sum(nil)4 e) L9 N/ q' X8 e- _
/* SHA256 Hash #2 */9 Y! v B" ^6 O3 m$ c. [
sha256_h.Reset()+ ]6 {5 \" d* N, H( v
sha256_h.Write(hash1)
hash2 := sha256_h.Sum(nil)
/* Append first four bytes of hash */
bcpy = append(bcpy, hash2[0:4]...)! _& c% U9 X) b+ L7 B
/* Encode base58 string */( R8 a3 n7 ?% j X5 B4 N& }
s = b58encode(bcpy)
// /* For number of leading 0's in bytes, prepend 1 */5 X% |9 J7 j6 t6 D/ Y9 ]
// for _, v := range bcpy {
// if v != 0 {2 H( Q1 D$ i) C$ L+ R5 y
// break
// }" Y- F0 c0 o0 Q9 B
// s = "1" + s
// }
return s
} F8 @4 H% w# T2 L( h$ |
WIF 是怎么来的?9 r7 P; T( M+ r9 W: D' U% q
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码 B$ @6 A+ _7 o/ m) X4 T
成为第一个吐槽的人