当我使用QtWebkit(内部使用QSslSocket)连接到www.hotmail.com时,登录后出现以下ssl错误:
登录前我可以在mail.yahoo.com上收到同样的错误。 我发现与这两个服务器的安全连接会触发ssl错误:
QT附带了一个名为securesocketclient的小型ssl演示程序。如果我使用这个小客户端连接到上述两个服务器的端口443,我可能会产生相同的错误。我99%肯定这是QT的一个错误,因为我们在许多开发系统上产生了这个问题。它会影响linux,mac和windows QT。它影响QT 4.7.4一直到QT 4.8.4(没有尝试早期版本)。使用openssl 0.9.8复制该错误。
有些人可能错误地声称使用带有预编译QT二进制文件的openssl 1.0.0修复了该错误。自从使用openssl 0.9.8头文件编译预编译的QT二进制文件以来,这是错误的。 Openssl 0.9.8与openssl 1.0.0不是二进制兼容的。如果使用一个版本的openssl编译QT但使用另一个版本的openssl二进制文件,一些结构成员将被误解。我做了很多步骤和跟踪到QT和openssl源代码来得出这个结论。
由于Hotmail和yahoo电子邮件是非常受欢迎的网站,我认为这个错误在QT的安全性方面是一个严重的错误。我已经将它报告给QT bug系统(https://bugreports.qt.io/browse/QTBUG-23625),我怀疑digia是否会根据我过去的经验得到它。
请随时分享您对如何解决此错误的看法。我知道如何调用ignoreSslErrors并假装没有问题。但这并不是打算如何使用openssl。
有关此问题的进一步调查的一些更新。
https://gfx8.hotmail.com的证书路径是:
根CA确实位于Windows证书存储区中,并在启动时正确加载。我想也许某种程度上QT没有获得中间证书。我使用openssl命令行程序运行测试,如下所示:
openssl s_client -showcerts -connect gfx8.hotmail.com:443
输出在这里:
已连接(000001AC)深度= 0 1.3.6.1.4.1.311.60.2.1.3 =美国, 1.3.6.1.4.1.311.60.2.1.2 = Washington,bu sinessCategory =私人组织,serialNumber = 600413485,C = US,postalC ode = 98052, ST = Washington,L = Redmond,street = 1 Microsoft Way,O = Microso ft Corporation,OU = Windows Azure CDN,CN = gfx-ecn.hotmail.com
验证错误:num = 20:无法获得本地颁发者证书验证 返回:1
深度= 0 1.3.6.1.4.1.311.60.2.1.3 =美国,1.3.6.1.4.1.311.60.2.1.2 = 华盛顿,bu sinessCategory =私人组织,serialNumber = 600413485,C = US,postalC ode = 98052,ST = Washington,L = Redmond, street = 1 Microsoft Way,O = Microso ft Corporation,OU = Windows Azure CDN,CN = gfx-ecn.hotmail.com
验证错误:num = 27:证书不受信任验证返回:1
深度= 0 1.3.6.1.4.1.311.60.2.1.3 =美国,1.3.6.1.4.1.311.60.2.1.2 = 华盛顿,bu sinessCategory =私人组织,serialNumber = 600413485,C = US,postalC ode = 98052,ST = Washington,L = Redmond, street = 1 Microsoft Way,O = Microso ft Corporation,OU = Windows Azure CDN,CN = gfx-ecn.hotmail.com
验证错误:num = 21:无法验证第一个证书验证 返回:1
证书链0 S:/1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Washington/businessCategory=Private 组织/ SERIALNUMBER = 600413485 / C = US / POSTALCODE = 98052 / ST =华盛顿/ L =雷蒙德/街道= 1 Microsoft Way / O = Microsoft Corporation / OU = Windows Azure CDN / CN = gfx-ecn.hotmail.com i:/ C = US / O = VeriSign,Inc. / OU = VeriSign 信任网络/ OU = https://www.verisign.com/rpa的使用条款 (c)06 / CN = VeriSign Class 3扩展验证SSL SGC CA
----- BEGIN CERTIFICATE -----
MIIG0DCCBbigAwIBAgIQfRbsuTLd2GrmU38TPnVOCTANBgkqhkiG9w0BAQUFADCB vjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykwNjE4MDYGA1UEAxMv VmVyaVNpZ24gQ2xhc3MgMyBFeHRlbmRlZCBWYWxpZGF0aW9uIFNTTCBTR0MgQ0Ew HhcNMTIxMTA3MDAwMDAwWhcNMTQxMTA4MjM1OTU5WjCCAR0xEzARBgsrBgEEAYI3 PAIBAxMCVVMxGzAZBgsrBgEEAYI3PAIBAhMKV2FzaGluZ3RvbjEdMBsGA1UEDxMU UHJpdmF0ZSBPcmdhbml6YXRpb24xEjAQBgNVBAUTCTYwMDQxMzQ4NTELMAkGA1UE BhMCVVMxDjAMBgNVBBEUBTk4MDUyMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD VQQHFAdSZWRtb25kMRgwFgYDVQQJFA8xIE1pY3Jvc29mdCBXYXkxHjAcBgNVBAoU FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEaMBgGA1UECxQRV2luZG93cyBBenVyZSBD RE4xHDAaBgNVBAMUE2dmeC1lY24uaG90bWFpbC5jb20wggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQC + jRmBRv2iw1N2LirFdhgqmZ3G + BBc8gAn50O6TT1u zNqrjicf3KJ + BDHSGcnkysvWovwnUhDhMzAWf521iYi2lFZqC3txewGvjrKM0Gqz DhHrF + bzvNjyrIION89354cFxU1eK2okegYHkWIuyPHyCN6PFGK52OlkBixb34xv WvAZfjSu /小时+ F + lkedFZvJdd6KS4e8N / TGJ / dndfmReaKSiNFWBFbwhkndLdXU3p ZnVLCysETMuUoIsDIPcgDfji1XkoKLsri9WijVhjNH1MFf / T6 / g4PpWqZGl4si3t yQ0rdefDGfgX8lvq63aXnaap4SbjTYLviFRle / PMkXV7AgMBAAGjggJmMIICYjCB 0AYDVR0RBIHIMIHFghBnZngxLmhvdG1haWwuY29tghBnZngyLmhvdG1haWwuY29t ghBnZngzLmhvdG1haWwuY29tghBnZng0LmhvdG1haWwuY29tghBnZng1LmhvdG1h aWwuY29tghBnZng2LmhvdG1haWwuY29tghBnZng3LmhvdG1haWwuY29tghBnZng4 LmhvdG1haWwuY29tghRncmFwaGljcy5ob3RtYWlsLmNvbYIIYS5nZngubXOCE2dm eC1lY24uaG90bWFpbC5jb20wCQYDVR0TBAIwADAdBgNVHQ4EFgQUH0b2ApITW9WB / LA + OAZ + 2ZnW / dwwDgYDVR0PAQH / BAQDAgWgMD4GA1UdHwQ3MDUwM6AxoC + GLWh0 dHA6Ly9FVkludGwtY3JsLnZlcmlzaWduLmNvbS9FVkludGwyMDA2LmNybDBEBgNV HSAEPTA7MDkGC2CGSAGG + EUBBxcGMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3 LnZlcmlzaWduLmNvbS9jcHMwNAYDVR0lBC0wKwYIKwYBBQUHAwEGCCsGAQUFBwMC BglghkgBhvhCBAEGCisGAQQBgjcKAwMwHwYDVR0jBBgwFoAUTkPIHXbvN1N6T / JY b5TzOOLVvd8wdgYIKwYBBQUHAQEEajBoMCsGCCsGAQUFBzABhh9odHRwOi8vRVZJ bnRsLW9jc3AudmVyaXNpZ24uY29tMDkGCCsGAQUFBzAChi1odHRwOi8vRVZJbnRs LWFpYS52ZXJpc2lnbi5jb20vRVZJbnRsMjAwNi5jZXIwDQYJKoZIhvcNAQEFBQAD ggEBAFdVsrzkxJ6aRnaGIO1hbCDlekEMCT6OTlZXckzZeaIrNfSFLXHe89pWkRr1 AKz43nnM0pLxVuEHRE9pMZH6Om7SjqU5BR1qd6xp + ZJhuJA2I2319PCSbKCpv67X 82J8 / JKjH8e4fpOzb70dKUlNNr7x0aIMYuCq6unXXZQ5u83Uny42jcIQWLOlZRKC dYSqW3JalTYVZNvdEoQVuUEJJLcY1qMVJ9NFtdnrzrmcpK + 52 + nZQXbCkM7W8Vl1 WM / dbOnqsu0 + SIPZ4Q2wIAnT1azmBvxZ2ULvzW98HIAn4 / RdPuimnox8T9R2yrv1 xd5P6oAZWvmnX6e461m6HohhhDw =
----- END CERTIFICATE -----
服务器证书 主题= / 1.3.6.1.4.1.311.60.2.1.3 = US / 1.3.6.1.4.1.311.60.2.1.2 =华盛顿/ businessCategory =私有 组织/ SERIALNUMBER = 600413485 / C = US / POSTALCODE = 98052 / ST =华盛顿/ L =雷蒙德/街道= 1 Microsoft Way / O = Microsoft Corporation / OU = Windows Azure CDN / CN = gfx-ecn.hotmail.com issuer = / C = US / O = VeriSign,Inc. / OU = VeriSign 信任网络/ OU = https://www.verisign.com/rpa的使用条款 (c)06 / CN = VeriSign Class 3扩展验证SSL SGC CA
没有发送客户端证书CA名称
SSL握手已读取1933个字节并写入480个字节
新增,TLSv1 / SSLv3,密码是AES256-SHA服务器公钥是2048位 支持安全重新协商压缩:无扩展:无 SSL会话: 协议:SSLv3 密码:AES256-SHA 会议编号:3CC559C15AF17B09346C371A1CB292DF77C272A37FDC4DF69EEE0EE9CC067B5C 会话ID-CTX: 主密钥:F626E23FDCC89B1329FD4F5D1ED5A940F0CB14A1C377BFDB6ABA6238B91F9C11390EC16FD117C090B3171FBEE762B792 Key-Arg:无 PSK身份:无 PSK身份提示:无 开始时间:1355423684 超时:7200(秒) 验证返回码:21(无法验证第一个证书)
从openssl的输出中,我们可以看到openssl程序产生类似的错误(这使得它看起来更像是一个openssl bug?)。我们还注意到hotmail服务器仅返回服务器证书,但不返回中间证书。这可能是导致错误的原因。我和其他https服务器尝试了相同的ssl命令。它们还返回服务器证书和中间证书。我测试的一些银行网站甚至将整个证书链一直返回到根CA.所以,问题是,如果没有返回中间证书,我应该从何处和在哪里获得它? IE和Chrome等其他浏览器如何处理这个问题?
此处有更多调查结果。
实际上,我提到的两台服务器都用于下载hotmail和yahoo邮件的页内组件。雅虎邮件主登录页面本身确实返回完整的证书链。当其他浏览器访问主页面时,它将在本地存储中间证书以供以后使用。但是,QTWebkit不会自动缓存中间证书,从而导致问题。如果我直接使用firefox的新副本访问服务器https://gfx8.hotmail.com,我会收到同样的错误。但是如果我首先访问https:/www.hotmail.com然后登录到hotmail,虽然登录后从gfx8服务器下载了一些组件,但没有错误。我发现QTWebkit既没有从系统证书存储加载中间证书,也没有将中间证书添加到系统证书存储中。它也不会在内存中缓存中间证书。这将导致某些服务器出现问题,而这些服务器并未返回完整的证书链。
我确实尝试将中间证书保存到PEM文件并在启动我的应用程序时加载它。它在Windows和Linux上运行良好。但不知何故,它对Mac没有影响。不知道为什么。
答案 0 :(得分:3)
为了支持应用程序中的安全连接,没有ignorig ssl错误,您必须拥有您正在加载的站点正在访问的所有域的根证书,例如,如果它访问CDN(在您的浏览器的网络面板),也可以获得该服务器的证书(例如,您可以通过查看当前站点的证书从Chrome下载它们,并获取整个链,但单独下载它们)。 将所有这些证书存储在一个单独的文件夹中后,在应用程序初始化后的某处添加此代码,它将在开头加载所有这些代码,因此QT可以访问它们:
import os
from PySide.QtNetwork import QSsl, QSslConfiguration, QSslCertificate
from PySide.QtCore import QFile, QIODevice
def load_certs(cert_path):
# cert_path is a string "/path/to/cert/files"
ssl_config = QSslConfiguration.defaultConfiguration()
ssl_config.setProtocol(QSsl.SecureProtocols)
certs = ssl_config.caCertificates()
for cert_filename in os.listdir(cert_path):
if os.path.splitext(cert_filename)[1] in ('.cer', '.crt', '.pem'):
cert_filepath = os.path.join(cert_path, cert_filename)
cert_file = QFile(cert_filepath)
cert_file.open(QIODevice.ReadOnly)
cert = QSslCertificate(cert_file)
certs.append(cert)
ssl_config.setCaCertificates(certs)
QSslConfiguration.setDefaultConfiguration(ssl_config)
现在,当网站需要证书验证时,它不会失败。然而,openssl在Debian系统上的TLS实现确实存在问题,在这些情况下,当您没有有效证书时,您将遇到诸如页面永不加载或类似错误之类的问题。在这些情况下,您必须强制使用SSLv3来处理这些页面,以使它们正常工作。您可以通过将自定义虚拟功能绑定到 QNetworkAccessManager 的 createRequest ,并覆盖每个请求的ssl协议来实现此目的。这样你就不必依赖所有网站的旧版ssl(因为你也可以全局设置默认的ssl协议,但这不是最好的主意),只针对那些有问题的版本:
class Browser(object):
def __init__(self):
self.network_manager = QNetworkAccessManager()
self.network_manager.createRequest = self._create_request
self.web_page = QWebPage()
self.web_page.setNetworkAccessManager(self.network_manager)
self.web_view = QWebView()
self.web_view.setPage(self.web_page)
self._override_ssl = None
def _create_request(self, operation, request, data):
ssl_protocols = {'sslv2': QSsl.SslV2,
'sslv3': QSsl.SslV3,
'tlsv1': QSsl.TlsV1,
'tlsv1sslv3': QSsl.TlsV1SslV3,
'unknownprotocol': QSsl.UnknownProtocol,
'secureprotocols': QSsl.SecureProtocols}
if self._override_ssl is not None:
ssl_config = QSslConfiguration.defaultConfiguration()
ssl_config.setProtocol(ssl_protocols[self._override_ssl])
request.setSslConfiguration(ssl_config)
reply = QNetworkAccessManager.createRequest(self.network_manager,
operation,
request,
data)
return reply
def override_ssl(self, protocol_id):
# protocol id is a string like 'sslv3' or 'tlsv1'
self._override_ssl = protocol_id
好的我从内存中输入了大部分代码,请报告是否有问题。