Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HttpClient does not send client certificate stored in SecurityContext on iOS #1277

Open
yjiang-c opened this issue Jul 28, 2024 · 3 comments
Labels
package:http type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)

Comments

@yjiang-c
Copy link

yjiang-c commented Jul 28, 2024

Recently, I added mTLS client certificate function into Immich mobile app. The feature work fine on Android platform but does not work for iOS app.

The core change source code is an override of HttpOverrides in this file. The SecurityContext::usePrivateKeyBytes() is invoked to set client certificate. Based my log, the client certificate is set without exception.

But the following source code from this file for a http request failed.

  Future<bool> _isEndpointAvailable(String serverUrl) async {
    final Client client = Client();

    if (!serverUrl.endsWith('/api')) {
      serverUrl += '/api';
    }

    try {
      final response = await client
          .get(
            Uri.parse("$serverUrl/server-info/ping"),
            headers: getRequestHeaders(),
          )
          .timeout(const Duration(seconds: 5));

      _log.info("Pinging server with response code ${response.statusCode}");
      if (response.statusCode != 200) {
        _log.severe(
          "Server Gateway Error: ${response.body} - Cannot communicate to the server",
        );
        return false;
      }
    } on TimeoutException catch (_) {
      return false;
    } on SocketException catch (_) {
      return false;
    } catch (error, stackTrace) {
      _log.severe(
        "Error while checking server availability",
        error,
        stackTrace,
      );
      return false;
    }
    return true;
  }

The response from my Nginx server are shown as below to complain that Http Client does not send the client certificate. I trun Nginx debug logs and confirmed that Nginx did not get client certificate.

2024-07-28 01:04:26.037742 | SEVERE   | ApiService           | Server Gateway Error: <html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx</center>
</body>
</html>
 - Cannot communicate to the server |

I tested with exact same server and client certificate, Immich Android app work fine but iOS 17.5.1 iPhone has this issue.

@yjiang-c yjiang-c added package:http type-bug Incorrect behavior (everything from a crash to more subtle misbehavior) labels Jul 28, 2024
@yjiang-c
Copy link
Author

yjiang-c commented Jul 28, 2024

Not sure whether it is related, NextCloud implemented mTLS client certificate on their iOS app recently. NextCloud iOS client was written with Apple native SDK but encountered the same issue. The issue is reported here. I did not see the same issue reported on NextCloud Android client.

@yjiang-c
Copy link
Author

Just checked the C++ source code related to SecurityContext::useCertificateChainBytes() in security_context.cc and found it is finally invoke openssl API SSL_CTX_use_certificate in function UseChainBytesPKCS12. If iOS use the openssl as well, I cannot understand why SecurityContext::useCertificateChainBytes() is not needed to be invoked on iOS shown as below documentation in security_context.dart. I did not see SecurityContext::usePrivateKey() invoke openssl API SSL_CTX_use_certificate.

Could please confirm whether the following documentation is still true?

  /// iOS note: Only PKCS12 data is supported. It should contain both the private
  /// key and the certificate chain. On iOS one call to [usePrivateKey] with this
  /// data is used instead of two calls to [useCertificateChain] and
  /// [usePrivateKey].

@yjiang-c
Copy link
Author

After some tests to call both SecurityContext::useCertificateChainBytes() and SecurityContext::usePrivateKey(), the issue is gone. The API documentation on both APIs related to iOS is wrong or deprecated. The API documentation needs to be updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
package:http type-bug Incorrect behavior (everything from a crash to more subtle misbehavior)
Projects
None yet
Development

No branches or pull requests

1 participant