【问题标题】:RSA and SHA-256 encryption for signing using PHP使用 PHP 进行签名的 RSA 和 SHA-256 加密
【发布时间】:2021-04-03 16:32:17
【问题描述】:

我正在接收来自 Web 服务 API 的 XML 请求。它包含一个签名,由请求者的私钥签名,我必须通过请求者的公钥来验证它。然后我必须发送一个带有签名的响应,由我的私钥签名。

这个过程应该在 PHP 中使用 RSA 和 SHA-256 来完成。

我目前有以下代码:

$data_to_encrypt = "MsgBody..../MsgBody"; // xml 

$msgbody = simplexml_load_string($data_to_encrypt);
$result = $msgbody->xpath('//MsgBody'); 

openssl_private_encrypt(json_encode($result), $encrypted, $private_key, OPENSSL_PKCS1_PADDING);

$signature = $encrypted;
$verify = openssl_verify($encrypt, $signature ,$publick_key, OPENSSL_ALGO_SHA256); 

$verify=0 的结果。为什么是错误的验证?

【问题讨论】:

  • 你在坚持什么?最有可能使用openssl_sign,但请确保您应该向所述请求者索取文档或更多详细信息
  • 请改用openssl_sign。当您使用openssl_private_encrypt 时,可能缺少哈希函数指示。整个功能应该被废弃。

标签: php encryption hash rsa


【解决方案1】:

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_signopenssl_verify隐式考虑散列和添加摘要 ID,因此签名是自动符合 PKCS#1 v1.5 填充。

通常,后者是更有效的签名/验证方式。不过openssl_private_encrypt也有一个用处,即不是要签名的数据本身,而是只有已经散列的数据可以签名。

代码中的问题是openssl_private_encryptopenssl_verify 的组合。当然,您可以将两者结合起来,但是您必须实现散列并添加代码中缺少的 openssl_private_encrypt 的摘要 ID。或者(正如 cmets 中已经指出的那样)可以应用 openssl_sign,这在此处更有效。
另一个不一致的地方是必须对相同的数据进行签名和验证。在代码中,json_encode($result) 用于签名,$encrypt(从中派生出json_encode($result))用于验证。

以下 PHP 代码演示了 openssl_private_encryptopenssl_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。这两种方法都不是为了直接对消息进行签名/验证,而是如上所述,允许用户对已经散列的消息进行签名/验证。

【讨论】:

  • 请注意,签名前的散列对于安全性至关重要。
猜你喜欢
  • 2016-08-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-15
  • 2019-03-13
相关资源
最近更新 更多