-
Notifications
You must be signed in to change notification settings - Fork 516
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
Extending NSUrlSessionHandler with client certificates #13856
Comments
Hello, There is indeed a way for you to accept client certificates in the NSUrlSessionHandler without the need to copy the class. As you can see in the class we provided two delegates: public delegate bool NSUrlSessionHandlerTrustOverrideCallback (NSUrlSessionHandler sender, SecTrust trust);
public delegate bool NSUrlSessionHandlerTrustOverrideForUrlCallback (NSUrlSessionHandler sender, string url, SecTrust trust); Which you can set via the properties:
The delegate NSUrlSessionHandlerTrustOverrideCallback has been obsoleted and you should use NSUrlSessionHandlerTrustOverrideForUrlCallback since it allows to filter per url. You can see in the code that those delegates are executed as part of the handler management of the certs: https://github.com/xamarin/xamarin-macios/blob/main/src/Foundation/NSUrlSessionHandler.cs#L823 I am closing the issue but feel free to re-open if needed. |
@mandel-macaque While I understand that this can be used to evaluate self signed certificates, I don't know how this could be used to add client certificates. |
@MitchellFreeman sorry, I misunderstood the question. Can you install the client certificate in the device? Or is this an issue. We might need to add an extra callback for this :/ |
@mandel-macaque Sorry that may be partially my fault. Based on how iOS installs certificates I don't believe this is something I will be able to do. But thanks for pointing me towards those existing callbacks as validating self signed certificates is something I also need to do. |
@MitchellFreeman ok, so since you have no way to install the client certificate we have to find a way to do so, I'll do some research on who is the secure way to do this, since we are bypassing the OS and we don't want to introduce an attack vector. Does that sound like a plan? Ideally I'll get back with a objc sample and will do the appropriate in c# |
@mandel-macaque Sounds good. Although I don't see how this is bypassing the OS as we are simply replying to |
Yes, I meant to be able not to install them and pass them to the OS, I have another issue in which we want to add some extra properties for the handler, this probably will be included there. I might merge both issues: #13579 |
I found this:
I think what is missing is also a check for |
Did a bit more hacking. At this line I inserted the following code. That is of course just for testing, but proves it can be done: if (sessionHandler.ClientCertificates is not null && sessionHandler.ClientCertificates.Count > 0 &&
challenge.ProtectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodClientCertificate)
{
var cert = new SecCertificate(sessionHandler.ClientCertificates[0]);
var identity = SecIdentity.Import(sessionHandler.ClientCertificates[0] as X509Certificate2);
var trust = new SecTrust(sessionHandler.ClientCertificates, SecPolicy.CreateBasicX509Policy());
var credential = new NSUrlCredential(identity, new SecCertificate[] { cert }, NSUrlCredentialPersistence.ForSession);
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, credential);
return;
} This (wrongly) assumes there's only one certificate and it has a private key available, but the request succeeded with this code added, so it is definitely on the right track. Testcode: X509Certificate2 certificate = await LoadCertificateFromFile();
var handler = new NSUrlSessionHandler();
handler.ClientCertificates = new X509Certificate2Collection(certificate); //Note: Also made handler.ClientCertificates settable to match other APIs
using var client = new HttpClient(handler);
var response = await client.GetAsync(infoUri);
response = response.EnsureSuccessStatusCode(); This is with file-based certificates - I haven't tried using certificates that are installed on the device. One simple solution that we could use for now, is to provide a public callback similar to the server validation callback, and requiring you to return a |
Here's that alternative solution with a callback. else if (challenge.ProtectionSpace.AuthenticationMethod == NSUrlProtectionSpace.AuthenticationMethodClientCertificate)
{
if(sessionHandler.TryInvokeClientCertificateChallengeCallback(inflight.Request, challenge, out NSUrlCredential? credential))
{
completionHandler(NSUrlSessionAuthChallengeDisposition.UseCredential, credential);
return;
}
} And added this to NSUrlSessionHandler: public Func<HttpRequestMessage, NSUrlAuthenticationChallenge, NSUrlCredential?>? ClientCertificateChallengeCallback
{
get; set;
}
private bool TryInvokeClientCertificateChallengeCallback(HttpRequestMessage request, NSUrlAuthenticationChallenge challenge, [NotNullWhen(true)] out NSUrlCredential? credential)
{
var callback = ClientCertificateChallengeCallback;
credential = null;
if (callback is null)
return false;
credential = callback(request, challenge);
return credential != null;
} |
@MitchellFreeman I put up a PR that adds certificate support linked above |
…icates (#21284) Addresses #13856 This was originally created by @dotMorten in #20434. Also make SecIdentity.Import use an in-memory keychain on macOS 15+, so that SecIdentity.Import works like all othe other platforms (i.e. not requiring access to the default keychain, which, among other things, is not ideal on bots). --------- Co-authored-by: Morten Nielsen <[email protected]> Co-authored-by: dotMorten <[email protected]> Co-authored-by: Manuel de la Pena <[email protected]>
Fixed in #21284. |
Just wanted to confirm that things works beautifully in RC2 ! Thank you all for helping this getting in |
I'm working on a Xamarin iOS project that requires the use of mutual TLS. I tried multiple different ways to get this working, but most used the
HttpClientHandler
and I couldn't get any of them to work.This was when I found the
DidReceiveChallenge
method ofNSUrlSessionHandler
. I made my own copy of this class and addedto this method along with some code to store the client certificate. This works, but I'm wondering why this this simple change isn't already supported in some way.
Expected Behavior
The
NSUrlSessionHandler
class supports client certificatesActual Behavior
The
DidReceiveChallenge
method of its delegate needs a small modification so that it supports client certificatesThe text was updated successfully, but these errors were encountered: