Skip to content

Certificate pinning / validation callback is broken on iOS 26 #24739

@plequere-ms

Description

@plequere-ms

Apple platform

iOS

Framework version

net10.0-*

Affected platform version

.net 10, iOS, MAUI app

Description

I use certificate pinning to prevent MITM attacks. This relies on:

private bool ValidateServerCertificate(HttpRequestMessage request, X509Certificate2? certificate, X509Chain? chain, SslPolicyErrors sslPolicyErrors, bool fullValidation)

The ServerCertificateCustomValidationCallback should be invoked even when certificate data cannot be decoded, allowing the application to detect the invalid certificate and handle the situation gracefully without crashing.
This is the behavior on Android and other platforms.

Since the release of iOS 26, my app crashes with the following stack trace before even hitting user-level-code when the user is connected to a network that injects a certificate (for example, airport Wi-Fi access points.)

Interop+AppleCrypto+AppleCommonCryptoCryptographicException: Unable to decode the provided data. (Interop+AppleCrypto+AppleCommonCryptoCryptographicException) at System.Security.Cryptography.X509Certificates.SecTrustChainPal.Execute(DateTime , Boolean , OidCollection , OidCollection , X509RevocationFlag ) at System.Security.Cryptography.X509Certificates.ChainPal.BuildChain(Boolean , ICertificatePal , X509Certificate2Collection , OidCollection , OidCollection , X509RevocationMode , X509RevocationFlag , X509Certificate2Collection , X509ChainTrustMode , DateTime , TimeSpan , Boolean ) at System.Security.Cryptography.X509Certificates.X509Chain.Build(X509Certificate2 , Boolean ) at System.Security.Cryptography.X509Certificates.X509Chain.Build(X509Certificate2 ) at System.Net.Http.NSUrlSessionHandler.ServerCertificateCustomValidationCallbackHelper.EvaluateSslPolicyErrors(X509Certificate2 , X509Chain , SecTrust ) at System.Net.Http.NSUrlSessionHandler.ServerCertificateCustomValidationCallbackHelper.Invoke(HttpRequestMessage , SecTrust ) at System.Net.Http.NSUrlSessionHandler.TryInvokeServerCertificateCustomValidationCallback(HttpRequestMessage , SecTrust , Boolean& )

Impact:
• Prevents implementation of certificate pinning on iOS
• Prevents detection of MITM attacks on iOS
• Forces developers to choose between app stability and security
• Affects production apps in real-world scenarios (airport Wi-Fi, corporate proxies, etc.)

The NSUrlSessionHandler should:

  1. Wrap the X509Chain.Build() call in a try-catch
  2. If building the chain fails, set sslPolicyErrors appropriately (e.g., SslPolicyErrors.RemoteCertificateChainErrors)
  3. Still invoke the custom validation callback with the certificate and error information
  4. Let the callback decide how to handle the error

This would match the behavior on other platforms and allow apps to implement robust security validation.

Steps to Reproduce

var handler = new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = (request, cert, chain, errors) =>
    {
        // This callback never executes when cert data is malformed
        Console.WriteLine("Validating certificate...");
        return true;
    }
};

var client = new HttpClient(handler);

Attempt to connect to a server that presents a certificate with malformed/corrupted data
• This can happen with certain MITM proxy tools (HTTP Toolkit, Charles Proxy, Fiddler)
• Or with corrupted intermediate certificates in the chain
• Or in airplane mode with certain Wi-Fi captive portals

The app crashes before the callback executes.

Did you find any workaround?

No.

  1. Try-catch around callback: Does not work - crash occurs before callback
  2. Wrapping HttpClient calls in try-catch: Does not work - exception is unhandled in runtime
  3. Checking certificate properties before chain build: Does not work - crash occurs in runtime code
  4. Disabling custom validation: Works but removes critical MITM detection capability

Relevant log output

Interop+AppleCrypto+AppleCommonCryptoCryptographicException: Unable to decode the provided data. (Interop+AppleCrypto+AppleCommonCryptoCryptographicException)
   at System.Security.Cryptography.X509Certificates.SecTrustChainPal.Execute(DateTime , Boolean , OidCollection , OidCollection , X509RevocationFlag )
   at System.Security.Cryptography.X509Certificates.ChainPal.BuildChain(Boolean , ICertificatePal , X509Certificate2Collection , OidCollection , OidCollection , X509RevocationMode , X509RevocationFlag , X509Certificate2Collection , X509ChainTrustMode , DateTime , TimeSpan , Boolean )
   at System.Security.Cryptography.X509Certificates.X509Chain.Build(X509Certificate2 , Boolean )
   at System.Security.Cryptography.X509Certificates.X509Chain.Build(X509Certificate2 )
   at System.Net.Http.NSUrlSessionHandler.ServerCertificateCustomValidationCallbackHelper.EvaluateSslPolicyErrors(X509Certificate2 , X509Chain , SecTrust )
   at System.Net.Http.NSUrlSessionHandler.ServerCertificateCustomValidationCallbackHelper.Invoke(HttpRequestMessage , SecTrust )
   at System.Net.Http.NSUrlSessionHandler.TryInvokeServerCertificateCustomValidationCallback(HttpRequestMessage , SecTrust , Boolean& )

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions