douluozhan4370 2018-09-10 10:23
浏览 265
已采纳

PHP创建ECDSA签名并使用Golang进行验证

I try to make the app with PHP that creates ECDSA signature for some document and that signature is verified with Golang app.

I use private keys generated with openssl tool. It is prime256v1 curve key. Created with the command:

openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem

In PHP i create signature using openssl_sign function.

And all my attempts to verify the signature with Golang fail. In Golang use the crypto/ecdsa, crypto/elliptic packages.

There is my code.

PHP

<?php

$stringtosign = "my test string to sign";

// Privcate key was geerated with openssl tool with the command
// openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem
$cert = file_get_contents('prime256v1-key.pem');

$prkey = openssl_pkey_get_private($cert);

// we sign only hashes, because Golang lib can wok with hashes only
$stringtosign = md5($stringtosign);

// we generate 64 length signature (r and s 32 bytes length)
while(1) {

    openssl_sign($stringtosign, $signature, $prkey, OPENSSL_ALGO_SHA256);

    $rlen = ord(substr($signature,3,1));

    $slen = ord(substr($signature,5+$rlen,1));

    if ($slen != 32 || $rlen != 32) {
        // try other signature if length is not 32 for both parts
        continue;
    }
    $r = substr($signature,4,$rlen);
    $s = substr($signature,6+$rlen,$slen);

    $signature = $r.$s;

    break;
}
openssl_free_key($prkey);

$signature = bin2hex($signature);

echo $signature."
";

Golang

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/md5"
    "encoding/hex"
    "fmt"
    "io"
    "io/ioutil"
    "math/big"

    "crypto/x509"

    "encoding/pem"
)

func main() {
    stringtosign := "my test string to sign"

    // This is outpur of PHP app. Signature generated by PHP openssl_sign
    signature := "18d5c1d044a4a752ad91bc06499c72a590b2842b3d3b4c4b1086bfd0eea3e7eb5c06b77e15542e5ba944f3a1a613c24eabaefa4e2b2251bd8c9355bba4d14640"

    // NOTE . Error verificaion is skipped here

    // Privcate key was geerated with openssl tool with the command
    // openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem
    prikeybytes, _ := ioutil.ReadFile("prime256v1-key.pem")

    p, _ := pem.Decode(prikeybytes)

    prikey, _ := x509.ParseECPrivateKey(p.Bytes)

    signatureBytes, _ := hex.DecodeString(signature)

    // make MD5 hash
    h := md5.New()
    io.WriteString(h, stringtosign)
    data := h.Sum(nil)

    // build key and verify data
    r := big.Int{}
    s := big.Int{}
    // make signature numbers
    sigLen := len(signatureBytes)
    r.SetBytes(signatureBytes[:(sigLen / 2)])
    s.SetBytes(signatureBytes[(sigLen / 2):])

    curve := elliptic.P256()

    // make public key from private key
    x := big.Int{}
    y := big.Int{}
    x.SetBytes(prikey.PublicKey.X.Bytes())
    y.SetBytes(prikey.PublicKey.Y.Bytes())
    rawPubKey := ecdsa.PublicKey{Curve: curve, X: &x, Y: &y}

    v := ecdsa.Verify(&rawPubKey, data, &r, &s)

    if v {
        fmt.Println("Success verify!")
        return
    }

    fmt.Println(fmt.Sprintf("Signatire doed not match"))

}

What do i do wrong? Can anyone show me working example where Golang verifies signatre created with PHP?

I tried to use different versions in openssl_sign instead of OPENSSL_ALGO_SHA256 . Tried OPENSSL_ALGO_SHA1, OPENSSL_ALGO_SHA512

  • 写回答

1条回答 默认 最新

  • dongyu2047 2018-09-10 12:04
    关注

    The problem with your code seems to be, that you hash the string in PHP using MD5 before signing it using OPENSSL_ALGO_SHA256, which hashes what you sign (the MD5 hash) again, while in your Go program, you only have the first of these 2 hashes. To fix this, I would remove the MD5 step in the PHP code and replace the h := md5.New() line in your code with the hash used by your signature algorithm (h := sha256.New() in your example).

    To elaborate a bit more on what theses signing functions do, I would first like to break signing and verifying down into the following steps:

    • Signing:
      1. Hash the message
      2. Encrypt the message's hash using the private key (this encrypted hash is the signature)
    • Verifying:
      1. Hash the message
      2. Decrypt the signature using the public key (this yields the hash which was encrypted while signing).
      3. Compare the calculated and decrypted hashes. If they match, then the signature is correct.

    Now the call to openssl_sign in your PHP code, does all the signing steps, while the call to ecdsa.Verify in Go, only does the second and third step of the verification process. And this is why it takes a hash as the second argument. So to verify a signature, you must implement the first verification step yourself, namely generating the hash.

    You must use the same hashing algorithm while signing and verifying, therefore you must use SHA256, not MD5, in your Go code (as you sign using OPENSSL_ALGO_SHA256), otherwise the hashes will (generally) not match.

    Also, I would recommend to not use MD5 for signatures, as it is no longer considered collision resistant (a hash collision is, when you have 2 different strings/files/... with the same hash). For more details about that, you can check the Wikipedia article on MD5, specifically the section "Collision vulnerabilities". This is a problem, as 2 messages with the same MD5 hash, will also have the same signature and an attacker could use the signature generated for one of the strings to trick you into thinking the other was signed (and therefore trust it).

    Additionally, ecdsa.PrivateKey can give you the corresponding public key, and you can call ecdsa.Verify like this:

    ecdsa.Verify(&prikey.PublicKey, data, &r, &s)
    

    This saves you the trouble of copying all the data from the private key to a new object.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 飞机曲面部件如机翼,壁板等具体的孔位模型
  • ¥15 vs2019中数据导出问题
  • ¥20 云服务Linux系统TCP-MSS值修改?
  • ¥20 关于#单片机#的问题:项目:使用模拟iic与ov2640通讯环境:F407问题:读取的ID号总是0xff,自己调了调发现在读从机数据时,SDA线上并未有信号变化(语言-c语言)
  • ¥20 怎么在stm32门禁成品上增加查询记录功能
  • ¥15 Source insight编写代码后使用CCS5.2版本import之后,代码跳到注释行里面
  • ¥50 NT4.0系统 STOP:0X0000007B
  • ¥15 想问一下stata17中这段代码哪里有问题呀
  • ¥15 flink cdc无法实时同步mysql数据
  • ¥100 有人会搭建GPT-J-6B框架吗?有偿