Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

放弃六月们
2326 0 0
私钥是怎么来的?私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。" H$ L# [7 o& W8 \; Y5 }) l
见 Account.swift 类:& V; Z1 V' [0 A# u
public init?() {' F5 B* U- n. {( P, C9 ~% y
        var pkeyData = Data(count: 32)
2 Y6 d1 q6 \& y; y" p1 `- G4 A' b$ U        let result = pkeyData.withUnsafeMutableBytes {
) m; O$ O: W9 a: e4 _            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0); Y' b8 V' P2 p8 ^" s7 d( t: x
        }
. b& I4 K0 o' g; R4 u) g+ A        
* M9 y, h8 D6 w2 @' n4 Z        if result != errSecSuccess {
, O' `1 Q! A) p1 X% s  }& b! P* x- H- V            fatalError()
& d0 w/ @; B3 ]& \% \        }# T: M. k" v$ J7 d
        1 W4 S. T& S# v4 v' o7 X
        var error: NSError?
# M7 V! f3 Y8 ?% k! `        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
1 ?4 e  _; p1 c3 U# D- F& o7 i        self.wif = wallet.wif()0 b2 p# L& M$ V0 A
        self.publicKey = wallet.publicKey()
) Z- `* w( S* X1 y  `3 q; B        self.privateKey = pkeyData
& A7 h/ T5 W6 i4 {        self.address = wallet.address()9 D$ E+ b( M) f
        self.hashedSignature = wallet.hashedSignature()7 ^/ D& j# l2 w1 T3 H$ H
        //default to mainnet$ f! C# q4 D* j& U. m
        self.neoClient = NeoClient.sharedMain" _4 X9 N: z; d( }3 N% |7 @5 Y
    }
# C9 o8 {) R. @" j4 z3 j5 ^它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:* G$ ]9 {" I( n
/*!+ G2 t. J7 i: [4 W
     @function SecRandomCopyBytes
/ ]. W1 G# E# I+ q     @abstract Return count random bytes in *bytes, allocated by the caller.
4 t3 d' x  K5 U$ O9 e        It is critical to check the return value for error' {- G  g# e& G, J
     @result Return 0 on success, any other value on failure.
4 w3 @/ G+ T0 G% D*/) W* t" ~) X7 r3 G
@available(iOS 2.0, *)$ ^$ O2 V* A3 u" P2 V
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
# I3 a) c, C! g随机生成一个32字节的 Data 数据,即 privatekeyData:5 l2 P! I, Z% r: \1 C. d4 q
var pkeyData = Data(count: 32)
# e8 |" K6 ]7 ]: }        let result = pkeyData.withUnsafeMutableBytes {
! S# q: E: t8 }6 D, I  w8 N. I            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
# r2 Q, f( J4 e5 S) E1 w4 a        }7 B7 c7 ]# o5 a( ]& [- M+ Q
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:: B0 d% O  B' o- y
var error: NSError?
4 M) b4 h% r; {guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }* q( J4 ]5 O5 o) U
// Generate a wallet from a private key
" d, e/ Q. a1 Z" C3 g( Ffunc GenerateFromPrivateKey(privateKey string) (*Wallet, error) {& ~- G. H! ~  r  q  S
    pb := hex2bytes(privateKey)# a+ B; r* t% z$ Y9 Z1 N$ ]9 j
    var priv btckey.PrivateKey
! Z& G/ A  b! e  }$ I* L  p" ]    err := priv.FromBytes(pb)
; D/ U) S6 {9 z- ?    if err != nil {3 b- f, A6 ], ]2 F0 ]. {
        return &Wallet{}, err
! [4 h; Q9 \  e3 @" \    }
* O* Z0 ?$ I0 @2 [. V6 s$ O* A    wallet := &Wallet{
7 T8 S2 U8 R. I: x7 f4 j4 s4 w        PublicKey:       priv.PublicKey.ToBytes(),. m3 ^# ^8 W; y9 M. h* ~
        PrivateKey:      priv.ToBytes(),6 }' o/ C, i# b5 |9 C
        Address:         priv.ToNeoAddress(),( Z; u6 Q& _" @
        WIF:             priv.ToWIFC(),9 L) t: o! ~) x
        HashedSignature: priv.ToNeoSignature(),1 b( q% I% @% \5 V) J9 P, q
    }
/ J& j! ^. k3 D$ _( k    return wallet, nil
5 J& k0 G) o3 s- h6 g. x}$ o6 ]) e7 V: k4 T0 }( b
公钥是怎么来的?
8 t5 E3 b6 `* h8 D: ^9 w公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。, P9 U! @5 T# [& i+ J6 u
见neowallet.go 和 btckey.go:
% q0 V# p! \, ~8 h- Q// Generate a wallet from a private key, {- q" ]% Z6 E" ~+ K
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {) v, H8 J, }  ~2 [' r+ ?. }
    pb := hex2bytes(privateKey)) O% K' G- u( i9 _9 `* g( X% J
    var priv btckey.PrivateKey
4 X7 L+ V9 n1 w    err := priv.FromBytes(pb)
) E- g$ K! q/ H    if err != nil {
' m, s, ^; K( p6 {        return &Wallet{}, err* g# i. m3 Y$ Y4 w4 y: P! k% a
    }4 W1 r. w) c5 d- t
    wallet := &Wallet{
# C# {" x& D2 B6 z% @' s! Y        PublicKey:       priv.PublicKey.ToBytes(),
; k/ N3 x* |0 d( D/ {3 v4 w        PrivateKey:      priv.ToBytes(),0 j+ b5 q6 e" W  P# t
        Address:         priv.ToNeoAddress(),
- U8 I) a8 }+ \' T( ?; o( H        WIF:             priv.ToWIFC(),
" v1 z* L$ |1 ]        HashedSignature: priv.ToNeoSignature(),' W% {6 [" T' Y: }9 p
    }
4 U2 u' b# Y  ?    return wallet, nil
7 p7 [  |8 _( V9 b. Z+ C8 g6 ]}; f3 M; S6 }; A0 Y/ d3 W
// derive derives a Bitcoin public key from a Bitcoin private key.1 I( N8 P+ B: G
func (priv *PrivateKey) derive() (pub *PublicKey) {; w$ Y6 s7 X- n. K1 t6 M2 h, I5 F
    /* See Certicom's SEC1 3.2.1, pg.23 */0 t- x% C/ {% k1 g9 D- D$ Y/ I6 Z
    /* Derive public key from Q = d*G */
4 s+ v4 e3 \% T# \2 T# G    Q := secp256r1.ScalarBaseMult(priv.D)4 e% [7 }0 ~: a
    /* Check that Q is on the curve */' \7 f) Q, B8 w8 u- v; `3 G2 ]$ f
    if !secp256r1.IsOnCurve(Q) {
: P' W( [7 V; n/ N  [! F8 v        panic("Catastrophic math logic failure in public key derivation.")
2 f/ T+ y5 W; b2 [1 R    }
; f( a+ H. P+ K    priv.X = Q.X
- c3 U! z( f8 e0 R" q0 m    priv.Y = Q.Y9 }6 o+ N2 ^' X* m0 H  P
    return &priv.PublicKey( m$ A5 m/ g* b& V( |
}" n( ?* }+ g8 W0 I
地址脚本是怎么来的?" s8 y) e/ s% q
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:! w& `* v7 A" Z0 N4 p- n
前面是:0x21后面是:0xAC; x3 \& j, j2 w1 W2 H

9 X" d/ l8 d; ]7 x9 z: F见btckey.go:: j8 l2 `5 h4 c" O, `4 [
/* Convert the public key to bytes */! x# `; q$ r0 n; m' b, T; M
    pub_bytes := pub.ToBytes()
2 |% i8 W- Z) S! D. e    pub_bytes = append([]byte{0x21}, pub_bytes...)) M! e2 A; J; P" |. F% Q8 u! ]
    pub_bytes = append(pub_bytes, 0xAC)
1 N& [4 P# V( d( D$ a地址ScriptHash是怎么来的?) c/ [! h- ^- r4 p) \
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:( [4 p0 b- \8 {4 ]. r7 H
见btckey.go:
2 u5 R; g, u) h/* SHA256 Hash */! A) |, y( d" m( a5 V
    sha256_h := sha256.New()
1 ]4 m) R# x( s9 y    sha256_h.Reset()
9 Y: n5 q, G' l# b% ~' E- w    sha256_h.Write(pub_bytes)' e$ l1 A9 |7 M4 S. o4 M
    pub_hash_1 := sha256_h.Sum(nil)2 W% e4 N3 a$ S0 \2 x
    /* RIPEMD-160 Hash */
- C2 t. c8 |* v5 s' |8 C4 W    ripemd160_h := ripemd160.New(): c3 v3 O1 O9 C4 B0 B
    ripemd160_h.Reset()* z' ^. a0 T$ l" [7 I/ l# s
    ripemd160_h.Write(pub_hash_1)2 W' E& e  c2 D; P" T# Z
    pub_hash_2 := ripemd160_h.Sum(nil)8 T+ _" f# ~6 `
    program_hash := pub_hash_24 I" I. b, O7 k0 O
地址是怎么来的?- r8 x: d( g+ V  k1 q" d. _
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:
( x9 C5 C0 f* H0 [9 Z- q加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码
+ m1 J$ M" |0 c) F/ g1 B4 b) Q: W  ?
% H, L5 D4 ]0 D  }4 Z* O
见 btckey.go 完整的由公钥生成地址的代码:
. M0 ^% c( z8 M7 D$ `// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.( I6 ?* B- }6 x; }4 O  J
func (pub *PublicKey) ToNeoAddress() (address string) {
  j, D* @. Y: X4 U$ v    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */5 G$ I% H7 ~3 k$ D+ g$ z# E- Q1 m& M$ o
    /* Convert the public key to bytes */
6 H1 t8 c, F; ^* z    pub_bytes := pub.ToBytes(), R% E$ s3 C1 F4 v
    pub_bytes = append([]byte{0x21}, pub_bytes...)( h1 N* X7 d  }$ Y& m  P. X
    pub_bytes = append(pub_bytes, 0xAC)
2 P4 e) w) G$ Q1 I6 d    /* SHA256 Hash */
9 U9 S8 g- H: U    sha256_h := sha256.New()
5 ~- ?' ?$ ^2 J4 {6 w6 t    sha256_h.Reset()
7 o8 O( M' c+ _5 B    sha256_h.Write(pub_bytes)
. r6 E9 {) c( N' Q    pub_hash_1 := sha256_h.Sum(nil)
- u) R1 ?- J- O: Y5 W    /* RIPEMD-160 Hash */
+ c" R2 r# P! x0 t& w0 V    ripemd160_h := ripemd160.New()
2 f9 X% @( N" r) E) j    ripemd160_h.Reset()$ J8 ^$ x# v! {) W0 a4 E/ v
    ripemd160_h.Write(pub_hash_1)5 p2 h0 K( z9 k; r
    pub_hash_2 := ripemd160_h.Sum(nil)
  y" ?1 }2 W  N+ g    program_hash := pub_hash_2
" V5 L# F7 S: r& I& \( Y% Z    //wallet version
) Z# k6 O5 H) N8 M8 g" X    //program_hash = append([]byte{0x17}, program_hash...)2 Z: i4 T* M: Z; }) Z1 E/ d( n, i
    // doublesha := sha256Bytes(sha256Bytes(program_hash))
5 E" v: ~9 [% m+ \3 j    // checksum := doublesha[0:4]5 M" @2 P" M+ x  b1 q1 G: l& n
    // result := append(program_hash, checksum...)2 T+ b2 q1 _6 x3 w0 d
    /* Convert hash bytes to base58 check encoded sequence */* V% F& j. @' O8 N- `' e' q% f$ }
    address = b58checkencodeNEO(0x17, program_hash)' z0 K$ O  x4 x6 q
    return address
9 w4 T6 Z8 X6 P& l, X+ T3 G6 ^" q. B}
5 I; Y9 z. ~3 V9 q// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.+ z# F7 @: Z) c+ I) S4 i1 M
func b58checkencodeNEO(ver uint8, b []byte) (s string) {
) p' D) I6 I$ w3 x% m9 c$ \8 p    /* Prepend version */" f+ H$ y: Z4 N& @$ S4 H( t
    bcpy := append([]byte{ver}, b...)9 N( w  \' |0 o; R* ^
    /* Create a new SHA256 context */
& B" w. H( f6 n1 h: r  y    sha256_h := sha256.New()
( _, W) b: z& R6 `* D8 e* o    /* SHA256 Hash #1 */9 y5 m# R/ B9 |3 B+ X% ?; }
    sha256_h.Reset()! L* d/ H  v2 [3 n. F
    sha256_h.Write(bcpy). G( @& N6 T) u+ k' Z! N( |) r
    hash1 := sha256_h.Sum(nil)% P* Z* W6 i% G  y  P
    /* SHA256 Hash #2 */
( x  A: ]6 Z3 P5 k7 y& E# a+ q$ ]    sha256_h.Reset()2 C8 }; ]" H- ^9 `
    sha256_h.Write(hash1)8 i# \* T4 }# g1 L
    hash2 := sha256_h.Sum(nil)
$ M8 U1 \8 e3 a    /* Append first four bytes of hash */
& P: S4 ~/ n! D5 d5 |# d; X' Q    bcpy = append(bcpy, hash2[0:4]...)5 Y2 P4 R) F: i, p( W
    /* Encode base58 string */
, f4 w2 \; y  H    s = b58encode(bcpy)) F, {. F+ \6 C1 K7 _% h
    // /* For number of leading 0's in bytes, prepend 1 */) p7 z5 @) `$ N# t: w
    // for _, v := range bcpy {- g6 f* D6 f6 u+ @7 B
    //  if v != 0 {) f! h- l: P% f  e" U
    //      break
+ G+ P9 U! t% G# Z' E' t& d    //  }& c4 v. Q4 m5 _) X/ z& Q% Q
    //  s = "1" + s' [/ u8 V3 O% m9 s# z! h+ _
    // }
$ H7 k; B2 b3 `/ _7 D    return s0 F7 ~) N1 ~7 r1 F' e3 c, O
}# a- K, |9 t* d
WIF 是怎么来的?6 j4 X/ r! H6 l+ O
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
, q6 u. `9 I9 a( x; o# o& L( Z前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码
" b4 s9 y$ S: P1 Y5 U
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

放弃六月们 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    8