一文弄懂:区块链钱包的私钥
放弃六月们
发表于 2022-11-27 15:40:31
2328
0
0
见 Account.swift 类:
public init?() {
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {: F+ Y1 D9 Z7 k1 { \
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)
}, h3 `+ X0 L9 p6 w: K! w {
if result != errSecSuccess {
fatalError()
}
b. X5 g5 s& @, K+ @- X1 j% N
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }5 O* G9 T' F4 Q: h$ B
self.wif = wallet.wif()
self.publicKey = wallet.publicKey()
self.privateKey = pkeyData
self.address = wallet.address()& { h- Y, f k+ q* z
self.hashedSignature = wallet.hashedSignature()1 F# Q! d$ x$ }( Z/ u. Y8 R
//default to mainnet
self.neoClient = NeoClient.sharedMain
}
它是通过 Security.framework 库里的 SecRandomCopyBytes 方法,生成一组密码安全的随机字节:; r6 l1 C# k' J" Z# P( P
/*!( T6 e; U, a `
@function SecRandomCopyBytes& b- t. K8 C2 Q0 j, R0 s
@abstract Return count random bytes in *bytes, allocated by the caller.3 H/ Y1 r3 o! z7 v
It is critical to check the return value for error
@result Return 0 on success, any other value on failure.
*/9 |- |5 Z4 Z/ K% X
@available(iOS 2.0, *)
public func SecRandomCopyBytes(_ rnd: SecRandomRef?, _ count: Int, _ bytes: UnsafeMutableRawPointer) -> Int325 U, W) _. `" G. j1 \' g9 ^
随机生成一个32字节的 Data 数据,即 privatekeyData:
var pkeyData = Data(count: 32)
let result = pkeyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, pkeyData.count, $0)2 M7 C& ^3 Y7 c) @# S- J
}
然后根据私钥(用 privatekeyData 的 HexString 作为参数)生成一个钱包,见 neo-utils:2 Z7 n m. \, {& ^. R6 J. p
var error: NSError?
guard let wallet = NeoutilsGeneratePublicKeyFromPrivateKey(pkeyData.fullHexString, &error) else { return nil }
// Generate a wallet from a private key% p: f0 a: H, x; R1 v; n
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {; w! i5 L8 y) @; h# n# T
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey
err := priv.FromBytes(pb)1 C/ U5 l7 C+ \
if err != nil {
return &Wallet{}, err8 m6 ~" _: ^4 t0 r* h5 I W# j
}, U$ \: u% T9 \' t6 C u
wallet := &Wallet{. d) d# C9 B# U; V; v. w. G
PublicKey: priv.PublicKey.ToBytes(),
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),$ m9 B. l8 m0 F: j! n
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),
}/ V9 S6 ^. _5 E; ?1 y3 m
return wallet, nil
}. u/ ^. ], T( ]& D3 R. o$ e+ K
公钥是怎么来的?
公钥是用私钥通过椭圆曲线算法得到的,但是无法从公钥算出私钥。: s+ B" _+ A; |2 F- `
见neowallet.go 和 btckey.go:0 Y, A& H/ v( V
// Generate a wallet from a private key
func GenerateFromPrivateKey(privateKey string) (*Wallet, error) {
pb := hex2bytes(privateKey)
var priv btckey.PrivateKey9 h8 F$ ^3 S; N; ]5 e# n! y
err := priv.FromBytes(pb)6 h, X# a, K! ^) k( u
if err != nil {
return &Wallet{}, err" `5 n/ z' y8 [- G& c V
}$ g1 P s* f9 W& B2 g" w! U1 e0 z% z
wallet := &Wallet{
PublicKey: priv.PublicKey.ToBytes(),0 A/ o! {, y( J% q" p0 z- M
PrivateKey: priv.ToBytes(),
Address: priv.ToNeoAddress(),
WIF: priv.ToWIFC(),
HashedSignature: priv.ToNeoSignature(),5 ?* D6 F. Z9 S+ I8 g' V5 F4 ^
}8 R# M+ b6 N9 O) ?
return wallet, nil- _( t* a& N( w0 F% F
}
// derive derives a Bitcoin public key from a Bitcoin private key.5 p" e7 m+ V" n
func (priv *PrivateKey) derive() (pub *PublicKey) {
/* See Certicom's SEC1 3.2.1, pg.23 */
/* Derive public key from Q = d*G */1 Q$ h9 F, E; {9 K' n
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.")
}) M7 {. F( v* G3 P
priv.X = Q.X
priv.Y = Q.Y
return &priv.PublicKey8 t( |) v P4 D; x3 v9 R
}$ o% u1 d$ ?% r3 D
地址脚本是怎么来的?$ D) t4 i; P; e2 V2 D/ j* P8 S i
地址脚本是由公钥前后各加了一个字节得到的,这两个字节是固定的:9 V1 t3 t6 r( z) \# g
前面是:0x21后面是:0xAC
见btckey.go:
/* Convert the public key to bytes */
pub_bytes := pub.ToBytes()2 K7 i* ~8 {8 Q' Z, h5 v8 P. L) ^
pub_bytes = append([]byte{0x21}, pub_bytes...)
pub_bytes = append(pub_bytes, 0xAC)
地址ScriptHash是怎么来的?
地址ScriptHash就是地址脚本取了个Hash,一次 sha256,一次ripemd160:
见btckey.go:
/* SHA256 Hash */
sha256_h := sha256.New()7 d6 D* n/ Z# a( T4 e
sha256_h.Reset()3 Y( C( _" f1 o
sha256_h.Write(pub_bytes)
pub_hash_1 := sha256_h.Sum(nil)4 }/ H4 ~! r7 ]& V: a; {, N! }
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)
pub_hash_2 := ripemd160_h.Sum(nil)7 W \8 z+ K1 [1 J/ I
program_hash := pub_hash_2
地址是怎么来的?
地址是由地址ScriptHash加了盐,加了验证功能,然后 Base58 编码得到的:8 C. o3 D9 g, k( K" ^7 \% z
加盐:前面加了一个字节 0x17加验证功能:把加盐后的字节做了一个 hash,两次 sha256,取前四个字节编码:Base58 编码
见 btckey.go 完整的由公钥生成地址的代码:
// ToAddress converts a Bitcoin public key to a compressed Bitcoin address string.
func (pub *PublicKey) ToNeoAddress() (address string) {: b. W4 D1 n8 X! v
/* See https://en.bitcoin.it/wiki/Technical_background_of_Bitcoin_addresses */
/* Convert the public key to bytes */( R: \/ e% e8 P5 q$ j! S; U
pub_bytes := pub.ToBytes()
pub_bytes = append([]byte{0x21}, pub_bytes...)# f8 d) C, {7 S8 x6 B2 W$ Z8 d
pub_bytes = append(pub_bytes, 0xAC)
/* SHA256 Hash */+ v% W# _8 v2 A( V9 ?6 K+ c' x& f
sha256_h := sha256.New()4 e* I6 w4 e8 V) Z5 F6 T1 C9 C
sha256_h.Reset()
sha256_h.Write(pub_bytes)/ C8 F: a' ]6 L$ J: ]0 c, g5 j
pub_hash_1 := sha256_h.Sum(nil)' M- c6 p+ E. Y4 h& B) @
/* RIPEMD-160 Hash */
ripemd160_h := ripemd160.New()% }' ~+ F- r$ p/ R: C/ C. N* G
ripemd160_h.Reset()
ripemd160_h.Write(pub_hash_1)3 n7 p @0 A# O7 Q2 z1 n
pub_hash_2 := ripemd160_h.Sum(nil)
program_hash := pub_hash_2. J/ M; B: E% N$ J9 a0 G4 E
//wallet version
//program_hash = append([]byte{0x17}, program_hash...)
// doublesha := sha256Bytes(sha256Bytes(program_hash))
// checksum := doublesha[0:4]' H# S D% [8 g0 c
// result := append(program_hash, checksum...)
/* Convert hash bytes to base58 check encoded sequence */& J& V. A# s# ?2 O
address = b58checkencodeNEO(0x17, program_hash)
return address
}, k' V; }6 K8 v* [
// b58checkencode encodes version ver and byte slice b into a base-58 check encoded string.
func b58checkencodeNEO(ver uint8, b []byte) (s string) {% P& O. X% Y& A9 a
/* Prepend version *// o* h* I2 e$ ~; F* \) q! n2 Y- v: H
bcpy := append([]byte{ver}, b...)
/* Create a new SHA256 context */1 M+ s: ~! C: k- C" t8 z; D6 Q
sha256_h := sha256.New()
/* SHA256 Hash #1 */
sha256_h.Reset()
sha256_h.Write(bcpy)
hash1 := sha256_h.Sum(nil)
/* SHA256 Hash #2 */( s' Z1 Z9 ?: _" M* M
sha256_h.Reset()
sha256_h.Write(hash1)3 y% r" {) i% P2 y! Q1 b
hash2 := sha256_h.Sum(nil)
/* Append first four bytes of hash */
bcpy = append(bcpy, hash2[0:4]...)% L; }! d: h9 j3 v( Q1 N) I5 g; h
/* Encode base58 string */! Y- N4 ~$ Q4 C+ @+ }+ O
s = b58encode(bcpy)( _! \$ ^% B9 Y/ A2 ~
// /* For number of leading 0's in bytes, prepend 1 */" N4 I/ R8 O* C- m" @; U
// for _, v := range bcpy {5 s& w7 }( @; g) W3 g* ~- O
// if v != 0 {
// break
// }/ b& H% n. @. a f, D. O( ~
// s = "1" + s
// }* ?$ e- v5 R5 K; h) J
return s$ W) m0 T g2 d8 Y6 r1 l- u
}) v/ r' p+ `2 H1 _1 f m: v
WIF 是怎么来的?- j* n6 Q. _' m$ n5 Y3 P
WIF(Wallet Import Format)是由私钥在前面加了一个版本号字节 0x80,在后面加了一个压缩标志的字节 0x01,然后对这34个字节进行哈希,取哈希值的前4个字节作为校验码加在最后面,最后经过 Base58 编码得到:
前面加版本字节:0x80后面加压缩标志字节:0x01对这34个字节进行哈希:取哈希值的前4个字节加在最后面编码:Base58 编码" V0 q5 K& n: k
成为第一个吐槽的人