Skip to content

Commit ec54d10

Browse files
committed
Fix ssl on APIs 21-23
Code improvements
1 parent fa76d3c commit ec54d10

File tree

6 files changed

+128
-14
lines changed

6 files changed

+128
-14
lines changed

src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Ssl.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,18 @@ private static unsafe partial int SSLStreamInitializeImpl(
5555
IntPtr managedContextHandle,
5656
delegate* unmanaged<IntPtr, byte*, int*, PAL_SSLStreamStatus> streamRead,
5757
delegate* unmanaged<IntPtr, byte*, int, void> streamWrite,
58-
int appBufferSize);
58+
int appBufferSize,
59+
[MarshalAs(UnmanagedType.LPUTF8Str)] string? peerHost);
5960
internal static unsafe void SSLStreamInitialize(
6061
SafeSslHandle sslHandle,
6162
bool isServer,
6263
IntPtr managedContextHandle,
6364
delegate* unmanaged<IntPtr, byte*, int*, PAL_SSLStreamStatus> streamRead,
6465
delegate* unmanaged<IntPtr, byte*, int, void> streamWrite,
65-
int appBufferSize)
66+
int appBufferSize,
67+
string? peerHost)
6668
{
67-
int ret = SSLStreamInitializeImpl(sslHandle, isServer, managedContextHandle, streamRead, streamWrite, appBufferSize);
69+
int ret = SSLStreamInitializeImpl(sslHandle, isServer, managedContextHandle, streamRead, streamWrite, appBufferSize, peerHost);
6870
if (ret != SUCCESS)
6971
throw new SslException();
7072
}

src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/SafeDeleteSslContext.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ private unsafe void InitializeSslContext(
221221
// Make sure the class instance is associated to the session and is provided
222222
// in the Read/Write callback connection parameter
223223
IntPtr managedContextHandle = GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.Weak));
224-
Interop.AndroidCrypto.SSLStreamInitialize(handle, isServer, managedContextHandle, &ReadFromConnection, &WriteToConnection, InitialBufferSize);
224+
string? peerHost = !isServer && !string.IsNullOrEmpty(authOptions.TargetHost) ? authOptions.TargetHost : null;
225+
Interop.AndroidCrypto.SSLStreamInitialize(handle, isServer, managedContextHandle, &ReadFromConnection, &WriteToConnection, InitialBufferSize, peerHost);
225226

226227
if (authOptions.EnabledSslProtocols != SslProtocols.None)
227228
{

src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,14 @@ jmethodID g_SSLParametersGetProtocols;
8383
jmethodID g_SSLParametersSetApplicationProtocols;
8484
jmethodID g_SSLParametersSetServerNames;
8585

86+
// com/android/org/conscrypt/OpenSSLEngineImpl
87+
jclass g_ConscryptOpenSSLEngineImplClass;
88+
jfieldID g_ConscryptOpenSSLEngineImplSslParametersField;
89+
90+
// com/android/org/conscrypt/SSLParametersImpl
91+
jclass g_ConscryptSSLParametersImplClass;
92+
jmethodID g_ConscryptSSLParametersImplSetUseSni;
93+
8694
// javax/net/ssl/SSLContext
8795
jclass g_sslCtxClass;
8896
jmethodID g_sslCtxGetDefaultMethod;
@@ -448,6 +456,7 @@ jmethodID g_SSLContextGetDefault;
448456
jmethodID g_SSLContextGetInstanceMethod;
449457
jmethodID g_SSLContextInitMethod;
450458
jmethodID g_SSLContextCreateSSLEngineMethod;
459+
jmethodID g_SSLContextCreateSSLEngineMethodWithHostAndPort;
451460

452461
// javax/net/ssl/SSLSession
453462
jclass g_SSLSession;
@@ -461,6 +470,7 @@ jmethodID g_SSLSessionGetProtocol;
461470
jclass g_SSLEngineResult;
462471
jmethodID g_SSLEngineResultGetStatus;
463472
jmethodID g_SSLEngineResultGetHandshakeStatus;
473+
bool g_SSLEngineResultStatusLegacyOrder;
464474

465475
// javax/crypto/KeyAgreement
466476
jclass g_KeyAgreementClass;
@@ -738,6 +748,15 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
738748
g_SSLParametersGetProtocols = GetMethod(env, false, g_SSLParametersClass, "getProtocols", "()[Ljava/lang/String;");
739749
g_SSLParametersSetApplicationProtocols = GetOptionalMethod(env, false, g_SSLParametersClass, "setApplicationProtocols", "([Ljava/lang/String;)V");
740750

751+
g_ConscryptOpenSSLEngineImplClass = GetOptionalClassGRef(env, "com/android/org/conscrypt/OpenSSLEngineImpl");
752+
if (g_ConscryptOpenSSLEngineImplClass != NULL)
753+
{
754+
g_ConscryptOpenSSLEngineImplSslParametersField = GetField(env, false, g_ConscryptOpenSSLEngineImplClass, "sslParameters", "Lcom/android/org/conscrypt/SSLParametersImpl;");
755+
756+
g_ConscryptSSLParametersImplClass = GetClassGRef(env, "com/android/org/conscrypt/SSLParametersImpl");
757+
g_ConscryptSSLParametersImplSetUseSni = GetMethod(env, false, g_ConscryptSSLParametersImplClass, "setUseSni", "(Z)V");
758+
}
759+
741760
g_sslCtxClass = GetClassGRef(env, "javax/net/ssl/SSLContext");
742761
g_sslCtxGetDefaultMethod = GetMethod(env, true, g_sslCtxClass, "getDefault", "()Ljavax/net/ssl/SSLContext;");
743762
g_sslCtxGetDefaultSslParamsMethod = GetMethod(env, false, g_sslCtxClass, "getDefaultSSLParameters", "()Ljavax/net/ssl/SSLParameters;");
@@ -1028,6 +1047,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
10281047
g_SSLContextGetInstanceMethod = GetMethod(env, true, g_SSLContext, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/SSLContext;");
10291048
g_SSLContextInitMethod = GetMethod(env, false, g_SSLContext, "init", "([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;Ljava/security/SecureRandom;)V");
10301049
g_SSLContextCreateSSLEngineMethod = GetMethod(env, false, g_SSLContext, "createSSLEngine", "()Ljavax/net/ssl/SSLEngine;");
1050+
g_SSLContextCreateSSLEngineMethodWithHostAndPort = GetMethod(env, false, g_SSLContext, "createSSLEngine", "(Ljava/lang/String;I)Ljavax/net/ssl/SSLEngine;");
10311051

10321052
g_SSLSession = GetClassGRef(env, "javax/net/ssl/SSLSession");
10331053
g_SSLSessionGetApplicationBufferSize = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I");
@@ -1039,6 +1059,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
10391059
g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult");
10401060
g_SSLEngineResultGetStatus = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;");
10411061
g_SSLEngineResultGetHandshakeStatus = GetMethod(env, false, g_SSLEngineResult, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;");
1062+
g_SSLEngineResultStatusLegacyOrder = android_get_device_api_level() < 24;
10421063

10431064
g_KeyAgreementClass = GetClassGRef(env, "javax/crypto/KeyAgreement");
10441065
g_KeyAgreementGetInstance = GetMethod(env, true, g_KeyAgreementClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/KeyAgreement;");

src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ extern jmethodID g_SSLParametersGetProtocols;
9595
extern jmethodID g_SSLParametersSetApplicationProtocols;
9696
extern jmethodID g_SSLParametersSetServerNames;
9797

98+
// com/android/org/conscrypt/OpenSSLEngineImpl
99+
extern jclass g_ConscryptOpenSSLEngineImplClass;
100+
extern jfieldID g_ConscryptOpenSSLEngineImplSslParametersField;
101+
102+
// com/android/org/conscrypt/SSLParametersImpl
103+
extern jclass g_ConscryptSSLParametersImplClass;
104+
extern jmethodID g_ConscryptSSLParametersImplSetUseSni;
105+
98106
// javax/net/ssl/SSLContext
99107
extern jclass g_sslCtxClass;
100108
extern jmethodID g_sslCtxGetDefaultMethod;
@@ -462,7 +470,7 @@ extern jmethodID g_SSLContextGetDefault;
462470
extern jmethodID g_SSLContextGetInstanceMethod;
463471
extern jmethodID g_SSLContextInitMethod;
464472
extern jmethodID g_SSLContextCreateSSLEngineMethod;
465-
extern jmethodID g_SSLContextCreateSSLEngineWithPeer;
473+
extern jmethodID g_SSLContextCreateSSLEngineMethodWithHostAndPort;
466474

467475
// javax/net/ssl/SSLSession
468476
extern jclass g_SSLSession;
@@ -476,6 +484,7 @@ extern jmethodID g_SSLSessionGetProtocol;
476484
extern jclass g_SSLEngineResult;
477485
extern jmethodID g_SSLEngineResultGetStatus;
478486
extern jmethodID g_SSLEngineResultGetHandshakeStatus;
487+
extern bool g_SSLEngineResultStatusLegacyOrder;
479488

480489
// javax/crypto/KeyAgreement
481490
extern jclass g_KeyAgreementClass;

src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c

Lines changed: 89 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ enum
1515
};
1616

1717
// javax/net/ssl/SSLEngineResult$Status
18+
// Android API 24+
1819
enum
1920
{
2021
STATUS__BUFFER_UNDERFLOW = 0,
@@ -23,6 +24,16 @@ enum
2324
STATUS__CLOSED = 3,
2425
};
2526

27+
// javax/net/ssl/SSLEngineResult$Status
28+
// Android API 21-23
29+
enum
30+
{
31+
LEGACY__STATUS__BUFFER_OVERFLOW = 0,
32+
LEGACY__STATUS__BUFFER_UNDERFLOW = 1,
33+
LEGACY__STATUS__OK = 3,
34+
LEGACY__STATUS__CLOSED = 2,
35+
};
36+
2637
struct ApplicationProtocolData_t
2738
{
2839
uint8_t* data;
@@ -115,6 +126,25 @@ ARGS_NON_NULL_ALL static jobject EnsureRemaining(JNIEnv* env, jobject oldBuffer,
115126
}
116127
}
117128

129+
// There has been a change in the SSLEngineResult.Status enum between API 23 and 24 that changed
130+
// the order/interger values of the enum options.
131+
static int MapLegacySSLEngineResultStatus(int legacyStatus) {
132+
switch (legacyStatus) {
133+
case LEGACY__STATUS__BUFFER_OVERFLOW:
134+
return STATUS__BUFFER_OVERFLOW;
135+
case LEGACY__STATUS__BUFFER_UNDERFLOW:
136+
return STATUS__BUFFER_UNDERFLOW;
137+
case LEGACY__STATUS__CLOSED:
138+
return STATUS__CLOSED;
139+
case LEGACY__STATUS__OK:
140+
return STATUS__OK;
141+
default:
142+
LOG_ERROR("Unknown legacy SSLEngineResult status: %d", legacyStatus);
143+
assert(false && "Unknown SSLEngineResult status");
144+
return -1;
145+
}
146+
}
147+
118148
ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus)
119149
{
120150
// appOutBuffer.flip();
@@ -134,6 +164,10 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslS
134164
int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus));
135165
(*env)->DeleteLocalRef(env, result);
136166

167+
if (g_SSLEngineResultStatusLegacyOrder) {
168+
status = MapLegacySSLEngineResultStatus(status);
169+
}
170+
137171
switch (status)
138172
{
139173
case STATUS__OK:
@@ -208,6 +242,11 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss
208242
*handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetHandshakeStatus));
209243
int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus));
210244
(*env)->DeleteLocalRef(env, result);
245+
246+
if (g_SSLEngineResultStatusLegacyOrder) {
247+
status = MapLegacySSLEngineResultStatus(status);
248+
}
249+
211250
switch (status)
212251
{
213252
case STATUS__OK:
@@ -425,7 +464,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8Pri
425464
}
426465

427466
int32_t AndroidCryptoNative_SSLStreamInitialize(
428-
SSLStream* sslStream, bool isServer, ManagedContextHandle managedContextHandle, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize)
467+
SSLStream* sslStream, bool isServer, ManagedContextHandle managedContextHandle, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize, char* peerHost)
429468
{
430469
abort_if_invalid_pointer_argument (sslStream);
431470
abort_unless(sslStream->sslContext != NULL, "sslContext is NULL in SSL stream");
@@ -435,10 +474,23 @@ int32_t AndroidCryptoNative_SSLStreamInitialize(
435474
int32_t ret = FAIL;
436475
JNIEnv* env = GetJNIEnv();
437476

438-
// SSLEngine sslEngine = sslContext.createSSLEngine();
477+
jobject sslEngine = NULL;
478+
if (peerHost)
479+
{
480+
// SSLEngine sslEngine = sslContext.createSSLEngine(peerHost, -1);
481+
jstring peerHostStr = make_java_string(env, peerHost);
482+
sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethodWithHostAndPort, peerHostStr, -1);
483+
ReleaseLRef(env, peerHostStr);
484+
ON_EXCEPTION_PRINT_AND_GOTO(exit);
485+
}
486+
else
487+
{
488+
// SSLEngine sslEngine = sslContext.createSSLEngine();
489+
sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod);
490+
ON_EXCEPTION_PRINT_AND_GOTO(exit);
491+
}
492+
439493
// sslEngine.setUseClientMode(!isServer);
440-
jobject sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod);
441-
ON_EXCEPTION_PRINT_AND_GOTO(exit);
442494
sslStream->sslEngine = ToGRef(env, sslEngine);
443495
(*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientMode, !isServer);
444496
ON_EXCEPTION_PRINT_AND_GOTO(exit);
@@ -476,19 +528,48 @@ int32_t AndroidCryptoNative_SSLStreamInitialize(
476528
return ret;
477529
}
478530

531+
// This method calls internal Android APIs that are specific to Android API 21-23 and it won't work
532+
// on newer API levels. By calling the sslEngine.sslParameters.useSni(true) method, the SSLEngine
533+
// will include the peerHost that was passed in to the SSLEngine factory method in the client hello
534+
// message.
535+
ARGS_NON_NULL_ALL static int32_t ApplyLegacyAndroidSNIWorkaround(JNIEnv* env, SSLStream* sslStream)
536+
{
537+
if (g_ConscryptOpenSSLEngineImplClass == NULL || !(*env)->IsInstanceOf(env, sslStream->sslEngine, g_ConscryptOpenSSLEngineImplClass))
538+
return FAIL;
539+
540+
int32_t ret = FAIL;
541+
INIT_LOCALS(loc, sslParameters);
542+
543+
loc[sslParameters] = (*env)->GetObjectField(env, sslStream->sslEngine, g_ConscryptOpenSSLEngineImplSslParametersField);
544+
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
545+
546+
if (!loc[sslParameters])
547+
goto cleanup;
548+
549+
(*env)->CallVoidMethod(env, loc[sslParameters], g_ConscryptSSLParametersImplSetUseSni, true);
550+
ON_EXCEPTION_PRINT_AND_GOTO(cleanup);
551+
552+
ret = SUCCESS;
553+
554+
cleanup:
555+
RELEASE_LOCALS(loc, env);
556+
return ret;
557+
}
558+
479559
int32_t AndroidCryptoNative_SSLStreamSetTargetHost(SSLStream* sslStream, char* targetHost)
480560
{
481561
abort_if_invalid_pointer_argument (sslStream);
482562
abort_if_invalid_pointer_argument (targetHost);
483563

564+
JNIEnv* env = GetJNIEnv();
565+
484566
if (g_SNIHostName == NULL || g_SSLParametersSetServerNames == NULL)
485567
{
486-
// SSL not supported below API Level 24
487-
return UNSUPPORTED_API_LEVEL;
568+
// SNIHostName is only available since API 24
569+
// on APIs 21-23 we use a workaround to force the SSLEngine to use SNI
570+
return ApplyLegacyAndroidSNIWorkaround(env, sslStream);
488571
}
489572

490-
JNIEnv* env = GetJNIEnv();
491-
492573
int32_t ret = FAIL;
493574
INIT_LOCALS(loc, hostStr, nameList, hostName, params);
494575

src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Initialize an SSL context
6767
Returns 1 on success, 0 otherwise
6868
*/
6969
PALEXPORT int32_t AndroidCryptoNative_SSLStreamInitialize(
70-
SSLStream* sslStream, bool isServer, ManagedContextHandle managedContextHandle, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize);
70+
SSLStream* sslStream, bool isServer, ManagedContextHandle managedContextHandle, STREAM_READER streamReader, STREAM_WRITER streamWriter, int32_t appBufferSize, char* peerHost);
7171

7272
/*
7373
Set target host

0 commit comments

Comments
 (0)