From 237d863080bf9887bef39994d0515828ceef8f1b Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 16 Apr 2021 11:00:52 -0700 Subject: [PATCH] [AndroidCrypto] Handle setting non-default application protocols (#51187) --- .../Interop.Ssl.ProtocolSupport.cs | 4 + .../Interop.Ssl.cs | 54 ++++++++- .../HttpClientHandlerTest.SslProtocols.cs | 9 +- .../TestUtilities/System/PlatformDetection.cs | 27 ++++- .../pal_jni.c | 36 +++--- .../pal_jni.h | 14 ++- .../pal_ssl.c | 5 + .../pal_ssl.h | 5 + .../pal_sslstream.c | 107 +++++++++++++++--- .../pal_sslstream.h | 21 +++- .../Pal.Android/SafeDeleteSslContext.cs | 17 ++- .../tests/FunctionalTests/TestHelper.cs | 9 +- 12 files changed, 253 insertions(+), 55 deletions(-) diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs index 65981eccfb3aba..ad49c6276ac29d 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.ProtocolSupport.cs @@ -10,5 +10,9 @@ internal static partial class AndroidCrypto { [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLGetSupportedProtocols")] internal static extern SslProtocols SSLGetSupportedProtocols(); + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration")] + [return:MarshalAs(UnmanagedType.U1)] + internal static extern bool SSLSupportsApplicationProtocolsConfiguration(); } } 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 1f88bc647514da..debe6ca458017c 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 @@ -3,6 +3,8 @@ using System; using System.Buffers; +using System.Collections.Generic; +using System.Net.Security; using System.Runtime.InteropServices; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; @@ -66,19 +68,63 @@ internal static void SSLStreamInitialize( throw new SslException(); } - [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamConfigureParameters")] - private static extern int SSLStreamConfigureParametersImpl( + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamSetTargetHost")] + private static extern int SSLStreamSetTargetHostImpl( SafeSslHandle sslHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost); - internal static void SSLStreamConfigureParameters( + internal static void SSLStreamSetTargetHost( SafeSslHandle sslHandle, string targetHost) { - int ret = SSLStreamConfigureParametersImpl(sslHandle, targetHost); + int ret = SSLStreamSetTargetHostImpl(sslHandle, targetHost); if (ret != SUCCESS) throw new SslException(); } + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRequestClientAuthentication")] + internal static extern void SSLStreamRequestClientAuthentication(SafeSslHandle sslHandle); + + [StructLayout(LayoutKind.Sequential)] + private unsafe struct ApplicationProtocolData + { + public byte* Data; + public int Length; + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamSetApplicationProtocols")] + private static unsafe extern int SSLStreamSetApplicationProtocols(SafeSslHandle sslHandle, ApplicationProtocolData[] protocolData, int count); + internal static unsafe void SSLStreamSetApplicationProtocols(SafeSslHandle sslHandle, List protocols) + { + int count = protocols.Count; + MemoryHandle[] memHandles = new MemoryHandle[count]; + ApplicationProtocolData[] protocolData = new ApplicationProtocolData[count]; + try + { + for (int i = 0; i < count; i++) + { + ReadOnlyMemory protocol = protocols[i].Protocol; + memHandles[i] = protocol.Pin(); + protocolData[i] = new ApplicationProtocolData + { + Data = (byte*)memHandles[i].Pointer, + Length = protocol.Length + }; + } + int ret = SSLStreamSetApplicationProtocols(sslHandle, protocolData, count); + if (ret != SUCCESS) + { + throw new SslException(); + } + } + finally + { + foreach (MemoryHandle memHandle in memHandles) + { + memHandle.Dispose(); + } + } + } + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamSetEnabledProtocols")] private static extern int SSLStreamSetEnabledProtocols(SafeSslHandle sslHandle, ref SslProtocols protocols, int length); internal static void SSLStreamSetEnabledProtocols(SafeSslHandle sslHandle, ReadOnlySpan protocols) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs index 1d521de2f10165..da250bf305f32c 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.SslProtocols.cs @@ -142,7 +142,14 @@ await TestHelper.WhenAllCompletedOrAnyFailed( string GetTestSNIName() { - return $"{nameof(GetAsync_AllowedSSLVersion_Succeeds)}_{acceptedProtocol}_{requestOnlyThisProtocol}"; + string name = $"{nameof(GetAsync_AllowedSSLVersion_Succeeds)}_{acceptedProtocol}_{requestOnlyThisProtocol}"; + if (PlatformDetection.IsAndroid) + { + // Android does not support underscores in host names + name = name.Replace("_", string.Empty); + } + + return name; } } diff --git a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs index 57b2df23622ce2..cb41c9f5295c8a 100644 --- a/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs +++ b/src/libraries/Common/tests/TestUtilities/System/PlatformDetection.cs @@ -134,15 +134,34 @@ public static bool IsNonZeroLowerBoundArraySupported public static bool IsDomainJoinedMachine => !Environment.MachineName.Equals(Environment.UserDomainName, StringComparison.OrdinalIgnoreCase); public static bool IsNotDomainJoinedMachine => !IsDomainJoinedMachine; + public static bool IsOpenSslSupported => IsLinux || IsFreeBSD || Isillumos || IsSolaris; + // Windows - Schannel supports alpn from win8.1/2012 R2 and higher. // Linux - OpenSsl supports alpn from openssl 1.0.2 and higher. // OSX - SecureTransport doesn't expose alpn APIs. TODO https://github.com/dotnet/runtime/issues/27727 - public static bool IsOpenSslSupported => IsLinux || IsFreeBSD || Isillumos || IsSolaris; + // Android - Platform supports alpn from API level 29 and higher + private static Lazy s_supportsAlpn = new Lazy(GetAlpnSupport); + private static bool GetAlpnSupport() + { + if (IsWindows && !IsWindows7 && !IsNetFramework) + { + return true; + } - public static bool SupportsAlpn => (IsWindows && !IsWindows7 && !IsNetFramework) || - (IsOpenSslSupported && - (OpenSslVersion.Major >= 1 && (OpenSslVersion.Minor >= 1 || OpenSslVersion.Build >= 2))); + if (IsOpenSslSupported) + { + return OpenSslVersion.Major >= 1 && (OpenSslVersion.Minor >= 1 || OpenSslVersion.Build >= 2); + } + + if (IsAndroid) + { + return Interop.AndroidCrypto.SSLSupportsApplicationProtocolsConfiguration(); + } + + return false; + } + public static bool SupportsAlpn => s_supportsAlpn.Value; public static bool SupportsClientAlpn => SupportsAlpn || IsOSX || IsMacCatalyst || IsiOS || IstvOS; private static Lazy s_supportsTls10 = new Lazy(GetTls10Support); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c index 280ef1dfbabd02..162c0fbf74f6ec 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.c @@ -79,8 +79,8 @@ jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters jclass g_SSLParametersClass; -jmethodID g_SSLParametersCtor; jmethodID g_SSLParametersGetProtocols; +jmethodID g_SSLParametersSetApplicationProtocols; jmethodID g_SSLParametersSetServerNames; // javax/net/ssl/SSLContext @@ -397,17 +397,19 @@ jmethodID g_SNIHostNameCtor; // javax/net/ssl/SSLEngine jclass g_SSLEngine; -jmethodID g_SSLEngineGetApplicationProtocol; -jmethodID g_SSLEngineSetUseClientMode; -jmethodID g_SSLEngineGetSession; jmethodID g_SSLEngineBeginHandshake; -jmethodID g_SSLEngineWrap; -jmethodID g_SSLEngineUnwrap; jmethodID g_SSLEngineCloseOutbound; +jmethodID g_SSLEngineGetApplicationProtocol; jmethodID g_SSLEngineGetHandshakeStatus; +jmethodID g_SSLEngineGetSession; +jmethodID g_SSLEngineGetSSLParameters; jmethodID g_SSLEngineGetSupportedProtocols; jmethodID g_SSLEngineSetEnabledProtocols; jmethodID g_SSLEngineSetSSLParameters; +jmethodID g_SSLEngineSetUseClientMode; +jmethodID g_SSLEngineSetWantClientAuth; +jmethodID g_SSLEngineUnwrap; +jmethodID g_SSLEngineWrap; // java/nio/ByteBuffer jclass g_ByteBuffer; @@ -711,10 +713,10 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_bitLengthMethod = GetMethod(env, false, g_bigNumClass, "bitLength", "()I"); g_sigNumMethod = GetMethod(env, false, g_bigNumClass, "signum", "()I"); - g_SSLParametersClass = GetClassGRef(env, "javax/net/ssl/SSLParameters"); - g_SSLParametersCtor = GetMethod(env, false, g_SSLParametersClass, "", "()V"); - g_SSLParametersGetProtocols = GetMethod(env, false, g_SSLParametersClass, "getProtocols", "()[Ljava/lang/String;"); - g_SSLParametersSetServerNames = GetMethod(env, false, g_SSLParametersClass, "setServerNames", "(Ljava/util/List;)V"); + g_SSLParametersClass = GetClassGRef(env, "javax/net/ssl/SSLParameters"); + g_SSLParametersGetProtocols = GetMethod(env, false, g_SSLParametersClass, "getProtocols", "()[Ljava/lang/String;"); + g_SSLParametersSetApplicationProtocols = GetOptionalMethod(env, false, g_SSLParametersClass, "setApplicationProtocols", "([Ljava/lang/String;)V"); + g_SSLParametersSetServerNames = GetMethod(env, false, g_SSLParametersClass, "setServerNames", "(Ljava/util/List;)V"); g_sslCtxClass = GetClassGRef(env, "javax/net/ssl/SSLContext"); g_sslCtxGetDefaultMethod = GetMethod(env, true, g_sslCtxClass, "getDefault", "()Ljavax/net/ssl/SSLContext;"); @@ -970,17 +972,19 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SNIHostNameCtor = GetMethod(env, false, g_SNIHostName, "", "(Ljava/lang/String;)V"); g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); - g_SSLEngineGetApplicationProtocol = GetMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); - g_SSLEngineSetUseClientMode = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); - g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineBeginHandshake = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); - g_SSLEngineWrap = GetMethod(env, false, g_SSLEngine, "wrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); - g_SSLEngineUnwrap = GetMethod(env, false, g_SSLEngine, "unwrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); - g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineCloseOutbound = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); + g_SSLEngineGetApplicationProtocol = GetMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); + g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); + g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); + g_SSLEngineGetSSLParameters = GetMethod(env, false, g_SSLEngine, "getSSLParameters", "()Ljavax/net/ssl/SSLParameters;"); g_SSLEngineGetSupportedProtocols = GetMethod(env, false, g_SSLEngine, "getSupportedProtocols", "()[Ljava/lang/String;"); g_SSLEngineSetEnabledProtocols = GetMethod(env, false, g_SSLEngine, "setEnabledProtocols", "([Ljava/lang/String;)V"); g_SSLEngineSetSSLParameters = GetMethod(env, false, g_SSLEngine, "setSSLParameters", "(Ljavax/net/ssl/SSLParameters;)V"); + g_SSLEngineSetUseClientMode = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); + g_SSLEngineSetWantClientAuth = GetMethod(env, false, g_SSLEngine, "setWantClientAuth", "(Z)V"); + g_SSLEngineUnwrap = GetMethod(env, false, g_SSLEngine, "unwrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); + g_SSLEngineWrap = GetMethod(env, false, g_SSLEngine, "wrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); g_ByteBufferAllocate = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h index 9d6f7199376d0d..8edd030da9e74e 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_jni.h @@ -88,8 +88,8 @@ extern jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters extern jclass g_SSLParametersClass; -extern jmethodID g_SSLParametersCtor; extern jmethodID g_SSLParametersGetProtocols; +extern jmethodID g_SSLParametersSetApplicationProtocols; extern jmethodID g_SSLParametersSetServerNames; // javax/net/ssl/SSLContext @@ -411,17 +411,19 @@ extern jmethodID g_SNIHostNameCtor; // javax/net/ssl/SSLEngine extern jclass g_SSLEngine; -extern jmethodID g_SSLEngineGetApplicationProtocol; -extern jmethodID g_SSLEngineSetUseClientMode; -extern jmethodID g_SSLEngineGetSession; extern jmethodID g_SSLEngineBeginHandshake; -extern jmethodID g_SSLEngineWrap; -extern jmethodID g_SSLEngineUnwrap; extern jmethodID g_SSLEngineCloseOutbound; +extern jmethodID g_SSLEngineGetApplicationProtocol; extern jmethodID g_SSLEngineGetHandshakeStatus; +extern jmethodID g_SSLEngineGetSession; +extern jmethodID g_SSLEngineGetSSLParameters; extern jmethodID g_SSLEngineGetSupportedProtocols; extern jmethodID g_SSLEngineSetEnabledProtocols; extern jmethodID g_SSLEngineSetSSLParameters; +extern jmethodID g_SSLEngineSetUseClientMode; +extern jmethodID g_SSLEngineSetWantClientAuth; +extern jmethodID g_SSLEngineUnwrap; +extern jmethodID g_SSLEngineWrap; // java/nio/ByteBuffer extern jclass g_ByteBuffer; diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c index e512c77ae66f6b..29571dd0289bad 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.c @@ -51,3 +51,8 @@ PAL_SslProtocol AndroidCryptoNative_SSLGetSupportedProtocols(void) RELEASE_LOCALS(loc, env); return supported; } + +bool AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration(void) +{ + return g_SSLParametersSetApplicationProtocols != NULL; +} diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h index d4f1dcd23bd8c8..3a77d9c1a29673 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_ssl.h @@ -10,3 +10,8 @@ Get the supported protocols */ PALEXPORT PAL_SslProtocol AndroidCryptoNative_SSLGetSupportedProtocols(void); + +/* +Returns whether or not configuration of application protocols is supported +*/ +PALEXPORT bool AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration(void); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c index 65b2479b2fadef..7f82df1e986900 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. #include "pal_sslstream.h" +#include "pal_ssl.h" // javax/net/ssl/SSLEngineResult$HandshakeStatus enum @@ -22,6 +23,12 @@ enum STATUS__CLOSED = 3, }; +struct ApplicationProtocolData_t +{ + uint8_t* data; + int32_t length; +}; + static uint16_t* AllocateString(JNIEnv* env, jstring source); static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream); @@ -283,10 +290,6 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(void) { JNIEnv* env = GetJNIEnv(); - // TODO: [AndroidCrypto] If we have certificates, get an SSLContext instance with the highest available - // protocol - TLSv1.2 (API level 16+) or TLSv1.3 (API level 29+), use KeyManagerFactory to create key - // managers that will return the certificates, and initialize the SSLContext with the key managers. - // SSLContext sslContext = SSLContext.getDefault(); jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetDefault); if (CheckJNIExceptions(env)) @@ -473,7 +476,7 @@ int32_t AndroidCryptoNative_SSLStreamInitialize( return ret; } -int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost) +int32_t AndroidCryptoNative_SSLStreamSetTargetHost(SSLStream* sslStream, char* targetHost) { assert(sslStream != NULL); assert(targetHost != NULL); @@ -494,10 +497,10 @@ int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, c (*env)->CallBooleanMethod(env, loc[nameList], g_ArrayListAdd, loc[hostName]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // SSLParameters params = new SSLParameters(); + // SSLParameters params = sslEngine.getSSLParameters(); // params.setServerNames(nameList); // sslEngine.setSSLParameters(params); - loc[params] = (*env)->NewObject(env, g_SSLParametersClass, g_SSLParametersCtor); + loc[params] = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSSLParameters); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); (*env)->CallVoidMethod(env, loc[params], g_SSLParametersSetServerNames, loc[nameList]); (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetSSLParameters, loc[params]); @@ -604,18 +607,35 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uin JNIEnv* env = GetJNIEnv(); PAL_SSLStreamStatus ret = SSLStreamStatus_Error; - // byte[] data = new byte[] { } - // appOutBuffer.put(data); - jbyteArray data = (*env)->NewByteArray(env, length); - (*env)->SetByteArrayRegion(env, data, 0, length, (jbyte*)buffer); - IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPutByteArray, data)); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + // int remaining = appOutBuffer.remaining(); + // int arraySize = length > remaining ? remaining : length; + // byte[] data = new byte[arraySize]; + int32_t remaining = (*env)->CallIntMethod(env, sslStream->appOutBuffer, g_ByteBufferRemaining); + int32_t arraySize = length > remaining ? remaining : length; + jbyteArray data = (*env)->NewByteArray(env, arraySize); - int handshakeStatus; - ret = DoWrap(env, sslStream, &handshakeStatus); - if (ret == SSLStreamStatus_OK && IsHandshaking(handshakeStatus)) + int32_t written = 0; + while (written < length) { - ret = SSLStreamStatus_Renegotiate; + int32_t toWrite = length - written > arraySize ? arraySize : length - written; + (*env)->SetByteArrayRegion(env, data, 0, toWrite, (jbyte*)(buffer + written)); + + // appOutBuffer.put(data, 0, toWrite); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPutByteArrayWithLength, data, 0, toWrite)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + written += toWrite; + + int handshakeStatus; + ret = DoWrap(env, sslStream, &handshakeStatus); + if (ret != SSLStreamStatus_OK) + { + goto cleanup; + } + else if (IsHandshaking(handshakeStatus)) + { + ret = SSLStreamStatus_Renegotiate; + goto cleanup; + } } cleanup: @@ -766,6 +786,59 @@ void AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobj (*env)->DeleteLocalRef(env, certs); } +void AndroidCryptoNative_SSLStreamRequestClientAuthentication(SSLStream* sslStream) +{ + assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); + + // sslEngine.setWantClientAuth(true); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetWantClientAuth, true); +} + +int32_t AndroidCryptoNative_SSLStreamSetApplicationProtocols(SSLStream* sslStream, + ApplicationProtocolData* protocolData, + int32_t count) +{ + assert(sslStream != NULL); + assert(protocolData != NULL); + assert(AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration()); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + INIT_LOCALS(loc, protocols, params); + + // String[] protocols = new String[count]; + loc[protocols] = (*env)->NewObjectArray(env, count, g_String, NULL); + for (int32_t i = 0; i < count; ++i) + { + // Data length + 1 for null terminator + int32_t len = protocolData[i].length; + char* data = (char*)malloc((size_t)(len + 1) * sizeof(char)); + memcpy(data, protocolData[i].data, (size_t)len); + data[len] = '\0'; + + jstring protocol = JSTRING(data); + free(data); + (*env)->SetObjectArrayElement(env, loc[protocols], i, protocol); + (*env)->DeleteLocalRef(env, protocol); + } + + // SSLParameters params = sslEngine.getSSLParameters(); + // params.setApplicationProtocols(protocols); + // sslEngine.setSSLParameters(params); + loc[params] = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSSLParameters); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[params], g_SSLParametersSetApplicationProtocols, loc[protocols]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetSSLParameters, loc[params]); + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + static jstring GetSslProtocolAsString(JNIEnv* env, PAL_SslProtocol protocol) { switch (protocol) diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h index 58ecc3440148b0..07a40bf8315cc6 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -24,6 +24,8 @@ typedef struct SSLStream STREAM_WRITER streamWriter; } SSLStream; +typedef struct ApplicationProtocolData_t ApplicationProtocolData; + // Matches managed PAL_SSLStreamStatus enum enum { @@ -66,12 +68,12 @@ PALEXPORT int32_t AndroidCryptoNative_SSLStreamInitialize( SSLStream* sslStream, bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize); /* -Set configuration parameters +Set target host - targetHost : SNI host name Returns 1 on success, 0 otherwise */ -PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamSetTargetHost(SSLStream* sslStream, char* targetHost); /* Start or continue the TLS handshake @@ -143,6 +145,21 @@ PALEXPORT void AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslSt jobject** /*X509Certificate[]*/ out, int32_t* outLen); +/* +Configure the session to request client authentication +*/ +PALEXPORT void AndroidCryptoNative_SSLStreamRequestClientAuthentication(SSLStream* sslStream); + +/* +Set application protocols + - protocolData : array of application protocols to set + - count : number of elements in protocolData +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamSetApplicationProtocols(SSLStream* sslStream, + ApplicationProtocolData* protocolData, + int32_t count); + /* Set enabled protocols - protocols : array of protocols to enable diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs index 69830d9ba75d68..b0177cbc432322 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs @@ -213,9 +213,7 @@ private static void InitializeSslContext( bool isServer = authOptions.IsServer; - if (authOptions.ApplicationProtocols != null - || authOptions.CipherSuitesPolicy != null - || (isServer && authOptions.RemoteCertRequired)) + if (authOptions.CipherSuitesPolicy != null) { // TODO: [AndroidCrypto] Handle non-system-default options throw new NotImplementedException(nameof(SafeDeleteSslContext)); @@ -235,9 +233,20 @@ private static void InitializeSslContext( Interop.AndroidCrypto.SSLStreamSetEnabledProtocols(handle, s_orderedSslProtocols.AsSpan(minIndex, maxIndex - minIndex + 1)); } + if (authOptions.ApplicationProtocols != null && Interop.AndroidCrypto.SSLSupportsApplicationProtocolsConfiguration()) + { + // Set application protocols if the platform supports it. Otherwise, we will silently ignore the option. + Interop.AndroidCrypto.SSLStreamSetApplicationProtocols(handle, authOptions.ApplicationProtocols); + } + + if (isServer && authOptions.RemoteCertRequired) + { + Interop.AndroidCrypto.SSLStreamRequestClientAuthentication(handle); + } + if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) { - Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); + Interop.AndroidCrypto.SSLStreamSetTargetHost(handle, authOptions.TargetHost); } } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs index 200f8a8e4d68a1..fabda4d8fa4a3b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs @@ -233,7 +233,14 @@ static string ProtocolToString(SslProtocols? protocol) var args = string.Join(".", protocols.Select(p => ProtocolToString(p))); var name = testMethodName.Length > 63 ? testMethodName.Substring(0, 63) : testMethodName; - return $"{name}.{args}"; + name = $"{name}.{args}"; + if (PlatformDetection.IsAndroid) + { + // Android does not support underscores in host names + name = name.Replace("_", string.Empty); + } + + return name; } } }