[英]Passing public key in PEM format to openssl_pkey_get_public gives error:0906D06C:PEM routines:PEM_read_bio:no start line
The following public RSA key in PEM format was provided to openssl_pkey_get_public.以下 PEM 格式的公共 RSA 密钥已提供给 openssl_pkey_get_public。
-----BEGIN PUBLIC KEY-----
MIIBITANBgkqhkiG9w0BAQEFAAOCAQ4AMIIBCQKCAQCIZouo/rL5IkIIGrke/qkY
Nsb9JDXUw2MfutYdwIVjPiEbAcLiVxK6tOVXy7dq+hU0zyNd68bUi7VJjXWoiepS
+Mm6v76GCGvVvno48m7ofWIq6VLEaMQjIM/pzkF0TW7CmtjKvgg722Hx87AI/KCM
sWuHjhcQZsMgV4ibC8EAY6GYwHYAPWfUq+LI2wfRsQHumFC2IuT4guO/Vs5FJGXw
Arrvv7VPyKwZ8cpcZn9ka1K0N7su7QiGnzOhS3n2THaj25alE6TMXnrKmt6yIiXh
amsKVEKPPzHpw9ldTao1aG7vVNC9QXC8i9uQTWhhokxvSNw5OYFFkDZC5jD7McvB
AgMBAAE=
-----END PUBLIC KEY-----
However, the method call fails, returning false, with the error string error:0906D06C:PEM routines:PEM_read_bio:no start line
但是,方法调用失败,返回 false,错误字符串
error:0906D06C:PEM routines:PEM_read_bio:no start line
Is the public key invalid?公钥无效吗? For the record, my code is starting with a public key modulus and exponent and converting it to PEM format using the algorithm posted here .
作为记录,我的代码从公钥模数和指数开始,并使用此处发布的算法将其转换为 PEM 格式。
Here's the full script:这是完整的脚本:
<?php
function createPemFromModulusAndExponent($n, $e)
{
$modulus = urlsafeB64Decode($n);
$publicExponent = urlsafeB64Decode($e);
$components = array(
'modulus' => pack('Ca*a*', 2, encodeLength(strlen($modulus)), $modulus),
'publicExponent' => pack('Ca*a*', 2, encodeLength(strlen($publicExponent)), $publicExponent)
);
$RSAPublicKey = pack('Ca*a*a*', 48, encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])), $components['modulus'], $components['publicExponent']);
$rsaOID = pack('H*', '300d06092a864886f70d0101010500');
$RSAPublicKey = chr(0) . $RSAPublicKey;
$RSAPublicKey = chr(3) . encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
$RSAPublicKey = pack('Ca*a*', 48, encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey);
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----" . chunk_split(base64_encode($RSAPublicKey), 64) . '-----END PUBLIC KEY-----';
return $RSAPublicKey;
}
function urlsafeB64Decode($input)
{
$remainder = strlen($input) % 4;
if ($remainder)
{
$padlen = 4 - $remainder;
$input .= str_repeat('=', $padlen);
}
return base64_decode(strtr($input, '-_', '+/'));
}
function encodeLength($length)
{
if ($length <= 0x7F)
{
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
$key = createPemFromModulusAndExponent('iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ', 'AQAB');
print_r($key);
print_r(openssl_pkey_get_public($key));
print_r(openssl_error_string());
First: openssl_pkey_get_public
is intended to either load the public key directly or extract it from a certificate, as described in the documentation of the certificate
parameter of openssl_pkey_get_public
.首先:
openssl_pkey_get_public
旨在直接加载公钥或从证书中提取公钥,如openssl_pkey_get_public
的certificate
参数文档中所述。
There has already been a bug filed for this issue, #75643 from Dec 2017 (version 7.1.12), which has the status No Feedback and is currently suspended (note that #75643 actually refers to openssl_public_encrypt
, which however uses the same logic regarding the key as openssl_pkey_get_public
, here ):已经为此问题提交了一个错误,即 2017 年 12 月(版本 7.1.12)的#75643 ,其状态为No Feedback ,目前已暂停(请注意,#75643 实际上是指
openssl_public_encrypt
,但它使用相同的逻辑关于密钥为openssl_pkey_get_public
, 在这里):
The error in the queue is expected.
队列中的错误是预期的。 If you supply string as a PEM (string not prefixed by "file://" which would be a file path), then certificate is tried first (using PEM_ASN1_read_bio).
如果您将字符串作为 PEM 提供(字符串不以“file://”为前缀,这将是一个文件路径),则首先尝试证书(使用 PEM_ASN1_read_bio)。 It means that it fails and the error is saved to the queue.
这意味着它失败并且错误被保存到队列中。 However this queue is just a copy of the OpenSSL which is emptied.
然而,这个队列只是被清空的 OpenSSL 的副本。 After that the key is loaded using PEM_read_bio_PUBKEY which is successful in your case so you get back the result.
之后,使用 PEM_read_bio_PUBKEY 加载密钥,这在您的情况下是成功的,因此您可以取回结果。 To sum it up openssl_error_string does not mean that the operation failed but just that some error was emitted...
总而言之,openssl_error_string 并不意味着操作失败,而只是发出了一些错误......
According to this, the error message is caused by the failure to extract the key from the certificate.据此,错误消息是由于未能从证书中提取密钥引起的。 However, processing is continued and the key is loaded directly.
但是,继续处理并直接加载密钥。 In other words, the error message occurs as expected when loading the key directly and can be ignored in this context (at least if the direct loading is successful).
换句话说,错误消息在直接加载密钥时按预期出现,并且在这种情况下可以忽略(至少在直接加载成功的情况下)。
For the records: As of 7.2(.17), a slightly different error message is displayed: error:0909006C:PEM routines:get_name:no start line .对于记录:从 7.2(.17) 开始,显示的错误消息略有不同: error:0909006C:PEMroutines:get_name:no start line 。
Update:更新:
As @President James Moveon Polk noted in his comment, createPemFromModulusAndExponent
doesn't generate the key correctly.正如@President James Moveon Polk 在他的评论中指出的那样,
createPemFromModulusAndExponent
没有正确生成密钥。 If the first / most significant byte is greater than 0x7F
, the modulus must be preceded by a 0x00
byte, which does currently not happen.如果第一个/最高有效字节大于
0x7F
,则模数前面必须有一个0x00
字节,这目前不会发生。 Eg in the posted code the modulus starts (Base64url decoded) with 0x88
, which means that the generated (= the posted) key is invalid.例如,在发布的代码中,模数以
0x88
开头(Base64url 解码),这意味着生成的(= 发布的)密钥无效。 If a 0x00
is prepended manually and the so corrected value is (Base64url encoded) passed to createPemFromModulusAndExponent
, the following, now valid key results:如果手动添加
0x00
并且如此更正的值(Base64url 编码)传递给createPemFromModulusAndExponent
,则以下现在有效的密钥结果:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiGaLqP6y+SJCCBq5Hv6p
GDbG/SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInq
UvjJur++hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPyg
jLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk+ILjv1bORSRl
8AK677+1T8isGfHKXGZ/ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl
4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw+zHL
wQIDAQAB
-----END PUBLIC KEY-----
Of course it would be better if createPemFromModulusAndExponent
would do this correction automatically.当然,如果
createPemFromModulusAndExponent
会自动进行此校正会更好。 @President James Moveon Polk has filed an issue for this, here . @President James Moveon Polk 已为此提出问题,请点击此处。
Allow me to propose an alternative way that's quite a bit simpler and more succinct.请允许我提出一种更简单、更简洁的替代方法。 Using phpseclib,
使用 phpseclib,
require __DIR__ . '/vendor/autoload.php';
use phpseclib\Math\BigInteger;
use phpseclib\Crypt\RSA;
$rsa = new RSA;
$rsa->loadKey([
'e' => new BigInteger(base64_decode('AQAB'), 256),
'n' => new BigInteger(base64_decode('iGaLqP6y-SJCCBq5Hv6pGDbG_SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInqUvjJur--hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPygjLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk-ILjv1bORSRl8AK677-1T8isGfHKXGZ_ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw-zHLwQ'), 256)
]);
print_r(openssl_pkey_get_public($rsa));
The code you're using is, in fact, using code that was lifted from phpseclib 2.0.实际上,您使用的代码是使用从 phpseclib 2.0 中提取的代码。 See https://github.com/dragosgaftoneanu/okta-simple-jwt-verifier/issues/1#issuecomment-612503921 for more info.
有关更多信息,请参阅https://github.com/dragosgaftoneanu/okta-simple-jwt-verifier/issues/1#issuecomment-612503921 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.