跨OpenSSL与Golang 1.18无法验证RSA-PSS签名问题
跨OpenSSL与Go的RSA-PSS签名双向验证失败问题
问题场景
- Bash中通过OpenSSL生成RSA密钥和RSA-PSS签名,OpenSSL自身验证通过,但Go验证该签名时触发
crypto/rsa: verification errorpanic - Go生成的RSA-PSS签名可通过自身验证,但OpenSSL验证时提示
Verification Failure
OpenSSL操作步骤
openssl genrsa -out key.pem 4096 openssl rsa -in key.pem -pubout > key.pub truncate -size=16 zeroes.bin openssl dgst -sign key.pem -keyform PEM -sha512 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:64 -out signature.bin -binary zeroes.bin
原Go验证代码(main.go)
package main import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha512" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" ) func main() { privatePem, _ := ioutil.ReadFile("key.pem") privateBlock, _ := pem.Decode(privatePem) if privateBlock == nil { panic("Failed to decode private pem") } privateKey, err := x509.ParsePKCS1PrivateKey(privateBlock.Bytes) if err != nil { panic(err) } h := sha512.New() h.Write(make([]byte, 16)) zeroes := h.Sum(nil) signature, err := rsa.SignPSS(rand.Reader, privateKey, crypto.SHA512, zeroes, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, // should be 64 for sha512 }) if err != nil { panic(err) } ioutil.WriteFile("signature.go.bin", signature, 0600) err = rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, zeroes, signature, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) if err != nil { panic(err) } fmt.Println("Verified go signature.") opensslSignature, _ := ioutil.ReadFile("signature.bin") err = rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, zeroes, opensslSignature, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) if err != nil { panic(err) } fmt.Println("Not getting here -- crypto/rsa: verification error") }
问题原因与修复方案
1. 签名输入的哈希处理逻辑差异
OpenSSL的dgst -sign命令会自动对输入文件做哈希计算,再用私钥签名哈希值;而Go的rsa.SignPSS函数要求传入的是原始数据(函数内部会自动计算指定哈希),原Go代码提前对原始数据做了哈希,导致两边签名的输入哈希不一致。
修复哈希计算部分
将原代码中提前计算哈希的逻辑改为直接使用原始16字节数据:
// 替换原哈希计算代码,直接使用原始16字节数据 zeroes := make([]byte, 16)
2. 确认参数一致性
SHA512哈希长度为64字节,OpenSSL指定的rsa_pss_saltlen:64与Go中rsa.PSSSaltLengthEqualsHash参数完全匹配;两边默认的MGF1哈希函数均与签名哈希(SHA512)一致,无需额外配置。
完整修复后的Go代码
package main import ( "crypto" "crypto/rand" "crypto/rsa" "crypto/sha512" "crypto/x509" "encoding/pem" "fmt" "io/ioutil" ) func main() { privatePem, err := ioutil.ReadFile("key.pem") if err != nil { panic(err) } privateBlock, _ := pem.Decode(privatePem) if privateBlock == nil { panic("Failed to decode private pem") } privateKey, err := x509.ParsePKCS1PrivateKey(privateBlock.Bytes) if err != nil { panic(err) } // 使用原始16字节数据,由SignPSS内部处理SHA512哈希 zeroes := make([]byte, 16) // 生成Go签名 signature, err := rsa.SignPSS(rand.Reader, privateKey, crypto.SHA512, zeroes, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) if err != nil { panic(err) } err = ioutil.WriteFile("signature.go.bin", signature, 0600) if err != nil { panic(err) } // 验证Go自身签名 err = rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, zeroes, signature, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) if err != nil { panic(err) } fmt.Println("Verified go signature.") // 验证OpenSSL生成的签名 opensslSignature, err := ioutil.ReadFile("signature.bin") if err != nil { panic(err) } err = rsa.VerifyPSS(&privateKey.PublicKey, crypto.SHA512, zeroes, opensslSignature, &rsa.PSSOptions{ SaltLength: rsa.PSSSaltLengthEqualsHash, }) if err != nil { panic(err) } fmt.Println("Verified OpenSSL signature successfully!") }
验证Go生成的签名在OpenSSL中通过
执行以下命令验证Go生成的signature.go.bin:
openssl dgst -verify key.pub -keyform PEM -sha512 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:64 -signature signature.go.bin zeroes.bin
此时会返回Verified OK。
内容的提问来源于stack exchange,提问作者Kent Thompson
相关产品推荐
相关产品推荐

