Шифрование RSA и SHA-256 для подписи с использованием PHP

#php #encryption #hash #rsa

#php #шифрование #хэш #rsa

Вопрос:

Я получаю XML в запросе от API веб-службы. Он содержит подпись, подписанную закрытым ключом запрашивающего, и я должен проверить ее с помощью открытого ключа запрашивающего. Затем я должен отправить ответ с подписью, подписанной моим закрытым ключом.

Этот процесс должен выполняться с помощью RSA и SHA-256 на PHP.

В настоящее время у меня есть следующий код:

 $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. Почему это плохая проверка?

Комментарии:

1. на чем вы застряли? Скорее всего, используется openssl_sign , но, чтобы быть уверенным, вам следует запросить документы или более подробную информацию у указанного запрашивающего

2. Используйте openssl_sign вместо этого. Вероятно, при использовании отсутствует индикация хэш-функции openssl_private_encrypt . Вся эта функция должна быть удалена.

Ответ №1:

Аналог openssl_private_encrypt is openssl_public_decrypt . Оба метода допускают низкоуровневую подпись / проверку с дополнением PKCS # 1 v1.5 (RSASSA-PKCS1-v1_5), где данные неявно не хэшируются, и идентификатор используемого дайджеста не добавляется к хэшу. То есть, чтобы результат соответствовал PKCS # 1 v1.5 заполнение, оба должны быть выполнены явно.

Напротив, хеширование и добавление идентификатора дайджеста неявно учитываются в openssl_sign и openssl_verify в контексте заполнения RSA и PKCS # 1 версии 5, поэтому подпись автоматически соответствует заполнению PKCS # 1 версии5.

Как правило, последний является более эффективным способом подписи / проверки. Однако openssl_private_encrypt оно также имеет применение, а именно, когда для подписи доступны не сами подписываемые данные, а только уже хэшированные данные.

Проблема в коде заключается в сочетании openssl_private_encrypt и openssl_verify . Конечно, вы можете объединить оба, но тогда вам придется реализовать хеширование и добавление идентификатора дайджеста для openssl_private_encrypt , который отсутствует в коде. В качестве альтернативы (как уже отмечалось в комментариях) 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 помощью без хеширования и без добавления идентификатора может быть расшифровано с openssl_public_decrypt помощью . На практике при подписании применяется хеширование, см. Комментарий келалаки и, например, здесь . Оба метода не предназначены для подписи / проверки сообщения напрямую, но, как уже упоминалось выше, позволяют пользователю подписывать / проверять уже хэшированное сообщение.

Комментарии:

1. Следует отметить, что хэширование перед подписанием имеет решающее значение для безопасности.