如何使Selenium WebDriver动态选择客户端证书而不需要可视化地检测弹出窗口

18

我正在尝试使用Java和Selenium测试一个需要客户端证书的网站。当我浏览到我的网站时,会弹出如下所示的弹窗来选择正确的证书。

Windows Security: Select a Certificate

我的要求如下:

  • 按名称选择证书
  • 在不同版本的Windows / IE / Edge上

理想情况下,永远不会显示弹出窗口;即解决方案将涉及调用某些API或设置某些配置以固定使用正确的证书。


我的解决方案想法:

  • 我尝试了一种基于视觉检测正确证书的解决方案,使用 SikuliX(确实有效),但我想知道是否有更好的解决方案,不依赖于视觉检测弹出窗口。希望能找到一种跨多个Windows版本更不容易出错的方法,并且如果Microsoft决定更改此弹出窗口的外观,则该方法应具备未来性。

  • 我还有一个想法(但不知道如何/是否可能),就是删除除一个证书以外的所有安装证书,这样就永远不会显示弹出窗口:

    • 备份整个存储库
    • 删除IE可以使用的所有客户端证书(除了我需要的那个)
    • 进行登录,不再需要选择任何证书
    • 恢复备份的存储库

    有人知道如何做到这一点吗(可能需要调用CLI命令的Java)?

  • 是否可以启动(使用Selenium Java)仅知道我需要的单个证书的Internet Explorer窗口?

  • 在Internet Explorer中,是否可以为给定域设置默认证书?


你看过这个代码吗:SSLContexts.custom().loadTrustMaterial(new File(PATH), PASSWORD, new TrustSelfSignedStrategy()).build();? 这会返回一个 SSLContext。如果这种方法可行的话,我可以帮你,我之前用 Selenium 做过自签名证书的 SSL。参考:https://www.programcreek.com/java-api-examples/index.php?api=org.apache.http.impl.conn.PoolingHttpClientConnectionManager (例子 2)。 - Bob
@Bob 你怎么在Selenium中注册这个SSLContext?这个解决方案会让Internet Explorer使用文件中的客户端证书而不弹出询问框吗? - neXus
2个回答

4
我们使用的解决方案非常干净、简单,并且在浏览器和操作系统之间可移植——使用代理服务器来处理SSL握手。您可以在运行测试的同一JVM中设置一个内存中的中间人代理服务器,或者甚至在不同端口上设置多个实例,每个实例分配给不同的客户端证书。然后,在创建WebDriver实例时,使用适合您的浏览器的setProxy method。请注意,浏览器将显示安装在代理本身上的服务器证书,而不是目标服务器,因此可能会出现一些无效的证书错误,应在WebDriver设置中抑制这些错误。或者,如果您拥有密钥,则代理可以简单地使用有效的服务器证书,这样连接对于测试脚本就是完全透明的。Java中提供了一个满足要求的简单代理服务器LittleProxy。也许像BrowserMob这样的东西提供了更完整的解决方案,具有现成的API。使用LittleProxy的示例只需要几十行样板代码。

步骤1:
使用客户端证书(例如p12文件或PEM文件),扩展org.littleshoot.proxy.MitmManager类,将其插入您的代码中。公开可用的工作示例在this repo

步骤2:
使用您选择的客户端证书和服务器证书启动代理服务器:

org.littleshoot.proxy.impl.DefaultHttpProxyServer.DefaultHttpProxyServer.bootstrap()
        .withIdleConnectionTimeout(FIVE_MINUTES)
        .withName(clientCertFile.getName())
        .withPort(port)
        .withAllowLocalOnly(localConnectionOnly)
        .withManInTheMiddle(new MutualAuthenticationCapableMitmManager(
                usingPKCS12File(clientCertFile, clientCertPassword),
                usingPemKeyPair(serverKeyPair[0], serverKeyPair[1])))
        .start();

为每个需要重用相同端口的客户端证书创建另一个代理,或者启动并发实例。
步骤3:
使用代理启动WebDriver。主要浏览器(IE,Firefox,Chrome)以类似的方式支持此设置:
org.openqa.selenium.Proxy proxy = new Proxy();
proxy.setSslProxy("127.0.0.1:5555");
proxy.setNoProxy("<-loopback>"); // overwrite the default no-proxy for localhost, 127.0.0.1

FirefoxOptions options = new FirefoxOptions();
options.setProxy(proxy);
WebDriver driver = new FirefoxDriver(options);

第四步:
运行测试时,浏览器将不会打扰您任何证书提示。利润。
如果使用这种技术,请特别小心保护机密,并确保代理服务器本身对第三方不可达。在安全的公司网络之外泄露密钥绝不是一个好主意,无论它们是真实的还是虚假的。

我大致按照您的指示操作了(除了我只关心客户端证书处理,所以在MitmManager中对于服务器证书,我传递了一个空的SslContext和虚假的“始终信任”管理器),但我的问题是无论我访问哪个网站,浏览器总是显示ERR_EMPTY_RESPONSE。我已经使用curl验证了代理确实在指定的端口上监听,但从Chrome的网络选项卡中,我无法确定问题出在哪里。您是否遇到过类似的情况? - johny.bravo
我没有遇到过这样的情况。空响应通常表示服务器配置错误,而在您的情况下当然是代理,因此它并没有真正缩小任何范围,但似乎您的浏览器面对代理的接口根本不支持SSL,并且浏览器无法理解它。我建议使用我在步骤1中链接的存储库中提供的确切配置,并仅将“client.example.com.p12”替换为您自己的文件。如果您不关心服务器端证书,则可以使用示例证书。 - JockX
此外,请检查您的防病毒软件/防火墙和可能由IT部门应用的Chrome策略。MITM是各种恶意软件常用的技术 - 使用防病毒软件来阻止它并不是不合理的。 - JockX
1
除了服务器 SSL 之外,我还使用了一些有问题的依赖版本 - 特别是对于 littleproxy - 我使用了 'org.littleshoot:littleproxy:1.1.2'。在切换到 'xyz.rogfam:littleproxy:2.0.9'(与您的存储库相同)并按照您的示例配置服务器 SSL 后,一切都按预期工作。您的答案真的很有帮助。谢谢 :) - johny.bravo
没有任何一个浏览器驱动程序能够很好地处理Selenium中的客户端证书。例如,我可以在Chrome的head模式下让证书工作,但在无头模式下却不行。@JockX的方法比使用本机Selenium证书支持要好得多。我很快就能让它工作了。现在我可以使用任何浏览器,包括head模式和无头模式,都可以完美地工作。有一个小提示-最终我使用了BrowserMob而不是LittleProxy,但总体方法与描述完全相同。我只希望早点找到这个答案!! - greghmerrill

0

我不能确定,但我不会感到惊讶如果这在Selenium中是不可能的。

这个问题让我没有信心: Selenium无法处理IE中的确认证书弹出窗口

你越远离网页,接近本地浏览器和操作系统控件,Selenium就越无能为力。

正如链接问题的唯一答案所述:您可以在浏览器中禁用弹出窗口,这可能是可行的解决方案,也可能不是。

如果您不想走这条路,人们最常退回到Java机器人来处理Selenium驱动程序无法处理的事情,例如与打印对话框和其他此类控件交互。

请参见mouseMove、mousePress、mouseRelease。您将调用每个函数,并提供按钮位置给mouseMove。 https://docs.oracle.com/javase/7/docs/api/java/awt/Robot.html


我本来就有这样的担心。机器人解决方案与我的SikuliX解决方案类似,而且正是我想避免的事情。我希望Java有一种干净的方式来操作证书存储,并能还原初始状态,或者最好有一些IE配置可以自动获取所需的证书。 - neXus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接