-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Android: Installed X509 certificates can't be used with SSL authentication #99874
Comments
Tagging subscribers to this area: @dotnet/ncl |
@simonrozsival Any update on this? We have quite a few customers who are not able to use our products because they can't authenticate with their servers using device-installed certificates. |
@dotMorten I haven't had time to look into this issue yet, but I will triage it later this week. |
I looked into the current capabilities and if I understand it correctly, the way we implement client certificates in the SocketsHttpHandler/SslStream doesn't support this workflow with aliases. Maybe @elinor-fung knows more. I also looked into how one could implement this with the |
The I would expect the certificate handling sits all the way down at the socket level which is also using the java apis, so I would expect to be able to use the installed certificates: https://github.com/dotnet/runtime/blob/b194416ea68e2d1c93a91fc7abf80eb2607b4831/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c |
Yes, I understand why you would prefer SocketsHttpHandler over the native one.
You're right, there's no reason why we couldn't implement it in the Java/JNI code. The problem I see at the moment is that there isn't a good way to pass the |
Looking at the code example above, I don't think passing a Line 118 in e377f16
Maybe it doesn't throw an exception? But this would be a handle to a Java object instance, and Is there a way to use the |
@jonathanpeppers it does work. I get all the properties reporting the correct values so seems fine. |
I was thinking if it might make sense to allow this constructor to be a "file path or alias" on Android. While technically it would be definitely possible, it's probably not a good idea: Line 129 in e377f16
It might be better to explore if some of the |
@filipnavara do you have any insight? |
Unfortunately, I am not particularly familiar with the Android Keychain APIs. There were certainly cases where I had a use case on iOS / Android to convert a native object ( The I'll try to sleep on it and perhaps I will come up with a better answer. |
So to do this on Windows, you need to install the I wonder if we should add a similar public API to // xamarin/xamarin-android
public static class X509Certificate2Factory
{
public static X509Certificate2 CreateFromAlias(Android.Content.Context context, string alias) { ... }
}
// dotnet/runtime
internal partial class AndroidCertificatePal
{
internal static X509Certificate2 FromAlias(IntPtr context, string alias) { ... }
} Unfortunately, unlike System.Windows.Extensions, xamari-android can't see the internals directly, but I don't think that would be a major problem. |
I'm not quite getting the argument for providing these extensions - sure they are helpful to avoid writing the code in the issue details, but at the end of the day we can still create the |
We can create the certificate, but AFAIK we're not able to include the private key information ( |
You are correct. Lines 18 to 19 in 1ded19e
Calling |
Aaaah I think that's where the confusion in this thread comes from. I do not think it's possible to ever get the private key. That's the entire point of using the keychain to store it, so that it can't be "stolen" and taking out of the device. It's the reason my customers are insisting on using installed certificates, over just a file-based one. What I'm hoping for is that we're able to push this certificate all the way down to the socket level where Android is again responsible for communicating with the other end and use platform APIs to ensure the encryption is handled. |
It isn't about getting private key. Private key is in the device. |
I've looked into the Apple platforms APIs and I noticed that the This approach wouldn't require any changes to the public APIs and the internal implementation of The usage would look like this: var privateKey = Android.Security.KeyChain.GetPrivateKey(Platform.CurrentActivity!, alias);
var certificateChain = Android.Security.KeyChain.GetCertificateChain(Platform.CurrentActivity!, alias);
var privateKeyEntry = new Java.Security.KeyStore.PrivateKeyEntry(privateKey, certificateChain);
var certificate = new X509Certificate2(privateKeyEntry.Handle);
handler.SslOptions.ClientCertificates = new X509Certificate2Collection(certificate); |
@simonrozsival That sounds great, but will you be able to tell the difference what kind of handle you're getting? Java.Security.Cert.X509Certificate javacert = GetCert();
var dotnetcert = new X509Certificate2(javacert.Handle); as when using the entry handle: Java.Security.KeyStore.PrivateKeyEntry entry = GetEntry();
var dotnetcert = new X509Certificate2(entry.Handle); Since it's just a pointer, you can't really tell the difference which kind of handle you're getting. Is there some magic internal secret sauce to find this? |
@dotMorten yes, that should be straightforward IMO. We can check if it is a java object reference and check the type of the java object using JNI. |
Ship it! |
Description
On Android when using the SocketsHttpHandler with X509 Client certificates to PKI authenticate with a server, you can only use certificates with a private key coming from a file. When using the more correct and secure way of only using certificates installed in the keychain authentication fails, because the SocketsHttpHandler wants direct access to the private key instead of using the Java APIs to authenticate.
Reproduction Steps
Reproduction steps are a little tricky since they require a server that requires PKI authentication, and a certificate installed on the users device. I'm going to assume you have this here.
KeyChainAliasCallback.cs
responsible for letting the user pick an installed certificate:Expected behavior
Certificates used from the keychain where the private key isn't directly accessible will work.
Actual behavior
Fails to authenticate.
Regression?
No response
Known Workarounds
None
Configuration
No response
Other information
Related issue: #45741 (comment)
This is a blocker for larger enterprises since they can't use their secured services with the level of security they require. Using file-based certificates in-app with the full exportable private key just isn't acceptable to them.
The text was updated successfully, but these errors were encountered: