如何在Golang中将十六进制ASN.1格式数据解码为比特币兼容公钥
解决方案
你手里的DER格式公钥是标准X.509 SubjectPublicKeyInfo结构,包含secp256k1算法标识和公钥原始坐标,直接用Go标准库即可完成解析,不需要依赖第三方工具。
完整代码示例
package main import ( "crypto/ecdsa" "crypto/x509" "encoding/hex" "fmt" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" ) func main() { // 此处可直接替换为HSM返回的原始DER公钥[]byte,无需经过hex编解码 derPubHex := "3056301006072a8648ce3d020106052b8104000a034200049bb8e80670371f45508b5f8f59946a7c4dea4b3a23a036cf24c1f40993f4a1daad1716de8bd664ecb4596648d722a4685293de208c1d2da9361b9cba74c3d1ec" derPubBytes, err := hex.DecodeString(derPubHex) if err != nil { panic(err) } // 解析X.509公钥结构 pubInterface, err := x509.ParsePKIXPublicKey(derPubBytes) if err != nil { panic(err) } // 断言为ECDSA公钥类型 ecdsaPub, ok := pubInterface.(*ecdsa.PublicKey) if !ok { panic("解析结果非ECDSA公钥") } // 拼接0x04开头的65字节未压缩公钥:固定标识0x04 + 32字节X坐标 + 32字节Y坐标 uncompressedPub := make([]byte, 65) uncompressedPub[0] = 0x04 copy(uncompressedPub[1:33], ecdsaPub.X.Bytes()) copy(uncompressedPub[33:], ecdsaPub.Y.Bytes()) // 输出验证,和你用在线工具得到的0x04开头结果完全一致 fmt.Printf("0x%s\n", hex.EncodeToString(uncompressedPub)) // 直接生成比特币地址 addrPubKey, err := btcutil.NewAddressPubKey(uncompressedPub, &chaincfg.TestNet3Params) if err != nil { panic(err) } fmt.Println("测试网地址:", addrPubKey.EncodeAddress()) // 如需33字节压缩公钥,直接调用内置方法转换即可 compressedPub := addrPubKey.SerializeCompressed() fmt.Printf("33字节压缩公钥:0x%s\n", hex.EncodeToString(compressedPub)) }
说明
- 核心逻辑依赖Go标准库
x509.ParsePKIXPublicKey,直接解析DER格式的公钥结构,自动识别secp256k1曲线参数 - 如果你已经拿到HSM返回的原始DER公钥[]byte,可跳过hex编解码步骤,直接传入解析方法,运行效率更高
- 代码同时支持输出65字节未压缩公钥和33字节压缩公钥,可按需选用
内容的提问来源于stack exchange,提问作者Pete
相关产品推荐
相关产品推荐

