【发布时间】:2020-05-09 11:48:54
【问题描述】:
完整标题应为“PHP - 发送电子邮件:fwrite(): SSL 操作失败,代码为 1。OpenSSL 错误消息:错误:1420C0CF:SSL 例程:ssl_write_internal:protocol is shutdown - 分析 stream_socket_enable_crypto 返回 false”。但这对于标题的 150 个字符来说太长了。
我们正在尝试在 PHP 脚本中发送电子邮件,但我们收到了
Warning: fwrite(): SSL operation failed with code 1. OpenSSL Error messages:error:1420C0CF:SSL routines:ssl_write_internal:protocol is shutdown in <...>/vendor/recolize/zendframework1/library/Zend/Mail/Protocol/Abstract.php on line 324
如上所示,我们正在使用早已过时的 zendframework1 的改编版本,但这不是当前的问题,因为这适用于其他两个主机。
由于问题是不确定的,我们现在无法重现它。
但是,我们有一个允许进行一些分析的测试脚本,但我需要帮助了解可能是什么问题。
事不宜迟:
我们正在使用来自这个 stackoverflow 答案的脚本:How do I verify a TLS SMTP certificate is valid in PHP? - Answer 1
重要的是:
// Establish the connection
$smtp = fsockopen( "tcp://$server", 25, $errno, $errstr );
fread( $smtp, 512 );
// Here you can check the usual banner from $server (or in general,
// check whether it contains $server's domain name, or whether the
// domain it advertises has $server among its MX's.
// But yet again, Google fails both these tests.
fwrite($smtp,"HELO $myself\r\n");
fread($smtp, 512);
// Switch to TLS
fwrite($smtp,"STARTTLS\r\n");
fread($smtp, 512);
stream_set_blocking($smtp, true);
stream_context_set_option($smtp, 'ssl', 'verify_peer', true);
stream_context_set_option($smtp, 'ssl', 'allow_self_signed', true);
stream_context_set_option($smtp, 'ssl', 'capture_peer_cert', true);
stream_context_set_option($smtp, 'ssl', 'cafile', $cabundle);
// Necessary block to circumvent https://www.php.net/manual/en/function.stream-socket-enable-crypto.php#119122
$crypto_method = STREAM_CRYPTO_METHOD_TLS_CLIENT;
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
$crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
$secure = stream_socket_enable_crypto($smtp, true, $crypto_method);
// $secure = stream_socket_enable_crypto($smtp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT );
var_dump($secure);
die;
已建立一个套接字,设置为阻塞,设置 SSL 选项并尝试使用 PHP 的 stream_socket_enable_crypto 开始加密。
stream_socket_enable_crypto 然而返回 bool(false) 并且没有任何失败原因的信息。
目前我们怀疑邮件服务器的证书验证失败,但无法分析。
我们如何获得更多关于为什么 stream_socket_enable_crypto 返回 false 的信息?
edit1:
修改了上面的代码块以反映STREAM_CRYPTO_METHOD_TLS_CLIENT's specialty since PHP 5.6.7,如下面的@Dilek 所述。谢谢你的回答! 遗憾的是,这并没有改变整体结果。 $secure 保持 bool(false)。
edit2:
回应@Dilek 的更新:
$ php ssl_test.php
Array
(
[ssl] => Array
(
[verify_host] => 1
[verify_peer] => 1
[allow_self_signed] =>
[cafile] => /etc/ssl/cert.pem
)
)
failed to connect securely
我确定cafile存在:
$ ll /etc/ssl/cert.pem
0 lrwxrwxrwx 1 root wheel 38 Dec 19 11:09 /etc/ssl/cert.pem -> /usr/local/share/certs/ca-root.crt
并快速检查了它看起来不错的内容:
Certificate:
Data:
Version: ...
Serial Number:
...
Signature Algorithm: ...
Issuer: ...
...
Signature Algorithm: ...
-----BEGIN CERTIFICATE-----
MI...6EULg==
-----END CERTIFICATE-----
Certificate:
Data:
Version: ...
Serial Number:
...
etc.
【问题讨论】:
-
只是出于好奇:以简单的方式发送邮件已经非常困难(这就是为什么有多个库可以让您免于麻烦)。现在,您首先通过简单的 SMTP 增加了另一层困难 - 这是什么原因?
-
我正试图找出问题的根源,或者至少找到一些关于发生错误的提示。我在上面提供的 stackoverflow 链接中找到了该脚本,目前最好的办法是获取其他信息。因此,由于缺乏替代想法,我真的只是想谈论普通的 SMTP。产生上述错误的原始电子邮件问题(警告:fwrite()...)来自框架(zendframework1),但我们无法确定地重现它。当前的脚本确实是我们现在唯一的想法。我很乐意尝试任何替代方法来解决这个问题。
-
要提供替代想法,需要了解该脚本的需求。你想达到什么目标?使用 PHPMailer、Swiftmailer 甚至使用普通的
mail函数,发送邮件更加容易。 -
我将使用 PHPMailer 生成一个脚本并更新我的答案。
-
我们终于找到了答案。我贴出来了。