SSL: CERTIFICATE_VERIFY_FAILED when connecting to a company Exchange Server
Asked Answered
B

1

3

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.

I read a lot about certificates and ca_bundles, but I don't know how to apply all of that to my issue.

Example-Script:

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

Error:

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-Script:

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="[email protected]", 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 = '[email protected]'

send(email_receiver=receiver)

print('Finish')

Error:

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
Barite answered 10/7, 2018 at 10:10 Comment(0)
B
4

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)

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()"

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')

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)

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
Barite answered 11/7, 2018 at 9:1 Comment(4)
what are these parameter:super(RootCAAdapter, self).cert_verify(conn=conn, url=url, verify=cert_file, cert=cert) i.e conn, url,cert what should we add there?I understood the verify paraeterEpinephrine
AFAIK you don't have to add anything there. The class is there to overwrite the original function that is used by the requests package. In order to use your extracted certificate in cert_verify.Barite
okay, i have one more doubt. I understood the path_pem. but what is this **'mail.internal': '/path/to/mail.internal.crt' ** ,and from where are we getting this certificate.Epinephrine
If you follow the link in "This link brought the solution", you get the idea of how to extract the cert from windows. The code below that extracts the cert and saves it as a perm file. Next code snippet is to test connection to the exchange server with extracted perm file. Below that, I wrote that I renamed the cert from *.perm to *.crt and added the path to that file in path/to/mail.internal.crt. It's a placeholder for your file path. I will edit my original post when I repaired my PC to make it more clear what to do..Barite

© 2022 - 2024 — McMap. All rights reserved.