RSA and SHA-256 encryption for signing using PHP

荒凉一梦 提交于 2021-02-16 20:26:34

问题


I'm receiving XML in request from a Web Service API. It contains a signature, signed by the requester's private key, and I have to verify it by the requester public key. Then I have to send a response with a signature, signed by my private key.

This process should be done with RSA and SHA-256 in PHP.

I currently have the following code:

$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); 

The result of $verify= 0. Why is it a bad verify?


回答1:


The counterpart to openssl_private_encrypt is openssl_public_decrypt. Both methods allow low level signing/verification with PKCS#1 v1.5 padding (RSASSA-PKCS1-v1_5), where the data is implicitly not hashed, nor is the ID of the digest used prepended to the hash. I.e., for the result to be compliant with PKCS#1 v1.5 padding, both must be done explicitly.

In contrast, hashing and adding the digest ID is implicitly taken into account in openssl_sign and openssl_verify in the context of RSA and PKCS#1 v1.5 padding, so the signature is automatically compliant with PKCS#1 v1.5 padding.

Generally, the latter is the more efficient way for signing/verifying. However, openssl_private_encrypt also has a use, namely when not the data to be signed itself, but only the already hashed data is available for signing.

The issue in the code is the combination of openssl_private_encrypt and openssl_verify. Of course you can combine both, but then you have to implement hashing and adding the digest ID for openssl_private_encrypt, which is missing in the code. Alternatively (as already noted in the comments) openssl_sign can be applied, which is more efficient here.
Another inconsistency is that signing and verifying must be done on the same data. In the code, json_encode($result) is used for signing and $encrypt (from which json_encode($result) was derived) for verifying.

The following PHP code demonstrates the combination of openssl_private_encrypt and openssl_verify (see Test 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://tools.ietf.org/html/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;

Edit: The last example is to be understood purely technical and should demonstrate that encryption with openssl_private_encrypt without hashing and without adding the ID can be decrypted with openssl_public_decrypt. In practice, hashing is applied when signing, see comment by kelalaka and e.g. here. Both methods are not intended to sign/verify the message directly, but, as already mentioned above, to allow the user to sign/verify an already hashed message.



来源:https://stackoverflow.com/questions/65451594/rsa-and-sha-256-encryption-for-signing-using-php

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!