openssl_verify 和“错误:0906D06C:PEM 例程:PEM_read_bio:没有起始行”

Posted

技术标签:

【中文标题】openssl_verify 和“错误:0906D06C:PEM 例程:PEM_read_bio:没有起始行”【英文标题】:openssl_verify and "error:0906D06C:PEM routines:PEM_read_bio:no start line" 【发布时间】:2011-04-05 14:40:32 【问题描述】:

我正在尝试在 php 中使用 OpenSSL 函数进行 RSA 签名/验证。 当我尝试使用我的公钥执行 openssl_verify 时,我收到此错误:error:0906D06C:PEM routines:PEM_read_bio:no start line,但函数本身可以正常工作(如果消息被修改,则返回 0 , 如果完整则为 1)。 openssl_sign 工作正常。

我该如何解决?

目前,我使用openssl生成的公钥:

define("SC_MSG_PUBLIC", <<<EOD
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALjPcOckMHDVLiUstcRwwx8kF5PzsiEs
rskyndWisbXMLU9BHomXwL7Qg2L91jE+sNSobkzBDF41CbwDiNlofZUCAwEAAQ==
-----END PUBLIC KEY-----
EOD
);

知道为什么会触发此错误,但一切正常吗?

试图从私钥生成公钥并使用它,但它似乎完全相同,相同的错误消息:-S

$pkey = openssl_pkey_get_private(SC_MSG_PRIVATE);
$keyDetails = openssl_pkey_get_details($pkey);
file_put_contents('c:\publickey', $keyDetails['key']);

另外,我尝试安装所有新版本(PHP 5.3.1、OpenSSL 1.0.0a)——结果相同。而且,我在 Windows 上。

【问题讨论】:

您必须在封装边界处保留 CR/LF(-----BEGIN PUBLIC KEY----------END PUBLIC KEY-----)。请注意,这是一个 CR/LF 对;而不是 CR (OS X) 而不是 LF (Linux)。没有它们,OpenSSL 无法解析 PEM。您还需要确保一行的长度小于 1000 个字符。另请参阅RFC 1421, Section 4.3.1, Constraints。 【参考方案1】:

您是否尝试使用包含您的公钥而不是纯公钥的(可能是自签名的)证书调用 openssl_verify()

据我所知,一些 PHP OpenSSL 函数不能正确支持裸公钥,尽管尽管出现错误但它确实正确验证似乎很奇怪。

<?php
$private = openssl_pkey_get_private(file_get_contents('private'), 'passphrase');

// This causes the "no start line" error when using a naked public key:
$public  = openssl_pkey_get_public(file_get_contents('public')); // <-- this should be cert

echo openssl_error_string()."\n";

openssl_sign('Test', $sig, $private);
var_dump(openssl_verify('Test', $sig, $public));

echo openssl_error_string()."\n";
?>

在 bash 等 Linux/UNIX shell 中将公钥转换为简单证书的示例(请参阅 OpenSSL 文档或一些教程了解更多信息):

# Create certificate request
openssl req -new -days 3600 -key [PRIVATE-KEY-FILE] -out [REQUEST-TMP-FILE]

# Create certificate from request
RANDFILE=[RANDOM-TMP-FILE] openssl x509 -req -in [REQUEST-TMP-FILE] -signkey [PRIVATE-KEY-FILE] -out [CERTIFICATE-OUT-FILE]

这还会创建您以后可能想要删除的临时文件,即 [REQUEST-TMP-FILE][RANDOM-TMP-FILE]

PHP 示例代码可以在http://de.php.net/manual/en/function.openssl-csr-new.php 找到。

【讨论】:

这是一个完美的解决方案。感谢您使用我的密钥生成证书的步骤。你有一个很好的代表:-) PHP 解决了这个可怕的问题,一个裸露的 PUB 将使许多人的使用变得更加容易。开玩笑的是,您可以将 openssl funktion 指向磁盘上的裸 pub 文件,但不能指向与字符串文字相同的内容!作为文字,您需要证书。非常感谢您指出这一点,为我节省了很多时间。 不,约翰所说的对我不起作用。我必须创建一个(自签名)证书才能读取公钥。【参考方案2】:

其他人都有一个 errno,它会通过成功的操作自动重置为零,而 OpenSSL 有一个“错误堆栈”,您需要手动清空它。请参阅函数openssl_error_string,即implemented,以ERR_get_error 表示。您看到的错误消息可能与您的代码无关;尝试在您的代码之前添加:

while ($msg = openssl_error_string()) ;

在每一行之间:

while ($msg = openssl_error_string())
    echo "OpenSSL error when doing foo:" . $msg . "<br />\n";

【讨论】:

【参考方案3】:

您可能会更轻松地使用 phpseclib 进行签名创建/验证:

http://phpseclib.sourceforge.net/documentation/misc_crypt.html#misc_crypt_rsa_examples

【讨论】:

这可能行得通,但这有点奇怪...代替广泛使用的本地和非常优化的库,我们必须使用基于慢字节码的库...... RSA 的缓慢部分是数学,并且由于 phpseclib 的 Crypt_RSA 使用 gmp,如果它可用,速度不是什么大问题。实际上,真正缓慢的部分是密钥生成。其他一切,即使正在使用 phpseclib 的本机 PHP BigInteger 实现,也非常快。如果您持怀疑态度,请尝试一下。 OpenSSL 的特点是它是为 C 开发人员设计的。 PHP 绑定低于标准,API 对 PHP 开发人员来说不是,恕我直言。 这将在最坏的情况下工作,但我宁愿使用 OpenSSL,它是事实上的标准,我在 PHP 中使用的消息也将由 C++ 部分使用签名/验证OpenSSL,因此额外的一致性将是一个加分项。【参考方案4】:

原因:

此错误通常是由 .crt 文件开头的一个损坏字符引起的。因此,您很可能在 SSL 证书文件 (.crt) 或 SSL 密钥文件 (.key) 中有额外的空格、额外的字符、额外的行等。

可能的解决方案:

    检查您的 .crt 文件。 字符问题可能出在您的密钥中,试试这个(没有换行符等):

.

define("SC_MSG_PUBLIC", "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALjPcOckMHDVLiUstcRwwx8kF5PzsiEsrskyndWisbXMLU9BHomXwL7Qg2L91jE+sNSobkzBDF41CbwDiNlofZUCAwEAAQ==");

【讨论】:

没有帮助,它无法解析 1 行中的键。只有在这 4 行中才会解析它。

以上是关于openssl_verify 和“错误:0906D06C:PEM 例程:PEM_read_bio:没有起始行”的主要内容,如果未能解决你的问题,请参考以下文章

我应该将密钥对象的哪一部分传递给“openssl_verify”以验证 Google 签名的 JWT?

为啥 openssl_verify() 无法验证我的 JWT 令牌签名?

openssl_verify():提供的密钥参数不能被强制转换为 .pem 文件的公钥

使用 php-jwt 库解码 firebase 自定义令牌时出现 openssl_verify() 错误

PEM 例程:PEM_read_bio:no start line - 无法解码 JWT 令牌

如何在 PHP 中验证 Paypal webhook 签名?