PHP创建ECDSA签名并使用Golang进行验证
我尝试用
PHP创建应用程序,为某些文档创建ECDSA签名,并使用Golang应用程序验证该签名.
我使用openssl工具生成的私钥.它是prime256v1曲线键.使用命令创建: openssl ecparam -name prime256v1 -genkey -noout -out prime256v1-key.pem 在PHP中,我使用openssl_sign函数创建签名. 我用Golang验证签名的所有尝试都失败了.在Golang中使用crypto / ecdsa,crypto / elliptic包. 有我的代码. 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."n"; 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")) } 我做错了什么?任何人都可以向我展示Golang验证用PHP创建的signatre的工作示例吗? 我试图在openssl_sign中使用不同的版本而不是OPENSSL_ALGO_SHA256.试过OPENSSL_ALGO_SHA1,OPENSSL_ALGO_SHA512 解决方法
您的代码的问题似乎是,您使用MD5在使用MD5对字符串进行哈希处理之前,使用OPENSSL_ALGO_SHA256对其进行哈希处理,这会再次对您签名(MD5哈希)进行哈希处理,而在您的Go程序中,您只拥有这些中的第一个哈希值.要解决这个问题,我会删除PHP代码中的MD5步骤,并使用签名算法使用的哈希替换代码中的h:= md5.New()行(示例中为h:= sha256.New()) .
为了详细说明这些签名函数的作用,我首先要打破签名并验证以下步骤: >签字: >哈希消息 >验证: >哈希消息 现在,在PHP代码中调用 在签名和验证时必须使用相同的散列算法,因此您必须在Go代码中使用SHA256而不是MD5(当您使用OPENSSL_ALGO_SHA256签名时),否则散列将(通常)不匹配. 另外,我建议不要将MD5用于签名,因为它不再被认为是抗冲突的(哈希冲突是,当你有2个不同的字符串/文件/ …具有相同的哈希).有关详细信息,请查看Wikipedia article on MD5,特别是“碰撞漏洞”部分.这是一个问题,因为具有相同MD5哈希的2条消息也将具有相同的签名,并且攻击者可以使用为其中一个字符串生成的签名来诱使您认为另一个已签名(因此信任它). 另外, ecdsa.Verify(&prikey.PublicKey,&s) 这样可以省去将私钥中的所有数据复制到新对象的麻烦. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |