我在Python 3中编写了一个拦截代理,它使用了一个中间人攻击"能够在运行中检查和修改通过它的页面的技术。 "安装"的过程的一部分或者设置代理涉及生成" root"要在浏览器中安装的证书,每次通过代理通过HTTPS命中新域时,代理会即时生成新的站点证书(并将生成的所有证书缓存到磁盘,因此它不会必须为已经生成证书的域重新生成证书,由根证书签名并使用站点证书与浏览器进行通信。 (当然,代理会伪造自己的HTTPS连接到远程服务器。如果您感到好奇,代理也会检查服务器证书的有效性。)
嗯,它适用于浏览器surf。 (并且,这可能是相关的 - 至少在几个版本中,冲浪没有检查/执行证书有效性。我无法证明这是否是最近的情况但是,Firefox在通过代理和Chromium(我还没有通过Chrome测试)测试的第二个(以及所有后来的)HTTPS请求上给出了SEC_ERROR_REUSED_ISSUER_AND_SERIAL错误,在每个HTTPS请求上都给出了NET :: ERR_CERT_COMMON_NAME_INVALID 。当尝试浏览我的拦截代理时,这些显然是一个主要问题。
我使用的SSL库是pyOpenSSL 0.14,如果这有任何区别的话。
关于Firefox的SEC_ERROR_REUSED_ISSUER_AND_SERIAL错误,我非常确定 重新使用序列号。 (如果有人想检查我的工作,那将是非常好的:cert.py - 请注意" crt.set_serial_number(getrandbits(20 * 8))"在第168行。)根证书发行人当然不会改变,但这不会发生变化,对吗?我不确定"发行人"的确切含义是什么?如果不是根证书颁发者,则在错误消息中。
此外,Firefox"查看证书"对话框显示代理生成的不同证书的完全不同的序列号。 (例如,我为www.google.com生成了序列号为00的一个:BF:7D:34:35:15:83:3A:6E:9B:59:49:A8: CC:88:01:BA:BE:23:A7:AD和另一个为www.reddit.com生成的序列号为78:51:04:48:4B:BC:E3:96:47:AC:DA :D4:50:EF:2B:21:88:99:AC:8C。)所以,我不确定Firefox究竟在抱怨什么。
我的代理为其即时创建的所有证书重用私钥(以及公钥/模数)。我开始怀疑这是Firefox正在讨论的内容,并尝试更改代码,为代理在运行中创建的每个证书生成一个新的密钥对。这并没有解决Firefox中的问题。我仍然得到相同的错误消息。我还没有测试它是否解决了Chromium问题。
关于Chromium的NET :: ERR_CERT_COMMON_NAME_INVALID错误,网站证书的通用名称应该是域名,对吧?我不应该包括端口号或任何东西,对吗? (同样,如果有人想查看我的工作,请参阅cert.py。)如果有任何帮助,我的拦截代理人不会在证书通用名称或任何内容中使用任何通配符。生成的每个证书都是针对一个特定的fqdn。
我确信无需使用Firefox或Chrome(或Chromium或IE等)即可完成此工作。我曾经工作过的公司购买并设置man-in-them-middling proxy,通过该公司,所有从公司网络到互联网的流量都必须通过。该公司的PC管理员在员工使用的每台公司拥有的计算机上的每个浏览器中都安装了一个自签名证书作为证书颁发机构,结果从未产生任何错误,例如Firefox和Chromium给我的证书我的自己的拦截代理软件产生。 PC管理员可以在Firefox中调整一些关于:配置设置以使这一切都能正常工作,但我对此表示怀疑。
公平地说,这家公司使用的代理是网络或传输层,而不是我的应用层。但我希望在应用层HTTP代理中可以实现相同的目标。
编辑:我已尝试按照brain99的建议设置subjectAltName。以下是我在Brain99建议的位置添加的行:
r.add_extensions([crypto.X509Extension(b"subjectAltName", False, b"DNS:" + cn.encode("UTF-8"))])
我仍然从Firefox获得SEC_ERROR_REUSED_ISSUER_AND_SERIAL(在第二次和随后的HTTPS请求中,我从Chromium获得了ERR_SSL_SERVER_CERT_BAD_FORMAT。
以下是代理生成的几个证书:
google.com:https://pastebin.com/YNr4zfZu
stackoverflow.com:https://pastebin.com/veT8sXZ4
答案 0 :(得分:1)
我注意到你只在X509Req中设置了CN。 Chrome和Firefox 都需要 subjectAltName扩展名才能存在;请参阅示例this Chrome help page或this Mozilla wiki page,讨论CA必需或建议的做法。引用Mozilla wiki:
有些CA错误地认为应该使用一个主DNS名称 主题公共名称和所有其他人进入SAN。
根据CA /浏览器论坛基准要求:
- BR#9.2.1(BR版本1.3中的7.1.4.2.1节),主题选择 名称扩展名
- 必填/可选:必需
- 内容:此扩展必须包含至少一个条目。每个条目必须是包含完全限定域名的dNSName或包含服务器IP地址的iPAddress。
您应该可以使用pyOpenSSL轻松完成此任务:
if not os.path.exists(path):
r = crypto.X509Req()
r.get_subject().CN = cn
r.add_extensions([crypto.X509Extension("subjectAltName", False, "DNS:" + cn])
r.set_pubkey(key)
r.sign(key, "sha1")
如果这不能解决问题,或者只是部分解决了问题,请发布一个或两个证明问题的示例证书。
除此之外,我还注意到你使用SHA1签名。请注意,使用SHA1签名的证书已在几个主流浏览器中弃用,因此我建议切换到SHA-256。
r.sign(key, "sha256")