Hi 游客

更多精彩,请登录!

比特池塘 区块链技术 正文

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

放弃六月们
2341 0 0
私钥是怎么来的?私钥是一个32字节的随机数,这个数的范围是介于 1 ~ 0xFFFF FFFF FFFF FFFF FFFF FFFF FFFF FFFE BAAE DCE6 AF48 A03B BFD2 5E8C D036 4141 之间。5 I% o- e) X6 W) W5 t4 Y
见 Account.swift 类:
# Y! a# V" k) a: [- g( E- j' s* b. d  lpublic init?() {, }7 {/ q% d6 e9 U& ?
        var pkeyData = Data(count: 32)
- R7 t# L+ ]+ c        let result = pkeyData.withUnsafeMutableBytes {
/ r' V& f9 x- i; t2 s- Y8 q, h( h            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
( [2 n) k# f0 H, d        }& H( Y* A/ s2 S8 N5 C, h; @
        ( A+ S" X2 t  l9 z1 G
        if result != errSecSuccess {
' w$ ]' r/ d! Q! t- ?            fatalError()4 }1 R, V" E2 o0 |7 ]7 `
        }9 D) X7 L2 H( ^- M+ m' a( b  P+ Z
        3 d6 ~' V+ d! s2 w& f% B
        var error: NSError?
$ _& o6 X* E$ c5 S        guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
2 R; ]* R; i, B' b5 ~7 k        self.wif = wallet.wif()
0 }% G( v1 d2 n2 g: x        self.publicKey = wallet.publicKey()
- ?0 U0 Z: ^! P1 j" }; l  E        self.privateKey = pkeyData
8 @3 D: F* n# R8 c: x/ p% m: u        self.address = wallet.address()
, B! x+ {5 \' C& }7 T- R        self.hashedSignature = wallet.hashedSignature()
8 [1 [6 x. T* I        //default to mainnet( B8 U! a3 ?8 I/ @7 s! {- Y
        self.neoClient = NeoClient.sharedMain8 F- o: _5 O% I6 o& ~
    }( W4 F8 x3 W; I, W
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
$ c! t7 c8 c% o7 l/*!
. F+ J1 f7 K: M1 H' }     @function SecRandomCopyBytes
, c. {/ e2 [2 |3 m7 _' K: u( h     @abstract Return count random bytes in *bytes, allocated by the caller.$ c0 f: D  ?, r- c1 w; I( {% y
        It is critical to check the return value for error6 l7 U, d) T; C, p0 A
     @result Return 0 on success, any other value on failure.6 K/ u; l- g8 Y& I9 y! o+ l
*/+ g  S5 B+ }" c- J* T
@available(iOS 2.0, *)3 c; B) ^8 T6 e( S1 }' s5 n
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
: Y+ h# R- _- e+ e# d6 l随机生成一个32字节的 Data 数据,即 privatekeyData:' O) T( t/ ~3 `2 J3 u/ ^' O4 x
var pkeyData = Data(count: 32)2 E% ~+ g; ~5 {4 |+ h5 N
        let result = pkeyData.withUnsafeMutableBytes {* u/ u0 }* @( A! m
            SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)0 c" o: |0 ^1 [4 D! N3 E& ?+ f
        }8 A0 [: n: G5 S4 {7 N6 f' y
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
: t3 a! b# I, mvar error: NSError?8 R& F/ G8 r; Y0 t- o  N8 U: U
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
  v3 \, \) T' ^+ V// Generate a wallet from a private key
* J/ R7 M: K! ^6 S, d9 E* Kfunc GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
3 R0 I5 d6 q% v2 Y    pb := hex2bytes(privateKey)5 Z+ ~( F. W; u) n
    var priv btckey.PrivateKey, U; h2 |7 ~6 o- V, E( O5 P$ ^
    err := priv.FromBytes(pb)
* ^( ~* P$ ]' ?6 j6 K. M$ j) H' L    if err != nil {
0 d, M( ~5 }; x) c        return &Wallet{}, err
6 s1 G% g* h: t: D4 F8 i" w& I' c    }
2 C) E. R  T) H9 t2 v" K' t5 J    wallet := &Wallet{
$ s% u! N8 d4 K! P7 w( D        PublicKey:       priv.PublicKey.ToBytes(),
5 N- ?! Q; s5 x! K        PrivateKey:      priv.ToBytes(),. J+ l( X& B+ |# L* S3 G, X
        Address:         priv.ToNeoAddress(),* W9 f* l' j: M; l) m$ b/ g
        WIF:             priv.ToWIFC(),
% j8 c  |6 L& Z& ~' F        HashedSignature: priv.ToNeoSignature(),# o! L2 k$ J7 b! e6 u
    }
& ?# n. u6 `! c% l* v% D% Y    return wallet, nil% |8 l' i( g/ f' E) X4 s, ]8 a
}" y3 Z- S0 X4 E+ e3 \0 f
公钥是怎么来的?
  b; E# m; p# Y公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。5 ^/ q) S! @& e6 b
见neowallet.go 和 btckey.go:
* w4 ]+ [' Q3 x// Generate a wallet from a private key$ r0 w  X0 \, d8 u  S3 e/ \
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {. W$ u! v' g5 D9 T: j# h, H
    pb := hex2bytes(privateKey)
6 H- V. e* M7 S: Y/ }8 e/ M( U    var priv btckey.PrivateKey
: f2 V7 w9 _2 e% B, X    err := priv.FromBytes(pb)) G( e7 z6 W& c+ L/ v9 e! W& X
    if err != nil {1 A( n; v, ^- l
        return &Wallet{}, err
" y' \% y2 I4 P1 o    }
1 q3 I1 R) ]7 X+ m( p    wallet := &Wallet{) f" X0 Q  ~0 m0 Z  ?6 U
        PublicKey:       priv.PublicKey.ToBytes(),' U. M) d! X( D" S  \; ?
        PrivateKey:      priv.ToBytes(),* z( F" s: L* |1 ?
        Address:         priv.ToNeoAddress(),# n; ~( X) N) s1 N9 Z9 }
        WIF:             priv.ToWIFC(),: F7 p% |8 r& }* T3 O% N
        HashedSignature: priv.ToNeoSignature(),8 u* f5 P: l9 E
    }4 E  ^( D9 A/ E; i! R
    return wallet, nil8 r9 L6 A  }# m3 j$ W7 r6 z$ N
}: ]% I- l) z% d7 R5 I1 z2 D' m: F/ V
// derive derives a Bitcoin public key from a Bitcoin private key./ ~/ q6 h* z: y4 b5 t5 b* |
func (priv *PrivateKey) derive() (pub *PublicKey) {
/ U  q0 O# l3 M$ E    /* See Certicom's SEC1 3.2.1, pg.23 */
/ x9 f; J& E) g1 A$ ~( |+ L0 C8 S    /* Derive public key from Q = d*G */# S4 U& U# X$ R6 ~6 Z
    Q := secp256r1.ScalarBaseMult(priv.D)0 m9 _( }  D7 n2 t, P
    /* Check that Q is on the curve */
* O5 T( i5 D3 Z& d9 S: X; d    if !secp256r1.IsOnCurve(Q) {
2 r$ N$ G1 k( @# J- g        panic("Catastrophic math logic failure in public key derivation.")) w- I! ^# P, }) f2 i& E
    }
- S3 A7 l2 W. ^, Y5 f. T6 j" \    priv.X = Q.X
- f" W  E5 f* W9 O2 W6 ^/ s- X    priv.Y = Q.Y8 Q# R5 D. i& y4 E
    return &priv.PublicKey+ L5 Y% k7 D( U
}
! H+ x/ M, [; q9 U地址脚本是怎么来的?4 |, q5 o- T/ C' {  I
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:
7 T: g0 r, y% @) @8 l& R8 K前面是:0x21后面是:0xAC
# J% m- c& [& F( M
1 j) Y( O9 C  f! t& G3 [
见btckey.go:
! p. R9 u" f- L) h: m2 {4 v$ m/* Convert the public key to bytes */! L# g3 Q3 x( l
    pub_bytes := pub.ToBytes()+ |% z$ g% l/ S7 S4 x3 U1 {
    pub_bytes = append([]byte{0x21}, pub_bytes...)# e# {, E" D* U; c, }4 Z
    pub_bytes = append(pub_bytes, 0xAC)
, x# h. ~4 D! M8 Q( Y地址ScriptHash是怎么来的?6 }* L7 J9 ?" r
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:7 H& z, D! {" \
见btckey.go:
+ b" T' l- W$ t1 G; E3 V% {/* SHA256 Hash */; H5 ?. V; n" J+ S8 m
    sha256_h := sha256.New()
  w7 A( E$ k8 j% P: h    sha256_h.Reset()
3 m+ l% A7 Q) Y/ F% S! j& v    sha256_h.Write(pub_bytes)
. D9 L8 `) T% m, {" \# l7 c, Q    pub_hash_1 := sha256_h.Sum(nil)
% ^/ |3 h" r. ?    /* RIPEMD-160 Hash */
' E6 v/ ]5 W3 o. K! ?' N8 I  P    ripemd160_h := ripemd160.New()
/ J! D; P9 X+ G# J: D5 R) i    ripemd160_h.Reset()
+ l( h1 S6 X( `. X1 l7 M    ripemd160_h.Write(pub_hash_1)
; A" K! _/ f6 ?# O6 N    pub_hash_2 := ripemd160_h.Sum(nil), y6 i! g* z" ?4 \$ i# y) h7 K
    program_hash := pub_hash_2
+ `# }6 y, s0 y- V( ~- {地址是怎么来的?
5 _5 t3 o# s& U, E3 i/ d地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:% G3 V4 Z( v: l9 g# Q  V
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码: Z4 b, m7 E5 ^" a- F

4 a/ ^, z3 K! f8 x见 btckey.go 完整的由公钥生成地址的代码:
* {$ f& W6 V; F; b3 a. F; P// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
/ |+ f( g! M. s; n& ?  }, Bfunc (pub *PublicKey) ToNeoAddress() (address string) {. H" u/ u: K. K: x! I/ h. |
    /* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */7 z! H- d3 u5 N8 z
    /* Convert the public key to bytes */
0 Q# `* g9 U4 Y! ^8 f; t) o    pub_bytes := pub.ToBytes()
, c4 Z5 R* y) L: G    pub_bytes = append([]byte{0x21}, pub_bytes...)% B" I8 S8 r# e- o' {0 S
    pub_bytes = append(pub_bytes, 0xAC)8 L0 i; g' c5 V, H8 R, ~
    /* SHA256 Hash */) V2 @0 a0 }' e8 N8 A9 y2 V
    sha256_h := sha256.New()( B3 F( V% y' H" L! C/ H
    sha256_h.Reset()
; n2 K. D/ W) N5 ^& o0 @; @    sha256_h.Write(pub_bytes)
. h; o' e# Q7 x: V. A    pub_hash_1 := sha256_h.Sum(nil)0 ~* L1 V) V/ l' X- L* J
    /* RIPEMD-160 Hash */
; s: f: \3 }1 w5 M, J( L- [    ripemd160_h := ripemd160.New()
  C/ `7 F% G. [! W. f$ R5 O    ripemd160_h.Reset()% z2 ^$ j. J: o, v; p1 o
    ripemd160_h.Write(pub_hash_1), L& q# M7 U: j
    pub_hash_2 := ripemd160_h.Sum(nil)
( m9 X% u- f  J7 D8 b! a+ k( ^    program_hash := pub_hash_2& l0 S* M2 E  H) {- j
    //wallet version
' ^( g( x+ j" Z/ L+ }5 H    //program_hash = append([]byte{0x17}, program_hash...)
/ X% B. T( j: L# P2 V: l    // doublesha := sha256Bytes(sha256Bytes(program_hash))+ K% Q. L' u3 P1 W" A. {& y  e
    // checksum := doublesha[0:4]
' b8 f* D& V: `. \2 P# j3 \+ U    // result := append(program_hash, checksum...)# W4 J+ e- q# z' C- m; H& a
    /* Convert hash bytes to base58 check encoded sequence */& Z8 C3 w1 s- {+ g; T( j& o
    address = b58checkencodeNEO(0x17, program_hash)
( r$ b% K$ J7 M    return address4 P+ ?" g8 p( v8 L' q2 N  `
}1 L' E7 W2 m' I: _
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
# T$ X/ d0 P& N/ w6 V3 Afunc b58checkencodeNEO(ver uint8, b []byte) (s string) {, y1 a* f( b! D) i6 E( f8 w
    /* Prepend version */0 u5 R- R4 o. C4 P0 Y
    bcpy := append([]byte{ver}, b...)
3 T7 _' T) f% t/ U/ A# i/ ~    /* Create a new SHA256 context */
# i4 H1 v. I1 c9 I0 P    sha256_h := sha256.New()
% M% y# h( s+ Q: w6 \1 G! \    /* SHA256 Hash #1 */8 ]7 p  L( O8 x9 ?/ J2 E
    sha256_h.Reset()$ h& l: C+ J) c$ F$ ]1 A
    sha256_h.Write(bcpy)
  v* n/ h! G/ }% L1 M' e    hash1 := sha256_h.Sum(nil)
, r+ g" H, w& v0 F( ]    /* SHA256 Hash #2 */- y- t) e# A/ U: ^; ~1 L" p6 U* f/ F
    sha256_h.Reset()  h, B" a. T* y, K0 t( S4 v- B
    sha256_h.Write(hash1)
3 a2 @" |6 Y. _/ N, s- ?    hash2 := sha256_h.Sum(nil)
6 m7 @/ I* d. S, @9 v    /* Append first four bytes of hash */
; [8 ]; `& S& N. P5 \/ [    bcpy = append(bcpy, hash2[0:4]...)
7 ?& Y; o$ G0 _  R% Q6 Y    /* Encode base58 string */. d. F% P+ ~: w
    s = b58encode(bcpy)
/ \4 L7 f+ E% m" H8 n3 V    // /* For number of leading 0's in bytes, prepend 1 */
$ y5 _( \3 q7 l* V( c    // for _, v := range bcpy {
/ y8 T5 r' e, }  p* g, Q* B    //  if v != 0 {  C& i  s" L! Z; B, }- {
    //      break
1 S* a+ O5 \( e; k    //  }! `7 `2 T7 M( v# X, O0 |0 E
    //  s = "1" + s" L5 _& B0 H! \
    // }2 L9 u8 p0 i6 m. O& V0 z
    return s
) k% i6 J. @) x6 c6 Z0 j2 e  D5 U}' `+ _; `' I8 I8 I1 c& o+ c
WIF 是怎么来的?
* y- Y$ L# z+ {7 O! j! _WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
7 K2 q# \( G! y, E前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码
% K1 `  L; r8 O8 p
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

放弃六月们 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    8