【问题标题】:CertificateError: hostname doesn't match证书错误:主机名不匹配
【发布时间】:2015-04-30 08:51:51
【问题描述】:

我正在使用代理(在公司防火墙后面)登录到 https 域。 SSL 握手似乎不太顺利:

CertificateError: hostname 'ats.finra.org:443' doesn't match 'ats.finra.org' 

我使用的是 Python 2.7.9 - Mechanize,我已经通过了所有登录、密码、安全问题屏幕,但它正在挂起认证。

任何帮助都会很棒。我试过在这里找到的猴子扳手:Forcing Mechanize to use SSLv3

但不适用于我的代码。

如果您想要代码文件,我很乐意发送。

【问题讨论】:

  • 你的monkey-patch code 能帮到你吗,也许这可以帮助你*.com/questions/28282797/…
  • 看看你正在使用的代码可能会很有趣。可能很简单,您在只需要主机名的地方提供“host:port”,以便它使用错误的名称(即“host:port”而不是“host”)来验证主机名。我确定它与 SSLv3 无关。
  • 嗨@cmidi 感谢您的回复。我将在星期一尝试该代码。
  • 嗨,@SteffenUllrich,感谢您的回复。我将在星期一粘贴代码,请回圈,我很高兴收到有人对此的回复。

标签: python ssl mechanize mechanize-python


【解决方案1】:

您可以通过猴子修补 ssl 来避免此错误:

import ssl
ssl.match_hostname = lambda cert, hostname: True

【讨论】:

  • 在哪里添加代码“ssl.match_hostname = lambda cert, hostname: True”
【解决方案2】:

ssl.math_hostname 中的这个错误出现在 v2.7.9 中(它不在 2.7.5 中),并且与没有从 hostname:port 语法中删除主机名有关。以下对 ssl.match_hostname 的重写修复了该错误。把它放在你的机械化代码之前:

import functools, re, urlparse
import ssl

old_match_hostname = ssl.match_hostname

@functools.wraps(old_match_hostname)
def match_hostname_bugfix_ssl_py_2_7_9(cert, hostname):
    m = re.search(r':\d+$',hostname)  # hostname:port
    if m is not None:
        o = urlparse.urlparse('https://' + hostname)
        hostname = o.hostname
    old_match_hostname(cert, hostname)

ssl.match_hostname = match_hostname_bugfix_ssl_py_2_7_9

下面的机械化代码现在应该可以工作了:

import mechanize
import cookielib

br = mechanize.Browser()

# Cookie Jar
cj = cookielib.LWPCookieJar()
br.set_cookiejar(cj)

# Browser options
br.set_handle_equiv(True)
br.set_handle_gzip(True)
br.set_handle_redirect(True)
br.set_handle_referer(True)
br.set_handle_robots(False)

# Follows refresh 0 but not hang on refresh > 0
br.set_handle_refresh(mechanize._http.HTTPRefreshProcessor(), max_time=1)

br.addheaders = [('User-Agent', 'Nutscrape 1.0')]
# Use this proxy
br.set_proxies({"http": "localhost:3128", "https": "localhost:3128"})
r = br.open('https://www.duckduckgo.com:443/')
html = br.response().read()
# Examine the html response from a browser
f = open('foo.html','w')
f.write(html)
f.close()

【讨论】:

  • 错误是否在某处报告?请问可以给个链接吗?它在较新的版本中修复了吗?
【解决方案3】:

在我的例子中,证书的 DNS 名称是 ::1(用于本地测试目的)并且主机名验证失败

ssl.CertificateError: hostname '::1' doesn't match '::1'

为了稍微正确地修复它,我用猴子修补了ssl.match_hostname

import ssl                                                                                                                                                                                             
ssl.match_hostname = lambda cert, hostname: hostname == cert['subjectAltName'][0][1]

实际检查主机名是否匹配。

【讨论】: