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