Golang 使用 rsa 签署结构
Golang signing a struct using rsa
我有一个包含以下字段的结构 Transaction
,
type Transaction struct {
Sender string `json:"sender"`
Receiver string `json:"receiver"`
Signature string `json:"signature"`
Amount int64 `json:"amount"`
}
我还有一个函数 GetPrivateKey()
其中 returns 一个 *rsa.PrivateKey
func GetPrivateKey() (*rsa.PrivateKey, error) {
key, err := ioutil.ReadFile("/Users/xxx/.ssh/id_rsa")
if err != nil {
return nil, err
}
block, _ := pem.Decode(key)
der, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return der, err
}
我这里的计划是使用系统中已有的私钥对结构transaction
的内容进行签名,然后将其作为字符串存储在结构中的字段signature
中,为此, 我有一个函数 SignPayload()
func SignPayload(txnObj *Transaction) error {
privateKey, err := GetPrivateKey()
if err != nil {
panic(err)
}
bodyHash, err := rsa.SignPKCS1v15(rand.Reader, privateKey,
crypto.SHA256, []byte(fmt.Sprintf("%v", *txnObj)))
if err != nil {
panic(err)
}
txnObj.Signature = string(bodyHash)
log.Println(txnObj.Signature)
return err
}
不幸的是,这不起作用,rsa.SignPKCS1v15()
抛出一条错误消息:
crypto/rsa: input must be hashed message
此处的最终目标是使用此签名通过将其与字段 sender
中存在的 public 密钥进行比较来验证结构的真实性。
我是密码学的绝对新手,我在这里做错了什么?更重要的是,我该怎么做?
提前致谢!
这里有两件事:
- 只能对小消息进行签名,这就是为什么它通常是散列的原因。
rsa.SignPKCS1v15
期望消息长度与哈希算法的输出匹配(256 位,或 sha-256 为 32 字节)
- 不要使用结构的
%v
表示,而是序列化它
第一个是spelled out in the rsa docs。给定一条消息,您可以使用以下方式对其进行签名:
message := []byte("message to be signed")
hashed := sha256.Sum256(message)
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
if err != nil {
panic(err)}
}
第二点更多的是关于让你的代码更健壮。您不希望更改 %v
逻辑或添加非导出字段导致问题。您可能还想检查 Go 以外的其他语言的签名。
因此,您应该首先编组您的结构。你已经有了 json 个标签,所以这很简单:
message, err := json.Marshal(txnObj)
if err != nil {
panic(err)
}
消息是上面传递给sha256.Sum256
的字节片。
验证签名时,您需要确保将结构的 Signature
字段清零,对其进行编组、散列,然后调用 rsa.VerifyPKCS1v15
.
我有一个包含以下字段的结构 Transaction
,
type Transaction struct {
Sender string `json:"sender"`
Receiver string `json:"receiver"`
Signature string `json:"signature"`
Amount int64 `json:"amount"`
}
我还有一个函数 GetPrivateKey()
其中 returns 一个 *rsa.PrivateKey
func GetPrivateKey() (*rsa.PrivateKey, error) {
key, err := ioutil.ReadFile("/Users/xxx/.ssh/id_rsa")
if err != nil {
return nil, err
}
block, _ := pem.Decode(key)
der, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return der, err
}
我这里的计划是使用系统中已有的私钥对结构transaction
的内容进行签名,然后将其作为字符串存储在结构中的字段signature
中,为此, 我有一个函数 SignPayload()
func SignPayload(txnObj *Transaction) error {
privateKey, err := GetPrivateKey()
if err != nil {
panic(err)
}
bodyHash, err := rsa.SignPKCS1v15(rand.Reader, privateKey,
crypto.SHA256, []byte(fmt.Sprintf("%v", *txnObj)))
if err != nil {
panic(err)
}
txnObj.Signature = string(bodyHash)
log.Println(txnObj.Signature)
return err
}
不幸的是,这不起作用,rsa.SignPKCS1v15()
抛出一条错误消息:
crypto/rsa: input must be hashed message
此处的最终目标是使用此签名通过将其与字段 sender
中存在的 public 密钥进行比较来验证结构的真实性。
我是密码学的绝对新手,我在这里做错了什么?更重要的是,我该怎么做?
提前致谢!
这里有两件事:
- 只能对小消息进行签名,这就是为什么它通常是散列的原因。
rsa.SignPKCS1v15
期望消息长度与哈希算法的输出匹配(256 位,或 sha-256 为 32 字节) - 不要使用结构的
%v
表示,而是序列化它
第一个是spelled out in the rsa docs。给定一条消息,您可以使用以下方式对其进行签名:
message := []byte("message to be signed")
hashed := sha256.Sum256(message)
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
if err != nil {
panic(err)}
}
第二点更多的是关于让你的代码更健壮。您不希望更改 %v
逻辑或添加非导出字段导致问题。您可能还想检查 Go 以外的其他语言的签名。
因此,您应该首先编组您的结构。你已经有了 json 个标签,所以这很简单:
message, err := json.Marshal(txnObj)
if err != nil {
panic(err)
}
消息是上面传递给sha256.Sum256
的字节片。
验证签名时,您需要确保将结构的 Signature
字段清零,对其进行编组、散列,然后调用 rsa.VerifyPKCS1v15
.