GO语言ECDSA验证有效签名无效

我在我的 C++ 程序中编写了 ECDSA 函数。当我测试我的签名时,它在我的 C++ ECDSA 验证函数中工作正常,但不是每个测试都通过了 GO 语言。

因此,我尝试将我的公钥(从我的 C++ 中)作为缓冲区的十六进制字符串(33 字节)导出到 GO 语言。然后,我使用ellipitc.UnmarshalCompressed在 GO 程序上检索我的公钥。后来,我发现使用标量乘法从密钥生成公钥时,Unmarshal 公钥具有不同的 Y 坐标。我把代码放在下面。

import (

    "crypto/ecdsa"

    "crypto/elliptic"

    "crypto/sha256"

    "encoding/hex"

    "fmt"

    "log"

    "math/big"

    "os"

)    

func main() {


    /* UnmarshalCompressed public key from C++ buffer (hex string) */

    publicKeyBufferFromCplusplus, err := hex.DecodeString("02d36b0e521ca9a28cd6f2ddc56dc0973215702f6f67ed0670b9bc9a98c28d473b")

    if err != nil {

        fmt.Println("Unable to convert hex to byte. ", err)

    }

    pk := new(ecdsa.PublicKey)

    pk.Curve = elliptic.P256()

    pk.X, pk.Y = elliptic.UnmarshalCompressed(elliptic.P256(), publicKeyBufferFromCplusplus[:])


    /* Generate the key pair in GO, using the private key (as decimal) from C++ */

    expect_sk := new(ecdsa.PrivateKey)

    expect_sk.D, _ = new(big.Int).SetString("50228957095953179898827503463423289296009712707225507368245266147079499081684", 10)

    expect_sk.PublicKey.Curve = elliptic.P256()

    expect_sk.PublicKey.X, expect_sk.PublicKey.Y = expect_sk.PublicKey.Curve.ScalarBaseMult(expect_sk.D.Bytes())

    expect_pk := expect_sk.PublicKey


    /* compare the two public keys, the X coordinate is the same, but Y is different */

    fmt.Printf("pk_x:\t\t%d\n", pk.X)

    fmt.Printf("expect pk_x:\t%d\n\n", expect_pk.X)

    fmt.Printf("pk_y:\t\t%d\n", pk.Y)

    fmt.Printf("expect pk_y:\t%d\n", expect_pk.Y)


}

这是终端的结果


pk_x:           95627162525183504786576659676808415919520991299985517290103803735976207796027

expect pk_x:    95627162525183504786576659676808415919520991299985517290103803735976207796027


pk_y:           106312815215663533204607583749797836088594130128596587441436180287153537381066

expect pk_y:    9479273994692715558089863199609737441492013286693726754097451021713560472885

请注意,不同之处在于 Y 坐标,而 X 坐标是相同的。


慕盖茨4494581
浏览 339回答 1
1回答

翻过高山走不出你

我发现我的 C++ 椭圆曲线库在将公钥(点)压缩(编组)为SEC 1,版本 2.0,第 2.3.3 节形式时有一个错误。有些点第一个字节错误(从奇数到偶数,或从偶数到奇数)。1*G     GO buffer: 0x036b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296     C++ buffer: 0x026b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2962*G     GO buffer: 0x037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc47669978     C++ buffer: 0x037cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc476699783*G     GO buffer: 0x025ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c     C++ buffer: 0x035ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c4*G     GO buffer: 0x02e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b030852     C++ buffer: 0x02e2534a3532d08fbba02dde659ee62bd0031fe2db785596ef509302446b0308525*G     GO buffer: 0x0251590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed     C++ buffer: 0x0251590b7a515140d2d784c85608668fdfef8c82fd1f5be52421554a0dc3d033ed6*G     GO buffer: 0x02b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a9     C++ buffer: 0x03b01a172a76a4602c92d3242cb897dde3024c740debb215b4c6b0aae93c2291a97*G     GO buffer: 0x028e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a3     C++ buffer: 0x038e533b6fa0bf7b4625bb30667c01fb607ef9f8b8a80fef5b300628703187b2a38*G     GO buffer: 0x0262d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb393     C++ buffer: 0x0362d9779dbee9b0534042742d3ab54cadc1d238980fce97dbb4dd9dc1db6fb3939*G     GO buffer: 0x02ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0     C++ buffer: 0x02ea68d7b6fedf0b71878938d51d71f8729e0acb8c2c6df8b3d79e8a4b90949ee0因此,一些公钥(点)被导出为而{x,-y} 不是{x,y}原来的椭圆曲线 (EC) 公钥遵循高效密码术标准 (SEC),SEC 1:椭圆曲线密码术部分中的 2.3.3 椭圆曲线点到八位字节字符串转换。如果 y 坐标为偶数,则压缩形式(作为字节)的公钥的第一个字节为0x02 。否则,如果 y 坐标为奇数,则为 0x03。在数学上,只给定一个 x 坐标y^2 = x^3 + ax + b (mod p)上面的等式有两个解,(x, y)和(x, -y)因为 y^2 = (-y)^2此外,-y 等于 (p - y),因为它是整数模素数p的字段。由于p 是素数且 p!=2,因此p 是奇数。然后,如果y是奇数,则-y(或(py))是偶数,反之亦然。ECDSA 验证算法不需要公钥的 y 坐标,因为它只需要它的 x 坐标。因此,当我检查签名元组{r,s}时,我将使用相同 x 坐标但不同 y 坐标(从奇数翻转到偶数,反之亦然)的公钥对其进行检查。我真的不知道为什么 GO 语言 ECDSA 包只允许两个 y 坐标值之一。下面是我的代码示例。假设我有一个压缩的公钥作为缓冲区publicKeyBuffer[:]然后我可以解组它以获得 x 坐标和 y 坐标  pk := new(ecdsa.PublicKey)    pk.Curve = elliptic.P256()        pk.X, pk.Y = elliptic.UnmarshalCompressed(elliptic.P256(), publicKeyBuffer[:])我们可以检查签名元组 {r,s} 是否有效valid := ecdsa.Verify(pk, hash[:], r, s)如果签名无效,即valid == false尝试将值更改pk.Y为对应的值。您可以通过两种简单的方式做到这一点。第一种方法,您将公钥缓冲区的第一个字节从 更改为0x02或0x03反之亦然。您可以通过XOR使用(仅 1)执行第一个字节来实现此目的0x01。    valid = ecdsa.Verify(pk, hash[:], r, s)    /* The 1st way */    if !valid {        publicKeyBuffer[0] ^= 1 // change the y-coordinate by switching 0x02 to 0x03 (or vice versa) of public key buffer        pk.X, pk.Y = elliptic.UnmarshalCompressed(elliptic.P256(), publicKeyBuffer[:])        valid = ecdsa.Verify(pk, hash[:], r, s)    }    fmt.Println("signature verified:", valid) 在第二种方式中,我们将 y 坐标更改为其 (py) 值,即,将其更改为 (-y)    valid = ecdsa.Verify(pk, hash[:], r, s)    /* The 2nd way */    if !valid {        pk.Y = new(big.Int).Sub(pk.Curve.Params().P, pk.Y) // replace y-coordinate pk.Y with (-y), Note. (-y) is equal (p - y)        valid = ecdsa.Verify(pk, hash[:], r, s)    }    fmt.Println("signature verified:", valid) 如果签名仍然无效,那么这与公钥 Unmarshal 无关。PS 我不知道为什么 GO ECDSA 语言不允许公钥的其他 y 坐标值
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go