
本文详细阐述了在go语言中,如何根据用户id和密码短语的sha224摘要正确生成ecdsa私钥,并使用该私钥对消息进行数字签名。核心内容包括私钥d值的正确推导方法,即直接将哈希摘要转换为*big.int,以及ecdsa签名的标准流程,旨在帮助开发者避免常见的私钥生成错误,确保签名的安全性和正确性。
在Go语言中实现椭圆曲线数字签名算法(ECDSA)是常见的加密需求,尤其是在需要对特定消息进行身份验证或确保数据完整性时。一个典型的场景是根据用户ID和密码短语等特定输入,通过哈希算法派生出ECDSA私钥,进而使用该私钥对其他消息进行签名。本教程将详细介绍如何正确地在Go中使用crypto/ecdsa包,结合SHA224哈希算法,安全且准确地完成这一过程。
ECDSA私钥的核心是一个大整数D。在Go语言的ecdsa.PrivateKey结构中,D字段的类型是*big.Int。这意味着任何用于生成私钥的原始数据(例如哈希摘要)最终都必须被正确地转换为一个*big.Int类型的值。
一个常见的错误是将哈希函数的字节数组摘要先转换为一个固定大小的整数类型(如int64),然后再将其赋值给big.Int。例如,以下代码片段展示了这种不正确的做法:
// 错误示例:将SHA224摘要转换为int64,再赋值给big.Int
sha := sha256.New224()
// ... 写入数据 ...
sum := sha.Sum(nil) // []byte,SHA224摘要长度为28字节
var in int64
reader := bytes.NewReader(sum)
// 尝试将28字节的摘要读取到8字节的int64中
err := binary.Read(reader, binary.BigEndian, &in)
if err != nil {
// 可能会因为摘要长度超过int64而出现EOF错误,或者只读取了部分字节
// 无论哪种情况,都会导致数据截断
}
prKey.D = big.NewInt(in) // 错误:数据丢失,安全性受损SHA224哈希算法会产生一个28字节的摘要。将其直接读取到int64(8字节)中会导致严重的数据截断和信息丢失。这不仅使得生成的私钥无法正确反映原始哈希值,更在密码学上造成了不可接受的安全漏洞,因为私钥的熵和随机性被大大降低。
立即学习“go语言免费学习笔记(深入)”;
正确的做法是将SHA224生成的28字节摘要直接转换为*big.Int。math/big包提供了SetBytes方法,能够将一个字节切片完整地解析为一个大整数。
以下是根据用户ID和密码短语(经过SHA224哈希)生成ECDSA私钥的正确实现:
package main
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"fmt"
"log"
"math/big"
)
// NewKey 根据用户ID和密码短语的SHA224摘要生成ECDSA私钥
func NewKey(userId int64, pass string) (prKey ecdsa.PrivateKey) {
// 1. 准备用于哈希的输入数据
buf := new(bytes.Buffer)
// 将userId以大端序写入缓冲区,确保字节序列化一致性
err := binary.Write(buf, binary.BigEndian, userId)
if err != nil {
log.Fatalf("Failed to write userId: %v", err)
}
passArr := []byte(pass)
// 2. 使用SHA224哈希算法计算摘要
sha := sha256.New224()
sha.Write(buf.Bytes()) // 写入userId的字节表示
sha.Write(passArr) // 写入密码短语的字节表示
sum := sha.Sum(nil) // 获取28字节的SHA224摘要
// 3. 将SHA224摘要直接转换为*big.Int作为私钥D值
// 这是关键步骤,确保不丢失任何信息
prKey.D = new(big.Int).SetBytes(sum)
// 4. 设置椭圆曲线。SHA224通常与P224曲线配合使用,因为它能提供足够的安全性。
prKey.PublicKey.Curve = elliptic.P224()
// 根据D值计算公钥X和Y坐标
prKey.PublicKey.X, prKey.PublicKey.Y = prKey.PublicKey.Curve.ScalarBaseMult(prKey.D.Bytes())
return prKey
}代码解析:
一旦私钥被正确生成,就可以用它来对消息进行签名。签名过程首先需要对消息内容进行哈希,然后使用ecdsa.Sign函数。
// 假设 enc 是一个将 big.Int 转换为字符串的辅助函数,例如 base64 编码
func enc(val string) string {
// 实际应用中可能需要更复杂的编码,例如 base64 或 hex
return fmt.Sprintf("encoded(%s)", val)
}
func main() {
userId := int64(12345)
pass := "mySecretPassphrase"
srNonce := "server_nonce_xyz"
clNonce := "client_nonce_abc"
// 1. 生成私钥
key := NewKey(userId, pass)
fmt.Printf("Generated Private Key D: %s\n", key.D.String())
// 2. 准备待签名的消息内容并进行SHA224哈希
// 注意:这里需要确保消息内容的字节序列化方式与接收方一致
msgBuffer := new(bytes.Buffer)
binary.Write(msgBuffer, binary.BigEndian, userId) // userId作为int64写入
msgBuffer.WriteString(srNonce) // server nonce作为字符串写入
msgBuffer.WriteString(clNonce) // client nonce作为字符串写入
// 对消息内容进行SHA224哈希
msgHasher := sha256.New224()
msgHasher.Write(msgBuffer.Bytes())
msgHash := msgHasher.Sum(nil) // 获取消息的SHA224摘要
// 3. 使用私钥对消息哈希进行签名
// rand.Reader 提供密码学安全的随机数源以上就是Go语言中基于SHA224摘要生成ECDSA私钥并进行数字签名的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号