diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs index addb8d540e43c1..e0a103c0e13979 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs @@ -86,6 +86,10 @@ internal static void SSLStreamSetTargetHost( throw new SslException(); } + [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamIsLocalCertificateUsed")] + [return: MarshalAs(UnmanagedType.U1)] + internal static partial bool SSLStreamIsLocalCertificateUsed(SafeSslHandle sslHandle); + [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRequestClientAuthentication")] internal static partial void SSLStreamRequestClientAuthentication(SafeSslHandle sslHandle); diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs index e81454f18d8d26..2141373d410259 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs @@ -91,9 +91,15 @@ internal static SslPolicyErrors VerifyCertificateProperties( return cert; } - // This is only called when we selected local client certificate. - // Currently this is only when Java crypto asked for it. - internal static bool IsLocalCertificateUsed(SafeDeleteContext? _) => true; + // Check if the local certificate has been sent to the peer during the handshake. + internal static bool IsLocalCertificateUsed(SafeDeleteContext? securityContext) + { + SafeSslHandle? sslContext = ((SafeDeleteSslContext?)securityContext)?.SslContext; + if (sslContext == null) + return false; + + return Interop.AndroidCrypto.SSLStreamIsLocalCertificateUsed(sslContext); + } // // Used only by client SSL code, never returns null. diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs index 698af3d28fee7b..e6cc9157aa4a08 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs @@ -86,7 +86,6 @@ public async Task SslStream_RequireClientCert_IsMutuallyAuthenticated_ReturnsTru [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] [ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/65563", TestPlatforms.Android)] public async Task SslStream_CachedCredentials_IsMutuallyAuthenticatedCorrect( SslProtocols protocol) { diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index 9ec692cb11b63c..1fdffac63e034f 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -463,6 +463,7 @@ jmethodID g_SSLContextCreateSSLEngineMethodWithHostAndPort; jclass g_SSLSession; jmethodID g_SSLSessionGetApplicationBufferSize; jmethodID g_SSLSessionGetCipherSuite; +jmethodID g_SSLSessionGetLocalCertificates; jmethodID g_SSLSessionGetPacketBufferSize; jmethodID g_SSLSessionGetPeerCertificates; jmethodID g_SSLSessionGetProtocol; @@ -1054,6 +1055,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLSession = GetClassGRef(env, "javax/net/ssl/SSLSession"); g_SSLSessionGetApplicationBufferSize = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I"); g_SSLSessionGetCipherSuite = GetMethod(env, false, g_SSLSession, "getCipherSuite", "()Ljava/lang/String;"); + g_SSLSessionGetLocalCertificates = GetMethod(env, false, g_SSLSession, "getLocalCertificates", "()[Ljava/security/cert/Certificate;"); g_SSLSessionGetPacketBufferSize = GetMethod(env, false, g_SSLSession, "getPacketBufferSize", "()I"); g_SSLSessionGetPeerCertificates = GetMethod(env, false, g_SSLSession, "getPeerCertificates", "()[Ljava/security/cert/Certificate;"); g_SSLSessionGetProtocol = GetMethod(env, false, g_SSLSession, "getProtocol", "()Ljava/lang/String;"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index ccdd8d27c3bc97..7933a8eac3fa21 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -477,6 +477,7 @@ extern jmethodID g_SSLContextCreateSSLEngineMethodWithHostAndPort; extern jclass g_SSLSession; extern jmethodID g_SSLSessionGetApplicationBufferSize; extern jmethodID g_SSLSessionGetCipherSuite; +extern jmethodID g_SSLSessionGetLocalCertificates; extern jmethodID g_SSLSessionGetPacketBufferSize; extern jmethodID g_SSLSessionGetPeerCertificates; extern jmethodID g_SSLSessionGetProtocol; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index b73e2c75fac1e3..637fa7fa89fe74 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -1039,6 +1039,25 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hos return ret; } +bool AndroidCryptoNative_SSLStreamIsLocalCertificateUsed(SSLStream* sslStream) +{ + abort_if_invalid_pointer_argument(sslStream); + JNIEnv* env = GetJNIEnv(); + + bool ret = false; + INIT_LOCALS(loc, localCertificates); + + // X509Certificate[] localCertificates = sslSession.getLocalCertificates(); + loc[localCertificates] = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetLocalCertificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = loc[localCertificates] != NULL; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + bool AndroidCryptoNative_SSLStreamShutdown(SSLStream* sslStream) { abort_if_invalid_pointer_argument (sslStream); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h index db20069eb843cc..51c692440337e9 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -77,6 +77,13 @@ Returns 1 on success, 0 otherwise */ PALEXPORT int32_t AndroidCryptoNative_SSLStreamSetTargetHost(SSLStream* sslStream, char* targetHost); +/* +Check if the local certificate has been sent to the peer during the TLS handshake. + +Returns true if the local certificate has been sent to the peer, false otherwise. +*/ +PALEXPORT bool AndroidCryptoNative_SSLStreamIsLocalCertificateUsed(SSLStream* sslStream); + /* Start or continue the TLS handshake */