BIP-38(Bitcoin Improvement Proposal 38)是比特币改进提案之一,旨在对私钥进行加密,以方便安全地存储和传输。通过加密私钥,用户可以使用一个密码对其进行保护。解密加密的私钥需要提供正确的密码,从而增加了私钥的安全性。
BIP-38 的主要功能
- 私钥加密:将比特币私钥加密成一个人类可读的字符串。
- 私钥解密:将加密的私钥解密回原始私钥,需要提供正确的密码。
BIP-38 的应用场景
- 纸钱包:BIP-38 加密的私钥可以安全地打印在纸上,即使纸钱包被他人获得,没有密码也无法使用私钥。
- 备份和存储:加密的私钥可以安全地存储在不安全的环境中,例如云存储或电子邮件中,而无需担心被窃取。
BIP-38 加密和解密机制
加密私钥的步骤:
- 生成私钥和公钥:
- 生成一个随机的私钥。
- 计算对应的公钥。
- 计算公钥哈希:计算公钥的哈希值(RIPEMD-160)。
- 生成盐值:取公钥哈希的前 4 个字节作为盐值。
- 生成加密密钥:使用 Scrypt 密钥派生函数和盐值生成加密密钥。
- 加密私钥:
- 将私钥分成两个 16 字节的部分。
- 分别与加密密钥的前 32 字节和后 32 字节进行异或运算,得到两部分加密的私钥。
- 生成加密字符串:
- 拼接加密后的私钥和盐值,并附加校验和,生成最终的加密字符串。
解密私钥的步骤:
- 解析加密字符串:解码加密字符串,提取盐值和加密的私钥部分。
- 生成解密密钥:使用 Scrypt 密钥派生函数和盐值生成解密密钥。
- 解密私钥:将加密的私钥部分与解密密钥的前 32 字节和后 32 字节进行异或运算,得到原始的私钥。
- 验证解密结果:计算解密后私钥对应的公钥哈希值,并与加密字符串中的哈希值进行比较,验证解密是否正确。
BIP-38 的优点和缺点
优点:
- 增强安全性:通过密码保护私钥,使其在不安全的环境中存储时更安全。
- 易于备份:加密的私钥可以安全地打印在纸上或存储在云端。
缺点:
- 复杂性增加:需要额外的步骤进行加密和解密。
- 依赖密码:如果密码丢失或忘记,则无法解密私钥。
实现 BIP-38 的示例代码
以下是一个详细的 Go 语言示例代码,展示了如何使用 BIP-38 加密和解密私钥。
// createHash creates a hash of the passphrase
func createHash(passphrase string) []byte {
hash := sha256.New()
hash.Write([]byte(passphrase))
return hash.Sum(nil)
}
// 双SHA256哈希
func doubleSha256(b []byte) []byte {
hash := sha256.Sum256(b)
hash = sha256.Sum256(hash[:])
return hash[:]
}
// RIPEMD-160哈希
// 已弃用:RIPEMD-160 是旧版哈希,不应用于新应用程序。此外,这个包现在和将来都不会提供优化的实现。
// 所以 使用SHA-256(crypto/sha256)替代
func ripemd160Hash(b []byte) []byte {
// hasher := ripemd160.New()
// hasher.Write(b)
// return hasher.Sum(nil)
hash := sha256.Sum256(b)
return hash[:]
}
// BIP38加密
func BIP38Encrypt(wifStr, passphrase string) (string, error) {
// 尝试解码WIF格式的私钥
wif, err := btcutil.DecodeWIF(wifStr)
if err != nil {
return "", errors.Wrap(err, "无法解码WIF格式私钥")
}
// 生成盐值 (来自于公钥的RIPEMD-160前4字节)
salt := ripemd160Hash(wif.PrivKey.PubKey().SerializeCompressed())[:4]
// 使用scrypt生成密钥
scryptKey, err := scrypt.Key([]byte(passphrase), salt, 16384, 8, 8, 64)
if err != nil {
return "", fmt.Errorf("scrypt密钥生成失败: %v", err)
}
derivedHalf1 := scryptKey[:32]
derivedHalf2 := scryptKey[32:]
block, err := aes.NewCipher(derivedHalf2)
if err != nil {
return "", fmt.Errorf("AES密码生成失败: %v", err)
}
// 私钥的前16字节和后16字节加密
xorBytes := func(a, b []byte) []byte {
n := len(a)
xored := make([]byte, n)
for i := 0; i < n; i++ {
xored[i] = a[i] ^ b[i]
}
return xored
}
privKeyBytes := wif.PrivKey.Serialize()
encryptedHalf1 := xorBytes(privKeyBytes[:16], derivedHalf1[:16])
encryptedHalf2 := xorBytes(privKeyBytes[16:], derivedHalf1[16:])
encryptedBytes := make([]byte, 32)
block.Encrypt(encryptedBytes[:16], encryptedHalf1)
block.Encrypt(encryptedBytes[16:], encryptedHalf2)
// 构建BIP38格式
bip38Key := append([]byte{0x01, 0x42, 0xC0}, salt...)
bip38Key = append(bip38Key, encryptedBytes...)
// 加入校验和
checksum := doubleSha256(bip38Key)[:4]
bip38Key = append(bip38Key, checksum...)
// Base58编码
return base58.Encode(bip38Key), nil
}
func BIP38Decrypt(encryptedKey, passphrase, network string) (string, error) {
// Base58解码
decoded := base58.Decode(encryptedKey)
// 检查校验和
checksum := decoded[len(decoded)-4:]
hash := doubleSha256(decoded[:len(decoded)-4])
if !reflect.DeepEqual(hash[:4], checksum) {
return "", errors.New("校验和不匹配")
}
// 从加密字节中提取盐值
salt := decoded[3:7]
encryptedHalf1 := decoded[7:23]
encryptedHalf2 := decoded[23:39]
// 使用scrypt生成密钥
scryptKey, err := scrypt.Key([]byte(passphrase), salt, 16384, 8, 8, 64)
if err != nil {
return "", errors.Wrap(err, "scrypt密钥生成失败")
}
derivedHalf1 := scryptKey[:32]
derivedHalf2 := scryptKey[32:]
block, err := aes.NewCipher(derivedHalf2)
if err != nil {
return "", errors.Wrap(err, "AES密码生成失败")
}
decryptedHalf1 := make([]byte, 16)
block.Decrypt(decryptedHalf1, encryptedHalf1)
decryptedHalf2 := make([]byte, 16)
block.Decrypt(decryptedHalf2, encryptedHalf2)
privKeyBytes := append(decryptedHalf1, decryptedHalf2...)
for i := 0; i < 32; i++ {
privKeyBytes[i] ^= derivedHalf1[i]
}
// 将解密后的私钥字节切片转换为 *btcec.PrivateKey 类型
privKey, _ := btcec.PrivKeyFromBytes(privKeyBytes)
// 使用解密的私钥生成WIF格式
wif, err := btcutil.NewWIF(privKey, GetNetwork(network), true)
if err != nil {
return "", errors.Wrap(err, "生成WIF失败")
}
return wif.String(), nil
}
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
腾讯云开发者社区:孟斯特