Xamarin Android:本机 TLS、ClientWebSocket 和自定义证书颁发机构证书验证失败

Ste*_*one 5 client-certificates x509certificate2 xamarin.android xamarin tls1.2

客户端环境是 Xamarin Android Native TLS 1.2 SSL/TLS 实现(boringssl aka btls),使用 System.Net.WebSockets.ClientWebSocket。它在 Android 7.0 设备上运行。Visual Studio 2017 15.8.1、Xamarin.Android 9.0.0.18。

服务器环境是 Windows .NET 4.7,运行 Fleck(WebSocket 服务器),配置了 TLS 1.2,使用自制(全球任何地方不受信任)证书颁发机构 (CA) 颁发的证书。

假设已通过“设置”->“安全”->“从 SD 卡安装”在 Android 设备上安装了自制的 CA 证书(.pem 或 .cer 格式),则 ClientWebSocket 使用 TLS 1.2 进行连接,正如人们所期望的那样,没有任何问题。由于这是针对本地(我的应用程序的一部分)问题的全局解决方案,更不用说为更大的设备生态系统打开安全漏洞,因此我不希望需要此设置。

然后,我尝试了多种方法将 CA 的信任本地化到仅我的应用程序,但没有成功。无论采用哪种方法,ClientWebSocket.ConnectAsync() 总是会抛出相同的异常A call to SSPI failedSsl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED at /Users/builder/jenkins/workspace/xamarin-android-d15-8/xamarin-android/external/mono/external/boringssl/ssl/handshake_client.c:1132

我创建了一个示例 Windows 服务器和控制台应用程序以及 Xamarin.Forms Android 应用程序,用于演示该问题以及下面描述的解决该问题的尝试。其中包括自定义 CA 证书。服务器代码动态颁发客户端证书,其中 SAN 绑定到您的 IP/主机名,以便于重现。

尝试1:

android:networkSecurityConfig="@xml/network_security_config"属性应用于 AndroidManifest.xml 文件中的应用程序元素,包括资源Resources\raw\sample_ca.pem Resources\xml\network_security_config.xml

<?xml version="1.0" encoding="utf-8" ?>
<network-security-config>
  <base-config>
    <trust-anchors>
      <certificates src="@raw/sample_ca"/>
      <certificates src="system"/>
      <certificates src="user"/>
    </trust-anchors>
  </base-config>
</network-security-config>
Run Code Online (Sandbox Code Playgroud)

这没有明显的效果,而且我在调试输出中看不到任何表明运行时甚至正在加载它的内容。我看到过这样的消息参考:

D/NetworkSecurityConfig: No Network Security Config specified, using platform default
Run Code Online (Sandbox Code Playgroud)

然而,无论有没有这个,我都从未见过这样或类似的消息。我真的不知道它是否被应用,或者 btls 实现是否使用/尊重它。

有趣的是,由于 Android minSdk 设置为 24,目标 sdk 设置为 27 ,如果我只是将 CA 添加到 Android 设备用户证书存储区,我预计缺少此声明会导致 TLS 1.2 无法工作我怀疑这有一些 Xamarin 错误。

尝试2:

将 CA 添加到 X509 存储区,希望 btls 使用它作为证书源。此方法适用于 Windows/.NET 4(它确实会弹出一个对话框以接受添加证书)。

        X509Store store = new X509Store(StoreName.Root, StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadWrite);
        var certs = store.Certificates.Find(X509FindType.FindByThumbprint, cert.Thumbprint, false);
        if (certs.Count == 0)
            store.Add(cert);
        store.Close();
Run Code Online (Sandbox Code Playgroud)

尝试3:

处理ServerCertificateValidationCallback。在 Xamarin Android 中永远不会调用此方法,但此方法适用于 Windows/.NET 4。

ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, errors) =>
{
    if (errors == SslPolicyErrors.None)
        return true;    
    //BUGBUG: Obviously there should be a much more extensive check here.
    if (certificate.Issuer == caCert.Issuer)
        return true;    
    return false;
};
Run Code Online (Sandbox Code Playgroud)

围绕 btls 和pull request存在一些 Mono问题,这使得这种方法在不久的将来看起来是可能的。

尝试4:

将 CA 证书(和/或 CA 颁发的证书)添加到ClientWebSocket.Options ClientCertificates 集合。从技术上讲,这不应该与 CA 证书一起使用,但应该是与自签名证书一起使用的方法。我在这里提供它只是为了完整性。正如预期的那样,它也不起作用。

完整重现代码

GitHub 上提供了易于使用的代码,通过上述所有尝试的解决方法演示了此问题。

小智 -2

我不知道问题是什么,但我也遇到了一些问题,因为我已经开始使用我的第一个 https Web 服务。原因是,我从一个自签名证书开始,如果没有丑陋的解决方法,什么是行不通的...所以..只使用来自公共供应商的签名(可信)证书,你不应该有任何问题...切换在 http 和 https 之间,您只需更改 url(从 http 到 https) - 无需在应用程序 ae 中进行进一步更改。我通常首先使用 http(从 .ini 文件加载 url)的 Web 服务进行(本地)测试,然后将 Web 服务复制到“真实”Web 服务器(带有证书和 https url)。我从来没有遇到过任何问题(当使用受信任的证书时)...