【问题标题】:SSL: CERTIFICATE_VERIFY_FAILED when connecting to a company Exchange ServerSSL:连接到公司 Exchange Server 时的 CERTIFICATE_VERIFY_FAILED
【发布时间】:2018-12-18 03:25:05
【问题描述】:

我尝试通过 Python 从外部公司交换服务器发送邮件。我的问题是,我收到 SSL 错误。我阅读了很多 pem-、cer-、crt-files,但我无法连接所有这些信息以获得成功。我敢肯定,如果有人可以解决我的小示例脚本中的问题,那么也可以解决交换脚本。

我阅读了很多关于证书和 ca_bundles 的内容,但我不知道如何将所有这些应用到我的问题中。

示例脚本

import requests
requests.get('https://mail.ourserver.loc')

错误

C:\Python\python.exe "PATHtoMYproject/testing.py"
Traceback (most recent call last):
  File "C:\Python\lib\site-packages\urllib3\contrib\pyopenssl.py", line 441, in wrap_socket
    cnx.do_handshake()
  File "C:\Python\lib\site-packages\OpenSSL\SSL.py", line 1806, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "C:\Python\lib\site-packages\OpenSSL\SSL.py", line 1546, in _raise_ssl_error
    _raise_current_error()
  File "C:\Python\lib\site-packages\OpenSSL\_util.py", line 54, in exception_from_error_queue
    raise exception_type(errors)
OpenSSL.SSL.Error: [('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  [.. a lot of text..]
ssl.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  [.. a lot of text..]
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='mail.ourserver.loc', port=443): Max retries exceeded with url: / (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

交换脚本

from exchangelib import DELEGATE, Account, Credentials, Configuration, Message

import os

import urllib3
urllib3.disable_warnings()

creds = Credentials(username='EX\\GE-USR', password='secret')
config = Configuration(server='mail.ourserver.loc', credentials=creds)
account = Account(primary_smtp_address="user-name@ex-ample.com", autodiscover=False, config=config,
                  access_type=DELEGATE)

def send(email_receiver):
    m = Message(
        account=account,
        subject='This is a test!',
        body='Hallo',
        to_recipients=[email_receiver])
    m.send_and_save()

receiver = 'user-name@ex-ample.com'

send(email_receiver=receiver)

print('Finish')

错误

C:\Python\python.exe "PATHtoMYproject//exchange_main.py"
Traceback (most recent call last):
  File "C:\Python\lib\site-packages\urllib3\contrib\pyopenssl.py", line 441, in wrap_socket
    cnx.do_handshake()
  File "C:\Python\lib\site-packages\OpenSSL\SSL.py", line 1806, in do_handshake
    self._raise_ssl_error(self._ssl, result)
  File "C:\Python\lib\site-packages\OpenSSL\SSL.py", line 1546, in _raise_ssl_error
    _raise_current_error()
  File "C:\Python\lib\site-packages\OpenSSL\_util.py", line 54, in exception_from_error_queue
    raise exception_type(errors)
OpenSSL.SSL.Error: [('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
[.. a lot of text..]
ssl.SSLError: ("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Python\lib\site-packages\requests\adapters.py", line 440, in send
    timeout=timeout
  File "C:\Python\lib\site-packages\urllib3\connectionpool.py", line 639, in urlopen
    _stacktrace=sys.exc_info()[2])
  File "C:\Python\lib\site-packages\urllib3\util\retry.py", line 388, in increment
    raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='mail.ourserver.loc', port=443): Max retries exceeded with url: /EWS/Exchange.asmx (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
[.. a lot of text..]
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='mail.ourserver.loc', port=443): Max retries exceeded with url: /EWS/Exchange.asmx (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

Process finished with exit code 1url: /EWS/Exchange.asmx (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  [.. a lot of text..]
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='mail.ourserver.loc', port=443): Max retries exceeded with url: /EWS/Exchange.asmx (Caused by SSLError(SSLError("bad handshake: Error([('SSL routines', 'tls_process_server_certificate', 'certificate verify failed')],)",),))

Process finished with exit code 1

【问题讨论】:

    标签: python openssl exchangewebservices tls1.2 exchangelib


    【解决方案1】:

    我让它工作了。我卸载了 pyopenssl,我的 Example-Script 的错误变为:

    requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)
    

    然后我发现了一些有用的命令来进入 pem-file 主题。基本上,我只需要添加证书即可解决我的问题。大多数人说我必须将我的证书添加到像 cacert.pem 这样的 pem 文件中,但是您可能安装了多个使用不同文件的模块。我发现这个主题对于找出我的 cacert.pem 文件的位置非常有用:LINK_1 e. g.:

    python -c "import requests; print requests.certs.where()"

    接下来我遇到了问题,我没有要添加到 pem 文件的证书。不知何故,我的浏览器能够发送 https 请求,因此浏览器能够使用来自 Windows 的证书。这个link带来了解决方案。

    import ssl
    
    context = ssl.create_default_context()
    der_certs = context.get_ca_certs(binary_form=True)
    pem_certs = [ssl.DER_cert_to_PEM_cert(der) for der in der_certs]
    
    with open('wincacerts.pem', 'w') as outfile:
        for pem in pem_certs:
            outfile.write(pem + '\n')
    

    我从 Windows 导出证书并将文件添加到我的请求脚本中:

    import os
    import requests
    
    root_path = os.getcwd()
    path_pem=os.path.join(root_path, 'wincacerts.pem')
    requests.get('https://mail.ourserver.loc', verify=path_pem)
    

    回到我的交换脚本,我将这些行添加到开头并添加了我的证书。我只是将文件从 .pem 重命名为 .crt。然后我可以通过交换服务器向自己发送电子邮件。

    root_path = os.getcwd()
    path_pem=os.path.join(root_path, 'files', 'wincacerts.crt')
    
    class RootCAAdapter(requests.adapters.HTTPAdapter):
        # An HTTP adapter that uses a custom root CA certificate at a hard coded location
        def cert_verify(self, conn, url, verify, cert):
            cert_file = {
                'mail.ourserver.loc': path_pem,
                'mail.internal': '/path/to/mail.internal.crt'
                }[urlparse(url).hostname]
            super(RootCAAdapter, self).cert_verify(conn=conn, url=url, verify=cert_file, cert=cert)
    
    # Tell exchangelib to use this adapter class instead of the default
    BaseProtocol.HTTP_ADAPTER_CLS = RootCAAdapter
    

    【讨论】:

    • 这些参数是什么:super(RootCAAdapter, self).cert_verify(conn=conn, url=url, verify=cert_file, cert=cert) 即 conn, url,cert 我们应该在那里添加什么?我理解了验证参数
    • AFAIK 你不必在那里添加任何东西。该类用于覆盖请求包使用的原始函数。为了在 cert_verify 中使用您提取的证书。
    • 好吧,我还有一个疑问。我了解path_pem。但是这是什么 **'mail.internal': '/path/to/mail.internal.crt' ** ,我们从哪里得到这个证书。
    • 如果您点击“此链接带来了解决方案”中的链接,您就会了解如何从 Windows 中提取证书。下面的代码提取证书并将其保存为 perm 文件。下一个代码 sn-p 是使用提取的 perm 文件测试与交换服务器的连接。在此之下,我写道我将证书从 *.perm 重命名为 *.crt,并将该文件的路径添加到 path/to/mail.internal.crt 中。它是您的文件路径的占位符。当我修理我的电脑时,我会编辑我原来的帖子,以便更清楚地知道该怎么做..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-03-22
    • 1970-01-01
    • 2015-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-07
    相关资源
    最近更新 更多