Существует две проблемы:
- Данные, которые должны быть подписаны, должны быть переданы методу
sign
, а не ha sh этих данных. Метод sign
выполняет хеширование самостоятельно. По умолчанию sha1 используется в качестве дайджеста. Другой дайджест должен быть указан явно с помощью setHash
. - . Подпись должна быть передана методу
verify
, а не закодированной в Base64 подписи.
Попробуйте следующие модификации:
...
$rsa->setHash('sha256'); // Specify digest, e.g. sha256 (default is sha1)
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$signature = $rsa->sign($plaintext); // Pass plaintext instead of plaintext hash.
echo base64_encode($signature) . "\n";
...
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified' . "\n"; // Pass signature instead of Base64 encoded signature
...
Также имеется соответствующий пример в документации.
Обновление:
Спецификация SignerUtilities.GetSigner("RSA")
в коде C# означает, что данные подписаны без предварительного хеширования, т.е. поэтому хеширование должно быть явно , выполненным ранее, что также имеет место в текущем C# код (с использованием SHA256).
Однако этот способ отличается от стандартного RSASSA-PKCS1-v1_5 (описанного в RF C 8017) тем, что RSASSA-PKCS1-v1_5 размещает идентификатор дайджеста перед ha sh (в случае SHA256 это ID 0x3031300d060960864801650304020105000420
). По этой причине проверка с помощью phpseclib , который (конечно) использует RSASSA-PKCS1-v1_5, завершается неудачей.
Чтобы также использовать RSASSA-PKCS1-v1_5 в коде C#, либо добавьте идентификатор дайджеста вручную, либо используйте SignerUtilities.GetSigner("SHA256withRSA")
, чтобы данные хэшировались перед подписанием (тогда, конечно, явное хеширование должно быть опущено).
Если C# код - это ссылка, тогда необходима библиотека, которую можно использовать для подписи без предварительного хеширования, аналогично коду C#.
phpseclib использует RSASSA-PKCS1-v1_5
заполнение для подписи / проверки (для режима подписи CRYPT_RSA_SIGNATURE_PKCS1
) и различное RSAES-PKCS1-v1_5
заполнение для шифрования / дешифрования (для режима шифрования CRYPT_RSA_ENCRYPTION_PKCS1
). Поэтому ни один из этих вариантов здесь не подходит.
Как отмечается в комментарии @ neubert, phpseclib предлагает возможность отключить заполнение во время шифрования / дешифрования (для режима шифрования CRYPT_RSA_ENCRYPTION_NONE
заполнение выполняется со значениями 0x00
). Это позволяет настраивать подпись путем реализации пользовательского заполнения с последующим шифрованием с помощью закрытого ключа. В текущем случае для этого потребуется реализация RSASSA-PKCS1-v1_5
padding без части, в которую добавляется идентификатор дайджеста.
Здесь проще использовать альтернативы, которые уже применяют RSASSA-PKCS1-v1_5
заполнение , если указаны идентификатор дайджеста и га sh, что в текущем случае означает, что нужно пропустить только га sh. Примерами таких альтернатив являются openssl_private_encrypt
для подписи и openssl_public_decrypt
для проверки:
$data = array(
"name" => "hello"
);
$data = json_encode($data, true);
$hashed = hash('sha256', $data, true); // Hash data using sha256
// Signing
openssl_private_encrypt($hashed, $signature, $privateKey); // Encrypt data using the private key
echo base64_encode($signature) . "\n"; // Output: csorNNekiVXJzQaT/FVCKrlSPfonQYw7dStKsjgVuW/jAcRafk4Yeuzw4rc1WxfgheMaa31DROnhrRCBS6VNUm8w2N/XwX2zmImFLj5KZlnRPne7CZX3YHM3qa5CRf/1VWdNnXMZnBecIe6QltECQLsQ/8fRzaYJpZO6tZjIRf4fBYwqMHWhHlTn6UR3A0GDKSEU5mI2lvzl+9ov+x6AFCLT3sa8hc3aZDfO7SWmQHNHCwrBC9QFNTOHY+/oJXtKYMfY3IskvLSmKmvKc6vh8XANBYp+NCl3/9hNdhbF4psdJXuZgf3aSpy0koBttdcE3js0NoS8Q91ry2WJtkcCrA==
// Verifying
openssl_public_decrypt($signature, $decrypted, $publicKey); // Decrypt data using the public key
$verified = ($decrypted == $hashed) ? 'verified' : 'unverified'; // Verify data
echo $verified;