拦截即时生成的代理证书会引发浏览器错误

Ant*_*iMS 7 ssl firefox proxy google-chrome certificate

我在Python 3中编写了一个拦截代理,它使用中间人"攻击"技术,能够动态地检查和修改通过它的页面."安装"或设置代理的过程的一部分涉及生成要在浏览器中安装的"根"证书,并且每次通过代理通过HTTPS命中新域时,代理都会生成新的站点证书. -the-fly(并将所有生成的证书缓存到磁盘,因此它不必为已生成证书的域重新生成证书)由根证书签名并使用站点证书与浏览器进行通信.(当然,代理会伪造自己的HTTPS连接到远程服务器.如果您感到好奇,代理也会检查服务器证书的有效性.)

嗯,它适用于浏览器冲浪.(并且,这可能是相关的 - 至少在几个版本中,冲浪没有检查/强制执行证书有效性.我无法证明更新版本是否就是这种情况.)但是,Firefox给出了通过代理和Chromium发出的第二个(以及所有更晚的)HTTPS请求的SEC_ERROR_REUSED_ISSUER_AND_SERIAL错误(我没有使用适当的Chrome测试)在每个HTTPS请求上都给出了NET :: ERR_CERT_COMMON_NAME_INVALID.当尝试浏览我的拦截代理时,这些显然是一个主要问题.

我正在使用的SSL库是pyOpenSSL 0.14,如果这有任何区别的话.

关于Firefox的SEC_ERROR_REUSED_ISSUER_AND_SERIAL错误,我很确定我不会重复使用序列号.(如果有人想检查我的工作,那将是非常的rad:cert.py - 请注意第168行的"crt.set_serial_number(getrandbits(20*8))".根证书颁发者当然不会改变,但这不会改变,对吧?如果不是根证书颁发者,我不确定错误消息中"issuer"究竟是什么意思.

此外,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等)的情况下完成这项工作.我曾经工作过的公司购买并建立了一个中间人代理服务器,通过该代理服务器,公司网络内部的所有流量都必须通过.该公司的PC管理员在员工使用的每台公司拥有的计算机上的每个浏览器中都安装了一个自签名证书作为证书颁发机构,结果从未产生任何错误,例如Firefox和Chromium给我的证书我的自己的拦截代理软件产生.PC管理员可能会在Firefox中调整一些关于:配置设置以使这一切都能正常工作,但我对此表示怀疑.

公平地说,这家公司使用的代理是网络或传输层,而不是我的应用层.但我希望在应用层HTTP(s)代理中可以实现相同的功能.

编辑:我已经尝试按照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

bra*_*n99 1

我注意到您只在 X509Req 中设置了 CN。Chrome 和 Firefox 都要求存在 subjectAltName 扩展;例如,请参阅此 Chrome 帮助页面此 Mozilla wiki 页面,讨论 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")
Run Code Online (Sandbox Code Playgroud)

如果这不能解决问题,或者只能部分解决问题,请发布一两个显示问题的示例证书。


除此之外,我还注意到您使用 SHA1 进行签名。请注意,使用 SHA1 签名的证书已在多个主要浏览器中弃用,因此我建议切换到 SHA-256。

r.sign(key, "sha256")
Run Code Online (Sandbox Code Playgroud)