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";
}
?>
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's openssl_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
"; // 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