Passing public key in PEM format to openssl_pkey_get_public gives error:0906D06C:PEM routines:PEM_read_bio:no start line
Passing public key in PEM format to openssl_pkey_get_public gives error:0906D06C:PEM routines:PEM_read_bio:no start line
以下 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-----
然而,方法调用失败,返回 false,错误字符串 error:0906D06C:PEM routines:PEM_read_bio:no start line
public密钥是否无效?作为记录,我的代码以 public 密钥模数和指数开始,并使用 here.
发布的算法将其转换为 PEM 格式
完整脚本如下:
<?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());
首先:openssl_pkey_get_public
旨在直接 加载 键 或 从 中提取 证书,如 openssl_pkey_get_public
.
的 certificate
参数的文档中所述
已经针对此问题提交了错误,#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):
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). 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. After that the key is loaded
using PEM_read_bio_PUBKEY which is successful in your case so you get
back the result. To sum it up openssl_error_string does not mean that
the operation failed but just that some error was emitted...
据此,错误信息是由于无法从证书中提取密钥导致的。但是,继续处理并直接加载密钥。换句话说,当直接直接加载密钥时,错误消息会按预期出现,并且在此上下文中可以忽略(至少在直接加载成功的情况下)。
记录:自 7.2(.17) 起,显示的错误消息略有不同:error:0909006C:PEM routines:get_name:no start line。
更新:
正如@President James Moveon Polk 在他的评论中指出的那样,createPemFromModulusAndExponent
没有正确生成密钥。如果第一个/最高有效字节大于 0x7F
,则模数前面必须有一个 0x00
字节,目前不会发生这种情况。例如。在发布的代码中,模数以 0x88
开头(Base64url 解码),这意味着生成的(= 发布的)密钥无效。如果手动添加 0x00
并将如此更正的值(Base64url 编码)传递给 createPemFromModulusAndExponent
,则以下内容现在 有效 关键结果:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiGaLqP6y+SJCCBq5Hv6p
GDbG/SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInq
UvjJur++hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPyg
jLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk+ILjv1bORSRl
8AK677+1T8isGfHKXGZ/ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl
4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw+zHL
wQIDAQAB
-----END PUBLIC KEY-----
当然如果createPemFromModulusAndExponent
能自动修正就更好了。 @President James Moveon Polk 已为此提出问题,here。
请允许我提出一种更简单、更简洁的替代方法。使用 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));
您使用的代码实际上是使用从 phpseclib 2.0 中提取的代码。有关详细信息,请参阅 https://github.com/dragosgaftoneanu/okta-simple-jwt-verifier/issues/1#issuecomment-612503921。
以下 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-----
然而,方法调用失败,返回 false,错误字符串 error:0906D06C:PEM routines:PEM_read_bio:no start line
public密钥是否无效?作为记录,我的代码以 public 密钥模数和指数开始,并使用 here.
发布的算法将其转换为 PEM 格式完整脚本如下:
<?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());
首先:openssl_pkey_get_public
旨在直接 加载 键 或 从 中提取 证书,如 openssl_pkey_get_public
.
certificate
参数的文档中所述
已经针对此问题提交了错误,#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):
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). 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. After that the key is loaded using PEM_read_bio_PUBKEY which is successful in your case so you get back the result. To sum it up openssl_error_string does not mean that the operation failed but just that some error was emitted...
据此,错误信息是由于无法从证书中提取密钥导致的。但是,继续处理并直接加载密钥。换句话说,当直接直接加载密钥时,错误消息会按预期出现,并且在此上下文中可以忽略(至少在直接加载成功的情况下)。
记录:自 7.2(.17) 起,显示的错误消息略有不同:error:0909006C:PEM routines:get_name:no start line。
更新:
正如@President James Moveon Polk 在他的评论中指出的那样,createPemFromModulusAndExponent
没有正确生成密钥。如果第一个/最高有效字节大于 0x7F
,则模数前面必须有一个 0x00
字节,目前不会发生这种情况。例如。在发布的代码中,模数以 0x88
开头(Base64url 解码),这意味着生成的(= 发布的)密钥无效。如果手动添加 0x00
并将如此更正的值(Base64url 编码)传递给 createPemFromModulusAndExponent
,则以下内容现在 有效 关键结果:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiGaLqP6y+SJCCBq5Hv6p
GDbG/SQ11MNjH7rWHcCFYz4hGwHC4lcSurTlV8u3avoVNM8jXevG1Iu1SY11qInq
UvjJur++hghr1b56OPJu6H1iKulSxGjEIyDP6c5BdE1uwprYyr4IO9th8fOwCPyg
jLFrh44XEGbDIFeImwvBAGOhmMB2AD1n1KviyNsH0bEB7phQtiLk+ILjv1bORSRl
8AK677+1T8isGfHKXGZ/ZGtStDe7Lu0Ihp8zoUt59kx2o9uWpROkzF56ypresiIl
4WprClRCjz8x6cPZXU2qNWhu71TQvUFwvIvbkE1oYaJMb0jcOTmBRZA2QuYw+zHL
wQIDAQAB
-----END PUBLIC KEY-----
当然如果createPemFromModulusAndExponent
能自动修正就更好了。 @President James Moveon Polk 已为此提出问题,here。
请允许我提出一种更简单、更简洁的替代方法。使用 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));
您使用的代码实际上是使用从 phpseclib 2.0 中提取的代码。有关详细信息,请参阅 https://github.com/dragosgaftoneanu/okta-simple-jwt-verifier/issues/1#issuecomment-612503921。