Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

放弃六月们
2343 0 0
私钥是怎么来的?私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。
: [8 [$ k: y" V见 Account.swift 类:
- Z* P7 \% h( g( ?public init?() {( |7 B7 O9 X8 c: m( b& }
        var pkeyData = Data(count: 32)+ f' X, K6 H0 e! s! e( G* |2 x
        let result = pkeyData.withUnsafeMutableBytes {
2 }7 g0 g8 X( N6 H# P  Q! `* t            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)0 E9 Z5 G! c6 y0 H& j$ _
        }9 ^9 B( e, `2 F1 m6 p! O+ M
        7 C6 q3 i7 u# M, G0 x
        if result != errSecSuccess {2 b- B, N+ S( @
            fatalError(): U6 [* ^2 Y7 \" h
        }& `0 n3 T9 k4 B6 P
        ( y6 e; m3 F( }1 v6 N2 D+ _
        var error: NSError?
$ g+ Q+ D- F9 b+ x5 f6 p% q        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
" B! F% p: {% u* m( s        self.wif = wallet.wif(): ^; a/ F' I% ^6 P7 _5 S/ o
        self.publicKey = wallet.publicKey(). ?  t* s* _8 u; Q# Q. Z
        self.privateKey = pkeyData4 D! `, m5 s. n  E1 t' |! P
        self.address = wallet.address()
, a9 Q$ Q4 f# H3 ^' b8 S        self.hashedSignature = wallet.hashedSignature()) x- J6 l2 V' Q, S# \" k
        //default to mainnet
/ R% s. G' n4 {* s+ U9 ^" L" s        self.neoClient = NeoClient.sharedMain9 l+ z+ \4 V5 s+ T& q# {0 J
    }4 A6 H2 d# O5 S' y
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
1 A- d4 J. e7 j0 S  o! v3 u% g/*!
! m" l/ a$ U5 h$ d( U9 G7 K1 \4 |     @function SecRandomCopyBytes( G( T+ U6 S9 X( C3 L5 @
     @abstract Return count random bytes in *bytes, allocated by the caller.2 R+ }& ?" }6 |# A
        It is critical to check the return value for error
9 `: M; q. O# V9 K$ {( o; z     @result Return 0 on success, any other value on failure.
. N$ a# \7 M( L$ I# ^" s9 u+ Q. A*/! I. x' x$ L+ Z6 r9 [
@available(iOS 2.0, *). F/ V& h4 Z7 `/ p4 b) }% B: i$ ?+ h
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32$ ^0 z5 V* U& g9 e
随机生成一个32字节的 Data 数据,即 privatekeyData:' f2 |: h* t* ?8 x: E: ]# F* Y- Q; x
var pkeyData = Data(count: 32)
- c# I! \5 _* J! g        let result = pkeyData.withUnsafeMutableBytes {, h7 |' I9 k* n& j% j) i3 W
            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
; G( p6 W1 v: ~( z  c/ H8 _& q& |        }/ U& W1 p" q, |4 L9 Z- E; w
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
6 ^  q8 ^' r* d2 P$ B) A9 kvar error: NSError?
) g0 L. W0 l( a' i0 f9 @6 z5 k' S+ `guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
  M9 O  _% M: K( X" {// Generate a wallet from a private key
) z4 l6 J% Z2 hfunc GenerateFromPrivateKey(privateKey string) (*Wallet, error) {6 t7 f3 G* E4 s/ s
    pb := hex2bytes(privateKey)1 t  w2 n- Q6 |% Z( U
    var priv btckey.PrivateKey
2 L4 ?  y6 H; P    err := priv.FromBytes(pb)+ k+ p4 P8 G& t- b. a# O4 ^
    if err != nil {
1 \  J0 }1 k/ b0 V9 S/ y        return &Wallet{}, err$ I: ~+ r) u. B9 p0 L
    }
7 o8 p" x' j/ c6 Q# O    wallet := &Wallet{
, u0 Q$ J/ c% z' ~. p' \        PublicKey:       priv.PublicKey.ToBytes(),' p* t( @" F+ D( w# L# z" {- Q) x
        PrivateKey:      priv.ToBytes(),
- M/ b0 n/ G8 H3 y/ [) A        Address:         priv.ToNeoAddress(),
% \1 {9 j& k9 R$ w+ W6 @        WIF:             priv.ToWIFC(),
. ^( q4 v: o: d- l2 }& F1 [7 Z        HashedSignature: priv.ToNeoSignature(),
1 j( x. l3 y0 l0 a- q9 o    }+ g8 F' p* |- F5 p+ }. j8 E% H3 _
    return wallet, nil1 _& G7 \5 y- C
}7 m3 h7 o7 ?0 B0 m5 u: J; j$ z" G# U
公钥是怎么来的?
3 C  |3 `! L# t- Z" J  E1 z, c公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。
; k2 [3 m$ e1 @见neowallet.go 和 btckey.go:2 }/ }: Y9 P4 |$ V# b
// Generate a wallet from a private key
. K( W  @6 J- O' U: ~  G8 G# \func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
+ b( U) U  z# o    pb := hex2bytes(privateKey)1 o1 b! t$ N, n9 i4 V0 g3 I4 ~* ]
    var priv btckey.PrivateKey
. ], ]/ R8 k6 r- T    err := priv.FromBytes(pb)
9 V: f( S4 l+ z0 U9 T+ r3 |    if err != nil {1 p- M6 i4 g3 m" c
        return &Wallet{}, err
/ J! h/ H5 M/ d# R+ \    }; d3 `! L2 \" z) q9 b
    wallet := &Wallet{
0 H4 l" g' V; b+ r4 s/ U9 l        PublicKey:       priv.PublicKey.ToBytes(),
1 f" }8 f# L. y- d/ x, ~% O9 R        PrivateKey:      priv.ToBytes(),
1 P+ d2 ~& V& d0 _        Address:         priv.ToNeoAddress(),& L* ^2 p  n9 b/ N' @
        WIF:             priv.ToWIFC(),* o* l/ t. R8 V' [; g
        HashedSignature: priv.ToNeoSignature(),
( @6 n1 h/ o0 H1 i# N# E    }$ m. f5 u% G+ k8 b+ |: Q
    return wallet, nil+ ^! @5 @+ A- B( ]8 k' Y+ E! E  Z
}% a# m6 p# h3 W7 e% Y1 ?; X
// derive derives a Bitcoin public key from a Bitcoin private key.7 Y7 L$ }7 N: \& {4 X- o3 ~
func (priv *PrivateKey) derive() (pub *PublicKey) {
* y9 t3 z# D& ?. T/ A    /* See Certicom's SEC1 3.2.1, pg.23 */. e9 ]6 M7 e' I& f5 \
    /* Derive public key from Q = d*G */% ?& q. ]. n9 _6 S% B6 z: o, S! v
    Q := secp256r1.ScalarBaseMult(priv.D)
* X' c8 }3 a7 E5 R. E    /* Check that Q is on the curve */
! b, X8 s6 ]1 c" j% M. S( Y    if !secp256r1.IsOnCurve(Q) {
+ R( }- @2 S% K' V9 Q        panic("Catastrophic math logic failure in public key derivation.")0 i" j+ i3 J! H0 V
    }2 a& W% L8 g) r. x: w4 b3 K( y
    priv.X = Q.X
# \- Q5 o4 t5 j% V: w; c: Z* t* p$ B. e    priv.Y = Q.Y' [" `; ?3 q. k7 T/ l
    return &priv.PublicKey
5 }" ?$ }$ @$ y' g! @6 B$ P* B}" z) P% f+ Y! l: v# v+ H
地址脚本是怎么来的?
; A- T6 E0 a: [9 u) t1 g1 A2 [地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:. E/ s6 s! n3 f
前面是:0x21后面是:0xAC
7 u  K. X! t8 P) I/ F9 P6 L
, g6 j- b2 V0 S6 u  z9 o
见btckey.go:
! N$ f0 u7 q, y. Z' J4 V4 t/* Convert the public key to bytes */. |# @8 E5 \% d
    pub_bytes := pub.ToBytes()
% g- ?/ {8 g4 i3 T0 p5 X9 p    pub_bytes = append([]byte{0x21}, pub_bytes...)
$ z1 g2 n4 C9 U6 M, `; B    pub_bytes = append(pub_bytes, 0xAC)8 p! v1 f% U1 \
地址ScriptHash是怎么来的?
: g$ S5 Y3 _% {& b, V& e7 _地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
# `! I; s" |+ y# U7 z" a- K& V2 p5 `见btckey.go:8 C0 u8 O8 M) }  f7 T
/* SHA256 Hash */( r; {! i: a0 R3 `4 x
    sha256_h := sha256.New()# {+ ]5 g4 p% o7 K  J
    sha256_h.Reset()* L- G6 A$ s. x$ f% P" R- b9 G5 Z
    sha256_h.Write(pub_bytes)
$ l; r+ M+ K' \    pub_hash_1 := sha256_h.Sum(nil)
9 Q& Z3 t6 n$ W) y3 \    /* RIPEMD-160 Hash */
# N" H8 J. ?8 @/ y- q$ y/ n    ripemd160_h := ripemd160.New()0 P$ h- x  ]. ~0 o6 [
    ripemd160_h.Reset()
& o6 |3 a6 ~4 y& z2 V$ P! W4 u* `; T    ripemd160_h.Write(pub_hash_1)
5 w4 g* T0 i3 @0 v/ U/ X7 |( g/ x    pub_hash_2 := ripemd160_h.Sum(nil)
5 @) q; _0 B& g: |% c  y    program_hash := pub_hash_2' Z* {; K# C; |; Q
地址是怎么来的?( {( h- O/ M, J$ j8 \
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:# T! Z5 w$ m* L2 X$ f( l4 Z
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码; w- H+ Z/ ~, F
# s9 {3 p1 p+ ^& ^" K$ D' k- v
见 btckey.go 完整的由公钥生成地址的代码:
& K9 e0 L- b5 d// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
& @0 V5 p$ j/ Ffunc (pub *PublicKey) ToNeoAddress() (address string) {2 Z; v5 Z4 ]9 H$ k
    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */
# C' @+ t. u" \6 W+ l8 F) F. Z    /* Convert the public key to bytes */; @6 i/ T0 j; \
    pub_bytes := pub.ToBytes()7 ^' \7 M$ b9 Y9 t& m$ |" g
    pub_bytes = append([]byte{0x21}, pub_bytes...)  z1 M: C0 l+ d8 h7 F; d2 `( ]
    pub_bytes = append(pub_bytes, 0xAC)
4 g. B3 x( q+ ~+ M4 |" j  M    /* SHA256 Hash */
! t& q" v, }% Y! t3 w9 L    sha256_h := sha256.New()
9 @3 X4 p& }8 I: n3 h- U) g" H" w    sha256_h.Reset()- ~: m6 n  T% Z; t( f! r
    sha256_h.Write(pub_bytes)
6 p% K" e4 H6 w3 W* P+ i1 p. S    pub_hash_1 := sha256_h.Sum(nil)) x2 U; B6 c5 v) A0 Z: Y/ `
    /* RIPEMD-160 Hash */) M( X9 Z! f; Y$ E
    ripemd160_h := ripemd160.New()
  ?4 E( }9 w5 o7 J" ~7 S$ V    ripemd160_h.Reset()
) ^2 f. h. |1 b$ B# J    ripemd160_h.Write(pub_hash_1)* G3 y4 u8 @: k  m, T; L
    pub_hash_2 := ripemd160_h.Sum(nil)9 z8 ~" i: _8 L. C& B
    program_hash := pub_hash_24 h, S1 M/ \0 D0 R- ?$ F' K/ ]
    //wallet version1 Q( A& Z7 L7 k  n/ n. A
    //program_hash = append([]byte{0x17}, program_hash...)0 Z9 m/ R  Q: P1 M: W- u
    // doublesha := sha256Bytes(sha256Bytes(program_hash))
& R- X: o; x- e9 ?( e" I- E0 }, p* \    // checksum := doublesha[0:4]" }) K4 K6 y# N4 T+ B+ C
    // result := append(program_hash, checksum...)
1 F3 d! s3 g4 [! j4 t( Q4 [    /* Convert hash bytes to base58 check encoded sequence */
' P# d& f& |% v* o4 ~    address = b58checkencodeNEO(0x17, program_hash)
- F4 _* Z. H: Q$ E    return address
6 u* u9 K4 h: ~* w8 c}1 n- G+ @8 L) K. C9 C" R0 Q. n9 o4 b
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.$ B, {4 ]  G7 n8 |/ ^
func b58checkencodeNEO(ver uint8, b []byte) (s string) {1 c; O& ^2 b7 b  |
    /* Prepend version */: I5 i3 ^. N& G6 `  k! `
    bcpy := append([]byte{ver}, b...)9 b$ j2 c; q  K% U, \8 H
    /* Create a new SHA256 context */
4 J2 ~8 N! G* F1 s    sha256_h := sha256.New()6 q! R, G+ E" K' B* M
    /* SHA256 Hash #1 */
  ^2 P9 |, g) V! b. ]    sha256_h.Reset()
! Z$ Y+ [6 d3 z3 y1 k! M# K    sha256_h.Write(bcpy)
3 F& ^, L! l' S+ d9 }. n    hash1 := sha256_h.Sum(nil): _$ @# }* f4 i& Y
    /* SHA256 Hash #2 */8 d6 g3 d* Y8 c2 o$ w% C8 }$ c* R
    sha256_h.Reset()
* B/ c# A6 U# h* U    sha256_h.Write(hash1)
" B" Y3 X2 `8 W2 A9 x    hash2 := sha256_h.Sum(nil)
& x1 E; S; P5 O  _, o" I& C( |    /* Append first four bytes of hash */, [6 m- C" y! _
    bcpy = append(bcpy, hash2[0:4]...)) B, b4 n$ g7 ?
    /* Encode base58 string */
3 u$ X3 c0 H1 q6 J  o% a) C    s = b58encode(bcpy)
+ x$ }4 d# |% o7 g+ q# T- @    // /* For number of leading 0's in bytes, prepend 1 */( K( r3 ?7 b2 F* Q
    // for _, v := range bcpy {
) G, y! P/ w: Q    //  if v != 0 {( \7 R* O+ {& }0 T% g& i
    //      break
& R5 ]" X8 {' d/ V1 D* P    //  }$ `: a# j4 w9 z/ s. V& Z
    //  s = "1" + s
0 o1 C4 \$ Q, Q1 W) I* R4 B2 k    // }/ c8 Z% i  z0 k8 v0 ]
    return s
9 F- R" A" |% v) K7 }! w}7 @; l& j* X$ C% ~1 ^0 I
WIF 是怎么来的?+ V8 `3 R$ F4 a4 Z
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
- Z; s$ N6 d7 I. p前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码
  s' W/ o8 e/ R
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

放弃六月们 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    8