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

[Android] Allow using installed certificates for client authentication in SslStream #103337

Conversation

simonrozsival
Copy link
Member

@simonrozsival simonrozsival commented Jun 12, 2024

Closes #99874

This PR adds the possibility to use certificates with non-exportable private keys installed on an Android device for client authentication in SslStream/SocketsHttpHandler.

The inspiration for these changes comes from AppleCertificatePal where we allow passing in either SecCertificate or SecIdentity via the IntPtr constructor. The Android IntPtr constructor will now accept not only handles of java.security.cert.X509Certificate, but also of java.security.KeyStore.PrivateKeyEntry. This will allow the developer to pass in certificates with non-exportable private keys and use them for client authentication (see the linked issue for an example usage with KeyChain.ChoosePrivateKeyAlias).

To implement this feature, it is necessary to introduce new Java class net.dot.android.crypto.DotnetX509KeyManager. This will require follow-up work in the .NET for Android SDK code to adjust the ProGuard configuration (https://github.com/xamarin/xamarin-android/blob/7e67d2c241e561b05065f2a57ddf59093f74de58/src/Xamarin.Android.Build.Tasks/Resources/proguard_xamarin.cfg#L29).

As a follow-up to this PR, we should introduce new APIs to the Android.Security.KeyChain class in the dotnet/android repository. This will allow the developers to avoid using this low level IntPtr constructor.

/cc @karelz @vitek-karas @grendello @filipnavara @dotMorten

Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-security, @bartonjs, @vcsjones
See info in area-owners.md if you want to be subscribed.

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@dotMorten
Copy link

@simonrozsival Thank you for working on this! Very exciting. What's the easiest way for me to test this with our various PKI tests on Android?

@simonrozsival
Copy link
Member Author

@dotMorten I'm not sure if there's an easy way to do it, you'll need to jump through a few hoops:

  1. build the code in this branch, you can follow https://github.com/dotnet/runtime/blob/main/docs/workflow/building/mono/README.md (alternatively I could send you the prebuilt nupkgs directly if you're OK with that, you can reach out to me via email or discord)
  2. add the local nupkg source to your MAUI app via NuGet.config
  3. follow this guide to use your locally built dev version of the runtimepack: https://github.com/xamarin/xamarin-android/blob/main/Documentation/workflow/DevelopmentTips.md?plain=1#L432
  4. use the new ctor as it's shown in the issue or in the new unit test in this PR

…eHelper.cs

Co-authored-by: Alexander Köplinger <alex.koeplinger@outlook.com>
@dotMorten
Copy link

dotMorten commented Jun 14, 2024

I tried this change, and it indeed addresses the issue for us without our test server and certificates. Nice work @simonrozsival (and thanks for helping getting it running).
🚢 it

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@simonrozsival
Copy link
Member Author

The failing tests in runtime-extra-platforms are known and unrelated (#103472)

Copy link
Member

@akoeplinger akoeplinger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, a couple comments but I'm fine if we address them in a follow-up PR

@simonrozsival
Copy link
Member Author

/azp run runtime-extra-platforms

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

jonathanpeppers pushed a commit to dotnet/android that referenced this pull request Jun 19, 2024
Context: dotnet/runtime#103016
Context: dotnet/runtime#103337

In dotnet/runtime we are adding a few more Java classes to assist with
.NET crypto. One was added in dotnet/runtime#103016, and another may
be added in dotnet/runtime#103337.

This PR changes ProGuard to keep all of the classes in this package
rather than individually adding them.

Co-authored-by: Alexander Köplinger <alex.koeplinger@outlook.com>
@simonrozsival
Copy link
Member Author

Failing runtime-extra-platforms tests are unrelated

Copy link
Member

@wfurt wfurt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Would X509Certificate2.HasPrivateKey return right value?

@vcsjones
Copy link
Member

Would X509Certificate2.HasPrivateKey return right value?

That is asserted here:

And the Android X.509 PAL changes look sensible.

public bool HasPrivateKey => _privateKey is not null || _keyStorePrivateKeyEntry is not null;

@simonrozsival
Copy link
Member Author

@wfurt yes, the AndroidCertificatePal class will return the right value whether it's created the previously existing way or the new way with KeyStore.PrivateKeyEntry.

@simonrozsival simonrozsival merged commit 83031f1 into dotnet:main Jun 19, 2024
127 of 136 checks passed
@@ -32,6 +32,31 @@ internal static byte[] X509Encode(SafeX509Handle x)

return encoded;
}
[LibraryImport(Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_X509IsKeyStorePrivateKeyEntry")]
[return: MarshalAs(UnmanagedType.U1)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simonrozsival the native code returns int32_t here so we should change this to UnmanagedType.Bool (4 bytes), or change the native code to use a C bool which is 1 byte. We seem to be using both variants in the Android crypto imports...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least in the Linux and MacOS Desktop PALs, we try to use int32_t and not a C bool since there is no guarantee by the C standard that it is exactly one byte.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, thanks for pointing that out!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Android: Installed X509 certificates can't be used with SSL authentication
5 participants