3
votes

I currently am trying to implement mutual TLS authentication between a client and a server. I'm running into an SSL error and it is not very descriptive. StackOverflow also does not have many questions related to it since most of the time it is a one-way TLS on the internet. However, from what I've seen, this error occurs because something is wrong with the client certificate, so below I have attached information relevant to that. If that is not the case, please let me know.

Generating Client Cert

My client's cnf(foo.config) looks like(with replacing all the sensitive information):

[ req ]
default_bits = 2048
default_md = sha256
prompt = no
req_extensions = req_ext
distinguished_name = dn
encrypt_key = no

[ dn ]
O=Bar
OU=User
[email protected]

[ req_ext ]
subjectAltName = email:[email protected]
nsCertType = client, email, objsign
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

I pass in this config file using openssl to create a csr:

openssl req -new -sha256 -key foo.key -out foo.csr -config foo.config

And then I send it to an infrastructure management API internally to sign the x509 certificate. I save the client certificate as foo.cert(it contains the trust chain up to the CA).

How my client sends the cert

When I send a urllib open request to my server, I send the ssl context with my HTTPSConnection:

... # Inside some handler code
context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH,
            cafile=ROOT_CA)
context.load_cert_chain(certfile=CERT_DEST, keyfile=KEY_DEST)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = True
return http.client.HTTPSConnection(host, context=context)

How my flask server sets up

When my testing flask server takes in the ssl context like so:

executor = Flask(__name__)
context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH, cafile=TEST_CA_CERT)
context.load_cert_chain(certfile=TEST_CERT, keyfile=TEST_KEY)
context.verify_mode = ssl.CERT_REQUIRED
context.check_hostname = False
executor.run(host=host, port=port, ssl_context=context)

It sets up its own certificates an key files. It also sets up client authentication requests.

Full error traceback

Upon runtime, during a call, I get this traceback(replacing all sensitive information):

Traceback (most recent call last):
  File "/auto/foo/python3-rhel6/lib/python3.6/urllib/request.py", line 1318, in do_open
    encode_chunked=req.has_header('Transfer-encoding'))
  File "/auto/foo/python3-rhel6/lib/python3.6/http/client.py", line 1239, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "/auto/foo/python3-rhel6/lib/python3.6/http/client.py", line 1285, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "/auto/foo/python3-rhel6/lib/python3.6/http/client.py", line 1234, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "/auto/foo/python3-rhel6/lib/python3.6/http/client.py", line 1026, in _send_output
    self.send(msg)
  File "/auto/foo/python3-rhel6/lib/python3.6/http/client.py", line 964, in send
    self.connect()
  File "/auto/foo/python3-rhel6/lib/python3.6/http/client.py", line 1400, in connect
    server_hostname=server_hostname)
  File "/auto/foo/python3-rhel6/lib/python3.6/ssl.py", line 407, in wrap_socket
    _context=self, _session=session)
  File "/auto/foo/python3-rhel6/lib/python3.6/ssl.py", line 814, in __init__
    self.do_handshake()
  File "/auto/foo/python3-rhel6/lib/python3.6/ssl.py", line 1068, in do_handshake
    self._sslobj.do_handshake()
  File "/auto/foo/python3-rhel6/lib/python3.6/ssl.py", line 689, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: SSLV3_ALERT_UNSUPPORTED_CERTIFICATE] sslv3 alert unsupported certificate (_ssl.c:833)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./bar.py", line 375, in <module>
    start(args, overrides, threadlock)
  File "./bar.py", line 332, in start
    approved, alloc_message = Executor.approve(flow.the_portal.inst)
  File "/home/myuser/dev/bar/Executor.py", line 638, in approve
    with opener.open(request, json_request) as response:
  File "/auto/foo/python3-rhel6/lib/python3.6/urllib/request.py", line 526, in open
    response = self._open(req, data)
  File "/auto/foo/python3-rhel6/lib/python3.6/urllib/request.py", line 544, in _open
    '_open', req)
  File "/auto/foo/python3-rhel6/lib/python3.6/urllib/request.py", line 504, in _call_chain
    result = func(*args)
  File "/home/myuser/dev/bar/Auth.py", line 77, in https_open
    return self.do_open(self.getConnection, req)
  File "/auto/foo/python3-rhel6/lib/python3.6/urllib/request.py", line 1320, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [SSL: SSLV3_ALERT_UNSUPPORTED_CERTIFICATE] sslv3 alert unsupported certificate (_ssl.c:833)>

If more information is required, please don't hesitate to let me know. Any help would be appreciated.

EDIT: Here's the certificate (in redacted form):

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ...
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: DC=com, DC=foo, CN=Foo Issuing CA - XXX
        Validity
            Not Before: ... 2018 GMT
            Not After : ... GMT
        Subject: O=Bar, OU=User, [email protected]
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                ...
        X509v3 extensions:
            X509v3 Key Usage:
                Key Encipherment, Data Encipherment
            X509v3 Subject Alternative Name:
                email:[email protected]
            X509v3 Subject Key Identifier:
                ...
            X509v3 Authority Key Identifier:
                ...

            X509v3 CRL Distribution Points:

                Full Name:
                    ...
            Authority Information Access:
                CA Issuers - ...
                CA Issuers - ...

            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication

Verifying this file with my cert bundle says:

openssl verify -CAfile /.../certification_bundle.pem foo.cert
foo.cert: OK

I am using OpenSSL v.1.0.1e-fips. This should support TLSv1.2.

2
The CSR is less relevant than the actual certificate since the CA does not need to make the certificate look like you've requested it. Could you include your actual certificate in your question (i.e. output from openssl x509 -text -in foo.cert, maybe redacting some details)?Steffen Ullrich
@SteffenUllrich I will update the post later with the certificate details(with some redacted). Standby.OneRaynyDay
@SteffenUllrich I have included a redacted certificate.OneRaynyDay

2 Answers

4
votes
       X509v3 Key Usage:
            Key Encipherment, Data Encipherment

A client certificate is verified by the client signing some challenge and the server validating the signature. In order to sign this challenge the certificate must have a key usage of Digital Signature. Your certificate does not have this which makes it unusable for client authentication.

See also Recommended key usage for a client certificate.

Interestingly, your CSR probably contained the appropriate key usage, at least you've tried to add it:

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

Thus the CA might have been issued the certificate in the wrong way, i.e. different from what you requested.

0
votes

Sorry, I can't make a comment, but it sounds like your certificate signing request is missing an extended key usage (EKU) extension. You may need to add:

extendedKeyUsage = clientAuth

to you req_ext section.

Then regenerate the CSR and verify with openssl req -in foo.csr -text that the signing request contains the EKU extension:

...
        X509v3 Extended Key Usage:
            TLS Web Client Authentication
...