Xamarin Forms双端证书固定场景下SSL连接无法建立问题求助
Xamarin Forms双端证书固定功能SSL连接报错问题
我在Xamarin Forms的Android、iOS双端实现证书固定功能时,原有可正常运行的代码近期突然抛出错误:"The SSL connection could not be established, see inner exception."
相关代码
public static async Task<HttpResponseMessage> SendWebApiRequest(HttpRequestMessage msg) { try { int timeout = 60; var handler = new HttpClientHandler { UseProxy = true, AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }; handler.SslProtocols = System.Security.Authentication.SslProtocols.Tls12; handler.ServerCertificateCustomValidationCallback = CheckCertificate; using (HttpClient client = new HttpClient(handler)) { client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip")); client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate")); using (CancellationTokenSource cts = new CancellationTokenSource()) { cts.CancelAfter(TimeSpan.FromSeconds(timeout)); HttpResponseMessage reply = await client.SendAsync(msg, cts.Token); ProcessResponseStatus(reply); if (reply.StatusCode == HttpStatusCode.Unauthorized) throw new InvalidOperationException("The authentication failed. Please logout and logback in with a valid account"); return reply; } } } catch (InvalidOperationException) { throw new InvalidOperationException("There was an issue connecting with the server, please try again later or contact support."); } catch (WebApiServiceExceptions) { throw new WebApiServiceExceptions(WebApiServiceExceptionType.NoInternetAccess, "No internet connection detect! Please check your internet connection!"); } catch (Exception ex) { Console.WriteLine(ex.Message); throw new Exception("There was an issue connecting with the server, please try again later or contact support."); } } private static bool CheckCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors) { var publicKey = "MY PUBLIC KEY"; return publicKey == certificate?.GetPublicKeyString(); }
各端错误详情
Android端
- 错误信息:
Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED - 堆栈跟踪:
at Mono.Net.Security.MobileAuthenticatedStream.ProcessAuthentication (System.Boolean runSynchronously, Mono.Net.Security.MonoSslAuthenticationOptions options, System.Threading.CancellationToken cancellationToken) [0x0025c] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs:310 at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore (System.IO.Stream stream, System.Net.Security.SslClientAuthenticationOptions sslOptions, System.Threading.CancellationToken cancellationToken) [0x0007b] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/ConnectHelper.cs:165
iOS端
- 错误信息:The authentication or decryption has failed.
- 堆栈跟踪:
at Mono.AppleTls.AppleTlsContext.EvaluateTrust () [0x000c7] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs:307 at Mono.AppleTls.AppleTlsContext.ProcessHandshake () [0x00075] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/System/Mono.AppleTls/AppleTlsContext.cs:213 at Mono.Net.Security.MobileAuthenticatedStream.ProcessHandshake (Mono.Net.Security.AsyncOperationStatus status, System.Boolean renegotiate) [0x000da] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs:715 at Mono.Net.Security.AsyncHandshakeRequest.Run (Mono.Net.Security.AsyncOperationStatus status) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs:289 at Mono.Net.Security.AsyncProtocolRequest.ProcessOperation (System.Threading.CancellationToken cancellationToken) [0x000fc] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/System/Mono.Net.Security/AsyncProtocolRequest.cs:223
涉及的证书为Azure函数应用证书。
解决方案
更新硬编码的公钥
Azure函数应用的证书默认开启自动轮转机制,有效期通常为1年,到期前会自动更新证书,公钥也会同步变更。你硬编码在CheckCertificate方法中的公钥已经和当前服务端证书公钥不匹配,是本次报错的核心原因。你可以通过浏览器访问Azure函数域名,导出当前证书的公钥字符串替换代码中的硬编码值即可临时恢复。优化证书固定逻辑避免后续轮转失效
不要只匹配叶子证书公钥,可同时固定中间CA和根CA的公钥,Azure默认使用DigiCert根证书,公钥长期不变,可避免单次叶子证书轮转导致功能失效。同时补充系统SSL错误校验逻辑,避免原有逻辑跳过系统校验带来的安全风险:
private static bool CheckCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslpolicyerrors) { // 先校验系统层面的SSL错误,无异常再做公钥匹配 if (sslpolicyerrors != SslPolicyErrors.None) { return false; } // 建议改为匹配公钥哈希而不是完整公钥字符串,兼容性更好 var allowedPublicKeys = new List<string> { "新的叶子证书公钥哈希", "Azure中间CA公钥哈希", "DigiCert根CA公钥哈希" }; var certPublicKeyHash = certificate?.GetCertHashString(); return allowedPublicKeys.Contains(certPublicKeyHash); }
- 适配双端平台限制
- Android 7.0以上系统默认不信任用户安装的证书,如果你使用的是自定义证书需要在
AndroidManifest.xml中配置网络安全配置,明确信任对应域名的证书 - iOS 14以上ATS策略要求TLS 1.2及以上版本,且证书必须符合苹果的证书要求,如果你固定的证书不符合ATS要求需要在
Info.plist中配置对应域名的例外规则,确保证书校验能正常触发自定义回调
- 优化HttpClient实例管理
原有代码每次请求都新建HttpClient实例,会导致大量端口占用,建议将HttpClient改为单例模式全局复用,避免不必要的资源消耗和连接异常。
内容的提问来源于stack exchange,提问作者user5678
相关产品推荐
相关产品推荐

