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() 错误