连接到公司Exchange Server时使用SSL:CERTIFICATE_VERIFY_FAILED
我尝试通过Python从公司交换服务器发送邮件.我的问题是,我收到一个SSL错误.我读了很多pem-,cer-,crt文件,但是我无法连接所有这些信息以取得成功.我敢肯定,如果可以在我的小示例脚本中解决此问题,那么也可以修复交换脚本.
I try to send mails from out company exchange server via Python. My problem is, that I get a SSL error. I read a lot of pem-, cer-, crt-files, but I can not connect all these information to have success. I am sure, that if one can fix the issue in my little example script, one could fix the exchange script, too.
我了解了很多有关证书和ca_bundles的信息,但是我不知道如何将所有这些都应用于我的问题.
I read a lot about certificates and ca_bundles, but I don't know how to apply all of that to my issue.
示例脚本:
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')],)",),))
Exchange脚本:
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
我可以使用它.我卸载了pyopenssl,我的 Example-Script 的错误更改为:
I got it to work. I uninstalled pyopenssl and the error of my Example-Script changed to this:
requests.exceptions.SSLError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed (_ssl.c:777)
然后,我找到了一些有用的命令来进入pem-file主题.基本上,我只需要添加证书即可解决我的问题.大多数人说,我必须将证书添加到cacert.pem之类的pem文件中,但是可能已经安装了几个使用不同文件的模块.我发现此主题对于查找cacert.pem文件的位置非常有用: LINK_1 e. g.:
Then I found some useful commands to get into the pem-file topic. Basically, I just had to add the certificate in order to solve my problem. Most people said that I have to add my certificate to a pem-file like cacert.pem, but it is possible that you have several modules installed that use different files. I found this topic very useful to find out the location of my cacert.pem file: LINK_1 e. g.:
python -c "import requests; print requests.certs.where()"
接下来,我遇到了问题,我没有要添加到pem文件中的证书.我的浏览器以某种方式能够发送https请求,因此浏览器能够使用Windows中的证书.此链接提出了解决方案.
Next I had the problem, that I didn't have a certificate to add to the pem-file. Somehow my browser was able to send a https request, so the browser was able to use a certificate from windows. This link brought the solution.
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导出了证书,并将文件添加到了我的请求脚本中:
I exported the certificate from windows and added the file to my request-script:
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.这样我就可以通过交换服务器向自己发送电子邮件.
Back to my exchange script I added these lines to the beginning and added my certificate. I simply renamed the file from .pem to .crt. I was then able to send an email via the exchange server to myself.
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