参照贴:https://blog.csdn.net/jason_cuijiahui/article/details/79305689
package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"fmt"
"golang.org/x/crypto/ripemd160"
"math/big"
"os"
"strings"
)
func main() {
//0 - 有一个私有的ECDSA键
fmt.Println("0 - 有一个私有的ECDSA键")
curve := elliptic.P256()
private, err := ecdsa.GenerateKey(curve, rand.Reader)
fmt.Println("这是私钥:" + Tool_DecimalByteSlice2HexString(private.D.Bytes()))
//1 - 生成相应的公钥
fmt.Println("1 - 生成相应的公钥")
pubKey := append(private.PublicKey.X.Bytes(), private.PublicKey.Y.Bytes()...)
fmt.Println("这是公钥:" + Tool_DecimalByteSlice2HexString(pubKey))
if err != nil {
fmt.Errorf(err.Error())
os.Exit(1)
}
//2 - 将公钥进行256hash
fmt.Println("2 - 将公钥进行256hash")
publicSHA256 := sha256.Sum256(pubKey)
fmt.Println("这是公钥hash:" + Tool_DecimalByteSlice2HexString(publicSHA256[:]))
//crypto.RIPEMD160.HashFunc().New()
RIPMD160Hasher := ripemd160.New()
//3 - 在SHA-256的结果上执行RIPEMD-160哈希。
fmt.Println("3 - 在SHA-256的结果上执行RIPEMD-160哈希。")
_, err = RIPMD160Hasher.Write(publicSHA256[:])
if err != nil {
fmt.Errorf(err.Error())
os.Exit(1)
}
//4 - 在RIPEMD-160散列前添加版本字节(主网络的0x00)
fmt.Println("4 - 在RIPEMD-160散列前添加版本字节(主网络的0x00)")
var by [1]byte
by[0] = 0x00
publicRIPEMD160 := RIPMD160Hasher.Sum(by[:])
fmt.Println("RIPEMD-160散列:" + Tool_DecimalByteSlice2HexString(publicRIPEMD160))
//注意下面的步骤是Base58Check编码,它有多个库选项可用来实现它。
//5、在扩展的RIPEMD-160结果上执行SHA-256散列。
fmt.Println("5、在扩展的RIPEMD-160结果上执行SHA-256散列。")
rehash := sha256.Sum256(publicRIPEMD160)
fmt.Println("第一次hash:" + Tool_DecimalByteSlice2HexString(rehash[:]))
//6、对之前的SHA-256散列的结果执行SHA-256散列。
fmt.Println("6、对之前的SHA-256散列的结果执行SHA-256散列。")
rerehash := sha256.Sum256(rehash[:])
fmt.Println("再一次hash:" + Tool_DecimalByteSlice2HexString(rerehash[:]))
//7、以第二个SHA-256散列的前4个字节为例。这是地址校验和。
fmt.Println("7、以第二个SHA-256散列的前4个字节为例。这是地址校验和。")
sum := checksum(rerehash[:])
fmt.Println("校验地址:" + Tool_DecimalByteSlice2HexString(sum))
//8、将第7阶段的4个校验和字节添加到第4阶段扩展的RIPEMD-160散列的末尾。这是25字节的二进制比特币地址。
fmt.Println("8、将第7阶段的4个校验和字节添加到第4阶段扩展的RIPEMD-160散列的末尾。这是25字节的二进制比特币地址。")
var b bytes.Buffer
b.Write(publicRIPEMD160[:])
b.Write(sum)
result := b.Bytes()
fmt.Println("拼接校验地址:" + Tool_DecimalByteSlice2HexString(result))
//9、使用Base58Check编码将一个字节字符串的结果转换为base58字符串。这是最常用的比特币地址格式。
fmt.Println("9、使用Base58Check编码将一个字节字符串的结果转换为base58字符串。这是最常用的比特币地址格式。")
address := Encode(result)
fmt.Println("最终地址:" + address)
}
func checksum(payload []byte) []byte {
addressChecksumLen := 4
firstSHA := sha256.Sum256(payload)
secondSHA := sha256.Sum256(firstSHA[:])
return secondSHA[:addressChecksumLen]
}
func Tool_DecimalByteSlice2HexString(DecimalSlice []byte) string {
var sa = make([]string, 0)
for _, v := range DecimalSlice {
sa = append(sa, fmt.Sprintf("%02X", v))
}
ss := strings.Join(sa, "")
return ss
}
const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
// EncodeBig encodes src, appending to dst. Be sure to use the returned
// new value of dst.
func EncodeBig(dst []byte, src *big.Int) []byte {
start := len(dst)
n := new(big.Int)
n.Set(src)
radix := big.NewInt(58)
zero := big.NewInt(0)
for n.Cmp(zero) > 0 {
mod := new(big.Int)
n.DivMod(n, radix, mod)
dst = append(dst, alphabet[mod.Int64()])
}
for i, j := start, len(dst)-1; i < j; i, j = i+1, j-1 {
dst[i], dst[j] = dst[j], dst[i]
}
return dst
}
func Encode(encoded []byte) string {
//Perform SHA-256 twice
hash := sha256.Sum256(encoded)
hash = sha256.Sum256(hash[:])
//First 4 bytes if this double-sha'd byte array is the checksum
//Append this checksum to the input bytes
encoded = append(encoded, hash[0:4]...)
//Convert this checksum'd version to a big Int
bigIntEncodedChecksum := new(big.Int).SetBytes(encoded)
//Encode the big int checksum'd version into a Base58Checked string
base58EncodedChecksum := EncodeBig(nil, bigIntEncodedChecksum)
//Now for each zero byte we counted above we need to prepend a 1 to our
//base58 encoded string. The rational behind this is that base58 removes 0's (0x00).
//So bitcoin demands we add leading 0s back on as 1s.
buffer := make([]byte, 0, len(base58EncodedChecksum))
//base58 alone is not enough. We need to first count each of the zero bytes
//which are at the beginning of the encodedCheckSum
for _, v := range encoded {
if v != 0 {
break
}
buffer = append(buffer, '1')
}
buffer = append(buffer, base58EncodedChecksum...)
return string(buffer)
}
注意上方引入的ripemd160包,如果使用golang自带的包,则会出现方法无法到达异常。
具体堆栈如下:
panic: crypto: requested hash function #9 is unavailable
goroutine 1 [running]: crypto.Hash.New(0x9, 0x40, 0x40) C:/Go/src/crypto/crypto.go:89 +0x117 main.HashPubKey(0xc042096100, 0x40, 0x40, 0xc0420491c0, 0xc0420491e0, 0xc042049180) D:/go/src/Blockchain5/wallet.go:49 +0x91 main.Wallet.GetAddress(0x637da0, 0xc04204f540, 0xc0420491c0, 0xc0420491e0, 0xc042049180, 0xc042096100, 0x40, 0x40, 0x0, 0x0, ...) D:/go/src/Blockchain5/wallet.go:34 +0x61 main.(*Wallets).CreateWallet(0xc0420724e8, 0x6365e0, 0xc042066a80) D:/go/src/Blockchain5/wallets.go:31 +0x5f main.(*CLI).createWallet(0xc04206ff70) D:/go/src/Blockchain5/cli_createwallet.go:7 +0x3e main.(*CLI).Run(0xc04206ff70) D:/go/src/Blockchain5/cli.go:100 +0x5a8 main.main() D:/go/src/Blockchain5/main.go:5 +0x32
执行结果参考:
0 - 有一个私有的ECDSA键
这是私钥:255B7133F493C6288D903D54FC69051890411C11090CA39E6D0F8D67A490CF28
1 - 生成相应的公钥
这是公钥:A7BF91E4DE89F28FAAF7392C36D9698358A4879A07B707CA2E882BAB7DE64A965D8A043E351CF8F2475600785A1F9ADBC266801AF77ED2E0A9B4D5374E6FC7ED
2 - 将公钥进行256hash
这是公钥hash:2D39DB4AB6772DA1F1A682A62530F3EC75C0105DEC79C555834D247DAB377993
3 - 在SHA-256的结果上执行RIPEMD-160哈希。
4 - 在RIPEMD-160散列前添加版本字节(主网络的0x00)
RIPEMD-160散列:00BBEB938C24AE7EE8A2DCCA286EC7EC99FDD0378B
5、在扩展的RIPEMD-160结果上执行SHA-256散列。
第一次hash:4B84E12A59098511B31D8430AEED1BE2106DD0D56121A6E75476BAE604FA3F02
6、对之前的SHA-256散列的结果执行SHA-256散列。
再一次hash:D7B2F0A128F959E1E00B28C3E3B411C3F5BF35A4BFC90D0D6B4F4D7F683A8DF7
7、以第二个SHA-256散列的前4个字节为例。这是地址校验和。
校验地址:74493FC5
8、将第7阶段的4个校验和字节添加到第4阶段扩展的RIPEMD-160散列的末尾。这是25字节的二进制比特币地址。
拼接校验地址:00BBEB938C24AE7EE8A2DCCA286EC7EC99FDD0378B74493FC5
9、使用Base58Check编码将一个字节字符串的结果转换为base58字符串。这是最常用的比特币地址格式。
最终地址:12w6y8LqLJdfUm1DhsniqSEn1wCmGjLC7PVVwrem
Process finished with exit code 0