Проблема с вашим кодом заключается в том, что вы хэшируете строку в PHP, используя MD5, прежде чем подписать ее, используя OPENSSL_ALGO_SHA256
, который снова хэширует то, что вы подписываете (хэш MD5), тогда как в вашей программе Go вы имеете только первый из этих 2 хэшей. Чтобы исправить это, я удалил бы шаг MD5
в коде PHP и заменил строку h := md5.New()
в вашем коде хэшем, используемым вашим алгоритмом подписи (h := sha256.New()
в вашем примере).
Чтобы подробнее рассказать о том, что делают эти функции подписи, сначала я хотел бы разбить подпись и проверку на следующие шаги:
- Подписание:
- Хеширование сообщения
- Зашифровать хеш сообщения с помощью закрытого ключа (этот зашифрованный хэш является подписью)
- Проверка:
- Хеш сообщения
- Расшифруйте подпись с помощью открытого ключа (это дает хэш, который был зашифрован при подписании).
- Сравните вычисленные и расшифрованные хэши. Если они совпадают, тогда подпись верна.
Теперь вызов openssl_sign
в вашем PHP-коде выполняет все шаги подписи, в то время как вызов ecdsa.Verify
в Go выполняет только второй и третий этап. процесса проверки. И именно поэтому он принимает хэш в качестве второго аргумента. Таким образом, чтобы проверить подпись, вы должны самостоятельно выполнить первый шаг проверки, а именно, создать хеш.
Вы должны использовать один и тот же алгоритм хеширования при подписании и проверке, поэтому вы должны использовать SHA256, а не MD5, в своем коде Go (как вы подписываете, используя OPENSSL_ALGO_SHA256
), иначе хеши (как правило) не будут совпадать.
Кроме того, я бы рекомендовал не использовать MD5 для подписей, так как он больше не считается устойчивым к коллизиям (хеш-коллизия возникает, когда у вас есть 2 разные строки / файлы / ... с одним и тем же хешем). Подробнее об этом вы можете прочитать в статье Википедии о MD5 , в частности, в разделе «Уязвимости при столкновениях». Это проблема, поскольку 2 сообщения с одинаковым хешем MD5 также будут иметь одинаковую подпись, и злоумышленник может использовать подпись, сгенерированную для одной из строк, чтобы заставить вас думать, что другая подписана (и, следовательно, доверяет ей).
Кроме того, ecdsa.PrivateKey
может дать вам соответствующий открытый ключ, и вы можете позвонить ecdsa.Verify
следующим образом:
ecdsa.Verify(&prikey.PublicKey, data, &r, &s)
Это избавляет вас от необходимости копировать все данные из закрытого ключа в новый объект.