我有一个公钥作为字符串存储在数据库中。生成库说它是一个 RSA 2048 公钥。这是存储公钥的示例:
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmHPAou1FpNAoLwF/mL0rei8QcC+LvKIYteEHOpxGibI6PNQ8kSpB7WNPJT2wM+B0+DT04L5LBi78PSo1UQO3umYEhHbAsKuNBV3CR8NGfUsWzSkgIzkbTCsELWkJT50foBT4YmBKEe6W8Ai2+3yzvMtic8RtBel73zS3dIdUUGaf7SMPtva+XDZTozRNiyligPh0nDgHcj6OQAtCxfTogLKukwaEPyWA06nQ68pMsxzzHqUZl5HJuDJRphAtOc2ZRFXZ1xaxVMO2D7UnkFA5q00UBW/i+KzkhgtNkLI8j2jiBim3IozRwpjQJvFsKoi29WDWPXcgtTU6k9kSmJBIqQIDAQAB
我有一个由同一个库生成的数据字符串和签名。根据文档,签名是 RSA PKCS#1v1.5 SHA 256 签名。
数据串:
{"username":"[email protected]","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 标题行中没有 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":"[email protected]","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);