【问题标题】:Mutual TLS Authentication - SSLV3_ALERT_UNSUPPORTED_CERTIFICATE双向 TLS 身份验证 - SSLV3_ALERT_UNSUPPORTED_CERTIFICATE
【发布时间】:2018-11-12 03:24:36
【问题描述】:

我目前正在尝试在客户端和服务器之间实现双向 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)

我的烧瓶服务器如何设置

当我的测试烧瓶服务器像这样接收 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)

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

完整的错误回溯

在运行时,在通话期间,我得到这个回溯(替换所有敏感信息):

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。

【问题讨论】:

  • CSR 与实际证书的相关性较低,因为 CA 不需要使证书看起来像您请求的那样。您能否在问题中包含您的实际证书(即来自openssl x509 -text -in foo.cert 的输出,可能会编辑一些细节)?
  • @SteffenUllrich 稍后我将使用证书详细信息更新帖子(部分已编辑)。待机。
  • @SteffenUllrich 我已经附上了经过编辑的证书。

标签: python ssl flask openssl tls1.2


【解决方案1】:
       X509v3 Key Usage:
            Key Encipherment, Data Encipherment

客户端证书由客户端签署一些质询和服务器验证签名来验证。为了签署此质询,证书必须具有 数字签名 的密钥用法。您的证书没有这使得它无法用于客户端身份验证。

另见Recommended key usage for a client certificate

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

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

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

【讨论】:

  • 嗨,有什么办法可以禁用digitalSignature这个字段的使用?我获得了没有此字段的客户证书。如果没有,那很好。感谢您的解释,我几乎 100% 确定这是问题所在,但我无法获得具有正确密钥用法的证书来重现解决方案。
  • 如何验证客户端证书在 OpenSSL 的代码中很深。我不认为你可以从 Python 中改变这种行为,无论如何这也不是一个好主意。此外,您似乎已正确请求证书,但 CA 并未为您提供所需的密钥用法(请参阅编辑后的答案)。
  • 是的,我联系了 CA,他们说我无法使用正确的字段制作客户端证书。因此,CA 不尊重我的扩展字段。不幸的是,这意味着我们可能无法使用此 CA 来制作客户端证书。
【解决方案2】:

抱歉,我无法发表评论,但听起来您的证书签名请求缺少扩展密钥使用 (EKU) 扩展。您可能需要添加:

extendedKeyUsage = clientAuth

给你req_ext 部分。

然后重新生成 CSR 并使用 openssl req -in foo.csr -text 验证签名请求是否包含 EKU 扩展:

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

【讨论】:

  • 我的证书中已经有“TLS Web 客户端身份验证”字段。 (我刚刚加了)
猜你喜欢
  • 1970-01-01
  • 2020-04-07
  • 2020-09-09
  • 1970-01-01
  • 1970-01-01
  • 2020-09-05
  • 2021-09-11
  • 2018-07-02
  • 2016-07-09
相关资源
最近更新 更多