我们需要对仅接受 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 内部调试了 RealConnection 类,它实际上在不协商客户端证书的情况下执行握手。
到目前为止,我们的研究表明,这可能是由于服务器使用了可选的 ssl 客户端身份验证,但这不是我们可以改变的,所以......
答案 0 :(得分:0)
从客户端的角度来看,可选的客户端证书身份验证与必需的客户端证书身份验证没有区别。客户端看到的只是一个 CertificateRequest 并且作为响应,它将发送一些证书(叶子和链),这些证书也可能是一个空的证书列表。由服务器决定这是否可以接受,即可选只是意味着服务器接受客户端发送一个空的证书列表。
因此问题不是可选的或必需的客户端证书,而是服务器在初始 TLS 握手中根本没有请求证书,并且可能仅在访问特定路径时请求证书(请求的路径只能在最初的握手)。对于 Apache,如果在服务器全局配置中未请求客户端证书,而是在特定于路径的 .htaccess 文件中请求客户端证书,则通常会出现这种情况。解决方法是将需求移至域级别,而不仅仅是路径。
答案 1 :(得分:0)
使用其构建器创建一个 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();
}