Сгенерировать подпись ключа RSA и указать неверные данные - PullRequest
1 голос
/ 27 марта 2020

Я использую https://github.com/phpseclib/phpseclib для создания подписи. Но когда я генерирую подпись с моим закрытым ключом RSA, он генерирует подпись, но он также дает мне unverified, когда я проверяю с помощью моего открытого ключа c.

Вот мой код:

<?php

// Include library
include('Crypt/RSA.php');

// Create an Instance
$rsa = new Crypt_RSA();
$hash = new Crypt_Hash('sha256');

$data = array(
     "name" => "hello"
);

$data = json_encode($data, true);

// Load Private Key
$rsa->loadKey('-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAlwK9IAETYwSW6p0hVEue3+WyMtRW5MA3BS2Bzf3B0Qimnvfl
....
-----END RSA PRIVATE KEY-----');

// Message to be signed
$plaintext = $data;

// Set signing signature
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$hashed = $hash->hash($plaintext); 
$encrypted = $rsa->sign($hashed); // Sign Data
$signature = base64_encode($encrypted); // Encode to base64 the signed Data

echo $signature;

$rsa->loadKey('-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlwK9IAETYwSW6p0hVEue
.....
-----END PUBLIC KEY-----'); // public key
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';

Теперь код выше генерирует подпись, но это не правильно.

Теперь после этого я попытался BouncyCastle lib сгенерировать подпись с ключами выше только в. NET. Генерируется идеальная подпись, а также проверяется.

Я не знаю, в чем проблема с phpseclib lib. Я также напрямую попытался сгенерировать с помощью openssl, но он не генерирует правильный.

1 Ответ

5 голосов
/ 27 марта 2020

Существует две проблемы:

  • Данные, которые должны быть подписаны, должны быть переданы методу 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;
...