0

I generated a self-signed certificate for my REST api. At first, I specified the common name of the certificate as my external IP (a.b.c.d). When I called it with Python's requests library, specifying the verify flag as my own cert, the call succeeded, but I got the following warning:

SubjectAltNameWarning: Certificate for a.b.c.d has no `subjectAltName`, falling back to check for a `commonName` for now. This feature is being removed by major browsers and deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 for details.)

No problem, I added the subjectAltName with the second solution here: Invalid self signed SSL cert - "Subject Alternative Name Missing"

However, now when I call it with requests, I get the following:

Max retries exceeded with url: /todo1 (Caused by SSLError(CertificateError("hostname 'a.b.c.d' doesn't match 'a.b.c.d'")))
hostname 'a.b.c.d' doesn't match 'a.b.c.d'

Yes, it says that the exact same string does not match itself. I even copy pasted the strings to make sure there were no funny characters being used and checked for == in Python, it returned True.

The behavior is the exact same whether I call it on the local machine or an external machine.

I searched everywhere but didn't find anything even close to this problem. At this point I'm prepared to give up, go back to using the common name only, and disable the subjectAltName warning. (I know that I can disable the hostname check, but that seems less secure than ignoring a warning, and it doesn't seem like the optimal solution.) Please help me figure out why it's erroring. Thank you very much.

The exact code running on server:

from flask import Flask, request
from flask_restful import Resource, Api

app = Flask(__name__)
api = Api(app)

todos = {}

class TodoSimple(Resource):
    def get(self, todo_id):
        return {todo_id: todos[todo_id]}

    def put(self, todo_id):
        todos[todo_id] = request.form['data']
        return {todo_id: todos[todo_id]}

class TodoDelete(Resource):
    def get(self, todo_id):
        del todos[todo_id]

api.add_resource(TodoSimple, '/<string:todo_id>')
api.add_resource(TodoDelete, '/delete/<string:todo_id>')
if __name__ == '__main__':
    #app.run(debug=True, port=443)
    app.run(debug=False, host='0.0.0.0', port=443, ssl_context=('/root/cert.pem', '/root/key.pem'))

The requests call:

put('https://a.b.c.d:443/todo1', data={'data': 'Remember the milk'}, verify='/root/cert.pem').json()
user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    Use `openssl s_client -connect` to get a verbose log of the handshake. `requests` just uses OpenSSL (assuming a typical set of compile-time options); you can get the same diagnostics from any other openssl client, there's no reason to treat it as a Python-specific problem. – Charles Duffy Jun 28 '23 at 02:13
  • 1
    BTW, note that there are different kinds of SubjectAltName encodings. If `a.b.c.d` is an IP address, you need to encode it as such when you're making the certificate; you can't enter it the same way you would a hostname. (Obviously, we can't tell if you got this right from seeing your Python code; we'd need to see the process you used to create your certificate request). – Charles Duffy Jun 28 '23 at 02:15
  • 1
    Is your certificate available on a public address? We might be able to offer more help if we could inspect the certificate. – larsks Jun 28 '23 at 02:30
  • When I used `openssl s_client -connect a.b.c.d:443`, I did not see any error of this form. I only see the self-signed certificate error. I created the certificate with `openssl req -x509 -nodes -days 7300 -newkey rsa:4096 -keyout key.pem -out cert.pem -config req.cnf -sha256` and inside `req.cnf` I have `subjectAltName = @alt_names [alt_names] DNS.1=a.b.c.d`. – fpvbmct Jun 28 '23 at 23:38
  • 1
    Ah, I see what you mean now, I used `IP.1=a.b.c.d` and it worked. Thank you very much @CharlesDuffy. – fpvbmct Jun 28 '23 at 23:46

0 Answers0