一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2343
0
0
见 Account.swift 类:
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 {
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?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
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()
self.hashedSignature = wallet.hashedSignature()) x- J6 l2 V' Q, S# \" k
//default to mainnet
self.neoClient = NeoClient.sharedMain9 l+ z+ \4 V5 s+ T& q# {0 J
}4 A6 H2 d# O5 S' y
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:
/*!
@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
@result Return 0 on success, any other value on failure.
*/! 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)
let result = pkeyData.withUnsafeMutableBytes {, h7 |' I9 k* n& j% j) i3 W
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}/ U& W1 p" q, |4 L9 Z- E; w
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private key
func 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
err := priv.FromBytes(pb)+ k+ p4 P8 G& t- b. a# O4 ^
if err != nil {
return &Wallet{}, err$ I: ~+ r) u. B9 p0 L
}
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),' p* t( @" F+ D( w# L# z" {- Q) x
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),
}+ 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
公钥是怎么来的?
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。
见neowallet.go 和 btckey.go:2 }/ }: Y9 P4 |$ V# b
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)1 o1 b! t$ N, n9 i4 V0 g3 I4 ~* ]
var priv btckey.PrivateKey
err := priv.FromBytes(pb)
if err != nil {1 p- M6 i4 g3 m" c
return &Wallet{}, err
}; d3 `! L2 \" z) q9 b
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),& L* ^2 p n9 b/ N' @
WIF: priv.ToWIFC(),* o* l/ t. R8 V' [; g
HashedSignature: priv.ToNeoSignature(),
}$ 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) {
/* 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)
/* Check that Q is on the curve */
if !secp256r1.IsOnCurve(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
priv.Y = Q.Y' [" `; ?3 q. k7 T/ l
return &priv.PublicKey
}" z) P% f+ Y! l: v# v+ H
地址脚本是怎么来的?
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:. E/ s6 s! n3 f
前面是:0x21后面是:0xAC
, g6 j- b2 V0 S6 u z9 o
见btckey.go:
/* Convert the public key to bytes */. |# @8 E5 \% d
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)8 p! v1 f% U1 \
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见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)
pub_hash_1 := sha256_h.Sum(nil)
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()0 P$ h- x ]. ~0 o6 [
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)
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 完整的由公钥生成地址的代码:
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
func (pub *PublicKey) ToNeoAddress() (address string) {2 Z; v5 Z4 ]9 H$ k
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */
/* 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)
/* SHA256 Hash */
sha256_h := sha256.New()
sha256_h.Reset()- ~: m6 n T% Z; t( f! r
sha256_h.Write(pub_bytes)
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()
ripemd160_h.Reset()
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))
// checksum := doublesha[0:4]" }) K4 K6 y# N4 T+ B+ C
// result := append(program_hash, checksum...)
/* Convert hash bytes to base58 check encoded sequence */
address = b58checkencodeNEO(0x17, program_hash)
return address
}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 */
sha256_h := sha256.New()6 q! R, G+ E" K' B* M
/* SHA256 Hash #1 */
sha256_h.Reset()
sha256_h.Write(bcpy)
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()
sha256_h.Write(hash1)
hash2 := sha256_h.Sum(nil)
/* Append first four bytes of hash */, [6 m- C" y! _
bcpy = append(bcpy, hash2[0:4]...)) B, b4 n$ g7 ?
/* Encode base58 string */
s = b58encode(bcpy)
// /* For number of leading 0's in bytes, prepend 1 */( K( r3 ?7 b2 F* Q
// for _, v := range bcpy {
// if v != 0 {( \7 R* O+ {& }0 T% g& i
// break
// }$ `: a# j4 w9 z/ s. V& Z
// s = "1" + s
// }/ c8 Z% i z0 k8 v0 ]
return s
}7 @; l& j* X$ C% ~1 ^0 I
WIF 是怎么来的?+ V8 `3 R$ F4 a4 Z
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码
成为第一个吐槽的人