Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

放弃六月们
2359 0 0
私钥是怎么来的?私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。* N( c* }& F: a, i! o7 A9 {) H" V/ t
见 Account.swift 类:
- d2 E3 o, ?7 O1 @2 y8 C) ~$ ^' Qpublic init?() {
) Y6 c! R( ]9 n; _) ?2 u- Z        var pkeyData = Data(count: 32)6 `5 F5 C; n9 y) R2 Q
        let result = pkeyData.withUnsafeMutableBytes {1 d! P/ [6 E% X. d
            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)$ R, P0 ]$ G9 ~9 ~2 N3 Z- }
        }
& i6 p0 P2 m, ]8 i- f. H        
# N8 i( L: C' z$ N4 d& S- ^        if result != errSecSuccess {8 L- D3 x6 r- X# m$ R- }
            fatalError()7 v! r4 F/ K: \6 n
        }
% P3 i. {( A0 R1 r" \/ n. ?; r+ C        
% h  t- j9 w; p$ e! N/ {1 S        var error: NSError?
7 R6 R6 b# s  T- C2 D6 Z& a0 k        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
- n) i. |* u, `. i0 X7 t7 r2 l& m        self.wif = wallet.wif()# \2 h; k1 ?0 t
        self.publicKey = wallet.publicKey()0 U3 j! g9 t% w, @! M+ A; Q
        self.privateKey = pkeyData9 N* @3 {! m) ]1 I- ^
        self.address = wallet.address()
% C# O2 V6 s5 ^        self.hashedSignature = wallet.hashedSignature()8 W& t, V" |7 y" ]/ w& `0 ~
        //default to mainnet
* p) D9 n( b- s  D& i! B, f        self.neoClient = NeoClient.sharedMain' Y6 e/ z6 i0 K9 E/ H! K. _
    }* M0 K4 l7 @+ R% f9 N2 i; Z6 Q4 j
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
3 @/ ?' `& n) K( W  ?/*!
; h2 G& i# \+ A1 o  ?! D; v) I: J     @function SecRandomCopyBytes# ^& w3 x2 `, S0 V5 B
     @abstract Return count random bytes in *bytes, allocated by the caller.
( n# X$ v, a4 \. R0 K        It is critical to check the return value for error6 x. Z' X6 E* S/ v% H8 h% v
     @result Return 0 on success, any other value on failure.* N" D+ q, g& ]' t; ^  F$ @& b  H* M
*/
1 Q$ d' }9 m0 y- B/ i! B* W3 F) `@available(iOS 2.0, *), h! N+ \& x2 w* g
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
: c: J- K5 J9 d& w" Y% [6 e随机生成一个32字节的 Data 数据,即 privatekeyData:- y& I7 K8 m4 h4 G2 j
var pkeyData = Data(count: 32)
2 A4 d' Z4 y( x( [        let result = pkeyData.withUnsafeMutableBytes {
1 N, z7 A9 _: |% n8 v! I5 {) d+ _            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)- W9 s+ G, ~+ l2 V& W
        }0 v1 [- ~! J+ I8 a5 x; h) F
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:: i3 b3 W! f3 c2 ^$ a* {
var error: NSError?& D- ]5 t. I$ r- ]
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
3 \& s% [; N. |5 ]0 C// Generate a wallet from a private key* Q: p! o& f2 S& c4 y
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
3 h2 w% H2 H& `% N. F    pb := hex2bytes(privateKey)) o3 |1 N% \: s# \( x
    var priv btckey.PrivateKey
* o, ]& r' K, V    err := priv.FromBytes(pb)" C+ ~0 \& d* l0 P
    if err != nil {& A6 N& o* r! e8 @  @
        return &Wallet{}, err
! f0 {7 W% ~" \4 c" ?    }
/ t/ J, c# Q6 s+ ^6 ~9 {    wallet := &Wallet{
$ ^( ~7 _' n6 B- w; n        PublicKey:       priv.PublicKey.ToBytes(),
! v6 Q; Z  S) y        PrivateKey:      priv.ToBytes(),
1 L$ P  c1 @7 d: B        Address:         priv.ToNeoAddress(),) }/ y$ l. ?9 ~
        WIF:             priv.ToWIFC(),
4 {0 A, O( a% I8 [) W2 W* |& C  [        HashedSignature: priv.ToNeoSignature(),9 s- A# j+ a- l) _
    }1 c( f8 S6 a0 S, p! f5 ]
    return wallet, nil' o: m& e# j6 I, o% w/ m! p
}
0 @2 r* I& P3 Y$ N公钥是怎么来的?0 c$ J* @3 M. ?2 z3 B* [+ x4 V
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。" c/ v: S- ~" _, M! h4 R
见neowallet.go 和 btckey.go:; M5 Q( M' w# E8 s5 B$ ~( Q
// Generate a wallet from a private key( M- f/ h/ t! k, c) o! ~
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
: y. t1 S& u% M4 i! o    pb := hex2bytes(privateKey)9 l9 ?# ]% B, ^9 j6 g
    var priv btckey.PrivateKey
1 c) S7 p4 {1 g5 Y4 u8 U+ G    err := priv.FromBytes(pb)' D4 P; S) x$ S) G- |1 ]( j
    if err != nil {
/ Q' r' }- X' H4 ]        return &Wallet{}, err
2 X0 a% C3 r1 [1 A' S    }9 ~$ n0 g6 }" v" q$ U( g
    wallet := &Wallet{
; J+ P2 \) X" b0 \) U* T4 e        PublicKey:       priv.PublicKey.ToBytes(),
% V6 v0 n5 z; K4 S        PrivateKey:      priv.ToBytes(),
5 O& y2 @3 p* H% W6 O3 E0 n        Address:         priv.ToNeoAddress(),- c9 B" l) D# W& g
        WIF:             priv.ToWIFC(),
5 F; K4 n" U# m        HashedSignature: priv.ToNeoSignature(),
  H0 ?- ^3 t* g/ C' K    }4 g: [5 h. H% y! }8 P0 S# o
    return wallet, nil
0 E7 a4 Q" M: O* J}
) S: ?2 ]( j% o// derive derives a Bitcoin public key from a Bitcoin private key.
% r- m6 @3 S: w5 r1 x# w" Nfunc (priv *PrivateKey) derive() (pub *PublicKey) {( e  R* S7 b' W
    /* See Certicom's SEC1 3.2.1, pg.23 */
" A/ I! `8 J4 \- Y    /* Derive public key from Q = d*G */& u; v" W; n" R. E- ?! n& B
    Q := secp256r1.ScalarBaseMult(priv.D)/ q: T, l) r. ]9 Q' m
    /* Check that Q is on the curve */' w- H% m" ^2 F$ T3 d: D
    if !secp256r1.IsOnCurve(Q) {4 m: R6 t0 k) s# a: \( |
        panic("Catastrophic math logic failure in public key derivation.")
' t# T  d6 l8 l; q' ]    }/ ~) V, |- o  g& `# }% N
    priv.X = Q.X
3 e3 u2 X6 H" x; ]; i    priv.Y = Q.Y
; e' K& ]7 S- N/ I7 e' X1 Y    return &priv.PublicKey+ ]3 i8 t/ G' n1 v" Z9 f
}
  M2 L8 V# o! c1 A' A1 I地址脚本是怎么来的?; \8 R5 i- y, A5 D
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:
. x# p) r3 S, [  N! \前面是:0x21后面是:0xAC5 v6 G7 y; y6 b2 E6 g5 t  g  _

$ X2 L- s+ |  T! I+ }+ n见btckey.go:+ s4 }/ c1 T0 l1 r* M5 ^( y
/* Convert the public key to bytes */
% d7 T( u4 ^1 O. F# ]0 l6 d% \4 ]. {( k    pub_bytes := pub.ToBytes()
6 i4 S) P* Y0 H4 T4 Y5 ^6 @    pub_bytes = append([]byte{0x21}, pub_bytes...)1 v3 P0 X( m4 E2 b) l! Q# v% w" _8 y, W
    pub_bytes = append(pub_bytes, 0xAC)" D! ?$ ~" I5 H. B8 u! ~
地址ScriptHash是怎么来的?
0 n( ]! T. k4 g$ |: A  v, e( ^地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
4 r2 l4 Z6 q' i3 Z: n见btckey.go:' Z# O* g* W' x# [) h$ o
/* SHA256 Hash */
3 o/ _7 G/ x2 q( p1 ~% T7 m    sha256_h := sha256.New()
+ |6 e3 X) ?: P0 N3 O    sha256_h.Reset()* U5 v" {- S6 o0 `5 H
    sha256_h.Write(pub_bytes)/ U: m' I% l$ ?: H% J( y
    pub_hash_1 := sha256_h.Sum(nil)
. O0 \, y5 w4 K( \4 F* L    /* RIPEMD-160 Hash */" e& E. R3 j3 L' E4 a8 T* a
    ripemd160_h := ripemd160.New(). D& I8 ]4 q2 M; i% `; X/ F
    ripemd160_h.Reset()  F1 t- @7 n9 w8 _) r& a
    ripemd160_h.Write(pub_hash_1)# F) o/ ?$ c3 k0 X
    pub_hash_2 := ripemd160_h.Sum(nil)
; I, i. `9 I1 E& d  O    program_hash := pub_hash_2
) R3 r# y" t2 I5 y8 Z0 b1 \! H, g# m7 T地址是怎么来的?
4 i% |4 E5 M2 @0 Q5 u地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:1 L" Y; {3 c/ Y  a) Z
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码# f, _9 ^. u0 b- ?6 D0 `/ Y$ J
( C  z( f2 T; k4 ?* |
见 btckey.go 完整的由公钥生成地址的代码:: D9 l5 o8 F( W# f* d( X6 O7 a
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
& z  _$ `$ T! \5 Z" Dfunc (pub *PublicKey) ToNeoAddress() (address string) {! o- b+ }0 B+ u6 x' s; x8 A) V
    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */1 e9 q2 D6 x6 w# V
    /* Convert the public key to bytes */
* A; L( A# i0 e& Y& ]9 N( E( q4 t    pub_bytes := pub.ToBytes()
2 W* F( s- n: Q, U* `, M. ?    pub_bytes = append([]byte{0x21}, pub_bytes...)
; B8 M( a% I! d1 u* S/ H0 l3 k: v    pub_bytes = append(pub_bytes, 0xAC)5 ~. F/ _) s5 _' l* t+ q! ^: u4 B0 Z
    /* SHA256 Hash */
8 f: r& K1 n; t2 w    sha256_h := sha256.New()
& K$ Y  U3 E6 M/ `! Z! k+ i1 k% P    sha256_h.Reset()# f- f3 }5 g+ _- @+ g0 q7 E# W
    sha256_h.Write(pub_bytes)5 U. S. ]7 }( m; T+ Y+ B  V8 F
    pub_hash_1 := sha256_h.Sum(nil)
8 }/ j. l, t& ~1 s, w+ Z2 b    /* RIPEMD-160 Hash */
1 [9 s5 _1 T# o. J% B) Y    ripemd160_h := ripemd160.New()& O0 S' a5 ?  a2 s
    ripemd160_h.Reset()/ y, R+ S3 y9 C! z8 I
    ripemd160_h.Write(pub_hash_1)8 i/ c3 \) c6 U0 B- d: a
    pub_hash_2 := ripemd160_h.Sum(nil)
: J" k' [0 M3 @4 E    program_hash := pub_hash_2
& Z+ B" g3 |4 |* l+ i+ h- ]    //wallet version
6 Z' o0 I! ~8 v7 F: R    //program_hash = append([]byte{0x17}, program_hash...), N( T3 L5 c4 M4 [" p  a& o
    // doublesha := sha256Bytes(sha256Bytes(program_hash))4 g% n/ q# ~2 J' `  M" J1 b, a
    // checksum := doublesha[0:4]& p1 C' W5 Q" [! c0 c( _: n
    // result := append(program_hash, checksum...)
/ w- u! R, |2 u- F8 c2 e    /* Convert hash bytes to base58 check encoded sequence */: s2 @: K% X  r8 g6 L
    address = b58checkencodeNEO(0x17, program_hash)0 j$ e( f; X9 Z1 l8 P
    return address) T! s$ w: Y5 ^* D3 f6 ~/ ]: k9 O* W
}( n5 w3 G# G$ R2 N
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
9 K; L* t3 b: v* Z& dfunc b58checkencodeNEO(ver uint8, b []byte) (s string) {
" a' I; E3 e- y$ \" W    /* Prepend version */, I3 @' A7 `- b; c, X3 V6 e
    bcpy := append([]byte{ver}, b...)
9 j; f7 k) `' c0 }0 w    /* Create a new SHA256 context */
! U* P8 s: M9 T1 r6 m4 }- [1 s    sha256_h := sha256.New()
1 }. {! _' P  ~- |; M3 m) b    /* SHA256 Hash #1 */
  i! k- _% \0 p0 W; A8 q& X    sha256_h.Reset()$ ?8 p7 d9 s1 u$ b5 X
    sha256_h.Write(bcpy)* m. N' _( X4 A- ?+ j
    hash1 := sha256_h.Sum(nil)$ z! ]" N8 m8 c* @6 B2 Y
    /* SHA256 Hash #2 */
  F% J3 K  [7 h    sha256_h.Reset()
  H! G. `$ L$ d) ^. u    sha256_h.Write(hash1)
* @% g# o# H6 f8 j% ^+ X& v    hash2 := sha256_h.Sum(nil)
/ y" d/ p& o, n+ C- y! Y    /* Append first four bytes of hash */- T: w- [' @) ?
    bcpy = append(bcpy, hash2[0:4]...)
$ M; P& C) C, s9 u, d    /* Encode base58 string *// i  `9 C/ @* Z. `+ Q$ B
    s = b58encode(bcpy)
3 y4 c9 i1 T' B4 Z" r( \5 m9 ]    // /* For number of leading 0's in bytes, prepend 1 */; ^7 k: ]* n1 q  `$ s1 B7 G
    // for _, v := range bcpy {2 l  A3 `# U) d. q3 [
    //  if v != 0 {
# V' R% J' T/ t& ^3 U8 U    //      break/ P& @6 @1 U' R6 I7 _$ h
    //  }# P* m+ g# c$ f' }5 Z4 p( @' \
    //  s = "1" + s
. t$ M! L  R5 H6 W    // }. F9 x. E" \" B' x) G% t
    return s. b# G. F, A% @" F- C6 d
}
1 D- I1 r- l2 q/ L4 XWIF 是怎么来的?
/ E. m  Y( k4 K8 CWIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
; `' P) n  O5 R4 h前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码
5 g' g5 @6 \0 T+ G# f; P
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

放弃六月们 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    8