openssl_private_encrypt 的对应项是 openssl_public_decrypt。这两种方法都允许使用 PKCS#1 v1.5 填充 (RSASSA-PKCS1-v1_5) 进行低级别签名/验证,其中数据未隐式散列,所用摘要的 ID 也未添加到散列前。即,要使结果符合 PKCS#1 v1.5 填充,两者都必须明确完成。
相比之下,在 RSA 和 PKCS#1 v1.5 填充的上下文中,openssl_sign 和 openssl_verify 会隐式考虑散列和添加摘要 ID,因此签名是自动符合 PKCS#1 v1.5 填充。
通常,后者是更有效的签名/验证方式。不过openssl_private_encrypt也有一个用处,即不是要签名的数据本身,而是只有已经散列的数据可以签名。
代码中的问题是openssl_private_encrypt 和openssl_verify 的组合。当然,您可以将两者结合起来,但是您必须实现散列并添加代码中缺少的 openssl_private_encrypt 的摘要 ID。或者(正如 cmets 中已经指出的那样)可以应用 openssl_sign,这在此处更有效。
另一个不一致的地方是必须对相同的数据进行签名和验证。在代码中,json_encode($result) 用于签名,$encrypt(从中派生出json_encode($result))用于验证。
以下 PHP 代码演示了 openssl_private_encrypt 和 openssl_verify 的组合(参见测试 1):
function getRSAKeys(){
$keyPairResource = openssl_pkey_new(array("private_key_bits" => 2048, "private_key_type" => OPENSSL_KEYTYPE_RSA));
openssl_pkey_export($keyPairResource, $privateKey);
return [$privateKey, openssl_pkey_get_details($keyPairResource)["key"]];
}
// Create test key
$newKeyPair = getRSAKeys();
$privateKey = $newKeyPair[0];
$publicKey = $newKeyPair[1];
// Test 1: openssl_private_encrypt and openssl_verify
$dataToSign = 'Test 1: The data to sign'; // Could correspond to e.g. json_encode($result) in the code
$dataToSignHashed = hash('sha256', $dataToSign, true);
$dataToSignHashedWithID = hex2bin("3031300d060960864801650304020105000420") . $dataToSignHashed; // ID from https://www.rfc-editor.org/rfc/rfc8017#page-47
openssl_private_encrypt($dataToSignHashedWithID, $signature, $privateKey, OPENSSL_PKCS1_PADDING);
$verified = openssl_verify($dataToSign, $signature, $publicKey, OPENSSL_ALGO_SHA256);
print($verified) . PHP_EOL;
// Test 2: openssl_sign and openssl_verify
$dataToSign = 'Test 2: The data to sign'; // Could correspond to e.g. json_encode($result) in the code
openssl_sign($dataToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256);
$verified = openssl_verify($dataToSign, $signature, $publicKey, OPENSSL_ALGO_SHA256);
print($verified) . PHP_EOL;
// Test 3: openssl_private_encrypt and openssl_public_decrypt (without hashing and adding the digest id)
$dataToSign = 'Test 3: The data to sign'; // Could correspond to e.g. json_encode($result) in the code
openssl_private_encrypt($dataToSign, $signature, $privateKey, OPENSSL_PKCS1_PADDING);
openssl_public_decrypt($signature, $decrypted, $publicKey, OPENSSL_PKCS1_PADDING);
print($dataToSign === $decrypted) . PHP_EOL;
编辑:最后一个例子可以理解纯技术,并且应该证明使用openssl_private_encrypt加密而不添加ID可以用@987654343解密@。在实践中,签名时会应用散列,请参阅 kelalaka 的评论,例如here。这两种方法都不是为了直接对消息进行签名/验证,而是如上所述,允许用户对已经散列的消息进行签名/验证。