PHP - Swiftmailer с использованием STARTTLS и самоподписанных сертификатов
Я пытаюсь отправить электронное письмо с php и swiftmailer, используя STARTTLS, но получаю ошибку сертификата. У меня есть root-доступ к SMTP-серверу, и используемый сертификат самоподписан.
Я использую Debian на обеих машинах (веб-сервер и smtp-сервер)
PHP message: PHP Warning: stream_socket_enable_crypto(): SSL operation failed with code 1. OpenSSL Error messages:
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed in [..]/lib/classes/Swift/Transport/StreamBuffer.php on line 97
PHP message: PHP Fatal error: Uncaught exception 'Swift_TransportException' with message 'Unable to connect with TLS encryption' in [..]/lib/classes/Swift/Transport/EsmtpTransport.php:294
Нужно ли мне добавить мой собственный сертификат, чтобы он был принят? Или это некоторая ошибка конфигурации OpenSSL?
Ответы
Ответ 1
Swiftmailer теперь обновлен, чтобы включить в него параметр. Теперь его можно решить с помощью метода setStreamOptions
из вашего экземпляра Swift_SmtpTransport
, а не для редактирования класса swift.
$transport = Swift_SmtpTransport::newInstance('smtp.server.com', 123, 'tls')
->setUsername('username')
->setPassword('password')
->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false)));
Ответ 2
У меня такая же проблема с использованием Swiftmailer в Laravel.
Похоже, в Swiftmailer нет возможности для этого. Чистым решением было бы добавить собственный корневой центр сертификации > на ваш сервер и подписать сертификат вашего почтового сервера с этим ЦС. После этого сертификат будет действителен. См. Например этот учебник.
В любом случае, быстрый грязный хак, который вы не должны использовать, - это отредактировать swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php
. В строке _establishSocketConnection()
строка 253 замените:
$options = array();
с чем-то вроде этого:
$options = array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false));
Это изменит параметры ssl stream_context_create() (несколько строк ниже $options
).
$this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno,
$errstr, $timeout, STREAM_CLIENT_CONNECT, stream_context_create($options));
Ответ 3
Вам не нужно редактировать файлы /vendor
. Вы можете указать (недокументированные) параметры в файле config/mail.php
:
'stream' => [
'ssl' => [
'allow_self_signed' => true,
'verify_peer' => false,
'verify_peer_name' => false,
],
],
Вы можете проверить его самостоятельно в vendor/laravel/framework/src/Illuminate/Mail/TransportManager.php
в строке ~ 50:
...
if (isset($config['stream'])) {
$transport->setStreamOptions($config['stream']);
}
...
Ответ 4
Мне пришлось добавить $transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false, 'verify_peer_name' => false)));
к файлу Mailer.php, смотрите:
/**
* Returns the SMTP transport
*
* @return \Swift_SmtpTransport
*/
protected function getSmtpInstance(): \Swift_SmtpTransport {
$transport = new \Swift_SmtpTransport();
$transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10));
$transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1'));
$transport->setPort($this->config->getSystemValue('mail_smtpport', 25));
if ($this->config->getSystemValue('mail_smtpauth', false)) {
$transport->setUsername($this->config->getSystemValue('mail_smtpname', ''));
$transport->setPassword($this->config->getSystemValue('mail_smtppassword', ''));
$transport->setAuthMode($this->config->getSystemValue('mail_smtpauthtype', 'LOGIN'));
}
$smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', '');
if (!empty($smtpSecurity)) {
$transport->setEncryption($smtpSecurity);
}
$streamingOptions = $this->config->getSystemValue('mail_smtpstreamoptions', []);
if (is_array($streamingOptions) && !empty($streamingOptions)) {
$transport->setStreamOptions($streamingOptions);
}
/* EDIT - allow self-signed mail cert */
$transport->setStreamOptions(array('ssl' => array('allow_self_signed' => true, 'verify_peer' => false, 'verify_peer_name' => false)));
/* EDIT end */
return $transport;
}
Я получил это по другой ссылке, не могу найти сейчас.
Одна мысль, которую я сделал дополнительно к другим ответам, состояла в том, чтобы указать 'verify_peer_name' => false