英文:
About VerifyPKCS1v15 (base64 and rsa check )
问题
给定"notify"、"publicKey"和"sign",它在Go语言中无法通过VerifyPKCS1v15
。这是我的代码,有什么问题吗?
package main
import (
"crypto"
"crypto/sha1"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
)
func main() {
notify := `YFSGlJTpNYakrZuZqZ55dcA5mVUb/JQBr3hdDjODsAVSdoVVytIagk9Wt0CD/uX+7jGL9pqev8/u0I0ZBKEmz5huXp8TdZSnskCZ7GTeHNW0VPJcW8OcBxAValA0jQSv2mBP+tc1r6mdvf66GEzhvgBfTnp3Sp7V3dijJ9bNstIDyrGm/BlByhcMr3UqXjTFJaui6t5TxvZhCuSV9sg+xVVA+sR3uFI78b5lKomg5Vu31EBZvXASlFfaOc4StltRUH2aSiRqjnbXe8dlRZO0Ih44htYs2QfehzeQnPHtTwNHUvtVIVcIdI/7j9yfy5es13QeIgfKghY/ENUnB2V7iA==`
sign := `s8XIN2TyC5niX1HFPDXOQj2eRvhW2qMPOdDuuXlOspYhxkjxunV4Ytgcw8GXg761HSbk4e5QsgKpU+vM2ggLhYni2GfXhGBVj/P13B6JhMmdrucU8ktlaH+fJGUmc3rqGMU3qiQgNAh/8PV1BS/5li7qzXHc0tgKL1zRgeu1CVw=`
notifyData, err := base64.StdEncoding.DecodeString(notify)
if err != nil {
fmt.Println("error1:", err)
return
}
signData, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
fmt.Println("error2:", err)
return
}
publicKey := []byte(`-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2kcrRvxURhFijDoPpqZ/IgPlA
gppkKrek6wSrua1zBiGTwHI2f+YCa5vC1JEiIi9uw4srS0OSCB6kY3bP2DGJagBo
Egj/rYAGjtYJxJrEiTxVs5/GfPuQBYmU0XAtPXFzciZy446VPJLHMPnmTALmIOR5
Dddd1Zklod9IQBMjjwIDAQAB
-----END PUBLIC KEY-----
`)
block, _ := pem.Decode(publicKey)
if block == nil {
fmt.Println("pem error:")
return
}
public, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
fmt.Println("public key error:", err)
return
}
pub := public.(*rsa.PublicKey)
fmt.Println(pub.N)
h := sha1.New()
h.Write([]byte(notifyData))
digest := h.Sum(nil)
err = rsa.VerifyPKCS1v15(pub, crypto.SHA1, digest, signData)
if err == nil {
fmt.Println("OK")
} else {
fmt.Println("verify fail", err)
}
}
附注:
这是PHP代码,使用相同的数据将通过验证。
<?php
$pubKey = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2kcrRvxURhFijDoPpqZ/IgPlA
gppkKrek6wSrua1zBiGTwHI2f+YCa5vC1JEiIi9uw4srS0OSCB6kY3bP2DGJagBo
Egj/rYAGjtYJxJrEiTxVs5/GfPuQBYmU0XAtPXFzciZy446VPJLHMPnmTALmIOR5
Dddd1Zklod9IQBMjjwIDAQAB
-----END PUBLIC KEY-----";
$pubRes = openssl_get_publickey($pubKey);
//通知数据
$notify_data = "YFSGlJTpNYakrZuZqZ55dcA5mVUb/JQBr3hdDjODsAVSdoVVytIagk9Wt0CD/uX+7jGL9pqev8/u0I0ZBKEmz5huXp8TdZSnskCZ7GTeHNW0VPJcW8OcBxAValA0jQSv2mBP+tc1r6mdvf66GEzhvgBfTnp3Sp7V3dijJ9bNstIDyrGm/BlByhcMr3UqXjTFJaui6t5TxvZhCuSV9sg+xVVA+sR3uFI78b5lKomg5Vu31EBZvXASlFfaOc4StltRUH2aSiRqjnbXe8dlRZO0Ih44htYs2QfehzeQnPHtTwNHUvtVIVcIdI/7j9yfy5es13QeIgfKghY/ENUnB2V7iA==";
//签名
$sign = "s8XIN2TyC5niX1HFPDXOQj2eRvhW2qMPOdDuuXlOspYhxkjxunV4Ytgcw8GXg761HSbk4e5QsgKpU+vM2ggLhYni2GfXhGBVj/P13B6JhMmdrucU8ktlaH+fJGUmc3rqGMU3qiQgNAh/8PV1BS/5li7qzXHc0tgKL1zRgeu1CVw=";
$data = base64_decode($notify_data);
$maxlength = 128;
$output = '';
while ($data) {
$input = substr($data, 0, $maxlength);
$data = substr($data, $maxlength);
openssl_public_decrypt($input, $out, $pubRes, OPENSSL_PKCS1_PADDING);
$output .= $out;
}
if (openssl_verify($output, base64_decode($sign), $pubRes)) {
echo "success";
}else{
echo "fail";
}
?>
英文:
Given "notify" ,"publicKey" and "sign" , it doesnt pass the VerifyPKCS1v15
in go . That 's mycode , is there something wrong ?
package main
import (
"crypto"
"crypto/sha1"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
)
func main() {
notify := `YFSGlJTpNYakrZuZqZ55dcA5mVUb/JQBr3hdDjODsAVSdoVVytIagk9Wt0CD/uX+7jGL9pqev8/u0I0ZBKEmz5huXp8TdZSnskCZ7GTeHNW0VPJcW8OcBxAValA0jQSv2mBP+tc1r6mdvf66GEzhvgBfTnp3Sp7V3dijJ9bNstIDyrGm/BlByhcMr3UqXjTFJaui6t5TxvZhCuSV9sg+xVVA+sR3uFI78b5lKomg5Vu31EBZvXASlFfaOc4StltRUH2aSiRqjnbXe8dlRZO0Ih44htYs2QfehzeQnPHtTwNHUvtVIVcIdI/7j9yfy5es13QeIgfKghY/ENUnB2V7iA==`
sign := `s8XIN2TyC5niX1HFPDXOQj2eRvhW2qMPOdDuuXlOspYhxkjxunV4Ytgcw8GXg761HSbk4e5QsgKpU+vM2ggLhYni2GfXhGBVj/P13B6JhMmdrucU8ktlaH+fJGUmc3rqGMU3qiQgNAh/8PV1BS/5li7qzXHc0tgKL1zRgeu1CVw=`
notifyData, err := base64.StdEncoding.DecodeString(notify)
if err != nil {
fmt.Println("error1:", err)
return
}
signData, err := base64.StdEncoding.DecodeString(sign)
if err != nil {
fmt.Println("error2:", err)
return
}
publicKey := []byte(`-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2kcrRvxURhFijDoPpqZ/IgPlA
gppkKrek6wSrua1zBiGTwHI2f+YCa5vC1JEiIi9uw4srS0OSCB6kY3bP2DGJagBo
Egj/rYAGjtYJxJrEiTxVs5/GfPuQBYmU0XAtPXFzciZy446VPJLHMPnmTALmIOR5
Dddd1Zklod9IQBMjjwIDAQAB
-----END PUBLIC KEY-----
`)
block, _ := pem.Decode(publicKey)
if block == nil {
fmt.Println("pem error :")
return
}
public, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
fmt.Println("public key error :", err)
return
}
pub := public.(*rsa.PublicKey)
fmt.Println(pub.N)
h := sha1.New()
h.Write([]byte(notifyData))
digest := h.Sum(nil)
err = rsa.VerifyPKCS1v15(pub, crypto.SHA1, digest, signData)
if err == nil {
fmt.Println("OK")
} else {
fmt.Println("verify fail", err)
}
}
P.S.
This is php code, and it'll pass with the same data.
<?php
$pubKey = "-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2kcrRvxURhFijDoPpqZ/IgPlA
gppkKrek6wSrua1zBiGTwHI2f+YCa5vC1JEiIi9uw4srS0OSCB6kY3bP2DGJagBo
Egj/rYAGjtYJxJrEiTxVs5/GfPuQBYmU0XAtPXFzciZy446VPJLHMPnmTALmIOR5
Dddd1Zklod9IQBMjjwIDAQAB
-----END PUBLIC KEY-----";
$pubRes = openssl_get_publickey($pubKey);
//通知数据
$notify_data = "YFSGlJTpNYakrZuZqZ55dcA5mVUb/JQBr3hdDjODsAVSdoVVytIagk9Wt0CD/uX+7jGL9pqev8/u0I0ZBKEmz5huXp8TdZSnskCZ7GTeHNW0VPJcW8OcBxAValA0jQSv2mBP+tc1r6mdvf66GEzhvgBfTnp3Sp7V3dijJ9bNstIDyrGm/BlByhcMr3UqXjTFJaui6t5TxvZhCuSV9sg+xVVA+sR3uFI78b5lKomg5Vu31EBZvXASlFfaOc4StltRUH2aSiRqjnbXe8dlRZO0Ih44htYs2QfehzeQnPHtTwNHUvtVIVcIdI/7j9yfy5es13QeIgfKghY/ENUnB2V7iA==";
//签名
$sign = "s8XIN2TyC5niX1HFPDXOQj2eRvhW2qMPOdDuuXlOspYhxkjxunV4Ytgcw8GXg761HSbk4e5QsgKpU+vM2ggLhYni2GfXhGBVj/P13B6JhMmdrucU8ktlaH+fJGUmc3rqGMU3qiQgNAh/8PV1BS/5li7qzXHc0tgKL1zRgeu1CVw=";
$data = base64_decode($notify_data);
$maxlength = 128;
$output = '';
while ($data) {
$input = substr($data, 0, $maxlength);
$data = substr($data, $maxlength);
openssl_public_decrypt($input, $out, $pubRes, OPENSSL_PKCS1_PADDING);
$output .= $out;
}
if (openssl_verify($output, base64_decode($sign), $pubRes)) {
echo "success";
}else{
echo "fail";
}
?>
答案1
得分: 13
您的代码中似乎存在几个不同的问题。
-
在您的PHP代码中,没有必要将数据截断为128个字符,因为您的Go代码中没有这样做。这种差异会导致字节不同,从而导致计算出的签名不同。
-
您正在使用
openssl_public_decrypt
函数对数据进行签名。虽然理论上这是可行的,但容易出错。您还在使用公钥对数据进行签名,这是错误的——只有私钥才能进行签名。最好使用PHP的openssl_sign
函数。
另一个错误的来源可能是使用私钥的签名代码,在这里没有显示。
PHP和Go的公钥加密应该是完全兼容的。为了测试这一点,我在PHP和Go中创建了以下完全相同的签名脚本。
您可以验证这些脚本确实会产生相同的输出并以相同的方式对数据进行签名。
我们还可以使用PHP和Go来验证签名。以下是一组PHP和Go脚本,将从标准输入读取签名并进行验证。
我们可以混合和匹配这些不同的脚本,并验证PHP和Go确实完全兼容:
请注意,这是您要翻译的内容,我将为您进行翻译。
英文:
You appear to have several different problems in your code.
-
There is no need to truncate the data to 128 characters in your PHP code as you are not doing the same in your go code. This discrepancy will cause the bytes to be different and therefore the computed signatures to be different.
-
You are using the
openssl_public_decrypt
function to sign the data. While this does work in theory it's error prone. You are also using a public-key to sign the data, which is wrong - only private-keys can sign. It's much better to use PHP'sopenssl_sign
function.
Another source of error could be your signing code that uses the private key, which is not shown here.
PHP's and Go's public key cryptography should be entirely compatible. To test this I've created the following identical signing scripts in both PHP and Go.
<?php
$data = "TEST DATA TO COMPUTE";
$privKeyPEM = "-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWOhTf8Ph07ZA0KjdbKtfL/
7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQJAQj9kJrZDuKT6ZyOQZfPD
tobRZ1xjo93/dWU72bF3aHDo4ILMy2Kigy5yhZU0ZGjOuPv5eUOLRe/yxYQf6B5J
AQIhANbhfZ4QJC8dLXAqcsxOXuLgztzbKixUre0gnhiVSd1hAiEAzv+sHJ4PMjKs
Iuf6/nUI9XFgQQRd+NGRovyHRZC18VsCIAX7AKQFjvxAs6MLi2ZkR//IgfljoCjb
snuHDN9iSEwBAiEAmAc1XCtGE+Mdg+GG+T3xn3pubDIN5oHcia0YmKIIzsMCIEy1
fWM5cIJ9bAUExKB6MV8PF+9EjDvXzbSk1/Ycta8z
-----END RSA PRIVATE KEY-----";
// Parse private key
$privkey = openssl_pkey_get_private($privKeyPEM);
if (!$privkey) {
exit("Could not parse private key");
}
// Compute the signature
$signature = '';
$ok = openssl_sign($data, $signature, $privkey, OPENSSL_ALGO_SHA1); //SHA1 of $data is computed automatically by this function
if (!$ok) {
exit("Could not compute signature");
}
// Print the output
print base64_encode($signature);
And the same thing in Go:
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"log"
)
const (
data = "TEST DATA TO COMPUTE"
privKeyPEM = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWOhTf8Ph07ZA0KjdbKtfL/
7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQJAQj9kJrZDuKT6ZyOQZfPD
tobRZ1xjo93/dWU72bF3aHDo4ILMy2Kigy5yhZU0ZGjOuPv5eUOLRe/yxYQf6B5J
AQIhANbhfZ4QJC8dLXAqcsxOXuLgztzbKixUre0gnhiVSd1hAiEAzv+sHJ4PMjKs
Iuf6/nUI9XFgQQRd+NGRovyHRZC18VsCIAX7AKQFjvxAs6MLi2ZkR//IgfljoCjb
snuHDN9iSEwBAiEAmAc1XCtGE+Mdg+GG+T3xn3pubDIN5oHcia0YmKIIzsMCIEy1
fWM5cIJ9bAUExKB6MV8PF+9EjDvXzbSk1/Ycta8z
-----END RSA PRIVATE KEY-----`
)
func main() {
// Parse private key into rsa.PrivateKey
PEMBlock, _ := pem.Decode([]byte(privKeyPEM))
if PEMBlock == nil {
log.Fatal("Could not parse Private Key PEM")
}
if PEMBlock.Type != "RSA PRIVATE KEY" {
log.Fatal("Found wrong key type")
}
privkey, err := x509.ParsePKCS1PrivateKey(PEMBlock.Bytes)
if err != nil {
log.Fatal(err)
}
// Compute the sha1
h := sha1.New()
h.Write([]byte(data))
// Sign the data
signature, err := rsa.SignPKCS1v15(rand.Reader, privkey, crypto.SHA1, h.Sum(nil))
if err != nil {
log.Fatal(err)
}
// Print the results
fmt.Print(base64.StdEncoding.EncodeToString(signature))
}
You can verify that these do, indeed, produce the same output and sign the same data in the same way.
We can also use both PHP and Go to verify the signatures. Here are a set of PHP and Go scripts that will both read a signature from standard input and verify it.
<?php
$data = "TEST DATA TO COMPUTE";
$pubKeyPEM = "-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWO
hTf8Ph07ZA0KjdbKtfL/7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQ==
-----END PUBLIC KEY-----";
// Parse public key
$pubkey = openssl_pkey_get_public($pubKeyPEM);
if (!$pubkey) {
exit("Could not parse public key");
}
// Read the signature from stdin
$stdin = file_get_contents("php://stdin");
$signature = base64_decode($stdin);
// Verify the signature
$ok = openssl_verify($data, $signature, $pubkey, OPENSSL_ALGO_SHA1); //SHA1 of $data is computed automatically by this function
if ($ok == 1) {
print "OK\n"; // it worked!
exit(0);
}
else if ($ok == 0) {
exit("Signature verification failed");
}
else {
exit("Error verifying signature");
}
And the same verification code in Go:
package main
import (
"crypto"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"os"
)
const (
data = "TEST DATA TO COMPUTE"
pubKeyPEM = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAK3ADijXKw72+YbC5QKK2y7IosCp7rWO
hTf8Ph07ZA0KjdbKtfL/7dmNKjSP6EkC/DJUWfZJNLIlGOtDLLA/AnsCAwEAAQ==
-----END PUBLIC KEY-----`
)
func main() {
// Parse public key into rsa.PublicKey
PEMBlock, _ := pem.Decode([]byte(pubKeyPEM))
if PEMBlock == nil {
log.Fatal("Could not parse Public Key PEM")
}
if PEMBlock.Type != "PUBLIC KEY" {
log.Fatal("Found wrong key type")
}
pubkey, err := x509.ParsePKIXPublicKey(PEMBlock.Bytes)
if err != nil {
log.Fatal(err)
}
// compute the sha1
h := sha1.New()
h.Write([]byte(data))
// Read the signature from stdin
b64 := base64.NewDecoder(base64.StdEncoding, os.Stdin)
signature, err := ioutil.ReadAll(b64)
if err != nil {
log.Fatal(err)
}
// Verify
err = rsa.VerifyPKCS1v15(pubkey.(*rsa.PublicKey), crypto.SHA1, h.Sum(nil), signature)
if err != nil {
log.Fatal(err)
}
// It verified!
fmt.Println("OK")
}
We can mix and match these different scripts together and verify that PHP and Go are indeed fully compatible:
$ go run go-sign.go | go run go-verify.go
OK
$ go run go-sign.go | php php-verify.php
OK
$ php php-sign.php | php php-verify.php
OK
$ php php-sign.php | go run go-verify.go
OK
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论