php - 使用 RSA 2048 公钥验证 RSA PKCS#1v1.5 SHA 256 签名

标签 php rsa php-7.1 php-openssl

我有一个公钥作为字符串存储在数据库中。生成库表示它是 RSA 2048 公钥。以下是存储公钥的示例:

MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHPAou1FpNAoLwF/mL0rei8QcC+LvKIYteEHOpxGibI6PNQ8kSpB7WNPJT2wM+B0+DT04L5LBi78PSo1UQO3umYEhHbAsKuNBV3CR8NGfUsWzSkgIzkbTCsELWkJT50foBT4YmBKEe6W8Ai2+3yzvMtic8RtBel73zS3dIdUUGaf7SMPtva+XDZTozRNiyligPh0nDgHcj6OQAtCxfTogLKukwaEPyWA06nQ68pMsxzzHqUZl5HJuDJRphAtOc2ZRFXZ1xaxVMO2D7UnkFA5q00UBW/i+KzkhgtNkLI8j2jiBim3IozRwpjQJvFsKoi29WDWPXcgtTU6k9kSmJBIqQIDAQAB

我有一个由同一库生成的数据字符串和签名。根据文档,签名是 RSA PKCS#1v1.5 SHA 256 签名。

数据字符串:

{"username":"<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="88fdfbedfac8edf0e9e5f8e4eda6ebe7e5" rel="noreferrer noopener nofollow">[email protected]</a>","requestTime":"1679669234"}

签名:

PiXcXFykgtOZY+tpmmXqt39VHwURd5NSIGL0U+TEj+qS6QwIT4Eh4W3fKYfIXwZGDu6Rdsu2PQxqBMhEQe7H7C0nq6ssih74MLavxpLuGPxmlZJ/EN2/m2fhLweerjS6U1uMIDcKE6cZgSD6z+KfkuWnKssM5yuMEkdztL5ef0Ay6Il0jXGayp+tLMGxGZaJLv31PRmYoK3c68buGphKV0hnj2y6+rKsoPfn+sVDxInH40oNcxF2zU+Y3ed9pW+/VOpnkTE9MoSNthNvwjNpRFJr4Awx7mKmOGe3dvC3yZ+iZ/lT892pTSwqo1O2mtXskbsUe1+xEfrzbi1/0ejy/g==

我一直在尝试使用openssl_verify验证此签名,但甚至无法使用 openssl_pkey_get_public 成功解析公钥。我尝试过以下几种变体:

$publickey = "-----BEGIN RSA PUBLIC KEY-----\n".$publickey."\n-----END RSA PUBLIC KEY-----";
$pkey = openssl_pkey_get_public($publickey);

我已经尝试了 -----BEGIN RSA PUBLIC KEY----- 的变体和 -----BEGIN PUBLIC KEY----- ,尽管我相信 RSA 版本是正确的。我也尝试过使用 chunk_split($publickey, 64, "\n")在传递到 openssl_pkey_get_public 之前分割行.

每次尝试后,openssl_error_string()返回以下内容:

error:0909006C:PEM routines:get_name:no start line

我不明白为什么会这样。谁能帮我验证这个签名?

虽然我希望避免使用额外的 PHP 库,但如果我必须安装 PHP 库(例如 phpseclib),我会这么做。

最佳答案

这就是所谓的 SPKI(主题公钥标识符)。这是 PEM header 行中没有 RSA 的一个,即只有 PUBLIC KEY。

也许问题在于 PEM 的结构不正确。

你可以例如请尝试以下操作:

$publickey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHPAou1FpNAoLwF/mL0rei8QcC+LvKIYteEHOpxGibI6PNQ8kSpB7WNPJT2wM+
B0+DT04L5LBi78PSo1UQO3umYEhHbAsKuNBV3CR8NGfUsWzSkgIzkbTCsELWkJT50foBT4YmBKEe6W8Ai2+3yzvMtic8RtBel73zS3dIdUUGaf7SMPtva+XD
ZTozRNiyligPh0nDgHcj6OQAtCxfTogLKukwaEPyWA06nQ68pMsxzzHqUZl5HJuDJRphAtOc2ZRFXZ1xaxVMO2D7UnkFA5q00UBW/i+KzkhgtNkLI8j2jiBi
m3IozRwpjQJvFsKoi29WDWPXcgtTU6k9kSmJBIqQIDAQAB";
preg_replace("(.{64})", "\1\r\n", $publickey);
$publickey = "-----BEGIN PUBLIC KEY-----\r\n".$publickey."\r\n-----END PUBLIC KEY-----";
$pkey = openssl_pkey_get_public($publickey);

请注意,PEM 文件确实需要 base64,但正式它们的行限制为 64 个字符。较旧的解析器可能需要使用 CR/LF 而不仅仅是 LF,因此字符串中使用 \r\n


因为你对此很友善,所以签名验证:

$data = '{"username":"<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="94e1e7f1e6d4f1ecf5f9e4f8f1baf7fbf9" rel="noreferrer noopener nofollow">[email protected]</a>","requestTime":"1679669234"}';
$sig = "PiXcXFykgtOZY+tpmmXqt39VHwURd5NSIGL0U+TEj+qS6QwIT4Eh4W3fKYfIXwZGDu6Rdsu2PQxqBMhEQe7H7C0nq6ssih74MLavxpLuGPxmlZJ/EN2/m2fhLweerjS6U1uMIDcKE6cZgSD6z+KfkuWnKssM5yuMEkdztL5ef0Ay6Il0jXGayp+tLMGxGZaJLv31PRmYoK3c68buGphKV0hnj2y6+rKsoPfn+sVDxInH40oNcxF2zU+Y3ed9pW+/VOpnkTE9MoSNthNvwjNpRFJr4Awx7mKmOGe3dvC3yZ+iZ/lT892pTSwqo1O2mtXskbsUe1+xEfrzbi1/0ejy/g==";
$res = openssl_verify($data, base64_decode($sig), $pkey, OPENSSL_ALGO_SHA256);

关于php - 使用 RSA 2048 公钥验证 RSA PKCS#1v1.5 SHA 256 签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75835228/

相关文章:

php - 在服务器上存储大量文件 - 按日期的子文件夹?

javascript - 为什么 Javascript 没有加密我的表单?

cryptography - RSA 和素数生成器算法

mysql - 如何在数据透视表字段中进行求和并在 yajra-laravel-datatable 包中搜索该字段(laravel 5.6)

php - 将 php5 模块移植到 php 7 和编译时出现 zend_string 问题

php - UTF-8贯穿始终

javascript - 使用 div 内的 GET 参数显示外部 php 文件的内容

php - 两个纪元时间戳之间的 MYSQL 查询有问题吗?

java - (Android/Java)创建RSA私钥实例

mysql - 如何在 Symfony 3 中使用 Doctrine 在类型 "json_array"的列上添加复合索引?