python - 相互 TLS 身份验证 - SSLV3_ALERT_UNSUPPORTED_CERTIFICATE

标签 python ssl flask openssl tls1.2

我目前正在尝试在客户端和服务器之间实现双向 TLS 身份验证。我遇到了一个 SSL 错误,它的描述性不强。 StackOverflow 也没有太多与之相关的问题,因为大多数时候它是互联网上的单向 TLS。 但是,据我所见,发生此错误是因为客户端证书有问题,因此我在下面附上了与​​此相关的信息。如果不是这种情况,请告诉我。

生成客户端证书

我客户的 cnf(foo.config) 看起来像(替换了所有敏感信息):

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

[ dn ]
O=Bar
OU=User
CN=myuser@foo.com

[ req_ext ]
subjectAltName = email:myuser@foo.com
nsCertType = client, email, objsign
keyUsage = nonRepudiation, digitalSignature, keyEncipherment

我使用 openssl 传入此配置文件以创建一个 csr:

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

然后我将其发送到内部基础架构管理 API 以签署 x509 证书。我将客户端证书保存为 foo.cert(它包含到 CA 的信任链)。

我的客户如何发送证书

当我向我的服务器发送一个 urllib 打开请求时,我发送带有我的 HTTPSConnection 的 ssl 上下文:

... # 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)

我的 flask 服务器是如何设置的

当我的测试 flask 服务器像这样接受 ssl 上下文时:

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)

它设置自己的证书和 key 文件。它还设置客户端身份验证请求。

完整的错误回溯

在运行时,在调用过程中,我得到这个回溯(替换所有敏感信息):

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

如果需要更多信息,请随时告诉我。任何帮助将不胜感激。

编辑: 这是证书(经过编辑的形式):

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, CN=myuser@foo.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                ...
        X509v3 extensions:
            X509v3 Key Usage:
                Key Encipherment, Data Encipherment
            X509v3 Subject Alternative Name:
                email:myuser@foo.com
            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

用我的证书包验证这个文件说:

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

我正在使用 OpenSSL v.1.0.1e-fips。这应该支持 TLSv1.2。

最佳答案

       X509v3 Key Usage:
            Key Encipherment, Data Encipherment

客户端证书由客户端签署一些质询和服务器验证签名来验证。为了签署此质询,证书必须使用数字签名 key 。您的证书没有此信息,因此无法用于客户端身份验证。

另见 Recommended key usage for a client certificate .

有趣的是,您的 CSR 可能包含适当的 key 用法,至少您已尝试添加它:

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

因此,CA 可能以错误的方式颁发了证书,即与您请求的方式不同。

关于python - 相互 TLS 身份验证 - SSLV3_ALERT_UNSUPPORTED_CERTIFICATE,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50652680/

相关文章:

python - 如何在第二级下删除多索引数据框中的第一列排除某些列

http - 已将 ssl 证书复制到测试站点,如何删除它?

python - 如何将 Superset Apache 嵌入到 Flask Web 应用程序中?

python - 如何在处理完 30 个元素后以间隔(5 分钟)处理一个大列表?

Python 单元测试类型错误

python - 如何使用 python 通过另一个脚本运行一个文件中的列表

java - 如何使用 Java 在 https 连接请求中附加 .pfx 证书?

python-3.x - Python SSL - 当数据大于 1400 时 recv 被偏移

python - 同时进行 alembic 数据库迁移

python - 当我触摸 wsgi 脚本时,带有 mod_wsgi 的 Flask 应用程序不会重新加载