一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2387
0
0
见 Account.swift 类:
public init?() {
var pkeyData = Data(count: 32)& n& A" P9 k4 ]5 P
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)) J: F1 I: D7 f
}
9 ^" y& {. V' R4 N' P& S4 ^! a
if result != errSecSuccess {, \0 M# |5 J2 C; [+ n$ g
fatalError()
}
var error: NSError?( _7 U: M( M+ e4 P
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
self.wif = wallet.wif()# K) H: k: U {8 f) a& S
self.publicKey = wallet.publicKey()
self.privateKey = pkeyData
self.address = wallet.address(); r/ d" V) |# Z
self.hashedSignature = wallet.hashedSignature()
//default to mainnet
self.neoClient = NeoClient.sharedMain1 u3 C3 W/ d+ m: @ g
}2 Y" k7 l# c( c) c
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
/*!/ `) S& n l/ I. Z/ @; y: v
@function SecRandomCopyBytes
@abstract Return count random bytes in *bytes, allocated by the caller." x& Z4 z7 Z; L" ]' d$ J" L
It is critical to check the return value for error+ `& ]: i" D, @* O6 f
@result Return 0 on success, any other value on failure.8 ?' j; K9 O. a
*/
@available(iOS 2.0, *)7 k0 Z1 w" P0 ~& V" z4 [: f
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int32
随机生成一个32字节的 Data 数据,即 privatekeyData:7 K- t. Q" h6 p) M" \. |! E, H4 v$ q
var pkeyData = Data(count: 32); g2 m: T- D+ G& @6 a) R3 Y
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:0 F4 x7 B0 i: A1 }) E5 x
var error: NSError?2 ]! U( h9 j9 @. {$ d/ V
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }/ ]: H+ g' O/ z7 i( ~/ r7 R& Z
// Generate a wallet from a private key, R U! w; B4 n1 w; u4 R" l
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey
err := priv.FromBytes(pb)
if err != nil {
return &Wallet{}, err, v# ^, J" z( I! H8 y4 w* k6 @
}" c+ R% a v3 W. b0 G
wallet := &Wallet{" M/ i' w# e3 N+ h8 M
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),* q: ]* F- L$ \
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),: Y6 s) _9 k" v" a3 q! s
}$ p8 [0 J5 K& ^) Q; J3 E) S
return wallet, nil
}4 Y6 Z1 \$ n8 a
公钥是怎么来的?
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。
见neowallet.go 和 btckey.go:! i; |: L( K' T) |5 n6 L
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)3 m) }' `7 v1 C Z I
var priv btckey.PrivateKey
err := priv.FromBytes(pb)
if err != nil {
return &Wallet{}, err' G; y: H( q# @/ w
}
wallet := &Wallet{$ Y- W- v3 P4 }/ k/ t3 E" y$ n# b
PublicKey: priv.PublicKey.ToBytes(),; z; g2 C# Z2 }0 ^+ |" l% o
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),
}
return wallet, nil6 {) l3 |( m- n! D; w. m
}
// derive derives a Bitcoin public key from a Bitcoin private key.
func (priv *PrivateKey) derive() (pub *PublicKey) {0 o0 F* L) y5 b. J
/* See Certicom's SEC1 3.2.1, pg.23 */
/* Derive public key from Q = d*G */
Q := secp256r1.ScalarBaseMult(priv.D)! E7 s* w! B" G E/ o3 Y
/* Check that Q is on the curve */4 W& G; j6 z2 K" A
if !secp256r1.IsOnCurve(Q) {
panic("Catastrophic math logic failure in public key derivation.")
}
priv.X = Q.X2 L/ i: w2 A* K* G, X! N
priv.Y = Q.Y
return &priv.PublicKey1 H. ]8 D4 U! W8 K
}
地址脚本是怎么来的?, |8 E/ i2 C9 `3 c3 \7 h' N
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:
前面是:0x21后面是:0xAC
9 i$ O! _+ v$ W$ P2 z2 T
见btckey.go:# P8 Y3 r2 b5 J5 z5 D/ W. W
/* Convert the public key to bytes */1 Z! [$ r' ~* p( I% o" b/ Z
pub_bytes := pub.ToBytes()( @; ]2 `( E6 _4 C
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)
地址ScriptHash是怎么来的?) S v+ |6 G. F3 X. `
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:
/* SHA256 Hash */: a- ^% T0 \7 e, g8 G! g7 a% ]2 H# X D) L
sha256_h := sha256.New()' B" z, B" e+ }3 t! R5 Z
sha256_h.Reset()" k- \- W5 z4 q2 a. d# Z9 |
sha256_h.Write(pub_bytes)" W( P/ c9 l5 C- U3 i
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */* W& r+ T: E: \
ripemd160_h := ripemd160.New(); A8 \2 O- m3 x6 W$ i" y
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)0 ^8 }9 O2 f9 Y# Y) ~; l
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
地址是怎么来的?4 C; d& R/ S1 Z1 n: |! y! o
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:. ?8 J) W2 H k+ O
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码5 u y6 ?4 U R S0 `8 G
" W8 M9 i0 v ^2 G6 d
见 btckey.go 完整的由公钥生成地址的代码:
// 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 */; G: j, Z- m; Q( ]6 r1 ^) y
/* Convert the public key to bytes */" y: |3 ^6 W+ O
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)0 S* s& \3 P F* S) Q
pub_bytes = append(pub_bytes, 0xAC)2 b$ Y7 M& ]1 Y5 a
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()1 G9 _2 K F% T% F
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */7 Z# ?8 E$ s8 X1 ^3 L. u# x: C
ripemd160_h := ripemd160.New(). W$ x# X A* h3 a( S F7 q
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2
//wallet version5 U2 i9 U3 H r
//program_hash = append([]byte{0x17}, program_hash...)8 L+ a$ L$ F3 r
// doublesha := sha256Bytes(sha256Bytes(program_hash))/ I/ n$ ?$ v# e( i
// checksum := doublesha[0:4]8 s) P" ?# q2 A, j/ T9 X! ~
// result := append(program_hash, checksum...): m, t3 ` `2 a& n! G
/* Convert hash bytes to base58 check encoded sequence */9 y. f) p- x$ B8 Q2 r6 @) Y
address = b58checkencodeNEO(0x17, program_hash)
return address
}' w6 Y; k1 ~8 w6 o/ }0 V
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
func b58checkencodeNEO(ver uint8, b []byte) (s string) {2 X/ h. [# s' v
/* Prepend version */
bcpy := append([]byte{ver}, b...)
/* Create a new SHA256 context */
sha256_h := sha256.New()# c/ u3 Q1 l& }( ]; V
/* SHA256 Hash #1 */
sha256_h.Reset()
sha256_h.Write(bcpy)
hash1 := sha256_h.Sum(nil)
/* SHA256 Hash #2 */6 k/ G9 i9 h6 \% W
sha256_h.Reset()% x9 G; q4 O f& j
sha256_h.Write(hash1)- |+ T7 [- G2 r/ G4 u
hash2 := sha256_h.Sum(nil)) \3 r' z+ _% I1 M5 E7 x
/* Append first four bytes of hash */& [( q8 t- ]6 ~ ^# a7 t
bcpy = append(bcpy, hash2[0:4]...)
/* Encode base58 string */
s = b58encode(bcpy)" O. t4 K( w4 \/ a
// /* For number of leading 0's in bytes, prepend 1 */
// for _, v := range bcpy {
// if v != 0 {* W, x1 H8 l9 _8 R4 z% b$ x4 C8 d
// break
// }
// s = "1" + s
// } m* a4 z0 \' U& ^- T C4 t: H. T
return s- A6 D# |# C' u3 d+ m1 h) W6 s a
}, N5 [3 R; o1 K- B8 S+ T/ ]) x ?
WIF 是怎么来的?
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:" x0 u8 V# Z/ K# @
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码4 Z0 ^. i; ^7 C0 ?
成为第一个吐槽的人