Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

一文弄懂:区块链钱包的私钥

放弃六月们
2328 0 0
私钥是怎么来的?私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。
7 L3 K; C" _% a3 P: X$ Z; x& o见 Account.swift 类:
9 V7 L8 G8 G6 `% L( h8 [/ o1 \5 Lpublic init?() {
# i! L# I  F" E1 N        var pkeyData = Data(count: 32)
9 e8 J% e% g- x$ |, Y" m        let result = pkeyData.withUnsafeMutableBytes {: F+ Y1 D9 Z7 k1 {  \
            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
; ^0 l4 K2 B+ @0 k  i. j        }, h3 `+ X0 L9 p6 w: K! w  {
        
$ `7 P) I3 G. C6 T        if result != errSecSuccess {
4 \) r/ J# l. O$ w: W            fatalError()
( c3 N$ X) J8 c% w& k( j* f: Q& h        }
+ Q- s6 R* W1 {8 H$ e' l! S          b. X5 g5 s& @, K+ @- X1 j% N
        var error: NSError?
9 ?' A. |2 m: ~  p0 e- S        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }5 O* G9 T' F4 Q: h$ B
        self.wif = wallet.wif()
* M7 r0 d+ M( h& m2 N' R        self.publicKey = wallet.publicKey()
+ _/ [1 F0 I- c, }) @: X        self.privateKey = pkeyData
$ M, J; p! f; M$ O( @8 n; q7 H3 ^* J        self.address = wallet.address()& {  h- Y, f  k+ q* z
        self.hashedSignature = wallet.hashedSignature()1 F# Q! d$ x$ }( Z/ u. Y8 R
        //default to mainnet
& f8 Z  c5 R4 O3 w) j        self.neoClient = NeoClient.sharedMain
# Q. B$ l5 T& h) K8 u; e( ]/ E    }
8 d* N0 Z+ b( w, D它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:; r6 l1 C# k' J" Z# P( P
/*!( T6 e; U, a  `
     @function SecRandomCopyBytes& b- t. K8 C2 Q0 j, R0 s
     @abstract Return count random bytes in *bytes, allocated by the caller.3 H/ Y1 r3 o! z7 v
        It is critical to check the return value for error
2 \  @9 e& [% ]; w; Y$ C     @result Return 0 on success, any other value on failure.
8 ?) ~0 s, m" B% A4 r*/9 |- |5 Z4 Z/ K% X
@available(iOS 2.0, *)
8 q/ Y2 f, r, y, Rpublic func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int325 U, W) _. `" G. j1 \' g9 ^
随机生成一个32字节的 Data 数据,即 privatekeyData:
$ h& G0 k1 \7 s2 v( X; p0 ovar pkeyData = Data(count: 32)
! c+ k% o) ]) ~9 W2 S5 n        let result = pkeyData.withUnsafeMutableBytes {
( B. ^3 C2 l3 k! n" e3 U7 O            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)2 M7 C& ^3 Y7 c) @# S- J
        }
! x- a! s. ]! {) ^+ j! ~& }" w然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:2 Z7 n  m. \, {& ^. R6 J. p
var error: NSError?
5 |) A8 F& G- iguard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
. ^2 i5 o6 J8 k' a: Y$ C3 Q// Generate a wallet from a private key% p: f0 a: H, x; R1 v; n
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {; w! i5 L8 y) @; h# n# T
    pb := hex2bytes(privateKey)
+ K: W% K$ S4 L8 k+ r# @    var priv btckey.PrivateKey
$ @( c0 L- Y0 R2 F. J  X    err := priv.FromBytes(pb)1 C/ U5 l7 C+ \
    if err != nil {
& o5 l" {$ G/ ^5 y        return &Wallet{}, err8 m6 ~" _: ^4 t0 r* h5 I  W# j
    }, U$ \: u% T9 \' t6 C  u
    wallet := &Wallet{. d) d# C9 B# U; V; v. w. G
        PublicKey:       priv.PublicKey.ToBytes(),
; m- @5 d* m! s# X( A0 Q. l8 u7 z/ a  N        PrivateKey:      priv.ToBytes(),
0 o0 e; v3 }" S" D        Address:         priv.ToNeoAddress(),$ m9 B. l8 m0 F: j! n
        WIF:             priv.ToWIFC(),
$ R' Q/ z0 C  j5 b; e        HashedSignature: priv.ToNeoSignature(),
$ @$ A3 W. s2 ~' w; k; o& G: z    }/ V9 S6 ^. _5 E; ?1 y3 m
    return wallet, nil
& @  p3 f+ i' S0 o6 R* l, ^}. u/ ^. ], T( ]& D3 R. o$ e+ K
公钥是怎么来的?
( x8 G! e) d7 y6 D/ @% U: t公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。: s+ B" _+ A; |2 F- `
见neowallet.go 和 btckey.go:0 Y, A& H/ v( V
// Generate a wallet from a private key
/ O7 i# x5 [/ o" A, S1 ?func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
1 b7 N/ t- v) c    pb := hex2bytes(privateKey)
1 Q. q1 k0 x$ q. g    var priv btckey.PrivateKey9 h8 F$ ^3 S; N; ]5 e# n! y
    err := priv.FromBytes(pb)6 h, X# a, K! ^) k( u
    if err != nil {
" J+ s4 e" [5 h! s; V7 v" q  }        return &Wallet{}, err" `5 n/ z' y8 [- G& c  V
    }$ g1 P  s* f9 W& B2 g" w! U1 e0 z% z
    wallet := &Wallet{
  K% ?2 x5 V% H! x        PublicKey:       priv.PublicKey.ToBytes(),0 A/ o! {, y( J% q" p0 z- M
        PrivateKey:      priv.ToBytes(),
/ Y$ g2 N5 ]* c/ |$ s2 t        Address:         priv.ToNeoAddress(),
. C2 s# P% w( H+ f- k        WIF:             priv.ToWIFC(),
7 S# i7 a; h5 \        HashedSignature: priv.ToNeoSignature(),5 ?* D6 F. Z9 S+ I8 g' V5 F4 ^
    }8 R# M+ b6 N9 O) ?
    return wallet, nil- _( t* a& N( w0 F% F
}
$ W' |. u" @4 A4 H// derive derives a Bitcoin public key from a Bitcoin private key.5 p" e7 m+ V" n
func (priv *PrivateKey) derive() (pub *PublicKey) {
- V3 `' L& y0 Q- [8 H6 m    /* See Certicom's SEC1 3.2.1, pg.23 */
% v; D, v, A" b6 D    /* Derive public key from Q = d*G */1 Q$ h9 F, E; {9 K' n
    Q := secp256r1.ScalarBaseMult(priv.D)
5 \6 }) m0 q  L& Z! R. @    /* Check that Q is on the curve */
2 U6 q: b. v) m- ~+ |    if !secp256r1.IsOnCurve(Q) {
& s" J( H9 ^! m: {9 S+ `! F, a        panic("Catastrophic math logic failure in public key derivation.")
& _# R: ]0 d0 v; T% w' I    }) M7 {. F( v* G3 P
    priv.X = Q.X
: Y9 y/ W; o, K7 c, R    priv.Y = Q.Y
4 X2 M: p9 [( {+ V    return &priv.PublicKey8 t( |) v  P4 D; x3 v9 R
}$ o% u1 d$ ?% r3 D
地址脚本是怎么来的?$ D) t4 i; P; e2 V2 D/ j* P8 S  i
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:9 V1 t3 t6 r( z) \# g
前面是:0x21后面是:0xAC
9 R9 a0 ?5 ?8 D/ H1 d; J

7 {9 g) Y# k4 M% X# p/ o1 h见btckey.go:
- g6 X/ K3 j% d( Q/* Convert the public key to bytes */
  ?/ v! b6 U" f& ^    pub_bytes := pub.ToBytes()2 K7 i* ~8 {8 Q' Z, h5 v8 P. L) ^
    pub_bytes = append([]byte{0x21}, pub_bytes...)
6 o! [1 L) j1 n" l+ \- P    pub_bytes = append(pub_bytes, 0xAC)
' i  z4 ]4 E' O7 k% ^% y地址ScriptHash是怎么来的?
0 A5 O& @4 W: h地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
0 J. c, F7 G" a7 k' a见btckey.go:
4 h4 A* j3 `( p* t) N( o( t/* SHA256 Hash */
: A; N3 Y1 ^" g    sha256_h := sha256.New()7 d6 D* n/ Z# a( T4 e
    sha256_h.Reset()3 Y( C( _" f1 o
    sha256_h.Write(pub_bytes)
% X% U& E0 m7 j0 T; F4 v    pub_hash_1 := sha256_h.Sum(nil)4 }/ H4 ~! r7 ]& V: a; {, N! }
    /* RIPEMD-160 Hash */
8 b+ @$ R2 z* Q& m    ripemd160_h := ripemd160.New()
* r4 }1 m5 q2 o' ~9 q; s    ripemd160_h.Reset()
& C9 P1 D0 u. E4 @  |* J$ ^, G    ripemd160_h.Write(pub_hash_1)
# @7 b6 i# A' m" c9 \/ ]2 O/ f5 G2 q    pub_hash_2 := ripemd160_h.Sum(nil)7 W  \8 z+ K1 [1 J/ I
    program_hash := pub_hash_2
* `8 D( r7 x3 f+ v! a/ s地址是怎么来的?
7 W5 U0 x8 y, q- [6 {1 ^地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:8 C. o3 D9 g, k( K" ^7 \% z
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码
, d& h) o5 K2 {% G6 S& t

3 ?0 N: O- H3 {! P( @& E* S9 e- _见 btckey.go 完整的由公钥生成地址的代码:
$ P8 m* U8 Z- I( X0 ^/ c// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
( [  l0 D: }% `" q2 i( wfunc (pub *PublicKey) ToNeoAddress() (address string) {: b. W4 D1 n8 X! v
    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */
* i$ N' k, i  P# q    /* Convert the public key to bytes */( R: \/ e% e8 P5 q$ j! S; U
    pub_bytes := pub.ToBytes()
7 p! Y- s/ k3 K' @% u    pub_bytes = append([]byte{0x21}, pub_bytes...)# f8 d) C, {7 S8 x6 B2 W$ Z8 d
    pub_bytes = append(pub_bytes, 0xAC)
# n! H  ^) [. R4 T8 \: p" `    /* SHA256 Hash */+ v% W# _8 v2 A( V9 ?6 K+ c' x& f
    sha256_h := sha256.New()4 e* I6 w4 e8 V) Z5 F6 T1 C9 C
    sha256_h.Reset()
$ q3 Y+ H+ I- w* ]( h3 @5 v    sha256_h.Write(pub_bytes)/ C8 F: a' ]6 L$ J: ]0 c, g5 j
    pub_hash_1 := sha256_h.Sum(nil)' M- c6 p+ E. Y4 h& B) @
    /* RIPEMD-160 Hash */
/ }3 a) i/ w# m- v0 L8 u; b: k1 z: S    ripemd160_h := ripemd160.New()% }' ~+ F- r$ p/ R: C/ C. N* G
    ripemd160_h.Reset()
& E8 l0 x2 X8 p/ s, Y    ripemd160_h.Write(pub_hash_1)3 n7 p  @0 A# O7 Q2 z1 n
    pub_hash_2 := ripemd160_h.Sum(nil)
# o3 k. L! f: k" l. t) T    program_hash := pub_hash_2. J/ M; B: E% N$ J9 a0 G4 E
    //wallet version
' X/ s2 Q9 _% `, ^% C    //program_hash = append([]byte{0x17}, program_hash...)
1 w# s; _; H1 e% W  y$ ^$ ?! l% G    // doublesha := sha256Bytes(sha256Bytes(program_hash))
9 c( @7 X1 p: }" K    // checksum := doublesha[0:4]' H# S  D% [8 g0 c
    // result := append(program_hash, checksum...)
9 r7 U4 c: X/ v- j1 m, }8 l5 w    /* Convert hash bytes to base58 check encoded sequence */& J& V. A# s# ?2 O
    address = b58checkencodeNEO(0x17, program_hash)
' }. r1 T( ?0 _: P) x    return address
/ D6 S' v0 V% H* }$ [}, k' V; }6 K8 v* [
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
$ W" i* U$ @) r0 N$ u/ ~. ifunc b58checkencodeNEO(ver uint8, b []byte) (s string) {% P& O. X% Y& A9 a
    /* Prepend version *// o* h* I2 e$ ~; F* \) q! n2 Y- v: H
    bcpy := append([]byte{ver}, b...)
% d* Z. k& q" W: I    /* Create a new SHA256 context */1 M+ s: ~! C: k- C" t8 z; D6 Q
    sha256_h := sha256.New()
% R" i) d8 [4 ^% a* W  _$ q    /* SHA256 Hash #1 */
( Z- V- {" d, B' P' h* s) X8 Q+ `    sha256_h.Reset()
( ]2 l" |8 d  m  S1 Z: b. _    sha256_h.Write(bcpy)
  ~6 J' W4 q7 k! t$ y# t# I6 u6 U5 p    hash1 := sha256_h.Sum(nil)
' O9 `" m0 _1 T    /* SHA256 Hash #2 */( s' Z1 Z9 ?: _" M* M
    sha256_h.Reset()
$ t: o: M5 H. L$ i    sha256_h.Write(hash1)3 y% r" {) i% P2 y! Q1 b
    hash2 := sha256_h.Sum(nil)
: W, E. B# x4 T8 N9 c    /* Append first four bytes of hash */
' J! ]$ L# g% p$ X- s0 i3 K    bcpy = append(bcpy, hash2[0:4]...)% L; }! d: h9 j3 v( Q1 N) I5 g; h
    /* Encode base58 string */! Y- N4 ~$ Q4 C+ @+ }+ O
    s = b58encode(bcpy)( _! \$ ^% B9 Y/ A2 ~
    // /* For number of leading 0's in bytes, prepend 1 */" N4 I/ R8 O* C- m" @; U
    // for _, v := range bcpy {5 s& w7 }( @; g) W3 g* ~- O
    //  if v != 0 {
- p8 ?, z# g: d2 J! r& ?' f) L    //      break
8 S% n* ^/ U+ j# ~% n    //  }/ b& H% n. @. a  f, D. O( ~
    //  s = "1" + s
' A" u6 \% u8 ~8 d. h) R4 S+ G' F    // }* ?$ e- v5 R5 K; h) J
    return s$ W) m0 T  g2 d8 Y6 r1 l- u
}) v/ r' p+ `2 H1 _1 f  m: v
WIF 是怎么来的?- j* n6 Q. _' m$ n5 Y3 P
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
/ z6 ?2 z0 o" c3 G# }前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码" V0 q5 K& n: k
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

放弃六月们 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    8