一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2330
0
0
见 Account.swift 类:
public init?() {# T, o( M! J2 u' I9 l$ v5 `
var pkeyData = Data(count: 32): `; F: n r0 N: V& L: [8 m
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}: w; D" \, @* Y* D/ R- A
* i' r8 J9 P$ x+ N. A0 c
if result != errSecSuccess {- c# D* q2 O7 t/ h3 q0 l0 b- D
fatalError()
}/ g6 S4 i$ P* ]. ]+ p% ~4 D$ T
S: w+ l! g( C# R; Q& b+ f
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }" h0 P# @) n& f! l% v
self.wif = wallet.wif()- f/ w2 A) H7 A1 i& k" E+ u
self.publicKey = wallet.publicKey()
self.privateKey = pkeyData+ J) D; E3 d T9 h! Z) ~* _
self.address = wallet.address()) i$ e. W& e2 [' F$ @; `6 r6 P
self.hashedSignature = wallet.hashedSignature()
//default to mainnet& {8 ~: Q8 \* i3 n. {
self.neoClient = NeoClient.sharedMain$ Y! U& n( A+ _8 i4 s; @# y) P' l
}
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:/ ~6 l y+ e4 O5 Z. s) N
/*!$ i, @) C, p+ \& w
@function SecRandomCopyBytes4 _3 M1 p8 [* O# W3 w1 a
@abstract Return count random bytes in *bytes, allocated by the caller.$ Z) K( o2 s/ ~" l) Q, l
It is critical to check the return value for error. [. j6 I0 e, m7 W
@result Return 0 on success, any other value on failure.
*/
@available(iOS 2.0, *)1 f- |- P# {; v) V% f
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32- B! z4 M2 w& \( s
随机生成一个32字节的 Data 数据,即 privatekeyData:' [. }) \% V: v( a% i P2 U7 \/ i
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes { O% m3 p( j3 `! S( b/ L4 b
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0): B% K) c" M) v9 w/ ]- K2 q
}/ w @" F1 k8 a, F
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }3 d+ l' E; g$ i. ]
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey3 V# q Y$ B5 n+ T, F
err := priv.FromBytes(pb)
if err != nil {
return &Wallet{}, err% Z, Y) `0 n/ Q+ M; `3 s |
}5 G0 ^0 p- d1 {! ]
wallet := &Wallet{ J( q& D) c* w. \3 o7 f
PublicKey: priv.PublicKey.ToBytes(),& {& I: Q% |5 N7 D% _$ N# R. m
PrivateKey: priv.ToBytes(),% S7 C% [8 o9 u. Z
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(), Q: d ?% {$ l% s6 d. u1 Q
}
return wallet, nil
}
公钥是怎么来的?, w8 e1 g$ x8 T/ s' W
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。# S) E- o$ y6 B- E& D
见neowallet.go 和 btckey.go:
// Generate a wallet from a private key+ @# v; C' s2 A( `, H2 ^
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {# A- T! |. |5 E' @. |
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey% W2 B9 I2 h* Y5 f
err := priv.FromBytes(pb)
if err != nil {
return &Wallet{}, err
}# Q h7 [$ ], W8 F
wallet := &Wallet{7 h2 @, M0 B8 T% b1 x
PublicKey: priv.PublicKey.ToBytes()," z: B; j$ R2 Z4 y* Q( @
PrivateKey: priv.ToBytes(),8 Q& c2 p. B4 l3 F) F& Z( b0 U
Address: priv.ToNeoAddress(),1 z# f3 j' R/ _# s
WIF: priv.ToWIFC(),. o- D9 q/ |! f$ J8 \
HashedSignature: priv.ToNeoSignature(),. q8 x9 L5 h, Q
}! u) ~. U" ]: c6 n$ u( i' Z
return wallet, nil- }5 Y2 b2 e% ?/ g( f* X$ a
}
// derive derives a Bitcoin public key from a Bitcoin private key.2 ^. C# G( J# O: e
func (priv *PrivateKey) derive() (pub *PublicKey) {
/* See Certicom's SEC1 3.2.1, pg.23 */7 V4 H o: ?- l! T# @7 R
/* Derive public key from Q = d*G */
Q := secp256r1.ScalarBaseMult(priv.D)
/* Check that Q is on the curve */
if !secp256r1.IsOnCurve(Q) {. X0 T& w E G" s% H1 E8 P
panic("Catastrophic math logic failure in public key derivation.")8 b0 z& e' U0 p% [ r
}
priv.X = Q.X
priv.Y = Q.Y
return &priv.PublicKey9 x& r7 Q5 d D' x, r
}: b, w/ p9 Y0 e( N* L
地址脚本是怎么来的?
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:& o, O% z* J& x. Z2 x) |, B; ?+ {, b
前面是:0x21后面是:0xAC
$ W7 C+ S& l7 C4 S- E
见btckey.go:
/* Convert the public key to bytes */; y5 }3 k3 A% I ~* g4 k7 v' @( l4 E
pub_bytes := pub.ToBytes()! [8 V3 v8 W! y8 X3 U$ F
pub_bytes = append([]byte{0x21}, pub_bytes...). f) l0 T' b- X' d3 \. b/ ?& r
pub_bytes = append(pub_bytes, 0xAC)% c4 H% x2 a" ]# w
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:- \' J. y+ N6 j+ j' Q8 f1 m
见btckey.go:+ Q$ c8 L" D/ H5 e+ b0 R
/* SHA256 Hash */. X$ a1 E5 c: }; Q) P* n( s) n
sha256_h := sha256.New()
sha256_h.Reset()9 @! l/ E; r4 }0 z& K- J- }$ i" y c
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil): S+ a9 S T- A& Q5 ]; \% h3 X
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()
ripemd160_h.Reset()* h, `! u( l9 Y2 ?' k# L' i, J. z n
ripemd160_h.Write(pub_hash_1). h2 w8 i# _' m
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
地址是怎么来的?& h% T6 Y* i3 o9 `) h
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码% y$ p+ b% F3 J3 B1 ]" @
7 I, W3 D. C5 I" w
见 btckey.go 完整的由公钥生成地址的代码:; L" g9 ?* M5 N) F1 F3 R
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
func (pub *PublicKey) ToNeoAddress() (address string) {
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */ w' W3 X/ T" C5 E! ]' R6 x/ q. @
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...): @. f4 `6 n: p, ~" I( G3 D
pub_bytes = append(pub_bytes, 0xAC)
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset(), n; ^; j2 ], D
sha256_h.Write(pub_bytes)3 t% D# s6 G5 H
pub_hash_1 := sha256_h.Sum(nil)1 J0 L. ~4 j6 L1 I) T
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()8 H' c, X! x3 Z8 `5 W
ripemd160_h.Reset()+ Y3 b9 Y! M4 J# f
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
//wallet version
//program_hash = append([]byte{0x17}, program_hash...)
// doublesha := sha256Bytes(sha256Bytes(program_hash))
// checksum := doublesha[0:4]
// result := append(program_hash, checksum...)
/* Convert hash bytes to base58 check encoded sequence */
address = b58checkencodeNEO(0x17, program_hash)' b3 l( _; O$ l% W4 v& X3 ~2 O
return address
}
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.; d1 H( [! q' c2 b- [0 z: t8 y2 u3 q
func b58checkencodeNEO(ver uint8, b []byte) (s string) {
/* Prepend version */( ~1 [0 o/ F$ w1 `; ~2 l
bcpy := append([]byte{ver}, b...) o) |. X. \6 G
/* Create a new SHA256 context */: F+ x4 m9 ~- v9 A4 |. i0 B* u
sha256_h := sha256.New()0 M8 ^" S6 s6 F7 r+ _: `/ @- C- f
/* SHA256 Hash #1 */
sha256_h.Reset()9 s3 h& |! p, H6 P6 F& L
sha256_h.Write(bcpy)
hash1 := sha256_h.Sum(nil)' H) ?* C' p) u: {8 u7 n# g
/* SHA256 Hash #2 */
sha256_h.Reset()6 d: m/ a" K+ [6 Z/ K
sha256_h.Write(hash1)
hash2 := sha256_h.Sum(nil) v' B" A# {/ v' t" |
/* Append first four bytes of hash */
bcpy = append(bcpy, hash2[0:4]...)
/* Encode base58 string */
s = b58encode(bcpy)
// /* For number of leading 0's in bytes, prepend 1 */- U8 U3 \. @4 G9 z, h
// for _, v := range bcpy {
// if v != 0 {
// break
// }0 K0 C- m2 i5 b* n: T
// s = "1" + s! U$ u6 I* I$ g- f( Z3 e P) W; M# Z
// }* W, ~3 S) f* I, W
return s9 k1 ]0 p7 K' K1 L T% L) V
}
WIF 是怎么来的?+ p( M4 W6 J* ^) J
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:4 G# e# {7 U5 ^$ W) q; a
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码% a: u* M# M7 @' r! n& i
成为第一个吐槽的人