drqefnd544707688 2014-08-12 09:37
浏览 683

关于VerifyPKCS1v15(base64和rsa检查)

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条回答 默认 最新

  • dqwh26750 2014-08-12 15:17
    关注

    You appear to have several different problems in your code.

    1. 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.

    2. 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
    
    评论

报告相同问题?