Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

放弃六月们
2329 0 0
私钥是怎么来的?私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。
& Q& j' h3 o" b& g$ y见 Account.swift 类:
6 S! U2 j0 G* d( o3 }& r: ?public init?() {' ^) h+ A$ L( {% u
        var pkeyData = Data(count: 32)8 d8 Y. [* A$ ~/ u. l4 f% V( m! n
        let result = pkeyData.withUnsafeMutableBytes {
7 E& v$ h5 j4 ]- U            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
5 k! t- T* M) Z+ _        }
8 M8 |: L! X3 |: J: u        
3 }; e% N' O" a- A0 k3 v        if result != errSecSuccess {
3 R2 s6 t9 d6 k, @' a# K& c            fatalError()3 w7 w8 i% a9 D( a. {( W: r( l
        }) i1 h7 y2 }( y: _
        
) |* O$ k5 [- Q8 K1 T5 t5 r        var error: NSError?
5 e% b. n/ j. a        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
1 W' k6 p6 y% _: _- I        self.wif = wallet.wif()- T, P5 U+ T# Y6 R, r: X
        self.publicKey = wallet.publicKey()! K! N* E1 B# k  Z9 o
        self.privateKey = pkeyData
5 [' C4 O. r  T# Q9 U5 J8 J) K        self.address = wallet.address()
3 X6 Q, H8 ]$ W7 r8 _4 L4 ^        self.hashedSignature = wallet.hashedSignature()# {: ]" q0 n& G
        //default to mainnet0 ^8 x" S3 R$ Y9 m2 b. H
        self.neoClient = NeoClient.sharedMain
! t3 a1 n: p) u$ b- g  a    }
/ m( d1 g4 x; Y2 ]* }3 u5 y5 k7 P" \它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
/ c3 z0 m( b0 R0 a, _/*!& T$ u  J! m* s4 Z2 x  a
     @function SecRandomCopyBytes
% M% R- B: S! d2 n4 h     @abstract Return count random bytes in *bytes, allocated by the caller.
0 i+ Z7 I- }5 l; n        It is critical to check the return value for error/ Z5 g9 K+ U+ J% i
     @result Return 0 on success, any other value on failure.
& G7 ]2 \0 y9 n0 ]*/2 p* U2 f& z4 Q" w, u$ l; L" ?
@available(iOS 2.0, *)! m& Q7 b+ f, m" I% ]" N7 F" W
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int321 v% S: n9 G( Q: a
随机生成一个32字节的 Data 数据,即 privatekeyData:8 n6 |5 m- k6 U# h1 y
var pkeyData = Data(count: 32)
" n( ~2 @! g/ S; I; C& c* b        let result = pkeyData.withUnsafeMutableBytes {
2 G) F- D: D# y4 T* t* |) {8 |5 E/ f            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0); f4 k4 _( q& t
        }
8 |5 m; U) o1 U% i然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
  |9 Y6 i6 H+ S% c; q7 Ovar error: NSError?
: r& A+ x! T/ D6 mguard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
+ `  v7 q% F; t2 g// Generate a wallet from a private key; p+ u- G3 n" @0 D" q4 c, X
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
) j* _* y! q  J; f% ?" Z  M    pb := hex2bytes(privateKey): Y5 B' C2 c1 B% ]$ w# h8 D- d0 a
    var priv btckey.PrivateKey
  d& S7 a, u$ j3 o7 d9 x/ W    err := priv.FromBytes(pb)
: ]8 z4 ?2 h2 H& p% |    if err != nil {% z" a; t  ]$ d4 C1 n3 X
        return &Wallet{}, err4 A: y: G; ?# t$ u
    }$ h/ _: k+ p1 l. ?
    wallet := &Wallet{% E2 r, g2 a( W
        PublicKey:       priv.PublicKey.ToBytes(),
: V/ N$ O5 B/ z3 u# V        PrivateKey:      priv.ToBytes(),
# h+ D0 ^- A% i$ f  \        Address:         priv.ToNeoAddress(),! {/ T4 j4 G% i9 X
        WIF:             priv.ToWIFC(),5 x* T$ J. Q9 Y
        HashedSignature: priv.ToNeoSignature(),. B3 B, R" H; v+ m% w
    }: J% c; F: c2 U. \1 n- V$ ]
    return wallet, nil
2 z. n2 m" _( S, w3 y( W}5 O% r" b2 ^( V; h
公钥是怎么来的?# ]4 v  D* I$ O+ l. r5 l
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。; `( a0 N  }4 Y! y
见neowallet.go 和 btckey.go:
- O2 H3 m" f+ I, R// Generate a wallet from a private key
2 G1 v. |/ m8 m- ]5 ?' C$ x3 R, nfunc GenerateFromPrivateKey(privateKey string) (*Wallet, error) {0 H, a  q- ^& P& W# n3 O
    pb := hex2bytes(privateKey)
' z4 f# x2 [* `  ~    var priv btckey.PrivateKey
2 t9 s! B' R& v0 t" F$ G, t8 g9 e* F    err := priv.FromBytes(pb)
2 G$ j/ ]& r' ?7 o: ?7 g( B    if err != nil {% M$ n. W& r" S) e! X* D4 P
        return &Wallet{}, err
5 [' W' l$ J# K& R" _1 h  H4 F    }% s$ n; Y2 ^5 \1 u( ]+ ^
    wallet := &Wallet{( @" F( T" t8 v2 ?9 k, O( D* Q6 l2 ~" Z
        PublicKey:       priv.PublicKey.ToBytes(),+ n6 s1 Z7 a# Y' e  I: U4 c
        PrivateKey:      priv.ToBytes(),
& [/ U) g/ }/ L6 o, d        Address:         priv.ToNeoAddress(),
3 }6 h* P" n# ^        WIF:             priv.ToWIFC(),; b4 y7 O5 U! S% a
        HashedSignature: priv.ToNeoSignature(),
; l/ P' A; i& p, ~/ P0 ~. I    }
' R7 M* s# m# E6 t6 j5 b" n/ \  d    return wallet, nil
! o7 q7 i. h- n6 d. ~+ }}
$ F+ p7 r* s7 V% @! `6 h3 Z& ]# i// derive derives a Bitcoin public key from a Bitcoin private key.. I3 d) S) U, @& n2 K
func (priv *PrivateKey) derive() (pub *PublicKey) {( H( o( d- n# M
    /* See Certicom's SEC1 3.2.1, pg.23 */
* i5 O9 ^) K1 t9 W4 b" c. n3 _! i1 I    /* Derive public key from Q = d*G */
, f: v- l, [/ j; r1 S    Q := secp256r1.ScalarBaseMult(priv.D)
5 L" \3 M; K- K9 B' J' n1 d8 `    /* Check that Q is on the curve */
. t+ n; m5 R' q: P    if !secp256r1.IsOnCurve(Q) {
" |/ m5 o' s7 H5 M: F        panic("Catastrophic math logic failure in public key derivation.")
+ [* g7 c+ w: J9 {: J/ t' ?: r    }
2 u- d( x6 d" j' x7 v' P    priv.X = Q.X
( N, I* `" T7 E1 }$ w/ ^' Y    priv.Y = Q.Y
# [7 W. l: \( j) J+ U: i, q    return &priv.PublicKey( B, B- s2 X9 Q4 e8 r
}' T  Q% Q5 z6 S3 f8 A
地址脚本是怎么来的?; u' D9 r3 h9 y# d2 A/ ?  g  R
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:
0 i1 f8 z+ q4 n6 ^前面是:0x21后面是:0xAC
% S; d! `: ~- V9 l2 K

8 E" D5 v2 ^/ l3 V6 w, Y见btckey.go:
* y; {$ q0 K! c7 ~; e0 d/* Convert the public key to bytes */7 g+ Q; e" K2 |5 N
    pub_bytes := pub.ToBytes()
9 Z) S! s( W- ]* {) X    pub_bytes = append([]byte{0x21}, pub_bytes...)
6 n& A5 f3 `0 S    pub_bytes = append(pub_bytes, 0xAC); g( u* j+ w6 D8 p# C* o
地址ScriptHash是怎么来的?
1 ]) v6 t! D* s! r; B* Z% O地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
; E5 k: }/ ~# V0 F见btckey.go:+ X$ R/ R5 h9 T. o8 q2 K% t
/* SHA256 Hash */6 Q0 ^. s9 ~$ t' ]5 e
    sha256_h := sha256.New()
& W1 y6 R+ l1 u$ g) G    sha256_h.Reset()
( \, i" s. R& u  K  r3 Q    sha256_h.Write(pub_bytes)% q4 ~& J6 `9 v' r* \% K& v8 R! g
    pub_hash_1 := sha256_h.Sum(nil)& Q5 K3 V& T2 |: l7 z
    /* RIPEMD-160 Hash */
5 Y1 g- R& a* w( ]$ ~    ripemd160_h := ripemd160.New()
4 C, ~6 Y9 M) x+ u. \    ripemd160_h.Reset(); M( B" h0 O/ ^- a! |1 J6 y
    ripemd160_h.Write(pub_hash_1): t; [# S  Z. D' H" t% M
    pub_hash_2 := ripemd160_h.Sum(nil)
. ]+ ?0 H9 w3 \! e' d$ Z    program_hash := pub_hash_2% c- ]9 |' G( x, J" T" k6 e
地址是怎么来的?$ w  I& k: p9 w: [* K
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:
& b3 \  U0 Q) F* \加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码- N; W! P0 [4 z& G  @& D/ }. B$ y3 q
: k; S3 m1 t- V& j
见 btckey.go 完整的由公钥生成地址的代码:# H! e/ t: {7 X0 [$ ~
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.- o7 E% X" P% J( E  Q, e, ]
func (pub *PublicKey) ToNeoAddress() (address string) {7 D$ n+ Y% D7 ~- l) n
    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */# ~! J" s' V. v: D
    /* Convert the public key to bytes */
0 X6 \6 V) L" R4 c- A9 `    pub_bytes := pub.ToBytes()
/ r8 W% W( d0 X2 V# }5 d3 g    pub_bytes = append([]byte{0x21}, pub_bytes...)
% Q! X" k3 a0 b5 ]3 \* j* \& a    pub_bytes = append(pub_bytes, 0xAC)
0 a! W; L1 a& w: _4 F    /* SHA256 Hash */0 \! x4 v+ U# m% t/ @0 _" A: Q' ]
    sha256_h := sha256.New()
3 P9 r3 X. f7 C    sha256_h.Reset()
3 r$ [0 V. T  u% k: L    sha256_h.Write(pub_bytes)3 g# P( A* S: u1 ]3 V
    pub_hash_1 := sha256_h.Sum(nil)! |/ i  m9 [% u1 W) l/ j
    /* RIPEMD-160 Hash */0 V1 B$ v4 k% Z1 T# U4 h- E7 {% N
    ripemd160_h := ripemd160.New()' R/ ]2 k) D- w
    ripemd160_h.Reset()4 Z7 P4 G0 {. f
    ripemd160_h.Write(pub_hash_1). k* ?* W( z: ]' V( i. q! q' o
    pub_hash_2 := ripemd160_h.Sum(nil)& T8 O3 C# I% S5 [! x; D  C0 D0 X
    program_hash := pub_hash_2
) |2 q( z2 t) M0 @1 {* _    //wallet version
+ ?7 q5 E) h& a% L  S    //program_hash = append([]byte{0x17}, program_hash...)$ M3 i+ d" e, s- H; |1 F) ?
    // doublesha := sha256Bytes(sha256Bytes(program_hash))2 K8 B8 J/ V; [4 k
    // checksum := doublesha[0:4]% k! J/ @# L! k6 G
    // result := append(program_hash, checksum...)7 s6 h7 b9 D* W( n  W' F
    /* Convert hash bytes to base58 check encoded sequence */9 a3 G, Y# a! J. P( c/ q% v
    address = b58checkencodeNEO(0x17, program_hash)
8 U6 Y, d1 `0 B. ^0 v" j    return address3 z# {% \! o* [. @4 B9 W( P& X1 J
}" b$ C+ E# @/ L5 G& k$ \! n
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.& B3 G) A! N3 X' e
func b58checkencodeNEO(ver uint8, b []byte) (s string) {( `3 r' w0 F3 E& b5 g( v0 r" d: c
    /* Prepend version */9 d& U8 S( p+ A" x0 }
    bcpy := append([]byte{ver}, b...)
; j9 D( }2 G- U3 Z! B: B    /* Create a new SHA256 context */
9 Y/ h8 T) f; a" {4 C    sha256_h := sha256.New()
7 N+ l! ^3 N8 G& r8 F: [5 P1 u    /* SHA256 Hash #1 */
4 t6 ^1 i7 i) B    sha256_h.Reset()
/ w& N8 T2 @4 V* n0 Y7 n    sha256_h.Write(bcpy)1 R8 x. w8 M1 t8 m* t
    hash1 := sha256_h.Sum(nil)' |4 a6 I1 H7 x; M
    /* SHA256 Hash #2 */
! n& j0 ~+ u" }+ R! j5 V) `    sha256_h.Reset()
6 Y1 A8 f5 E* ?- O/ n$ Q    sha256_h.Write(hash1)" X# Y# l7 x0 y4 v8 M
    hash2 := sha256_h.Sum(nil)
+ s/ `, b) z1 K9 c) N& P    /* Append first four bytes of hash */: J2 M* C, _6 ]5 h$ U8 {  `
    bcpy = append(bcpy, hash2[0:4]...)& X  f2 g* V# I: g6 L
    /* Encode base58 string */
; t# h! l! F$ V8 {. ^    s = b58encode(bcpy)$ H/ @; V" o& f* r/ Z) i) v4 l
    // /* For number of leading 0's in bytes, prepend 1 */
- D# q; h1 \6 T, v    // for _, v := range bcpy {8 c* M$ j1 s2 f! p
    //  if v != 0 {+ e4 r. W* I/ _& `4 y! n
    //      break
5 c: D1 r3 H9 a+ x    //  }
8 w4 t$ V+ K0 N! }' ~    //  s = "1" + s; H& O$ G% u1 C6 p" V/ Y7 [' c2 Q
    // }7 q# j: ?( p* u3 x
    return s0 U, n; K* [# h! Z6 Z) e3 C
}
8 @% N, [* t4 x$ AWIF 是怎么来的?
9 E9 o  b5 m2 F) F: d9 V7 R+ mWIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:  k2 q2 D7 X. D
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码: d4 Z' {4 D6 q& w
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

放弃六月们 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    8