Skip to content

Commit dc9c9d5

Browse files
[Android] Fix SslStream on Android API 21-23 (#94408)
1 parent 173eb1f commit dc9c9d5

3 files changed

Lines changed: 44 additions & 7 deletions

File tree

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ jmethodID g_SSLParametersSetServerNames;
8686
// com/android/org/conscrypt/OpenSSLEngineImpl
8787
jclass g_ConscryptOpenSSLEngineImplClass;
8888
jfieldID g_ConscryptOpenSSLEngineImplSslParametersField;
89+
jfieldID g_ConscryptOpenSSLEngineImplHandshakeSessionField;
8990

9091
// com/android/org/conscrypt/SSLParametersImpl
9192
jclass g_ConscryptSSLParametersImplClass;
@@ -627,6 +628,17 @@ jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, co
627628
return fid;
628629
}
629630

631+
jfieldID GetOptionalField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig)
632+
{
633+
jfieldID fid = isStatic ? (*env)->GetStaticFieldID(env, klass, name, sig) : (*env)->GetFieldID(env, klass, name, sig);
634+
if (!fid) {
635+
LOG_INFO("optional field %s %s was not found", name, sig);
636+
// Failing to find an optional field causes an exception state, which we need to clear.
637+
TryClearJNIExceptions(env);
638+
}
639+
return fid;
640+
}
641+
630642
static void DetachThreadFromJNI(void* unused)
631643
{
632644
LOG_DEBUG("Detaching thread from JNI");
@@ -761,6 +773,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
761773
if (g_ConscryptOpenSSLEngineImplClass != NULL)
762774
{
763775
g_ConscryptOpenSSLEngineImplSslParametersField = GetField(env, false, g_ConscryptOpenSSLEngineImplClass, "sslParameters", "Lcom/android/org/conscrypt/SSLParametersImpl;");
776+
g_ConscryptOpenSSLEngineImplHandshakeSessionField = GetOptionalField(env, false, g_ConscryptOpenSSLEngineImplClass, "handshakeSession", "Lcom/android/org/conscrypt/OpenSSLSessionImpl;");
764777

765778
g_ConscryptSSLParametersImplClass = GetClassGRef(env, "com/android/org/conscrypt/SSLParametersImpl");
766779
g_ConscryptSSLParametersImplSetUseSni = GetMethod(env, false, g_ConscryptSSLParametersImplClass, "setUseSni", "(Z)V");

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ extern jmethodID g_SSLParametersSetServerNames;
9898
// com/android/org/conscrypt/OpenSSLEngineImpl
9999
extern jclass g_ConscryptOpenSSLEngineImplClass;
100100
extern jfieldID g_ConscryptOpenSSLEngineImplSslParametersField;
101+
extern jfieldID g_ConscryptOpenSSLEngineImplHandshakeSessionField;
101102

102103
// com/android/org/conscrypt/SSLParametersImpl
103104
extern jclass g_ConscryptSSLParametersImplClass;
@@ -590,6 +591,7 @@ bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException) ARGS_N
590591
jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig) ARGS_NON_NULL_ALL;
591592
jmethodID GetOptionalMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig) ARGS_NON_NULL_ALL;
592593
jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig) ARGS_NON_NULL_ALL;
594+
jfieldID GetOptionalField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig) ARGS_NON_NULL_ALL;
593595
JNIEnv* GetJNIEnv(void);
594596

595597
int GetEnumAsInt(JNIEnv *env, jobject enumObj) ARGS_NON_NULL_ALL;

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

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,38 @@ static bool IsHandshaking(int handshakeStatus)
6565
ARGS_NON_NULL(1, 2) static jobject GetSslSession(JNIEnv* env, SSLStream* sslStream, int handshakeStatus)
6666
{
6767
// During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates
68-
// which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLSession.
68+
// which we need for hostname verification. There are different ways to access the handshake session
69+
// in different Android API levels.
6970
// SSLEngine.getHandshakeSession() is available since API 24.
71+
// In older Android versions (API 21-23) we need to access the handshake session by accessing
72+
// a private field instead.
7073

71-
jobject sslSession = IsHandshaking(handshakeStatus) && g_SSLEngineGetHandshakeSession != NULL
72-
? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession)
73-
: (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession);
74-
if (CheckJNIExceptions(env))
75-
return NULL;
74+
if (g_SSLEngineGetHandshakeSession != NULL)
75+
{
76+
jobject sslSession = IsHandshaking(handshakeStatus)
77+
? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession)
78+
: (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession);
79+
if (CheckJNIExceptions(env))
80+
return NULL;
7681

77-
return sslSession;
82+
return sslSession;
83+
}
84+
else if (g_ConscryptOpenSSLEngineImplHandshakeSessionField != NULL)
85+
{
86+
jobject sslSession = IsHandshaking(handshakeStatus)
87+
? (*env)->GetObjectField(env, sslStream->sslEngine, g_ConscryptOpenSSLEngineImplHandshakeSessionField)
88+
: (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession);
89+
if (CheckJNIExceptions(env))
90+
return NULL;
91+
92+
return sslSession;
93+
}
94+
else
95+
{
96+
LOG_ERROR("Unable to get the current SSLSession from SSLEngine.");
97+
assert(false && "Unable to get the current SSLSession from SSLEngine.");
98+
return NULL;
99+
}
78100
}
79101

80102
ARGS_NON_NULL_ALL static jobject GetCurrentSslSession(JNIEnv* env, SSLStream* sslStream)

0 commit comments

Comments
 (0)