如何强制 Android 11 上的 OkHttp 使用 TLS 1.3 发送 ssl 客户端证书身份验证

how to force OkHttp on Android 11 to send ssl client cert auth with TLS 1.3

我们需要对仅接受 TLS 1.3 连接的服务器执行基于证书的客户端身份验证。 服务器使用 Apache 2 和 HTTP 1.1 并配置为 允许 客户端证书身份验证但不强制执行它,因为某些资源需要客户端身份验证而其他资源则不需要。

我们在 Android 11 上使用 OkHttp 4.9.1 来执行调用,我们遵循有关如何执行客户端身份验证的标准文档:https://github.com/square/okhttp/tree/master/okhttp-tls

然而服务器回复:403(不允许重新协商身份验证)

这符合 TLS 1.3 规范,除非在初始握手期间达成一致,否则不允许重新协商身份验证。

到目前为止,我们已经调试了连接和 OkHttp 内部的 class RealConnection,它实际上在不协商客户端证书的情况下执行握手。

我们目前的研究表明这可能是由于服务器使用了可选的 ssl 客户端身份验证,但这不是我们可以改变的,所以....

  1. 我们可以在 OkHttp 初始化期间通过任何其他选项来强制它使用客户端证书执行初始握手吗?
  2. 如果 OkHttp 不是一个可行的选择,是否有任何其他 HTTP 客户端实现允许我们在初始握手期间强制执行此类身份验证?

从客户端的角度来看,可选的客户端证书身份验证与必需的客户端证书身份验证没有区别。客户端看到的只是一个 CertificateRequest,作为响应,它将发送一些证书(叶和链),这也可能是一个空的证书列表。由服务器决定这是否可以接受,即可选只是意味着服务器接受客户端发送一个空的证书列表。

因此问题不是可选或必需的客户端证书,而是服务器在初始 TLS 握手中根本没有请求证书,并且可能只在访问特定路径时请求证书(请求的路径只能是在初次握手后看到)。对于 Apache,如果客户端证书不是在服务器全局配置中而是在路径特定的 .htaccess 文件中请求的,通常就是这种情况。解决方法是在域级别而不是仅在路径级别移动需求。

使用构建器创建 HandshakeCertificates 对象。您将需要您的私钥、客户端证书和客户端的任何中间件。

客户端还需要您的服务器的根证书才能信任它。如果您愿意,构建器具有使用内置证书颁发机构的功能。

  private OkHttpClient buildClient(
      HeldCertificate heldCertificate, X509Certificate... intermediates) {
    HandshakeCertificates.Builder builder = new HandshakeCertificates.Builder()
        .addTrustedCertificate(serverRootCa.certificate());

    if (heldCertificate != null) {
      builder.heldCertificate(heldCertificate, intermediates);
    }

    HandshakeCertificates handshakeCertificates = builder.build();
    return clientTestRule.newClientBuilder()
        .sslSocketFactory(
            handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager())
        .build();
  }

https://github.com/square/okhttp/blob/1ce86f35a9d957bae711fb81cec60abe9f43dda0/okhttp/src/test/java/okhttp3/internal/tls/ClientAuthTest.java#L126