From a2b3c47aa873265d2e211c166e7eec5a49121822 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 24 Mar 2021 22:32:39 -0700 Subject: [PATCH 01/13] Hook up AndroidCrypto_SSLStream* functions to managed --- .../Interop.Ssl.cs | 51 ++++++++++ .../pal_sslstream.c | 92 ++++++++++++++++--- .../pal_sslstream.h | 3 + .../Pal.Android/SafeDeleteSslContext.cs | 74 ++++++++++++++- .../Net/Security/SslConnectionInfo.Android.cs | 6 ++ .../Net/Security/SslStreamPal.Android.cs | 51 +++++++++- 6 files changed, 262 insertions(+), 15 deletions(-) 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 133dc3ec445d13..1bcd0331292872 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 @@ -4,11 +4,62 @@ using System; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; +using SafeSslHandle = System.Net.SafeSslHandle; internal static partial class Interop { internal static partial class AndroidCrypto { + internal unsafe delegate int SSLReadCallback(byte* data, int offset, int length); + internal unsafe delegate void SSLWriteCallback(byte* data, int offset, int length); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] + internal static extern SafeSslHandle SSLStreamCreate( + bool isServer, + SSLReadCallback streamRead, + SSLWriteCallback streamWrite, + int appOutBufferSize, + int appInBufferSize); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamHandshake")] + internal static extern SafeSslHandle SSLStreamHandshake(SafeSslHandle sslHandle); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateAndStartHandshake")] + private static extern SafeSslHandle SSLStreamCreateAndStartHandshake( + SSLReadCallback streamRead, + SSLWriteCallback streamWrite, + int tlsVersion, + int appOutBufferSize, + int appInBufferSize); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRead")] + private static unsafe extern int SSLStreamRead( + SafeSslHandle sslHandle, + byte* buffer, + int offset, + int length); + internal static unsafe bool SSLStreamRead(SafeSslHandle handle, byte* buffer, int count, out int read) + { + read = SSLStreamRead(handle, buffer, 0, count); + return true; + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamWrite")] + private static unsafe extern void SSLStreamWrite( + SafeSslHandle sslHandle, + byte* buffer, + int offset, + int length); + internal static unsafe bool SSLStreamWrite(SafeSslHandle handle, ReadOnlySpan buffer) + { + fixed (byte* bufferPtr = buffer) + { + SSLStreamWrite(handle, bufferPtr, 0, buffer.Length); + } + + return true; + } + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRelease")] internal static extern void SSLStreamRelease(IntPtr ptr); 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 b8ac3849ad6bc7..91cb59cefc7933 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 @@ -3,7 +3,7 @@ #include "pal_sslstream.h" -void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); +static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) { @@ -246,7 +246,7 @@ static void doUnwrap(JNIEnv* env, SSLStream* sslStream) } } -void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) +static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) { /* switch (handshakeStatus) { @@ -278,6 +278,84 @@ void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus } } +static void FreeSSLStream(JNIEnv *env, SSLStream *sslStream) +{ + assert(sslStream != NULL); + ReleaseGRef(env, sslStream->sslContext); + ReleaseGRef(env, sslStream->sslEngine); + ReleaseGRef(env, sslStream->sslSession); + ReleaseGRef(env, sslStream->appOutBuffer); + ReleaseGRef(env, sslStream->netOutBuffer); + ReleaseGRef(env, sslStream->netInBuffer); + ReleaseGRef(env, sslStream->appInBuffer); + free(sslStream); +} + +SSLStream* AndroidCryptoNative_SSLStreamCreate( + bool isServer, + STREAM_READER streamReader, + STREAM_WRITER streamWriter, + int appOutBufferSize, + int appInBufferSize) +{ + JNIEnv* env = GetJNIEnv(); + + SSLStream* sslStream = malloc(sizeof(SSLStream)); + + // SSLContext sslContext = SSLContext.getDefault(); + jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_sslCtxGetDefaultMethod); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + sslStream->sslContext = ToGRef(env, sslContext); + + // SSLEngine sslEngine = sslContext.createSSLEngine(); + // sslEngine.setUseClientMode(!isServer); + jobject sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + sslStream->sslEngine = ToGRef(env, sslEngine); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientModeMethod, !isServer); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // SSLSession sslSession = sslEngine.getSession(); + sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSessionMethod)); + + // final int applicationBufferSize = sslSession.getApplicationBufferSize(); + // final int packetBufferSize = sslSession.getPacketBufferSize(); + int applicationBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod); + int packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); + + // ByteBuffer appOutBuffer = ByteBuffer.allocate(appOutBufferSize); + // ByteBuffer netOutBuffer = ByteBuffer.allocate(packetBufferSize); + // ByteBuffer netInBuffer = ByteBuffer.allocate(packetBufferSize); + // ByteBuffer appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appInBufferSize)); + sslStream->appOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, appOutBufferSize)); + sslStream->netOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); + sslStream->appInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, + applicationBufferSize > appInBufferSize ? applicationBufferSize : appInBufferSize)); + sslStream->netInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); + + sslStream->streamReader = streamReader; + sslStream->streamWriter = streamWriter; + + return sslStream; + +cleanup: + FreeSSLStream(env, sslStream); + return NULL; +} + +int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream) +{ + assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); + + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshakeMethod); + if (CheckJNIExceptions(env)) + return FAIL; + + checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); + return SUCCESS; +} + SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( STREAM_READER streamReader, STREAM_WRITER streamWriter, @@ -402,13 +480,5 @@ void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, i void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) { JNIEnv* env = GetJNIEnv(); - ReleaseGRef(env, sslStream->sslContext); - ReleaseGRef(env, sslStream->sslEngine); - ReleaseGRef(env, sslStream->sslSession); - ReleaseGRef(env, sslStream->appOutBuffer); - ReleaseGRef(env, sslStream->netOutBuffer); - ReleaseGRef(env, sslStream->netInBuffer); - ReleaseGRef(env, sslStream->appInBuffer); - free(sslStream); - AssertOnJNIExceptions(env); + FreeSSLStream(env, sslStream); } 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 348760cfdc0a0a..753ccf0d5333e5 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 @@ -38,6 +38,9 @@ typedef struct SSLStream #define STATUS__OK 2 #define STATUS__CLOSED 3 +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int appOutBufferSize, int appInBufferSize); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream); + PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); PALEXPORT int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length); PALEXPORT void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int offset, int length); 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 016b33b4ee3dab..a52194106679ba 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 @@ -14,7 +14,10 @@ namespace System.Net internal sealed class SafeDeleteSslContext : SafeDeleteContext { private const int InitialBufferSize = 2048; + private readonly SafeSslHandle _sslContext; + private readonly Interop.AndroidCrypto.SSLReadCallback _readCallback; + private readonly Interop.AndroidCrypto.SSLWriteCallback _writeCallback; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); @@ -26,8 +29,75 @@ public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthentication { Debug.Assert((credential != null) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext"); - _sslContext = new SafeSslHandle(); - throw new NotImplementedException(nameof(SafeDeleteSslContext)); + try + { + unsafe + { + _readCallback = ReadFromConnection; + _writeCallback = WriteToConnection; + } + + _sslContext = CreateSslContext(_readCallback, _writeCallback, credential, authOptions); + } + catch (Exception ex) + { + Debug.Write("Exception Caught. - " + ex); + Dispose(); + throw; + } + } + + private static SafeSslHandle CreateSslContext( + Interop.AndroidCrypto.SSLReadCallback readCallback, + Interop.AndroidCrypto.SSLWriteCallback writeCallback, + SafeFreeSslCredentials credential, + SslAuthenticationOptions authOptions) + { + bool isServer = authOptions.IsServer; + SafeSslHandle handle = Interop.AndroidCrypto.SSLStreamCreate( + isServer, + readCallback, + writeCallback, + InitialBufferSize, + InitialBufferSize); + + // Interop.AndroidCrypto.SSLStreamSetParameters + if (authOptions.ApplicationProtocols != null) + { + // SSLParameters.setApplicationProtocols + // SSLEngine.setSSLParameters + } + + if (credential.Protocols != SslProtocols.None) + { + // new SSLParameters(cipherSuites, protocols) + // SSLEngine.setEnabledProtocols + } + + if (authOptions.CipherSuitesPolicy != null) + { + // new SSLParameters(cipherSuites, protocols) + // SSLEngine.setEnabledCipherSuites + } + + if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) + { + // SSLParameters.setServerNames + // SSLEngine.setSSLParameters + } + + if (isServer && authOptions.RemoteCertRequired) + { + // SSLParameters.setNeedClientAuth + // SSLEngine.setSSLParameters + } + + if (credential.CertificateContext != null && credential.CertificateContext.IntermediateCertificates.Length > 0) + { + throw new NotImplementedException(); + } + + return handle; } public override bool IsInvalid => _sslContext?.IsInvalid ?? true; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs index 1d468dba8a5743..424a4db1ce99a8 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs @@ -12,6 +12,12 @@ internal sealed partial class SslConnectionInfo public SslConnectionInfo(SafeSslHandle sslContext) { throw new NotImplementedException(nameof(SslConnectionInfo)); + + // SslProtocols protocol; + // TlsCipherSuite cipherSuite; + // Interop.AndroidCrypto.SSLGetProtocolVersion + // Interop.AndroidCrypto.SSLGetCipherSuite + // MapCipherSuite(cipherSuite) } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 6245f00ae4570a..5c61e1567b06a6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -72,7 +72,31 @@ public static SecurityStatusPal EncryptMessage( resultSize = 0; Debug.Assert(input.Length > 0, $"{nameof(input.Length)} > 0 since {nameof(CanEncryptEmptyMessage)} is false"); - throw new NotImplementedException(nameof(EncryptMessage)); + try + { + SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; + SafeSslHandle sslHandle = sslContext.SslContext; + + bool success = Interop.AndroidCrypto.SSLStreamWrite(sslHandle, input.Span); + if (!success) + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + + if (sslContext.BytesReadyForConnection <= output?.Length) + { + resultSize = sslContext.ReadPendingWrites(output, 0, output.Length); + } + else + { + output = sslContext.ReadPendingWrites()!; + resultSize = output.Length; + } + + return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + } + catch (Exception e) + { + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, e); + } } public static SecurityStatusPal DecryptMessage( @@ -81,7 +105,30 @@ public static SecurityStatusPal DecryptMessage( ref int offset, ref int count) { - throw new NotImplementedException(nameof(DecryptMessage)); + try + { + SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; + SafeSslHandle sslHandle = sslContext.SslContext; + + sslContext.Write(buffer.AsSpan(offset, count)); + + unsafe + { + fixed (byte* offsetInput = &buffer[offset]) + { + bool success = Interop.AndroidCrypto.SSLStreamRead(sslHandle, offsetInput, count, out int read); + if (!success) + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + + count = read; + return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + } + } + } + catch (Exception e) + { + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, e); + } } public static ChannelBinding? QueryContextChannelBinding( From edfa839e561ffc9ed54f3f973101d13a4704a09c Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 24 Mar 2021 22:34:08 -0700 Subject: [PATCH 02/13] Implement SSLStreamGetApplicationProtocol --- .../Interop.Ssl.cs | 20 ++++++++++ .../pal_jni.c | 9 +++++ .../pal_jni.h | 5 +++ .../pal_sslstream.c | 40 +++++++++++++++++++ .../pal_sslstream.h | 2 + .../Net/Security/SslStreamPal.Android.cs | 2 +- 6 files changed, 77 insertions(+), 1 deletion(-) 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 1bcd0331292872..6bc5d527037342 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 @@ -10,6 +10,9 @@ internal static partial class Interop { internal static partial class AndroidCrypto { + private const int INSUFFICIENT_BUFFER = -1; + private const int SUCCESS = 1; + internal unsafe delegate int SSLReadCallback(byte* data, int offset, int length); internal unsafe delegate void SSLWriteCallback(byte* data, int offset, int length); @@ -32,6 +35,23 @@ private static extern SafeSslHandle SSLStreamCreateAndStartHandshake( int appOutBufferSize, int appInBufferSize); + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetApplicationProtocol")] + private static extern int SSLStreamGetApplicationProtocol(SafeSslHandle ssl, [Out] byte[]? buf, ref int len); + internal static byte[]? SSLStreamGetApplicationProtocol(SafeSslHandle ssl) + { + int len = 0; + int ret = SSLStreamGetApplicationProtocol(ssl, null, ref len); + if (ret != INSUFFICIENT_BUFFER) + return null; + + byte[] bytes = new byte[len]; + ret = SSLStreamGetApplicationProtocol(ssl, bytes, ref len); + if (ret != SUCCESS) + return null; + + return bytes; + } + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRead")] private static unsafe extern int SSLStreamRead( SafeSslHandle sslHandle, 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 f5f6819e9afbfd..2a0b28ecbc262f 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 @@ -14,6 +14,10 @@ jmethodID g_ByteArrayInputStreamReset; jclass g_Enum; jmethodID g_EnumOrdinal; +// java/lang/String +jclass g_String; +jmethodID g_StringGetBytes; + // java/lang/Throwable jclass g_ThrowableClass; jmethodID g_ThrowableGetCause; @@ -367,6 +371,7 @@ jmethodID g_ListGet; // javax/net/ssl/SSLEngine jclass g_SSLEngine; +jmethodID g_SSLEngineGetApplicationProtocol; jmethodID g_SSLEngineSetUseClientModeMethod; jmethodID g_SSLEngineGetSessionMethod; jmethodID g_SSLEngineBeginHandshakeMethod; @@ -602,6 +607,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_Enum = GetClassGRef(env, "java/lang/Enum"); g_EnumOrdinal = GetMethod(env, false, g_Enum, "ordinal", "()I"); + g_String = GetClassGRef(env, "java/lang/String"); + g_StringGetBytes = GetMethod(env, false, g_String, "getBytes", "()[B"); + g_ThrowableClass = GetClassGRef(env, "java/lang/Throwable"); g_ThrowableGetCause = GetMethod(env, false, g_ThrowableClass, "getCause", "()Ljava/lang/Throwable;"); g_ThrowableGetMessage = GetMethod(env, false, g_ThrowableClass, "getMessage", "()Ljava/lang/String;"); @@ -894,6 +902,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ListGet = GetMethod(env, false, g_ListClass, "get", "(I)Ljava/lang/Object;"); g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); + g_SSLEngineGetApplicationProtocol = GetMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); g_SSLEngineSetUseClientModeMethod = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); g_SSLEngineGetSessionMethod = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineBeginHandshakeMethod = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); 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 1414e740ad3af5..1e4b9541800dad 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 @@ -23,6 +23,10 @@ extern jmethodID g_ByteArrayInputStreamReset; extern jclass g_Enum; extern jmethodID g_EnumOrdinal; +// java/lang/String +extern jclass g_String; +extern jmethodID g_StringGetBytes; + // java/lang/Throwable extern jclass g_ThrowableClass; extern jmethodID g_ThrowableGetCause; @@ -376,6 +380,7 @@ extern jmethodID g_ListGet; // javax/net/ssl/SSLEngine extern jclass g_SSLEngine; +extern jmethodID g_SSLEngineGetApplicationProtocol; extern jmethodID g_SSLEngineSetUseClientModeMethod; extern jmethodID g_SSLEngineGetSessionMethod; extern jmethodID g_SSLEngineBeginHandshakeMethod; 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 91cb59cefc7933..27382f9384a649 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 @@ -3,6 +3,8 @@ #include "pal_sslstream.h" +#define INSUFFICIENT_BUFFER -1 + static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) @@ -482,3 +484,41 @@ void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) JNIEnv* env = GetJNIEnv(); FreeSSLStream(env, sslStream); } + +static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) +{ + jsize bytesLen = (*env)->GetArrayLength(env, source); + + bool insufficientBuffer = *len < bytesLen; + *len = bytesLen; + if (insufficientBuffer) + return INSUFFICIENT_BUFFER; + + (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen) +{ + assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + INIT_LOCALS(loc, protocol, bytes); + + // String protocol = sslEngine.getApplicationProtocol(); + loc[protocol] = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetApplicationProtocol); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + if (loc[protocol] == NULL) + goto cleanup; + + // byte[] bytes = protocol.getBytes(); + loc[bytes] = (*env)->CallObjectMethod(env, loc[protocol], g_StringGetBytes); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = PopulateByteArray(env, loc[bytes], out, outLen); + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} 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 753ccf0d5333e5..7fd50c6e54b7ec 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 @@ -45,3 +45,5 @@ PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM PALEXPORT int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length); PALEXPORT void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int offset, int length); PALEXPORT void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream); + +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 5c61e1567b06a6..da616f1d2b2910 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -58,7 +58,7 @@ public static SafeFreeCredentials AcquireCredentialsHandle( if (context == null) return null; - throw new NotImplementedException(nameof(GetNegotiatedApplicationProtocol)); + return Interop.AndroidCrypto.SSLStreamGetApplicationProtocol(((SafeDeleteSslContext)context).SslContext); } public static SecurityStatusPal EncryptMessage( From 17310401289ddab5ae71ea95c45c8d42c1b2fdb9 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 24 Feb 2021 00:44:17 -0800 Subject: [PATCH 03/13] Implment SslConnectionInfo --- .../Interop.Ssl.cs | 41 ++++++- .../pal_jni.c | 4 + .../pal_jni.h | 2 + .../pal_sslstream.c | 104 +++++++++++++++--- .../pal_sslstream.h | 4 +- .../Net/Security/SslConnectionInfo.Android.cs | 23 +++- 6 files changed, 153 insertions(+), 25 deletions(-) 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 6bc5d527037342..c36c87be5d76d3 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,7 +3,10 @@ using System; using System.Runtime.InteropServices; +using System.Security.Cryptography; + using Microsoft.Win32.SafeHandles; + using SafeSslHandle = System.Net.SafeSslHandle; internal static partial class Interop @@ -18,14 +21,14 @@ internal static partial class AndroidCrypto [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] internal static extern SafeSslHandle SSLStreamCreate( - bool isServer, + [MarshalAs(UnmanagedType.U1)] bool isServer, SSLReadCallback streamRead, SSLWriteCallback streamWrite, int appOutBufferSize, int appInBufferSize); [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamHandshake")] - internal static extern SafeSslHandle SSLStreamHandshake(SafeSslHandle sslHandle); + internal static extern int SSLStreamHandshake(SafeSslHandle sslHandle); [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateAndStartHandshake")] private static extern SafeSslHandle SSLStreamCreateAndStartHandshake( @@ -94,6 +97,40 @@ internal SslException(int errorCode) HResult = errorCode; } } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetProtocol")] + private static extern int SSLStreamGetProtocol(SafeSslHandle ssl, out IntPtr protocol); + internal static string SSLStreamGetProtocol(SafeSslHandle ssl) + { + IntPtr protocolPtr; + int ret = SSLStreamGetProtocol(ssl, out protocolPtr); + if (ret != SUCCESS) + throw new CryptographicException(); + + if (protocolPtr == IntPtr.Zero) + return string.Empty; + + string protocol = Marshal.PtrToStringUni(protocolPtr)!; + Marshal.FreeHGlobal(protocolPtr); + return protocol; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetCipherSuite")] + private static extern int SSLStreamGetCipherSuite(SafeSslHandle ssl, out IntPtr cipherSuite); + internal static string SSLStreamGetCipherSuite(SafeSslHandle ssl) + { + IntPtr cipherSuitePtr; + int ret = SSLStreamGetCipherSuite(ssl, out cipherSuitePtr); + if (ret != SUCCESS) + throw new CryptographicException(); + + if (cipherSuitePtr == IntPtr.Zero) + return string.Empty; + + string cipherSuite = Marshal.PtrToStringUni(cipherSuitePtr)!; + Marshal.FreeHGlobal(cipherSuitePtr); + return cipherSuite; + } } } 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 2a0b28ecbc262f..6ea9e5d536e84b 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 @@ -404,7 +404,9 @@ jmethodID g_SSLContextCreateSSLEngineMethod; // javax/net/ssl/SSLSession jclass g_SSLSession; jmethodID g_SSLSessionGetApplicationBufferSizeMethod; +jmethodID g_SSLSessionGetCipherSuite; jmethodID g_SSLSessionGetPacketBufferSizeMethod; +jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult jclass g_SSLEngineResult; @@ -932,7 +934,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLSession = GetClassGRef(env, "javax/net/ssl/SSLSession"); g_SSLSessionGetApplicationBufferSizeMethod = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I"); + g_SSLSessionGetCipherSuite = GetMethod(env, false, g_SSLSession, "getCipherSuite", "()Ljava/lang/String;"); g_SSLSessionGetPacketBufferSizeMethod = GetMethod(env, false, g_SSLSession, "getPacketBufferSize", "()I"); + g_SSLSessionGetProtocol = GetMethod(env, false, g_SSLSession, "getProtocol", "()Ljava/lang/String;"); g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult"); g_SSLEngineResultGetStatusMethod = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;"); 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 1e4b9541800dad..65cdd214491e52 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 @@ -413,7 +413,9 @@ extern jmethodID g_SSLContextCreateSSLEngineMethod; // javax/net/ssl/SSLSession extern jclass g_SSLSession; extern jmethodID g_SSLSessionGetApplicationBufferSizeMethod; +extern jmethodID g_SSLSessionGetCipherSuite; extern jmethodID g_SSLSessionGetPacketBufferSizeMethod; +extern jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult extern jclass g_SSLEngineResult; 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 27382f9384a649..6315eb89bb4ede 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 @@ -5,6 +5,9 @@ #define INSUFFICIENT_BUFFER -1 +static uint16_t* AllocateString(JNIEnv *env, jstring source); +static int32_t PopulateByteArray(JNIEnv *env, jbyteArray source, uint8_t *dest, int32_t *len); + static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) @@ -105,6 +108,7 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf static void doWrap(JNIEnv* env, SSLStream* sslStream) { + LOG_DEBUG("doWwrap"); /* appOutBuffer.flip(); final SSLEngineResult result; @@ -141,6 +145,7 @@ static void doWrap(JNIEnv* env, SSLStream* sslStream) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompactMethod)); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); + LOG_DEBUG("doWwrap status: %d", status); switch (status) { case STATUS__OK: @@ -163,6 +168,7 @@ static void doWrap(JNIEnv* env, SSLStream* sslStream) static void doUnwrap(JNIEnv* env, SSLStream* sslStream) { + LOG_DEBUG("doUnwrap"); /* if (netInBuffer.position() == 0) { @@ -217,6 +223,7 @@ static void doUnwrap(JNIEnv* env, SSLStream* sslStream) handleEndOfStream(env, sslStream); return; } + LOG_DEBUG("streamReader return count: %d", count); (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPut3Method, tmp, 0, count)); free(tmpNative); @@ -228,6 +235,7 @@ static void doUnwrap(JNIEnv* env, SSLStream* sslStream) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompactMethod)); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); + LOG_DEBUG("doUnwwrap status: %d", status); switch (status) { case STATUS__OK: @@ -266,6 +274,7 @@ static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshak } */ + LOG_DEBUG("Handshake status: %d", handshakeStatus); AssertOnJNIExceptions(env); switch (handshakeStatus) { @@ -304,10 +313,15 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( SSLStream* sslStream = malloc(sizeof(SSLStream)); - // SSLContext sslContext = SSLContext.getDefault(); - jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_sslCtxGetDefaultMethod); + // SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + // sslContext.init(null, null, null); + // TODO: set TrustManager[] argument in init to be able to intercept cert validation process (and callback to C#). + jstring protocol = JSTRING("TLSv1.2"); + jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, protocol); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); sslStream->sslContext = ToGRef(env, sslContext); + (*env)->CallVoidMethod(env, sslStream->sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); // SSLEngine sslEngine = sslContext.createSSLEngine(); // sslEngine.setUseClientMode(!isServer); @@ -345,7 +359,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( return NULL; } -int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream) +int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) { assert(sslStream != NULL); JNIEnv* env = GetJNIEnv(); @@ -485,19 +499,6 @@ void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) FreeSSLStream(env, sslStream); } -static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) -{ - jsize bytesLen = (*env)->GetArrayLength(env, source); - - bool insufficientBuffer = *len < bytesLen; - *len = bytesLen; - if (insufficientBuffer) - return INSUFFICIENT_BUFFER; - - (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen) { assert(sslStream != NULL); @@ -522,3 +523,74 @@ int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream RELEASE_LOCALS(loc, env); return ret; } + +int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream *sslStream, uint16_t** out) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + + // String cipherSuite = sslSession.getCipherSuite(); + jstring cipherSuite = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetCipherSuite); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + *out = AllocateString(env, cipherSuite); + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, cipherSuite); + return ret; +} + +int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t** out) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + + // String protocol = sslSession.getProtocol(); + jstring protocol = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetProtocol); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + *out = AllocateString(env, protocol); + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, protocol); + return ret; +} + +static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) +{ + jsize bytesLen = (*env)->GetArrayLength(env, source); + + bool insufficientBuffer = *len < bytesLen; + *len = bytesLen; + if (insufficientBuffer) + return INSUFFICIENT_BUFFER; + + (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + +static uint16_t* AllocateString(JNIEnv *env, jstring source) +{ + if (source == NULL) + return NULL; + + // Length with null terminator + jsize len = (*env)->GetStringLength(env, source); + + // +1 for null terminator. + uint16_t* buffer = malloc(sizeof(uint16_t) * (size_t)(len + 1)); + buffer[len] = '\0'; + + (*env)->GetStringRegion(env, source, 0, len, (jchar*)buffer); + return buffer; +} 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 7fd50c6e54b7ec..6c2f33d48b1bc5 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 @@ -39,7 +39,7 @@ typedef struct SSLStream #define STATUS__CLOSED 3 PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int appOutBufferSize, int appInBufferSize); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream); PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); PALEXPORT int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length); @@ -47,3 +47,5 @@ PALEXPORT void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* PALEXPORT void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream); PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream *sslStream, uint16_t** out); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t** out); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs index 424a4db1ce99a8..9635907c86f0b3 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslConnectionInfo.Android.cs @@ -11,13 +11,24 @@ internal sealed partial class SslConnectionInfo { public SslConnectionInfo(SafeSslHandle sslContext) { - throw new NotImplementedException(nameof(SslConnectionInfo)); + string protocolString = Interop.AndroidCrypto.SSLStreamGetProtocol(sslContext); + SslProtocols protocol = protocolString switch + { +#pragma warning disable 0618 // Ssl2 and Ssl3 are deprecated. + "SSLv2" => SslProtocols.Ssl2, + "SSLv3" => SslProtocols.Ssl3, +#pragma warning restore + "TLSv1" => SslProtocols.Tls, + "TLSv1.1" => SslProtocols.Tls11, + "TLSv1.2" => SslProtocols.Tls12, + "TLSv1.3" => SslProtocols.Tls13, + _ => SslProtocols.None, + }; + Protocol = (int)protocol; - // SslProtocols protocol; - // TlsCipherSuite cipherSuite; - // Interop.AndroidCrypto.SSLGetProtocolVersion - // Interop.AndroidCrypto.SSLGetCipherSuite - // MapCipherSuite(cipherSuite) + // Enum value names should match the cipher suite name, so we just parse the + string cipherSuite = Interop.AndroidCrypto.SSLStreamGetCipherSuite(sslContext); + MapCipherSuite(Enum.Parse(cipherSuite)); } } } From 5c719d618dbe37ca061316d212ec90c7a991ad2c Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 24 Feb 2021 21:21:47 -0800 Subject: [PATCH 04/13] Set SSLParameters - only target host right now TODO - calling AndroidCrypto.SSLStreamHandshake results in stack overflow --- .../Interop.Ssl.cs | 5 ++ .../pal_jni.c | 15 ++++++ .../pal_jni.h | 14 ++++++ .../pal_sslstream.c | 50 +++++++++++++++++-- .../pal_sslstream.h | 1 + .../Pal.Android/SafeDeleteSslContext.cs | 3 +- .../Net/Security/SslStreamPal.Android.cs | 6 ++- 7 files changed, 86 insertions(+), 8 deletions(-) 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 c36c87be5d76d3..826e6e08ed3649 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 @@ -27,6 +27,11 @@ internal static extern SafeSslHandle SSLStreamCreate( int appOutBufferSize, int appInBufferSize); + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamConfigureParameters")] + internal static extern int SSLStreamConfigureParameters( + SafeSslHandle sslHandle, + [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost); + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamHandshake")] internal static extern int SSLStreamHandshake(SafeSslHandle sslHandle); 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 6ea9e5d536e84b..8e338becb4c6f3 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 @@ -78,7 +78,9 @@ jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters jclass g_sslParamsClass; +jmethodID g_sslParamsCtor; jmethodID g_sslParamsGetProtocolsMethod; +jmethodID g_sslParamsSetServerNames; // javax/net/ssl/SSLContext jclass g_sslCtxClass; @@ -369,6 +371,10 @@ jmethodID g_IteratorNext; jclass g_ListClass; jmethodID g_ListGet; +// javax/net/ssl/SNIHostName +jclass g_SNIHostName; +jmethodID g_SNIHostNameCtor; + // javax/net/ssl/SSLEngine jclass g_SSLEngine; jmethodID g_SSLEngineGetApplicationProtocol; @@ -380,6 +386,7 @@ jmethodID g_SSLEngineUnwrapMethod; jmethodID g_SSLEngineCloseInboundMethod; jmethodID g_SSLEngineCloseOutboundMethod; jmethodID g_SSLEngineGetHandshakeStatusMethod; +jmethodID g_SSLEngineSetSSLParameters; // java/nio/ByteBuffer jclass g_ByteBuffer; @@ -397,6 +404,7 @@ jmethodID g_ByteBufferPositionMethod; // javax/net/ssl/SSLContext jclass g_SSLContext; +jmethodID g_SSLContextGetDefault; jmethodID g_SSLContextGetInstanceMethod; jmethodID g_SSLContextInitMethod; jmethodID g_SSLContextCreateSSLEngineMethod; @@ -666,7 +674,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_sigNumMethod = GetMethod(env, false, g_bigNumClass, "signum", "()I"); g_sslParamsClass = GetClassGRef(env, "javax/net/ssl/SSLParameters"); + g_sslParamsCtor = GetMethod(env, false, g_sslParamsClass, "", "()V"); g_sslParamsGetProtocolsMethod = GetMethod(env, false, g_sslParamsClass, "getProtocols", "()[Ljava/lang/String;"); + g_sslParamsSetServerNames = GetMethod(env, false, g_sslParamsClass, "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;"); @@ -903,6 +913,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ListClass = GetClassGRef(env, "java/util/List"); g_ListGet = GetMethod(env, false, g_ListClass, "get", "(I)Ljava/lang/Object;"); + g_SNIHostName = GetClassGRef(env, "javax/net/ssl/SNIHostName"); + 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_SSLEngineSetUseClientModeMethod = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); @@ -913,6 +926,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLEngineGetHandshakeStatusMethod = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineCloseInboundMethod = GetMethod(env, false, g_SSLEngine, "closeInbound", "()V"); g_SSLEngineCloseOutboundMethod = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); + g_SSLEngineSetSSLParameters = GetMethod(env, false, g_SSLEngine, "setSSLParameters", "(Ljavax/net/ssl/SSLParameters;)V"); g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); g_ByteBufferAllocateMethod = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); @@ -928,6 +942,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ByteBufferPositionMethod = GetMethod(env, false, g_ByteBuffer, "position", "()I"); g_SSLContext = GetClassGRef(env, "javax/net/ssl/SSLContext"); + g_SSLContextGetDefault = GetMethod(env, true, g_SSLContext, "getDefault", "()Ljavax/net/ssl/SSLContext;"); g_SSLContextGetInstanceMethod = GetMethod(env, true, g_SSLContext, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/SSLContext;"); g_SSLContextInitMethod = GetMethod(env, false, g_SSLContext, "init", "([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;Ljava/security/SecureRandom;)V"); g_SSLContextCreateSSLEngineMethod = GetMethod(env, false, g_SSLContext, "createSSLEngine", "()Ljavax/net/ssl/SSLEngine;"); 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 65cdd214491e52..9595cdadf020ca 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 @@ -87,7 +87,9 @@ extern jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters extern jclass g_sslParamsClass; +extern jmethodID g_sslParamsCtor; extern jmethodID g_sslParamsGetProtocolsMethod; +extern jmethodID g_sslParamsSetServerNames; // javax/net/ssl/SSLContext extern jclass g_sslCtxClass; @@ -355,6 +357,11 @@ extern jclass g_CollectionClass; extern jmethodID g_CollectionIterator; extern jmethodID g_CollectionSize; +// java/util/ArrayListe +extern jclass g_ArrayList; +extern jmethodID g_ArrayListCtor; +extern jmethodID g_ArrayListAdd; + // java/util/Date extern jclass g_DateClass; extern jmethodID g_DateCtor; @@ -378,6 +385,10 @@ extern jmethodID g_IteratorNext; extern jclass g_ListClass; extern jmethodID g_ListGet; +// javax/net/ssl/SNIHostName +extern jclass g_SNIHostName; +extern jmethodID g_SNIHostNameCtor; + // javax/net/ssl/SSLEngine extern jclass g_SSLEngine; extern jmethodID g_SSLEngineGetApplicationProtocol; @@ -389,6 +400,7 @@ extern jmethodID g_SSLEngineUnwrapMethod; extern jmethodID g_SSLEngineCloseInboundMethod; extern jmethodID g_SSLEngineCloseOutboundMethod; extern jmethodID g_SSLEngineGetHandshakeStatusMethod; +extern jmethodID g_SSLEngineSetSSLParameters; // java/nio/ByteBuffer extern jclass g_ByteBuffer; @@ -406,9 +418,11 @@ extern jmethodID g_ByteBufferPositionMethod; // javax/net/ssl/SSLContext extern jclass g_SSLContext; +extern jmethodID g_SSLContextGetDefault; extern jmethodID g_SSLContextGetInstanceMethod; extern jmethodID g_SSLContextInitMethod; extern jmethodID g_SSLContextCreateSSLEngineMethod; +extern jmethodID g_SSLContextCreateSSLEngineWithPeer; // javax/net/ssl/SSLSession extern jclass g_SSLSession; 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 6315eb89bb4ede..acc4031f09ea66 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 @@ -316,12 +316,17 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( // SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); // sslContext.init(null, null, null); // TODO: set TrustManager[] argument in init to be able to intercept cert validation process (and callback to C#). - jstring protocol = JSTRING("TLSv1.2"); - jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, protocol); + // jstring protocol = JSTRING("TLSv1.2"); + // jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, protocol); + // ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + // sslStream->sslContext = ToGRef(env, sslContext); + // (*env)->CallVoidMethod(env, sslStream->sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); + // ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // SSLContext sslContext = SSLContext.getDefault(); + jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetDefault); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); sslStream->sslContext = ToGRef(env, sslContext); - (*env)->CallVoidMethod(env, sslStream->sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); // SSLEngine sslEngine = sslContext.createSSLEngine(); // sslEngine.setUseClientMode(!isServer); @@ -359,6 +364,42 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( return NULL; } +int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, char* targetHost) +{ + assert(sslStream != NULL); + assert(targetHost != NULL); + + JNIEnv* env = GetJNIEnv(); + + int32_t ret = FAIL; + INIT_LOCALS(loc, hostStr, nameList, hostName, params); + + // ArrayList nameList = new ArrayList(); + // SNIHostName hostName = new SNIHostName(targetHost); + // nameList.add(hostName); + loc[hostStr] = JSTRING(targetHost); + loc[nameList] = (*env)->NewObject(env, g_ArrayListClass, g_ArrayListCtor); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + loc[hostName] = (*env)->NewObject(env, g_SNIHostName, g_SNIHostNameCtor, loc[hostStr]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallBooleanMethod(env, loc[nameList], g_ArrayListAdd, loc[hostName]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // SSLParameters params = new SSLParameters(); + // params.setServerNames(nameList); + // sslEngine.setSSLParameters(params); + loc[params] = (*env)->NewObject(env, g_sslParamsClass, g_sslParamsCtor); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[params], g_sslParamsSetServerNames, loc[nameList]); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetSSLParameters, loc[params]); + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) { assert(sslStream != NULL); @@ -502,6 +543,7 @@ void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen) { assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); int32_t ret = FAIL; 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 6c2f33d48b1bc5..cbba152e18cfd8 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 @@ -39,6 +39,7 @@ typedef struct SSLStream #define STATUS__CLOSED 3 PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int appOutBufferSize, int appInBufferSize); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, char* targetHost); PALEXPORT int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream); PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); 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 a52194106679ba..133721948229ea 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 @@ -82,8 +82,7 @@ private static SafeSslHandle CreateSslContext( if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) { - // SSLParameters.setServerNames - // SSLEngine.setSSLParameters + Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); } if (isServer && authOptions.RemoteCertRequired) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index da616f1d2b2910..5c0c4efd74cd48 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -181,8 +181,10 @@ private static SecurityStatusPal HandshakeInternal( SafeSslHandle sslHandle = sslContext!.SslContext; - // Do handshake - // Interop.AndroidCrypto.SSLStreamHandshake + // TODO: [AndroidCrypto] Do handshake + // int retVal = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle); + // if (retVal != Interop.AndroidCrypto.SuccessReturnCode) + // return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); outputBuffer = sslContext.ReadPendingWrites(); From f7e8e25bfcd6576bf609ae87015d72900aa62756 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Thu, 25 Mar 2021 19:17:58 -0700 Subject: [PATCH 05/13] Do SSL handshake --- .../Interop.Ssl.cs | 9 +- .../pal_sslstream.c | 124 +++++++++++++----- .../pal_sslstream.h | 26 ++-- .../Net/Security/SslStreamPal.Android.cs | 13 +- 4 files changed, 114 insertions(+), 58 deletions(-) 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 826e6e08ed3649..91f3f564667233 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 @@ -19,6 +19,13 @@ internal static partial class AndroidCrypto internal unsafe delegate int SSLReadCallback(byte* data, int offset, int length); internal unsafe delegate void SSLWriteCallback(byte* data, int offset, int length); + internal enum PAL_SSLStreamStatus + { + OK = 0, + NeedData = 1, + Error = 2 + }; + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] internal static extern SafeSslHandle SSLStreamCreate( [MarshalAs(UnmanagedType.U1)] bool isServer, @@ -33,7 +40,7 @@ internal static extern int SSLStreamConfigureParameters( [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost); [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamHandshake")] - internal static extern int SSLStreamHandshake(SafeSslHandle sslHandle); + internal static extern PAL_SSLStreamStatus SSLStreamHandshake(SafeSslHandle sslHandle); [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateAndStartHandshake")] private static extern SafeSslHandle SSLStreamCreateAndStartHandshake( 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 acc4031f09ea66..f6199c53800b68 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 @@ -5,10 +5,29 @@ #define INSUFFICIENT_BUFFER -1 +// javax/net/ssl/SSLEngineResult$HandshakeStatus +enum +{ + HANDSHAKE_STATUS__NOT_HANDSHAKING = 0, + HANDSHAKE_STATUS__FINISHED = 1, + HANDSHAKE_STATUS__NEED_TASK = 2, + HANDSHAKE_STATUS__NEED_WRAP = 3, + HANDSHAKE_STATUS__NEED_UNWRAP = 4, +}; + +// javax/net/ssl/SSLEngineResult$Status +enum +{ + STATUS__BUFFER_UNDERFLOW = 0, + STATUS__BUFFER_OVERFLOW = 1, + STATUS__OK = 2, + STATUS__CLOSED = 3, +}; + static uint16_t* AllocateString(JNIEnv *env, jstring source); static int32_t PopulateByteArray(JNIEnv *env, jbyteArray source, uint8_t *dest, int32_t *len); -static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); +static PAL_SSLStreamStatus checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) { @@ -22,7 +41,7 @@ static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineR return status; } -static void close(JNIEnv* env, SSLStream* sslStream) { +static PAL_SSLStreamStatus close(JNIEnv* env, SSLStream* sslStream) { /* sslEngine.closeOutbound(); checkHandshakeStatus(); @@ -30,7 +49,7 @@ static void close(JNIEnv* env, SSLStream* sslStream) { AssertOnJNIExceptions(env); (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); + return checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); } static void handleEndOfStream(JNIEnv* env, SSLStream* sslStream) { @@ -106,9 +125,9 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf } } -static void doWrap(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream) { - LOG_DEBUG("doWwrap"); + LOG_DEBUG("doWrap"); /* appOutBuffer.flip(); final SSLEngineResult result; @@ -142,31 +161,48 @@ static void doWrap(JNIEnv* env, SSLStream* sslStream) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferFlipMethod)); jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineWrapMethod, sslStream->appOutBuffer, sslStream->netOutBuffer); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompactMethod)); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); - LOG_DEBUG("doWwrap status: %d", status); + LOG_DEBUG("doWrap status: %d", status); switch (status) { case STATUS__OK: + { flush(env, sslStream); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); + PAL_SSLStreamStatus statusLocal = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); + if (statusLocal != SSLStreamStatus_OK) + { + return statusLocal; + } + if ((*env)->CallIntMethod(env, sslStream->appOutBuffer, g_ByteBufferPositionMethod) > 0) - doWrap(env, sslStream); - break; + { + return doWrap(env, sslStream); + } + + return SSLStreamStatus_OK; + } case STATUS__CLOSED: + { flush(env, sslStream); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - close(env, sslStream); - break; + PAL_SSLStreamStatus statusLocal = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); + assert(statusLocal == SSLStreamStatus_OK); + return close(env, sslStream); + } case STATUS__BUFFER_OVERFLOW: sslStream->netOutBuffer = ensureRemaining(env, sslStream, sslStream->netOutBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod)); - doWrap(env, sslStream); - break; + return doWrap(env, sslStream); + default: + LOG_ERROR("Unknown SSLEngineResult status"); + return SSLStreamStatus_Error; } } -static void doUnwrap(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream) { LOG_DEBUG("doUnwrap"); /* @@ -221,9 +257,14 @@ static void doUnwrap(JNIEnv* env, SSLStream* sslStream) if (count == -1) { handleEndOfStream(env, sslStream); - return; + // [elfung] sholud this be something else + return SSLStreamStatus_OK; + } + + if (count == 0) + { + return SSLStreamStatus_NeedData; } - LOG_DEBUG("streamReader return count: %d", count); (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPut3Method, tmp, 0, count)); free(tmpNative); @@ -232,6 +273,9 @@ static void doUnwrap(JNIEnv* env, SSLStream* sslStream) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferFlipMethod)); jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineUnwrapMethod, sslStream->netInBuffer, sslStream->appInBuffer); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompactMethod)); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); @@ -239,24 +283,26 @@ static void doUnwrap(JNIEnv* env, SSLStream* sslStream) switch (status) { case STATUS__OK: - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - break; + return checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); case STATUS__CLOSED: - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - close(env, sslStream); - break; + { + PAL_SSLStreamStatus statusLocal = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); + assert(statusLocal == SSLStreamStatus_OK); + return close(env, sslStream); + } case STATUS__BUFFER_UNDERFLOW: sslStream->netInBuffer = ensureRemaining(env, sslStream, sslStream->netInBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod)); - doUnwrap(env, sslStream); - break; + return doUnwrap(env, sslStream); case STATUS__BUFFER_OVERFLOW: sslStream->appInBuffer = ensureRemaining(env, sslStream, sslStream->appInBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod)); - doUnwrap(env, sslStream); - break; + return doUnwrap(env, sslStream); + default: + LOG_ERROR("Unknown SSLEngineResult status"); + return SSLStreamStatus_Error; } } -static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) +static PAL_SSLStreamStatus checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) { /* switch (handshakeStatus) { @@ -279,14 +325,17 @@ static void checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshak switch (handshakeStatus) { case HANDSHAKE_STATUS__NEED_WRAP: - doWrap(env, sslStream); - break; + return doWrap(env, sslStream); case HANDSHAKE_STATUS__NEED_UNWRAP: - doUnwrap(env, sslStream); - break; + return doUnwrap(env, sslStream); + case HANDSHAKE_STATUS__NOT_HANDSHAKING: + case HANDSHAKE_STATUS__FINISHED: + return SSLStreamStatus_OK; case HANDSHAKE_STATUS__NEED_TASK: assert(0 && "unexpected NEED_TASK handshake status"); } + + return SSLStreamStatus_Error; } static void FreeSSLStream(JNIEnv *env, SSLStream *sslStream) @@ -312,6 +361,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( JNIEnv* env = GetJNIEnv(); SSLStream* sslStream = malloc(sizeof(SSLStream)); + memset(sslStream, 0, sizeof(SSLStream)); // SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); // sslContext.init(null, null, null); @@ -400,17 +450,18 @@ int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, c return ret; } -int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) +PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) { assert(sslStream != NULL); JNIEnv* env = GetJNIEnv(); (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshakeMethod); if (CheckJNIExceptions(env)) - return FAIL; + return SSLStreamStatus_Error; - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); - return SUCCESS; + PAL_SSLStreamStatus status = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); + LOG_DEBUG("Returning %d", status); + return status; } SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( @@ -536,6 +587,9 @@ void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, i void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) { + if (sslStream == NULL) + return; + JNIEnv* env = GetJNIEnv(); FreeSSLStream(env, sslStream); } 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 cbba152e18cfd8..ec66598f03cc36 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 @@ -21,26 +21,18 @@ typedef struct SSLStream STREAM_WRITER streamWriter; } SSLStream; -#define TLS11 11 -#define TLS12 12 -#define TLS13 13 - -// javax/net/ssl/SSLEngineResult$HandshakeStatus -#define HANDSHAKE_STATUS__NOT_HANDSHAKING 0 -#define HANDSHAKE_STATUS__FINISHED 1 -#define HANDSHAKE_STATUS__NEED_TASK 2 -#define HANDSHAKE_STATUS__NEED_WRAP 3 -#define HANDSHAKE_STATUS__NEED_UNWRAP 4 - -// javax/net/ssl/SSLEngineResult$Status -#define STATUS__BUFFER_UNDERFLOW 0 -#define STATUS__BUFFER_OVERFLOW 1 -#define STATUS__OK 2 -#define STATUS__CLOSED 3 +// Matches managed PAL_SSLStreamStatus enum +enum +{ + SSLStreamStatus_OK = 0, + SSLStreamStatus_NeedData = 1, + SSLStreamStatus_Error = 2 +}; +typedef int32_t PAL_SSLStreamStatus; PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int appOutBufferSize, int appInBufferSize); PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, char* targetHost); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream); +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream); PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); PALEXPORT int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 5c0c4efd74cd48..760d9011994861 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -181,14 +181,17 @@ private static SecurityStatusPal HandshakeInternal( SafeSslHandle sslHandle = sslContext!.SslContext; - // TODO: [AndroidCrypto] Do handshake - // int retVal = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle); - // if (retVal != Interop.AndroidCrypto.SuccessReturnCode) - // return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + Interop.AndroidCrypto.PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle); + SecurityStatusPalErrorCode statusCode = ret switch + { + Interop.AndroidCrypto.PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + Interop.AndroidCrypto.PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, + _ => SecurityStatusPalErrorCode.InternalError + }; outputBuffer = sslContext.ReadPendingWrites(); - return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + return new SecurityStatusPal(statusCode); } catch (Exception exc) { From fd97ea316d461a56b8117a0674b390cf98ea3d8c Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 26 Mar 2021 11:16:54 -0700 Subject: [PATCH 06/13] Implement GetRemoteCertificate --- .../Interop.Ssl.cs | 32 +++++++++- .../pal_jni.c | 2 + .../pal_jni.h | 1 + .../pal_sslstream.c | 63 +++++++++++++++++++ .../pal_sslstream.h | 3 + .../src/System.Net.Security.csproj | 4 ++ .../Net/CertificateValidationPal.Android.cs | 34 +++++++++- 7 files changed, 135 insertions(+), 4 deletions(-) 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 91f3f564667233..d8baaa10c31205 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 @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; @@ -13,9 +14,6 @@ internal static partial class Interop { internal static partial class AndroidCrypto { - private const int INSUFFICIENT_BUFFER = -1; - private const int SUCCESS = 1; - internal unsafe delegate int SSLReadCallback(byte* data, int offset, int length); internal unsafe delegate void SSLWriteCallback(byte* data, int offset, int length); @@ -127,6 +125,34 @@ internal static string SSLStreamGetProtocol(SafeSslHandle ssl) return protocol; } + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetPeerCertificate")] + private static extern int SSLStreamGetPeerCertificate(SafeSslHandle ssl, out SafeX509Handle cert); + internal static SafeX509Handle SSLStreamGetPeerCertificate(SafeSslHandle ssl) + { + SafeX509Handle cert; + int ret = Interop.AndroidCrypto.SSLStreamGetPeerCertificate(ssl, out cert); + if (ret != SUCCESS) + throw new SslException(); + + return cert; + } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetPeerCertificates")] + private static extern int SSLStreamGetPeerCertificates( + SafeSslHandle ssl, + [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out IntPtr[] certs, + out int count); + internal static IntPtr[] SSLStreamGetPeerCertificates(SafeSslHandle ssl) + { + IntPtr[] ptrs; + int count; + int ret = Interop.AndroidCrypto.SSLStreamGetPeerCertificates(ssl, out ptrs, out count); + if (ret != SUCCESS) + throw new SslException(); + + return ptrs; + } + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetCipherSuite")] private static extern int SSLStreamGetCipherSuite(SafeSslHandle ssl, out IntPtr cipherSuite); internal static string SSLStreamGetCipherSuite(SafeSslHandle ssl) 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 8e338becb4c6f3..d4cd04debb3731 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 @@ -414,6 +414,7 @@ jclass g_SSLSession; jmethodID g_SSLSessionGetApplicationBufferSizeMethod; jmethodID g_SSLSessionGetCipherSuite; jmethodID g_SSLSessionGetPacketBufferSizeMethod; +jmethodID g_SSLSessionGetPeerCertificates; jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult @@ -951,6 +952,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLSessionGetApplicationBufferSizeMethod = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I"); g_SSLSessionGetCipherSuite = GetMethod(env, false, g_SSLSession, "getCipherSuite", "()Ljava/lang/String;"); g_SSLSessionGetPacketBufferSizeMethod = 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;"); g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult"); 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 9595cdadf020ca..5e390478f83072 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 @@ -429,6 +429,7 @@ extern jclass g_SSLSession; extern jmethodID g_SSLSessionGetApplicationBufferSizeMethod; extern jmethodID g_SSLSessionGetCipherSuite; extern jmethodID g_SSLSessionGetPacketBufferSizeMethod; +extern jmethodID g_SSLSessionGetPeerCertificates; extern jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult 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 f6199c53800b68..3207ae3e275ca2 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 @@ -662,6 +662,69 @@ int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t* return ret; } +int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jobject* out) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + + // Certificate[] certs = sslSession.getPeerCertificates(); + jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + jsize len = (*env)->GetArrayLength(env, certs); + if (len > 0) + { + // First element is the peer's own certificate + jobject cert =(*env)->GetObjectArrayElement(env, certs, 0); + *out = ToGRef(env, cert); + } + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, certs); + return ret; +} + +int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, jobject** out, int* outLen) +{ + assert(sslStream != NULL); + assert(out != NULL); + + JNIEnv* env = GetJNIEnv(); + int32_t ret = FAIL; + *out = NULL; + *outLen = 0; + + // Certificate[] certs = sslSession.getPeerCertificates(); + // for (int i = 0; i < certs.length; i++) { + // out[i] = certs[i]; + // } + jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + jsize len = (*env)->GetArrayLength(env, certs); + *outLen = len; + if (len > 0) + { + *out = malloc(sizeof(jobject) * (size_t)len); + for (int i = 0; i < len; i++) + { + jobject cert =(*env)->GetObjectArrayElement(env, certs, i); + (*out)[i] = ToGRef(env, cert); + } + } + + ret = SUCCESS; + +cleanup: + (*env)->DeleteLocalRef(env, certs); + return ret; +} + + static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) { jsize bytesLen = (*env)->GetArrayLength(env, source); 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 ec66598f03cc36..b8db5b911158f8 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 @@ -42,3 +42,6 @@ PALEXPORT void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream); PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen); PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream *sslStream, uint16_t** out); PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t** out); + +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jobject* out); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, jobject** out, int* outLen); diff --git a/src/libraries/System.Net.Security/src/System.Net.Security.csproj b/src/libraries/System.Net.Security/src/System.Net.Security.csproj index 6c0a5b530fdf59..be5811068e77db 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -331,8 +331,12 @@ Link="Common\System\Net\Security\Unix\SafeFreeSslCredentials.cs" /> + + 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 0289095ab53666..10963fadda8798 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 @@ -63,7 +63,39 @@ internal static SslPolicyErrors VerifyCertificateProperties( if (sslContext == null) return null; - throw new NotImplementedException(nameof(GetRemoteCertificate)); + X509Certificate2? cert = null; + if (remoteCertificateStore == null) + { + // Constructing a new X509Certificate2 adds a global reference to the pointer, so we dispose this handle + using (SafeX509Handle handle = Interop.AndroidCrypto.SSLStreamGetPeerCertificate(sslContext)) + { + if (!handle.IsInvalid) + { + cert = new X509Certificate2(handle.DangerousGetHandle()); + } + } + } + else + { + IntPtr[] ptrs = Interop.AndroidCrypto.SSLStreamGetPeerCertificates(sslContext); + if (ptrs.Length > 0) + { + // This is intentionally a different object from the cert added to the remote certificate store + // to match the behaviour on other platforms. + cert = new X509Certificate2(ptrs[0]); + foreach (IntPtr ptr in ptrs) + { + // Constructing a new X509Certificate2 adds a global reference to the pointer, so we dispose this handle + using (var handle = new SafeX509Handle(ptr)) + { + remoteCertificateStore.Add(new X509Certificate2(handle.DangerousGetHandle())); + } + } + + } + } + + return cert; } // From ef1a794005067141196ddc03f3ec9c9f982e5892 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Fri, 26 Mar 2021 23:25:00 -0700 Subject: [PATCH 07/13] Fix SSLStreamRead/Write and implement SSLStreamVerifyHostname Now working with: new HttpClient().GetStringAsync("https://api.github.com/zen") --- .../Interop.Ssl.cs | 33 ++--- .../pal_jni.c | 14 +++ .../pal_jni.h | 8 ++ .../pal_sslstream.c | 117 +++++++++++------- .../pal_sslstream.h | 10 +- .../Net/CertificateValidationPal.Android.cs | 13 +- .../Pal.Android/SafeDeleteSslContext.cs | 22 ++-- .../Net/Security/SslStreamPal.Android.cs | 51 ++++++-- 8 files changed, 177 insertions(+), 91 deletions(-) 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 d8baaa10c31205..bd6e25769aecee 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 @@ -14,8 +14,8 @@ internal static partial class Interop { internal static partial class AndroidCrypto { - internal unsafe delegate int SSLReadCallback(byte* data, int offset, int length); - internal unsafe delegate void SSLWriteCallback(byte* data, int offset, int length); + internal unsafe delegate PAL_SSLStreamStatus SSLReadCallback(byte* data, int* length); + internal unsafe delegate void SSLWriteCallback(byte* data, int length); internal enum PAL_SSLStreamStatus { @@ -66,32 +66,17 @@ private static extern SafeSslHandle SSLStreamCreateAndStartHandshake( } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRead")] - private static unsafe extern int SSLStreamRead( + internal static unsafe extern PAL_SSLStreamStatus SSLStreamRead( SafeSslHandle sslHandle, byte* buffer, - int offset, - int length); - internal static unsafe bool SSLStreamRead(SafeSslHandle handle, byte* buffer, int count, out int read) - { - read = SSLStreamRead(handle, buffer, 0, count); - return true; - } + int length, + out int bytesRead); [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamWrite")] - private static unsafe extern void SSLStreamWrite( + internal static unsafe extern PAL_SSLStreamStatus SSLStreamWrite( SafeSslHandle sslHandle, byte* buffer, - int offset, int length); - internal static unsafe bool SSLStreamWrite(SafeSslHandle handle, ReadOnlySpan buffer) - { - fixed (byte* bufferPtr = buffer) - { - SSLStreamWrite(handle, bufferPtr, 0, buffer.Length); - } - - return true; - } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRelease")] internal static extern void SSLStreamRelease(IntPtr ptr); @@ -169,6 +154,12 @@ internal static string SSLStreamGetCipherSuite(SafeSslHandle ssl) Marshal.FreeHGlobal(cipherSuitePtr); return cipherSuite; } + + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamVerifyHostname")] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool SSLStreamVerifyHostname( + SafeSslHandle ssl, + [MarshalAs(UnmanagedType.LPUTF8Str)] string hostname); } } 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 d4cd04debb3731..1158065f80c284 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 @@ -371,6 +371,14 @@ jmethodID g_IteratorNext; jclass g_ListClass; jmethodID g_ListGet; +// javax/net/ssl/HostnameVerifier +jclass g_HostnameVerifier; +jmethodID g_HostnameVerifierVerify; + +// javax/net/ssl/HttpsURLConnection +jclass g_HttpsURLConnection; +jmethodID g_HttpsURLConnectionGetDefaultHostnameVerifier; + // javax/net/ssl/SNIHostName jclass g_SNIHostName; jmethodID g_SNIHostNameCtor; @@ -914,6 +922,12 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ListClass = GetClassGRef(env, "java/util/List"); g_ListGet = GetMethod(env, false, g_ListClass, "get", "(I)Ljava/lang/Object;"); + g_HostnameVerifier = GetClassGRef(env, "javax/net/ssl/HostnameVerifier"); + g_HostnameVerifierVerify = GetMethod(env, false, g_HostnameVerifier, "verify", "(Ljava/lang/String;Ljavax/net/ssl/SSLSession;)Z"); + + g_HttpsURLConnection = GetClassGRef(env, "javax/net/ssl/HttpsURLConnection"); + g_HttpsURLConnectionGetDefaultHostnameVerifier = GetMethod(env, true, g_HttpsURLConnection, "getDefaultHostnameVerifier", "()Ljavax/net/ssl/HostnameVerifier;"); + g_SNIHostName = GetClassGRef(env, "javax/net/ssl/SNIHostName"); g_SNIHostNameCtor = GetMethod(env, false, g_SNIHostName, "", "(Ljava/lang/String;)V"); 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 5e390478f83072..4faec56e3a6b14 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 @@ -385,6 +385,14 @@ extern jmethodID g_IteratorNext; extern jclass g_ListClass; extern jmethodID g_ListGet; +// javax/net/ssl/HostnameVerifier +extern jclass g_HostnameVerifier; +extern jmethodID g_HostnameVerifierVerify; + +// javax/net/ssl/HttpsURLConnection +extern jclass g_HttpsURLConnection; +extern jmethodID g_HttpsURLConnectionGetDefaultHostnameVerifier; + // javax/net/ssl/SNIHostName extern jclass g_SNIHostName; extern jmethodID g_SNIHostNameCtor; 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 3207ae3e275ca2..05bfed1e490e1b 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 @@ -86,7 +86,7 @@ static void flush(JNIEnv* env, SSLStream* sslStream) uint8_t* dataPtr = (uint8_t*)malloc((size_t)bufferLimit); (*env)->GetByteArrayRegion(env, data, 0, bufferLimit, (jbyte*) dataPtr); - sslStream->streamWriter(dataPtr, 0, (uint32_t)bufferLimit); + sslStream->streamWriter(dataPtr, bufferLimit); free(dataPtr); // DeleteLocalRef because we don't need the return value (Buffer) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferCompactMethod)); @@ -127,7 +127,6 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream) { - LOG_DEBUG("doWrap"); /* appOutBuffer.flip(); final SSLEngineResult result; @@ -167,7 +166,6 @@ static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompactMethod)); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); - LOG_DEBUG("doWrap status: %d", status); switch (status) { case STATUS__OK: @@ -204,7 +202,6 @@ static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream) static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream) { - LOG_DEBUG("doUnwrap"); /* if (netInBuffer.position() == 0) { @@ -253,18 +250,13 @@ static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream) int netInBufferLimit = (*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferLimitMethod); jbyteArray tmp = (*env)->NewByteArray(env, netInBufferLimit); uint8_t* tmpNative = (uint8_t*)malloc((size_t)netInBufferLimit); - int count = sslStream->streamReader(tmpNative, 0, (uint32_t)netInBufferLimit); - if (count == -1) + int count = netInBufferLimit; + PAL_SSLStreamStatus status = sslStream->streamReader(tmpNative, &count); + if (status != SSLStreamStatus_OK) { - handleEndOfStream(env, sslStream); - // [elfung] sholud this be something else - return SSLStreamStatus_OK; + return status; } - if (count == 0) - { - return SSLStreamStatus_NeedData; - } (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPut3Method, tmp, 0, count)); free(tmpNative); @@ -279,7 +271,6 @@ static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream) (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompactMethod)); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); - LOG_DEBUG("doUnwwrap status: %d", status); switch (status) { case STATUS__OK: @@ -320,7 +311,6 @@ static PAL_SSLStreamStatus checkHandshakeStatus(JNIEnv* env, SSLStream* sslStrea } */ - LOG_DEBUG("Handshake status: %d", handshakeStatus); AssertOnJNIExceptions(env); switch (handshakeStatus) { @@ -368,10 +358,11 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( // TODO: set TrustManager[] argument in init to be able to intercept cert validation process (and callback to C#). // jstring protocol = JSTRING("TLSv1.2"); // jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, protocol); + // (*env)->DeleteLocalRef(env, protocol); // ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // sslStream->sslContext = ToGRef(env, sslContext); - // (*env)->CallVoidMethod(env, sslStream->sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); + // (*env)->CallVoidMethod(env, sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); // ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + // sslStream->sslContext = ToGRef(env, sslContext); // SSLContext sslContext = SSLContext.getDefault(); jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetDefault); @@ -459,9 +450,7 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; - PAL_SSLStreamStatus status = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); - LOG_DEBUG("Returning %d", status); - return status; + return checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); } SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( @@ -528,48 +517,71 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( return sslStream; } -int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length) +PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int length, int* read) { + assert(sslStream != NULL); + assert(read != NULL); + + jbyteArray data = NULL; JNIEnv* env = GetJNIEnv(); + PAL_SSLStreamStatus ret = SSLStreamStatus_Error; + *read = 0; /* - while (true) { - appInBuffer.flip(); - try { - if (appInBuffer.remaining() > 0) { - byte[] data = new byte[appInBuffer.remaining()]; - appInBuffer.get(data); - return data; - } - } finally { - appInBuffer.compact(); - } + appInBuffer.flip(); + if (appInBuffer.remaining() == 0) { + appInBuffer.compact(); doUnwrap(); + appInBuffer.flip(); + } + if (appInBuffer.remaining() > 0) { + byte[] data = new byte[appInBuffer.remaining()]; + appInBuffer.get(data); + appInBuffer.compact(); + return SSLStreamStatus_OK; + } else { + return SSLStreamStatus_NeedData; } */ - AssertOnJNIExceptions(env); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlipMethod)); int rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); + if (rem == 0) + { + (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); + PAL_SSLStreamStatus unwrapStatus = doUnwrap(env, sslStream); + if (unwrapStatus != SSLStreamStatus_OK) + { + ret = unwrapStatus; + goto cleanup; + } + + (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlipMethod)); + } + + rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); if (rem > 0) { - jbyteArray data = (*env)->NewByteArray(env, rem); + data = (*env)->NewByteArray(env, rem); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferGetMethod, data)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); (*env)->GetByteArrayRegion(env, data, 0, rem, (jbyte*) buffer); - AssertOnJNIExceptions(env); - return rem; + *read = rem; + ret = SSLStreamStatus_OK; } else { - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); - doUnwrap(env, sslStream); - AssertOnJNIExceptions(env); - return AndroidCryptoNative_SSLStreamRead(sslStream, buffer, offset, length); + ret = SSLStreamStatus_NeedData; } + +cleanup: + ReleaseLRef(env, data); + return ret; } -void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int offset, int length) +PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int length) { /* appOutBuffer.put(message); @@ -578,11 +590,10 @@ void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, i JNIEnv* env = GetJNIEnv(); jbyteArray data = (*env)->NewByteArray(env, length); - (*env)->SetByteArrayRegion(env, data, 0, length, (jbyte*)(buffer + offset)); + (*env)->SetByteArrayRegion(env, data, 0, length, (jbyte*)buffer); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPut2Method, data)); (*env)->DeleteLocalRef(env, data); - doWrap(env, sslStream); - AssertOnJNIExceptions(env); + return doWrap(env, sslStream); } void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) @@ -724,6 +735,24 @@ int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, j return ret; } +bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hostname) +{ + assert(sslStream != NULL); + assert(hostname != NULL); + JNIEnv* env = GetJNIEnv(); + + bool ret = false; + INIT_LOCALS(loc, name, verifier); + + // HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); + // return verifier.verify(hostname, sslSession); + loc[name] = JSTRING(hostname); + loc[verifier] = (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); + ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], sslStream->sslSession); + + RELEASE_LOCALS(loc, env); + return ret; +} static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) { 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 b8db5b911158f8..8dfce56b38abe2 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 @@ -5,8 +5,8 @@ #include "pal_jni.h" -typedef void (*STREAM_WRITER)(uint8_t*, uint32_t, uint32_t); -typedef int (*STREAM_READER)(uint8_t*, uint32_t, uint32_t); +typedef void (*STREAM_WRITER)(uint8_t*, int32_t); +typedef int (*STREAM_READER)(uint8_t*, int32_t*); typedef struct SSLStream { @@ -35,8 +35,8 @@ PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *ss PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream); PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); -PALEXPORT int AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int offset, int length); -PALEXPORT void AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int offset, int length); +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int length, int* read); +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int length); PALEXPORT void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream); PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen); @@ -45,3 +45,5 @@ PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jobject* out); PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, jobject** out, int* outLen); + +PALEXPORT bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hostname); 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 10963fadda8798..7643482f9336f5 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 @@ -24,10 +24,17 @@ internal static SslPolicyErrors VerifyCertificateProperties( ? SslPolicyErrors.None : SslPolicyErrors.RemoteCertificateChainErrors; - if (!checkCertName) - return errors; + if (checkCertName) + { + System.Diagnostics.Debug.Assert(hostName != null); + SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; + if (!Interop.AndroidCrypto.SSLStreamVerifyHostname(sslContext.SslContext, hostName!)) + { + errors |= SslPolicyErrors.RemoteCertificateNameMismatch; + } + } - throw new NotImplementedException(nameof(VerifyCertificateProperties)); + return errors; } // 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 133721948229ea..6e9ce6a475f372 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 @@ -9,6 +9,8 @@ using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; +using PAL_SSLStreamStatus = Interop.AndroidCrypto.PAL_SSLStreamStatus; + namespace System.Net { internal sealed class SafeDeleteSslContext : SafeDeleteContext @@ -117,7 +119,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - private unsafe void WriteToConnection(byte* data, int offset, int dataLength) + private unsafe void WriteToConnection(byte* data, int dataLength) { var inputBuffer = new ReadOnlySpan(data, dataLength); @@ -126,19 +128,25 @@ private unsafe void WriteToConnection(byte* data, int offset, int dataLength) _outputBuffer.Commit(dataLength); } - private unsafe int ReadFromConnection(byte* data, int offset, int dataLength) + private unsafe PAL_SSLStreamStatus ReadFromConnection(byte* data, int* dataLength) { - if (dataLength == 0) - return 0; + int toRead = *dataLength; + if (toRead == 0) + return PAL_SSLStreamStatus.OK; if (_inputBuffer.ActiveLength == 0) - return 0; + { + *dataLength = 0; + return PAL_SSLStreamStatus.NeedData; + } - int toRead = Math.Min(dataLength, _inputBuffer.ActiveLength); + toRead = Math.Min(toRead, _inputBuffer.ActiveLength); _inputBuffer.ActiveSpan.Slice(0, toRead).CopyTo(new Span(data, toRead)); _inputBuffer.Discard(toRead); - return toRead; + + *dataLength = toRead; + return PAL_SSLStreamStatus.OK; } internal void Write(ReadOnlySpan buf) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 760d9011994861..d7399e4d38ad22 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -7,6 +7,8 @@ using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography.X509Certificates; +using PAL_SSLStreamStatus = Interop.AndroidCrypto.PAL_SSLStreamStatus; + namespace System.Net.Security { internal static class SslStreamPal @@ -77,9 +79,26 @@ public static SecurityStatusPal EncryptMessage( SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; SafeSslHandle sslHandle = sslContext.SslContext; - bool success = Interop.AndroidCrypto.SSLStreamWrite(sslHandle, input.Span); - if (!success) - return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + PAL_SSLStreamStatus ret; + unsafe + { + MemoryHandle memHandle = input.Pin(); + try + { + ret = Interop.AndroidCrypto.SSLStreamWrite(sslHandle, (byte*)memHandle.Pointer, input.Length); + } + finally + { + memHandle.Dispose(); + } + } + + SecurityStatusPalErrorCode statusCode = ret switch + { + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, + _ => SecurityStatusPalErrorCode.InternalError + }; if (sslContext.BytesReadyForConnection <= output?.Length) { @@ -91,7 +110,7 @@ public static SecurityStatusPal EncryptMessage( resultSize = output.Length; } - return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + return new SecurityStatusPal(statusCode); } catch (Exception e) { @@ -116,12 +135,20 @@ public static SecurityStatusPal DecryptMessage( { fixed (byte* offsetInput = &buffer[offset]) { - bool success = Interop.AndroidCrypto.SSLStreamRead(sslHandle, offsetInput, count, out int read); - if (!success) + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, offsetInput, count, out int read); + if (ret == PAL_SSLStreamStatus.Error) return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); count = read; - return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); + + SecurityStatusPalErrorCode statusCode = ret switch + { + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.OK, + _ => SecurityStatusPalErrorCode.InternalError + }; + + return new SecurityStatusPal(statusCode); } } } @@ -181,11 +208,11 @@ private static SecurityStatusPal HandshakeInternal( SafeSslHandle sslHandle = sslContext!.SslContext; - Interop.AndroidCrypto.PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle); + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle); SecurityStatusPalErrorCode statusCode = ret switch { - Interop.AndroidCrypto.PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, - Interop.AndroidCrypto.PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, _ => SecurityStatusPalErrorCode.InternalError }; @@ -218,8 +245,8 @@ public static SecurityStatusPal ApplyShutdownToken( SafeSslHandle sslHandle = sslContext.SslContext; - // bool success = Interop.AndroidCrypto.SslShutdown(sslHandle); - bool success = true; + // bool success = Interop.AndroidCrypto.SSLStreamShutdown(sslHandle); + bool success = false; if (success) { return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); From e9d027e0bb8f7df6254b55c1e24a3c03d950655e Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 30 Mar 2021 00:08:15 -0700 Subject: [PATCH 08/13] Rework native implementation to better align with managed --- .../Interop.Ssl.cs | 8 +- .../pal_jni.c | 8 +- .../pal_jni.h | 4 +- .../pal_sslstream.c | 307 ++++++++---------- .../pal_sslstream.h | 6 +- .../Pal.Android/SafeDeleteSslContext.cs | 41 +-- .../Net/Security/SslStreamPal.Android.cs | 7 +- 7 files changed, 169 insertions(+), 212 deletions(-) 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 bd6e25769aecee..f335d6187aef75 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 @@ -21,7 +21,9 @@ internal enum PAL_SSLStreamStatus { OK = 0, NeedData = 1, - Error = 2 + Error = 2, + Renegotiate = 3, + Closed = 4, }; [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] @@ -155,6 +157,10 @@ internal static string SSLStreamGetCipherSuite(SafeSslHandle ssl) return cipherSuite; } + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamShutdown")] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool SSLStreamShutdown(SafeSslHandle ssl); + [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamVerifyHostname")] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool SSLStreamVerifyHostname( 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 1158065f80c284..9ec826fec27f62 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 @@ -400,8 +400,8 @@ jmethodID g_SSLEngineSetSSLParameters; jclass g_ByteBuffer; jmethodID g_ByteBufferAllocateMethod; jmethodID g_ByteBufferPutMethod; -jmethodID g_ByteBufferPut2Method; -jmethodID g_ByteBufferPut3Method; +jmethodID g_ByteBufferPutWithByteArray; +jmethodID g_ByteBufferPutWythByteArrayLength; jmethodID g_ByteBufferFlipMethod; jmethodID g_ByteBufferGetMethod; jmethodID g_ByteBufferPutBufferMethod; @@ -946,8 +946,8 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); g_ByteBufferAllocateMethod = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); g_ByteBufferPutMethod = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); - g_ByteBufferPut2Method = GetMethod(env, false, g_ByteBuffer, "put", "([B)Ljava/nio/ByteBuffer;"); - g_ByteBufferPut3Method = GetMethod(env, false, g_ByteBuffer, "put", "([BII)Ljava/nio/ByteBuffer;"); + g_ByteBufferPutWithByteArray = GetMethod(env, false, g_ByteBuffer, "put", "([B)Ljava/nio/ByteBuffer;"); + g_ByteBufferPutWythByteArrayLength = GetMethod(env, false, g_ByteBuffer, "put", "([BII)Ljava/nio/ByteBuffer;"); g_ByteBufferFlipMethod = GetMethod(env, false, g_ByteBuffer, "flip", "()Ljava/nio/Buffer;"); g_ByteBufferLimitMethod = GetMethod(env, false, g_ByteBuffer, "limit", "()I"); g_ByteBufferGetMethod = GetMethod(env, false, g_ByteBuffer, "get", "([B)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 4faec56e3a6b14..24fa4ed23e06c8 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 @@ -414,8 +414,8 @@ extern jmethodID g_SSLEngineSetSSLParameters; extern jclass g_ByteBuffer; extern jmethodID g_ByteBufferAllocateMethod; extern jmethodID g_ByteBufferPutMethod; -extern jmethodID g_ByteBufferPut2Method; -extern jmethodID g_ByteBufferPut3Method; +extern jmethodID g_ByteBufferPutWithByteArray; +extern jmethodID g_ByteBufferPutWythByteArrayLength; extern jmethodID g_ByteBufferFlipMethod; extern jmethodID g_ByteBufferGetMethod; extern jmethodID g_ByteBufferLimitMethod; 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 05bfed1e490e1b..90d6735fb94411 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 @@ -27,7 +27,9 @@ enum static uint16_t* AllocateString(JNIEnv *env, jstring source); static int32_t PopulateByteArray(JNIEnv *env, jbyteArray source, uint8_t *dest, int32_t *len); -static PAL_SSLStreamStatus checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus); +static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream); +static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream, int* hs); +static void flush(JNIEnv* env, SSLStream* sslStream); static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) { @@ -41,27 +43,20 @@ static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineR return status; } -static PAL_SSLStreamStatus close(JNIEnv* env, SSLStream* sslStream) { - /* - sslEngine.closeOutbound(); - checkHandshakeStatus(); - */ - - AssertOnJNIExceptions(env); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); - return checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); +static bool IsHandshaking(int handshakeStatus) +{ + return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING + && handshakeStatus != HANDSHAKE_STATUS__FINISHED; } -static void handleEndOfStream(JNIEnv* env, SSLStream* sslStream) { - /* - sslEngine.closeInbound(); - close(); - */ +static PAL_SSLStreamStatus close(JNIEnv* env, SSLStream* sslStream) +{ + // sslEngine.closeOutbound(); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); - AssertOnJNIExceptions(env); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseInboundMethod); - close(env, sslStream); - AssertOnJNIExceptions(env); + // Call wrap to clear any remaining data + int unused; + return doWrap(env, sslStream, &unused); } static void flush(JNIEnv* env, SSLStream* sslStream) @@ -125,126 +120,59 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf } } -static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus) { - /* - appOutBuffer.flip(); - final SSLEngineResult result; - try { - result = sslEngine.wrap(appOutBuffer, netOutBuffer); - } catch (SSLException e) { - return; - } - appOutBuffer.compact(); - - final SSLEngineResult.Status status = result.getStatus(); - switch (status) { - case OK: - flush(); - checkHandshakeStatus(result.getHandshakeStatus()); - if (appOutBuffer.position() > 0) doWrap(); - break; - case CLOSED: - flush(); - checkHandshakeStatus(result.getHandshakeStatus()); - close(); - break; - case BUFFER_OVERFLOW: - netOutBuffer = ensureRemaining(netOutBuffer, sslEngine.getSession().getPacketBufferSize()); - doWrap(); - break; - } - */ - - AssertOnJNIExceptions(env); - + // appOutBuffer.flip(); + // SSLEngineResult result = sslEngine.wrap(appOutBuffer, netOutBuffer); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferFlipMethod)); jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineWrapMethod, sslStream->appOutBuffer, sslStream->netOutBuffer); if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; + // appOutBuffer.compact(); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompactMethod)); + // handshakeStatus = result.getHandshakeStatus(); + // SSLEngineResult.Status status = result.getStatus(); + *handshakeStatus = getHandshakeStatus(env, sslStream, sslEngineResult); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); switch (status) { case STATUS__OK: { flush(env, sslStream); - PAL_SSLStreamStatus statusLocal = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - if (statusLocal != SSLStreamStatus_OK) - { - return statusLocal; - } - - if ((*env)->CallIntMethod(env, sslStream->appOutBuffer, g_ByteBufferPositionMethod) > 0) - { - return doWrap(env, sslStream); - } - return SSLStreamStatus_OK; } case STATUS__CLOSED: { flush(env, sslStream); - PAL_SSLStreamStatus statusLocal = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - assert(statusLocal == SSLStreamStatus_OK); - return close(env, sslStream); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); + return SSLStreamStatus_Closed; } case STATUS__BUFFER_OVERFLOW: - sslStream->netOutBuffer = ensureRemaining(env, sslStream, sslStream->netOutBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod)); - return doWrap(env, sslStream); + { + // Expand buffer + // int newRemaining = sslSession.getPacketBufferSize(); + int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); + sslStream->netOutBuffer = ensureRemaining(env, sslStream, sslStream->netOutBuffer, newRemaining); + return SSLStreamStatus_OK; + } default: - LOG_ERROR("Unknown SSLEngineResult status"); + { + LOG_ERROR("Unknown SSLEngineResult status: %d", status); return SSLStreamStatus_Error; + } } } -static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream, int *handshakeStatus) { - /* - if (netInBuffer.position() == 0) - { - byte[] tmp = new byte[netInBuffer.limit()]; - - int count = ReadFromInputStream(tmp, 0, tmp.length); - if (count == -1) { - handleEndOfStream(); - return; - } - netInBuffer.put(tmp, 0, count); - } - - netInBuffer.flip(); - final SSLEngineResult result; - try { - result = sslEngine.unwrap(netInBuffer, appInBuffer); - } catch (SSLException e) { - return; - } - netInBuffer.compact(); - final SSLEngineResult.Status status = result.getStatus(); - switch (status) { - case OK: - checkHandshakeStatus(result.getHandshakeStatus()); - break; - case CLOSED: - checkHandshakeStatus(result.getHandshakeStatus()); - close(); - break; - case BUFFER_UNDERFLOW: - netInBuffer = ensureRemaining(netInBuffer, sslEngine.getSession().getPacketBufferSize()); - doUnwrap(); - break; - case BUFFER_OVERFLOW: - appInBuffer = ensureRemaining(appInBuffer, sslEngine.getSession().getApplicationBufferSize()); - doUnwrap(); - break; - } - */ - - AssertOnJNIExceptions(env); - + // if (netInBuffer.position() == 0) + // { + // byte[] tmp = new byte[netInBuffer.limit()]; + // int count = ReadFromInputStream(tmp, 0, tmp.length); + // netInBuffer.put(tmp, 0, count); + // } if ((*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferPositionMethod) == 0) { int netInBufferLimit = (*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferLimitMethod); @@ -258,74 +186,86 @@ static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream) } (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPut3Method, tmp, 0, count)); + (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPutWythByteArrayLength, tmp, 0, count)); free(tmpNative); (*env)->DeleteLocalRef(env, tmp); } + // netInBuffer.flip(); + // SSLEngineResult result = sslEngine.unwrap(netInBuffer, appInBuffer); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferFlipMethod)); jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineUnwrapMethod, sslStream->netInBuffer, sslStream->appInBuffer); if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; + // netInBuffer.compact(); (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompactMethod)); + // handshakeStatus = result.getHandshakeStatus(); + // SSLEngineResult.Status status = result.getStatus(); + *handshakeStatus = getHandshakeStatus(env, sslStream, sslEngineResult); int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); switch (status) { case STATUS__OK: - return checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); + { + return SSLStreamStatus_OK; + } case STATUS__CLOSED: { - PAL_SSLStreamStatus statusLocal = checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, sslEngineResult)); - assert(statusLocal == SSLStreamStatus_OK); return close(env, sslStream); } case STATUS__BUFFER_UNDERFLOW: - sslStream->netInBuffer = ensureRemaining(env, sslStream, sslStream->netInBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod)); - return doUnwrap(env, sslStream); + { + // Expand buffer + // int newRemaining = sslSession.getPacketBufferSize(); + int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); + sslStream->netInBuffer = ensureRemaining(env, sslStream, sslStream->netInBuffer, newRemaining); + return SSLStreamStatus_OK; + } case STATUS__BUFFER_OVERFLOW: - sslStream->appInBuffer = ensureRemaining(env, sslStream, sslStream->appInBuffer, (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod)); - return doUnwrap(env, sslStream); + { + // Expand buffer + // int newRemaining = sslSession.getApplicationBufferSize(); + int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod); + sslStream->appInBuffer = ensureRemaining(env, sslStream, sslStream->appInBuffer, newRemaining); + return SSLStreamStatus_OK; + } default: - LOG_ERROR("Unknown SSLEngineResult status"); + { + LOG_ERROR("Unknown SSLEngineResult status: %d", status); return SSLStreamStatus_Error; + } } } -static PAL_SSLStreamStatus checkHandshakeStatus(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) +static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream) { - /* - switch (handshakeStatus) { - case NEED_WRAP: - doWrap(); + assert(env != NULL); + assert(sslStream != NULL); + + PAL_SSLStreamStatus status = SSLStreamStatus_OK; + int handshakeStatus = getHandshakeStatus(env, sslStream, NULL); + while (IsHandshaking(handshakeStatus) && status == SSLStreamStatus_OK) + { + switch (handshakeStatus) + { + case HANDSHAKE_STATUS__NEED_WRAP: + status = doWrap(env, sslStream, &handshakeStatus); break; - case NEED_UNWRAP: - doUnwrap(); + case HANDSHAKE_STATUS__NEED_UNWRAP: + status = doUnwrap(env, sslStream, &handshakeStatus); break; - case NEED_TASK: - Runnable task; - while ((task = sslEngine.getDelegatedTask()) != null) task.run(); - checkHandshakeStatus(); + case HANDSHAKE_STATUS__NOT_HANDSHAKING: + case HANDSHAKE_STATUS__FINISHED: + status = SSLStreamStatus_OK; break; + case HANDSHAKE_STATUS__NEED_TASK: + assert(0 && "unexpected NEED_TASK handshake status"); } - */ - - AssertOnJNIExceptions(env); - switch (handshakeStatus) - { - case HANDSHAKE_STATUS__NEED_WRAP: - return doWrap(env, sslStream); - case HANDSHAKE_STATUS__NEED_UNWRAP: - return doUnwrap(env, sslStream); - case HANDSHAKE_STATUS__NOT_HANDSHAKING: - case HANDSHAKE_STATUS__FINISHED: - return SSLStreamStatus_OK; - case HANDSHAKE_STATUS__NEED_TASK: - assert(0 && "unexpected NEED_TASK handshake status"); } - return SSLStreamStatus_Error; + return status; } static void FreeSSLStream(JNIEnv *env, SSLStream *sslStream) @@ -353,29 +293,22 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( SSLStream* sslStream = malloc(sizeof(SSLStream)); memset(sslStream, 0, sizeof(SSLStream)); - // SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - // sslContext.init(null, null, null); - // TODO: set TrustManager[] argument in init to be able to intercept cert validation process (and callback to C#). - // jstring protocol = JSTRING("TLSv1.2"); - // jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, protocol); - // (*env)->DeleteLocalRef(env, protocol); - // ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // (*env)->CallVoidMethod(env, sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); - // ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // sslStream->sslContext = ToGRef(env, sslContext); + // 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); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + ON_EXCEPTION_PRINT_AND_GOTO(fail); sslStream->sslContext = ToGRef(env, sslContext); // SSLEngine sslEngine = sslContext.createSSLEngine(); // sslEngine.setUseClientMode(!isServer); jobject sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + ON_EXCEPTION_PRINT_AND_GOTO(fail); sslStream->sslEngine = ToGRef(env, sslEngine); (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientModeMethod, !isServer); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + ON_EXCEPTION_PRINT_AND_GOTO(fail); // SSLSession sslSession = sslEngine.getSession(); sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSessionMethod)); @@ -400,8 +333,10 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( return sslStream; -cleanup: - FreeSSLStream(env, sslStream); +fail: + if (sslStream != NULL) + FreeSSLStream(env, sslStream); + return NULL; } @@ -450,7 +385,7 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; - return checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); + return DoHandshake(env, sslStream); } SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( @@ -511,7 +446,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshakeMethod); - checkHandshakeStatus(env, sslStream, getHandshakeStatus(env, sslStream, NULL)); + DoHandshake(env, sslStream); (*env)->DeleteLocalRef(env, tlsVerStr); AssertOnJNIExceptions(env); return sslStream; @@ -549,7 +484,8 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint if (rem == 0) { (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); - PAL_SSLStreamStatus unwrapStatus = doUnwrap(env, sslStream); + int handshakeStatus; + PAL_SSLStreamStatus unwrapStatus = doUnwrap(env, sslStream, &handshakeStatus); if (unwrapStatus != SSLStreamStatus_OK) { ret = unwrapStatus; @@ -557,9 +493,16 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint } (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlipMethod)); + + if (IsHandshaking(handshakeStatus)) + { + ret = SSLStreamStatus_Renegotiate; + goto cleanup; + } + + rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); } - rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); if (rem > 0) { data = (*env)->NewByteArray(env, rem); @@ -583,17 +526,28 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int length) { - /* - appOutBuffer.put(message); - doWrap(); - */ + assert(sslStream != NULL); 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); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPut2Method, data)); + (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPutWithByteArray, data)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + int handshakeStatus; + ret = doWrap(env, sslStream, &handshakeStatus); + if (ret == SSLStreamStatus_OK && IsHandshaking(handshakeStatus)) + { + ret = SSLStreamStatus_Renegotiate; + } + +cleanup: (*env)->DeleteLocalRef(env, data); - return doWrap(env, sslStream); + return ret; } void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream) @@ -754,6 +708,15 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hos return ret; } +bool AndroidCryptoNative_SSLStreamShutdown(SSLStream *sslStream) +{ + assert(sslStream != NULL); + JNIEnv* env = GetJNIEnv(); + + PAL_SSLStreamStatus status = close(env, sslStream); + return status == SSLStreamStatus_Closed; +} + static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) { jsize bytesLen = (*env)->GetArrayLength(env, source); 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 8dfce56b38abe2..4f7fc33c27b0e3 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 @@ -26,7 +26,9 @@ enum { SSLStreamStatus_OK = 0, SSLStreamStatus_NeedData = 1, - SSLStreamStatus_Error = 2 + SSLStreamStatus_Error = 2, + SSLStreamStatus_Renegotiate = 3, + SSLStreamStatus_Closed = 4, }; typedef int32_t PAL_SSLStreamStatus; @@ -47,3 +49,5 @@ PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *ssl PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, jobject** out, int* outLen); PALEXPORT bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hostname); + +PALEXPORT bool AndroidCryptoNative_SSLStreamShutdown(SSLStream *sslStream); 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 6e9ce6a475f372..d8f51469c45982 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 @@ -56,6 +56,17 @@ private static SafeSslHandle CreateSslContext( SslAuthenticationOptions authOptions) { bool isServer = authOptions.IsServer; + + if (authOptions.ApplicationProtocols != null + || authOptions.CipherSuitesPolicy != null + || credential.Protocols != SslProtocols.None + || (isServer && authOptions.RemoteCertRequired) + || (credential.CertificateContext != null)) + { + // TODO: [AndroidCrypto] Handle non-system-default options + throw new NotImplementedException(); + } + SafeSslHandle handle = Interop.AndroidCrypto.SSLStreamCreate( isServer, readCallback, @@ -63,41 +74,11 @@ private static SafeSslHandle CreateSslContext( InitialBufferSize, InitialBufferSize); - // Interop.AndroidCrypto.SSLStreamSetParameters - if (authOptions.ApplicationProtocols != null) - { - // SSLParameters.setApplicationProtocols - // SSLEngine.setSSLParameters - } - - if (credential.Protocols != SslProtocols.None) - { - // new SSLParameters(cipherSuites, protocols) - // SSLEngine.setEnabledProtocols - } - - if (authOptions.CipherSuitesPolicy != null) - { - // new SSLParameters(cipherSuites, protocols) - // SSLEngine.setEnabledCipherSuites - } - if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) { Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); } - if (isServer && authOptions.RemoteCertRequired) - { - // SSLParameters.setNeedClientAuth - // SSLEngine.setSSLParameters - } - - if (credential.CertificateContext != null && credential.CertificateContext.IntermediateCertificates.Length > 0) - { - throw new NotImplementedException(); - } - return handle; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index d7399e4d38ad22..0cda2ef2b761d0 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -97,6 +97,8 @@ public static SecurityStatusPal EncryptMessage( { PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded, + PAL_SSLStreamStatus.Renegotiate => SecurityStatusPalErrorCode.Renegotiate, + PAL_SSLStreamStatus.Closed => SecurityStatusPalErrorCode.ContextExpired, _ => SecurityStatusPalErrorCode.InternalError }; @@ -145,6 +147,8 @@ public static SecurityStatusPal DecryptMessage( { PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.Renegotiate => SecurityStatusPalErrorCode.Renegotiate, + PAL_SSLStreamStatus.Closed => SecurityStatusPalErrorCode.ContextExpired, _ => SecurityStatusPalErrorCode.InternalError }; @@ -245,8 +249,7 @@ public static SecurityStatusPal ApplyShutdownToken( SafeSslHandle sslHandle = sslContext.SslContext; - // bool success = Interop.AndroidCrypto.SSLStreamShutdown(sslHandle); - bool success = false; + bool success = Interop.AndroidCrypto.SSLStreamShutdown(sslHandle); if (success) { return new SecurityStatusPal(SecurityStatusPalErrorCode.OK); From 4c7af00a19aab01b5948ecbb078328eca664a127 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 30 Mar 2021 20:56:11 -0700 Subject: [PATCH 09/13] Cleanup --- .../Interop.Ssl.cs | 26 +- .../pal_jni.c | 147 ++++---- .../pal_jni.h | 61 ++-- .../pal_ssl.c | 2 +- .../pal_sslstream.c | 319 +++++++----------- .../pal_sslstream.h | 109 +++++- .../pal_x509.c | 17 - .../Pal.Android/SafeDeleteSslContext.cs | 65 ++-- 8 files changed, 362 insertions(+), 384 deletions(-) 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 f335d6187aef75..668ed010c0303f 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,7 +3,6 @@ using System; using System.Runtime.InteropServices; -using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; @@ -31,25 +30,24 @@ internal static extern SafeSslHandle SSLStreamCreate( [MarshalAs(UnmanagedType.U1)] bool isServer, SSLReadCallback streamRead, SSLWriteCallback streamWrite, - int appOutBufferSize, - int appInBufferSize); + int appBufferSize); [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamConfigureParameters")] - internal static extern int SSLStreamConfigureParameters( + private static extern int SSLStreamConfigureParametersImpl( SafeSslHandle sslHandle, [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost); + internal static void SSLStreamConfigureParameters( + SafeSslHandle sslHandle, + [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost) + { + int ret = SSLStreamConfigureParametersImpl(sslHandle, targetHost); + if (ret != SUCCESS) + throw new SslException(); + } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamHandshake")] internal static extern PAL_SSLStreamStatus SSLStreamHandshake(SafeSslHandle sslHandle); - [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateAndStartHandshake")] - private static extern SafeSslHandle SSLStreamCreateAndStartHandshake( - SSLReadCallback streamRead, - SSLWriteCallback streamWrite, - int tlsVersion, - int appOutBufferSize, - int appInBufferSize); - [DllImport(Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamGetApplicationProtocol")] private static extern int SSLStreamGetApplicationProtocol(SafeSslHandle ssl, [Out] byte[]? buf, ref int len); internal static byte[]? SSLStreamGetApplicationProtocol(SafeSslHandle ssl) @@ -102,7 +100,7 @@ internal static string SSLStreamGetProtocol(SafeSslHandle ssl) IntPtr protocolPtr; int ret = SSLStreamGetProtocol(ssl, out protocolPtr); if (ret != SUCCESS) - throw new CryptographicException(); + throw new SslException(); if (protocolPtr == IntPtr.Zero) return string.Empty; @@ -147,7 +145,7 @@ internal static string SSLStreamGetCipherSuite(SafeSslHandle ssl) IntPtr cipherSuitePtr; int ret = SSLStreamGetCipherSuite(ssl, out cipherSuitePtr); if (ret != SUCCESS) - throw new CryptographicException(); + throw new SslException(); if (cipherSuitePtr == IntPtr.Zero) return string.Empty; 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 9ec826fec27f62..251d3b15a6d3ff 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 @@ -77,10 +77,10 @@ jmethodID g_bitLengthMethod; jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters -jclass g_sslParamsClass; -jmethodID g_sslParamsCtor; -jmethodID g_sslParamsGetProtocolsMethod; -jmethodID g_sslParamsSetServerNames; +jclass g_SSLParametersClass; +jmethodID g_SSLParametersCtor; +jmethodID g_SSLParametersGetProtocols; +jmethodID g_SSLParametersSetServerNames; // javax/net/ssl/SSLContext jclass g_sslCtxClass; @@ -386,29 +386,27 @@ jmethodID g_SNIHostNameCtor; // javax/net/ssl/SSLEngine jclass g_SSLEngine; jmethodID g_SSLEngineGetApplicationProtocol; -jmethodID g_SSLEngineSetUseClientModeMethod; -jmethodID g_SSLEngineGetSessionMethod; -jmethodID g_SSLEngineBeginHandshakeMethod; -jmethodID g_SSLEngineWrapMethod; -jmethodID g_SSLEngineUnwrapMethod; -jmethodID g_SSLEngineCloseInboundMethod; -jmethodID g_SSLEngineCloseOutboundMethod; -jmethodID g_SSLEngineGetHandshakeStatusMethod; +jmethodID g_SSLEngineSetUseClientMode; +jmethodID g_SSLEngineGetSession; +jmethodID g_SSLEngineBeginHandshake; +jmethodID g_SSLEngineWrap; +jmethodID g_SSLEngineUnwrap; +jmethodID g_SSLEngineCloseOutbound; +jmethodID g_SSLEngineGetHandshakeStatus; jmethodID g_SSLEngineSetSSLParameters; // java/nio/ByteBuffer jclass g_ByteBuffer; -jmethodID g_ByteBufferAllocateMethod; -jmethodID g_ByteBufferPutMethod; -jmethodID g_ByteBufferPutWithByteArray; -jmethodID g_ByteBufferPutWythByteArrayLength; -jmethodID g_ByteBufferFlipMethod; -jmethodID g_ByteBufferGetMethod; -jmethodID g_ByteBufferPutBufferMethod; -jmethodID g_ByteBufferLimitMethod; -jmethodID g_ByteBufferRemainingMethod; -jmethodID g_ByteBufferCompactMethod; -jmethodID g_ByteBufferPositionMethod; +jmethodID g_ByteBufferAllocate; +jmethodID g_ByteBufferCompact; +jmethodID g_ByteBufferFlip; +jmethodID g_ByteBufferGet; +jmethodID g_ByteBufferLimit; +jmethodID g_ByteBufferPosition; +jmethodID g_ByteBufferPutBuffer; +jmethodID g_ByteBufferPutByteArray; +jmethodID g_ByteBufferPutByteArrayWithLength; +jmethodID g_ByteBufferRemaining; // javax/net/ssl/SSLContext jclass g_SSLContext; @@ -419,19 +417,16 @@ jmethodID g_SSLContextCreateSSLEngineMethod; // javax/net/ssl/SSLSession jclass g_SSLSession; -jmethodID g_SSLSessionGetApplicationBufferSizeMethod; +jmethodID g_SSLSessionGetApplicationBufferSize; jmethodID g_SSLSessionGetCipherSuite; -jmethodID g_SSLSessionGetPacketBufferSizeMethod; +jmethodID g_SSLSessionGetPacketBufferSize; jmethodID g_SSLSessionGetPeerCertificates; jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult jclass g_SSLEngineResult; -jmethodID g_SSLEngineResultGetStatusMethod; -jmethodID g_SSLEngineResultGetHandshakeStatusMethod; - -// javax/net/ssl/TrustManager -jclass g_TrustManager; +jmethodID g_SSLEngineResultGetStatus; +jmethodID g_SSLEngineResultGetHandshakeStatus; // javax/crypto/KeyAgreement jclass g_KeyAgreementClass; @@ -542,11 +537,6 @@ bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException) return true; } -void AssertOnJNIExceptions(JNIEnv* env) -{ - assert(!CheckJNIExceptions(env)); -} - void SaveTo(uint8_t* src, uint8_t** dst, size_t len, bool overwrite) { assert(overwrite || !(*dst)); @@ -609,6 +599,19 @@ int GetEnumAsInt(JNIEnv *env, jobject enumObj) return value; } +int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) +{ + jsize bytesLen = (*env)->GetArrayLength(env, source); + + bool insufficientBuffer = *len < bytesLen; + *len = bytesLen; + if (insufficientBuffer) + return INSUFFICIENT_BUFFER; + + (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { @@ -682,10 +685,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_sslParamsClass = GetClassGRef(env, "javax/net/ssl/SSLParameters"); - g_sslParamsCtor = GetMethod(env, false, g_sslParamsClass, "", "()V"); - g_sslParamsGetProtocolsMethod = GetMethod(env, false, g_sslParamsClass, "getProtocols", "()[Ljava/lang/String;"); - g_sslParamsSetServerNames = GetMethod(env, false, g_sslParamsClass, "setServerNames", "(Ljava/util/List;)V"); + 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_sslCtxClass = GetClassGRef(env, "javax/net/ssl/SSLContext"); g_sslCtxGetDefaultMethod = GetMethod(env, true, g_sslCtxClass, "getDefault", "()Ljavax/net/ssl/SSLContext;"); @@ -931,30 +934,28 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SNIHostName = GetClassGRef(env, "javax/net/ssl/SNIHostName"); 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_SSLEngineSetUseClientModeMethod = GetMethod(env, false, g_SSLEngine, "setUseClientMode", "(Z)V"); - g_SSLEngineGetSessionMethod = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); - g_SSLEngineBeginHandshakeMethod = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); - g_SSLEngineWrapMethod = GetMethod(env, false, g_SSLEngine, "wrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); - g_SSLEngineUnwrapMethod = GetMethod(env, false, g_SSLEngine, "unwrap", "(Ljava/nio/ByteBuffer;Ljava/nio/ByteBuffer;)Ljavax/net/ssl/SSLEngineResult;"); - g_SSLEngineGetHandshakeStatusMethod = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); - g_SSLEngineCloseInboundMethod = GetMethod(env, false, g_SSLEngine, "closeInbound", "()V"); - g_SSLEngineCloseOutboundMethod = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); - g_SSLEngineSetSSLParameters = GetMethod(env, false, g_SSLEngine, "setSSLParameters", "(Ljavax/net/ssl/SSLParameters;)V"); - - g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); - g_ByteBufferAllocateMethod = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); - g_ByteBufferPutMethod = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); - g_ByteBufferPutWithByteArray = GetMethod(env, false, g_ByteBuffer, "put", "([B)Ljava/nio/ByteBuffer;"); - g_ByteBufferPutWythByteArrayLength = GetMethod(env, false, g_ByteBuffer, "put", "([BII)Ljava/nio/ByteBuffer;"); - g_ByteBufferFlipMethod = GetMethod(env, false, g_ByteBuffer, "flip", "()Ljava/nio/Buffer;"); - g_ByteBufferLimitMethod = GetMethod(env, false, g_ByteBuffer, "limit", "()I"); - g_ByteBufferGetMethod = GetMethod(env, false, g_ByteBuffer, "get", "([B)Ljava/nio/ByteBuffer;"); - g_ByteBufferPutBufferMethod = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); - g_ByteBufferRemainingMethod = GetMethod(env, false, g_ByteBuffer, "remaining", "()I"); - g_ByteBufferCompactMethod = GetMethod(env, false, g_ByteBuffer, "compact", "()Ljava/nio/ByteBuffer;"); - g_ByteBufferPositionMethod = GetMethod(env, false, g_ByteBuffer, "position", "()I"); + 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_SSLEngineSetSSLParameters = GetMethod(env, false, g_SSLEngine, "setSSLParameters", "(Ljavax/net/ssl/SSLParameters;)V"); + + g_ByteBuffer = GetClassGRef(env, "java/nio/ByteBuffer"); + g_ByteBufferAllocate = GetMethod(env, true, g_ByteBuffer, "allocate", "(I)Ljava/nio/ByteBuffer;"); + g_ByteBufferCompact = GetMethod(env, false, g_ByteBuffer, "compact", "()Ljava/nio/ByteBuffer;"); + g_ByteBufferFlip = GetMethod(env, false, g_ByteBuffer, "flip", "()Ljava/nio/Buffer;"); + g_ByteBufferGet = GetMethod(env, false, g_ByteBuffer, "get", "([B)Ljava/nio/ByteBuffer;"); + g_ByteBufferLimit = GetMethod(env, false, g_ByteBuffer, "limit", "()I"); + g_ByteBufferPosition = GetMethod(env, false, g_ByteBuffer, "position", "()I"); + g_ByteBufferPutBuffer = GetMethod(env, false, g_ByteBuffer, "put", "(Ljava/nio/ByteBuffer;)Ljava/nio/ByteBuffer;"); + g_ByteBufferPutByteArray = GetMethod(env, false, g_ByteBuffer, "put", "([B)Ljava/nio/ByteBuffer;"); + g_ByteBufferPutByteArrayWithLength = GetMethod(env, false, g_ByteBuffer, "put", "([BII)Ljava/nio/ByteBuffer;"); + g_ByteBufferRemaining = GetMethod(env, false, g_ByteBuffer, "remaining", "()I"); g_SSLContext = GetClassGRef(env, "javax/net/ssl/SSLContext"); g_SSLContextGetDefault = GetMethod(env, true, g_SSLContext, "getDefault", "()Ljavax/net/ssl/SSLContext;"); @@ -962,18 +963,16 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLContextInitMethod = GetMethod(env, false, g_SSLContext, "init", "([Ljavax/net/ssl/KeyManager;[Ljavax/net/ssl/TrustManager;Ljava/security/SecureRandom;)V"); g_SSLContextCreateSSLEngineMethod = GetMethod(env, false, g_SSLContext, "createSSLEngine", "()Ljavax/net/ssl/SSLEngine;"); - g_SSLSession = GetClassGRef(env, "javax/net/ssl/SSLSession"); - g_SSLSessionGetApplicationBufferSizeMethod = GetMethod(env, false, g_SSLSession, "getApplicationBufferSize", "()I"); - g_SSLSessionGetCipherSuite = GetMethod(env, false, g_SSLSession, "getCipherSuite", "()Ljava/lang/String;"); - g_SSLSessionGetPacketBufferSizeMethod = 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;"); - - g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult"); - g_SSLEngineResultGetStatusMethod = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;"); - g_SSLEngineResultGetHandshakeStatusMethod = GetMethod(env, false, g_SSLEngineResult, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); + 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_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;"); - g_TrustManager = GetClassGRef(env, "javax/net/ssl/TrustManager"); + g_SSLEngineResult = GetClassGRef(env, "javax/net/ssl/SSLEngineResult"); + g_SSLEngineResultGetStatus = GetMethod(env, false, g_SSLEngineResult, "getStatus", "()Ljavax/net/ssl/SSLEngineResult$Status;"); + g_SSLEngineResultGetHandshakeStatus = GetMethod(env, false, g_SSLEngineResult, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_KeyAgreementClass = GetClassGRef(env, "javax/crypto/KeyAgreement"); g_KeyAgreementGetInstance = GetMethod(env, true, g_KeyAgreementClass, "getInstance", "(Ljava/lang/String;)Ljavax/crypto/KeyAgreement;"); 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 24fa4ed23e06c8..fa751087bbe498 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 @@ -11,6 +11,7 @@ #define FAIL 0 #define SUCCESS 1 +#define INSUFFICIENT_BUFFER -1 extern JavaVM* gJvm; @@ -86,10 +87,10 @@ extern jmethodID g_bitLengthMethod; extern jmethodID g_sigNumMethod; // javax/net/ssl/SSLParameters -extern jclass g_sslParamsClass; -extern jmethodID g_sslParamsCtor; -extern jmethodID g_sslParamsGetProtocolsMethod; -extern jmethodID g_sslParamsSetServerNames; +extern jclass g_SSLParametersClass; +extern jmethodID g_SSLParametersCtor; +extern jmethodID g_SSLParametersGetProtocols; +extern jmethodID g_SSLParametersSetServerNames; // javax/net/ssl/SSLContext extern jclass g_sslCtxClass; @@ -400,29 +401,27 @@ extern jmethodID g_SNIHostNameCtor; // javax/net/ssl/SSLEngine extern jclass g_SSLEngine; extern jmethodID g_SSLEngineGetApplicationProtocol; -extern jmethodID g_SSLEngineSetUseClientModeMethod; -extern jmethodID g_SSLEngineGetSessionMethod; -extern jmethodID g_SSLEngineBeginHandshakeMethod; -extern jmethodID g_SSLEngineWrapMethod; -extern jmethodID g_SSLEngineUnwrapMethod; -extern jmethodID g_SSLEngineCloseInboundMethod; -extern jmethodID g_SSLEngineCloseOutboundMethod; -extern jmethodID g_SSLEngineGetHandshakeStatusMethod; +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_SSLEngineGetHandshakeStatus; extern jmethodID g_SSLEngineSetSSLParameters; // java/nio/ByteBuffer extern jclass g_ByteBuffer; -extern jmethodID g_ByteBufferAllocateMethod; -extern jmethodID g_ByteBufferPutMethod; -extern jmethodID g_ByteBufferPutWithByteArray; -extern jmethodID g_ByteBufferPutWythByteArrayLength; -extern jmethodID g_ByteBufferFlipMethod; -extern jmethodID g_ByteBufferGetMethod; -extern jmethodID g_ByteBufferLimitMethod; -extern jmethodID g_ByteBufferRemainingMethod; -extern jmethodID g_ByteBufferPutBufferMethod; -extern jmethodID g_ByteBufferCompactMethod; -extern jmethodID g_ByteBufferPositionMethod; +extern jmethodID g_ByteBufferAllocate; +extern jmethodID g_ByteBufferCompact; +extern jmethodID g_ByteBufferFlip; +extern jmethodID g_ByteBufferGet; +extern jmethodID g_ByteBufferLimit; +extern jmethodID g_ByteBufferPosition; +extern jmethodID g_ByteBufferPutBuffer; +extern jmethodID g_ByteBufferPutByteArray; +extern jmethodID g_ByteBufferPutByteArrayWithLength; +extern jmethodID g_ByteBufferRemaining; // javax/net/ssl/SSLContext extern jclass g_SSLContext; @@ -434,19 +433,16 @@ extern jmethodID g_SSLContextCreateSSLEngineWithPeer; // javax/net/ssl/SSLSession extern jclass g_SSLSession; -extern jmethodID g_SSLSessionGetApplicationBufferSizeMethod; +extern jmethodID g_SSLSessionGetApplicationBufferSize; extern jmethodID g_SSLSessionGetCipherSuite; -extern jmethodID g_SSLSessionGetPacketBufferSizeMethod; +extern jmethodID g_SSLSessionGetPacketBufferSize; extern jmethodID g_SSLSessionGetPeerCertificates; extern jmethodID g_SSLSessionGetProtocol; // javax/net/ssl/SSLEngineResult extern jclass g_SSLEngineResult; -extern jmethodID g_SSLEngineResultGetStatusMethod; -extern jmethodID g_SSLEngineResultGetHandshakeStatusMethod; - -// javax/net/ssl/TrustManager -extern jclass g_TrustManager; +extern jmethodID g_SSLEngineResultGetStatus; +extern jmethodID g_SSLEngineResultGetHandshakeStatus; // javax/crypto/KeyAgreement extern jclass g_KeyAgreementClass; @@ -500,11 +496,10 @@ bool TryClearJNIExceptions(JNIEnv* env); // Get any pending JNI exception. Returns true if there was an exception, false otherwise. bool TryGetJNIException(JNIEnv* env, jthrowable *ex, bool printException); -// Assert on any JNI exceptions. Prints the exception before asserting. -void AssertOnJNIExceptions(JNIEnv* env); - jmethodID GetMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); jmethodID GetOptionalMethod(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); JNIEnv* GetJNIEnv(void); int GetEnumAsInt(JNIEnv *env, jobject enumObj); + +int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len); 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 fb7eac36c0b6f6..d2866f906ae383 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 @@ -8,7 +8,7 @@ int32_t CryptoNative_OpenSslGetProtocolSupport(SslProtocols protocol) JNIEnv* env = GetJNIEnv(); jobject sslCtxObj = (*env)->CallStaticObjectMethod(env, g_sslCtxClass, g_sslCtxGetDefaultMethod); jobject sslParametersObj = (*env)->CallObjectMethod(env, sslCtxObj, g_sslCtxGetDefaultSslParamsMethod); - jobjectArray protocols = (jobjectArray)(*env)->CallObjectMethod(env, sslParametersObj, g_sslParamsGetProtocolsMethod); + jobjectArray protocols = (jobjectArray)(*env)->CallObjectMethod(env, sslParametersObj, g_SSLParametersGetProtocols); int protocolsCount = (*env)->GetArrayLength(env, protocols); int supported = 0; 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 90d6735fb94411..1b41a19e0a9035 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 @@ -3,7 +3,8 @@ #include "pal_sslstream.h" -#define INSUFFICIENT_BUFFER -1 +// Explicitly ignore jobject return value - assumes there is a JNIEnv* variable named env +#define IGNORE_RETURN(retval) (*env)->DeleteLocalRef(env, retval) // javax/net/ssl/SSLEngineResult$HandshakeStatus enum @@ -24,71 +25,58 @@ enum STATUS__CLOSED = 3, }; -static uint16_t* AllocateString(JNIEnv *env, jstring source); -static int32_t PopulateByteArray(JNIEnv *env, jbyteArray source, uint8_t *dest, int32_t *len); +static uint16_t* AllocateString(JNIEnv* env, jstring source); static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream); -static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream, int* hs); -static void flush(JNIEnv* env, SSLStream* sslStream); - -static int getHandshakeStatus(JNIEnv* env, SSLStream* sslStream, jobject engineResult) -{ - AssertOnJNIExceptions(env); - int status = -1; - if (engineResult) - status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, engineResult, g_SSLEngineResultGetHandshakeStatusMethod)); - else - status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatusMethod)); - AssertOnJNIExceptions(env); - return status; -} +static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus); +static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus); static bool IsHandshaking(int handshakeStatus) { - return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING - && handshakeStatus != HANDSHAKE_STATUS__FINISHED; + return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING && handshakeStatus != HANDSHAKE_STATUS__FINISHED; } -static PAL_SSLStreamStatus close(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus Close(JNIEnv* env, SSLStream* sslStream) { // sslEngine.closeOutbound(); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutbound); // Call wrap to clear any remaining data int unused; - return doWrap(env, sslStream, &unused); + return DoWrap(env, sslStream, &unused); } -static void flush(JNIEnv* env, SSLStream* sslStream) +static PAL_SSLStreamStatus Flush(JNIEnv* env, SSLStream* sslStream) { /* netOutBuffer.flip(); byte[] data = new byte[netOutBuffer.limit()]; netOutBuffer.get(data); - WriteToOutputStream(data, 0, data.length); + streamWriter(data, 0, data.length); netOutBuffer.compact(); */ - AssertOnJNIExceptions(env); - - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferFlipMethod)); - int bufferLimit = (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferLimitMethod); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferFlip)); + int bufferLimit = (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferLimit); jbyteArray data = (*env)->NewByteArray(env, bufferLimit); - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferGetMethod, data)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferGet, data)); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; uint8_t* dataPtr = (uint8_t*)malloc((size_t)bufferLimit); - (*env)->GetByteArrayRegion(env, data, 0, bufferLimit, (jbyte*) dataPtr); + (*env)->GetByteArrayRegion(env, data, 0, bufferLimit, (jbyte*)dataPtr); sslStream->streamWriter(dataPtr, bufferLimit); free(dataPtr); - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferCompactMethod)); - AssertOnJNIExceptions(env); + + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netOutBuffer, g_ByteBufferCompact)); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + + return SSLStreamStatus_OK; } -static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuffer, int newRemaining) +static jobject EnsureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuffer, int newRemaining) { /* if (oldBuffer.remaining() < newRemaining) { @@ -101,16 +89,14 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf } */ - AssertOnJNIExceptions(env); - - int oldRemaining = (*env)->CallIntMethod(env, oldBuffer, g_ByteBufferRemainingMethod); + int oldRemaining = (*env)->CallIntMethod(env, oldBuffer, g_ByteBufferRemaining); if (oldRemaining < newRemaining) { - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, oldBuffer, g_ByteBufferFlipMethod)); - jobject newBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, oldRemaining + newRemaining)); - // DeleteLocalRef because we don't need the return value (Buffer) - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, newBuffer, g_ByteBufferPutBufferMethod, oldBuffer)); + IGNORE_RETURN((*env)->CallObjectMethod(env, oldBuffer, g_ByteBufferFlip)); + jobject newBuffer = ToGRef( + env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, oldRemaining + newRemaining)); + + IGNORE_RETURN((*env)->CallObjectMethod(env, newBuffer, g_ByteBufferPutBuffer, oldBuffer)); ReleaseGRef(env, oldBuffer); return newBuffer; } @@ -120,41 +106,41 @@ static jobject ensureRemaining(JNIEnv* env, SSLStream* sslStream, jobject oldBuf } } -static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus) +static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus) { // appOutBuffer.flip(); // SSLEngineResult result = sslEngine.wrap(appOutBuffer, netOutBuffer); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferFlipMethod)); - jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineWrapMethod, sslStream->appOutBuffer, sslStream->netOutBuffer); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferFlip)); + jobject result = (*env)->CallObjectMethod( + env, sslStream->sslEngine, g_SSLEngineWrap, sslStream->appOutBuffer, sslStream->netOutBuffer); if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; // appOutBuffer.compact(); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompactMethod)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferCompact)); // handshakeStatus = result.getHandshakeStatus(); // SSLEngineResult.Status status = result.getStatus(); - *handshakeStatus = getHandshakeStatus(env, sslStream, sslEngineResult); - int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); + *handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetHandshakeStatus)); + int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); switch (status) { case STATUS__OK: { - flush(env, sslStream); - return SSLStreamStatus_OK; + return Flush(env, sslStream); } case STATUS__CLOSED: { - flush(env, sslStream); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutboundMethod); + (void)Flush(env, sslStream); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineCloseOutbound); return SSLStreamStatus_Closed; } case STATUS__BUFFER_OVERFLOW: { // Expand buffer // int newRemaining = sslSession.getPacketBufferSize(); - int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); - sslStream->netOutBuffer = ensureRemaining(env, sslStream, sslStream->netOutBuffer, newRemaining); + int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); + sslStream->netOutBuffer = EnsureRemaining(env, sslStream, sslStream->netOutBuffer, newRemaining); return SSLStreamStatus_OK; } default: @@ -165,17 +151,17 @@ static PAL_SSLStreamStatus doWrap(JNIEnv* env, SSLStream* sslStream, int* handsh } } -static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream, int *handshakeStatus) +static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus) { // if (netInBuffer.position() == 0) // { // byte[] tmp = new byte[netInBuffer.limit()]; - // int count = ReadFromInputStream(tmp, 0, tmp.length); + // int count = streamReader(tmp, 0, tmp.length); // netInBuffer.put(tmp, 0, count); // } - if ((*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferPositionMethod) == 0) + if ((*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferPosition) == 0) { - int netInBufferLimit = (*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferLimitMethod); + int netInBufferLimit = (*env)->CallIntMethod(env, sslStream->netInBuffer, g_ByteBufferLimit); jbyteArray tmp = (*env)->NewByteArray(env, netInBufferLimit); uint8_t* tmpNative = (uint8_t*)malloc((size_t)netInBufferLimit); int count = netInBufferLimit; @@ -186,25 +172,27 @@ static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream, int *hand } (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPutWythByteArrayLength, tmp, 0, count)); + IGNORE_RETURN( + (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPutByteArrayWithLength, tmp, 0, count)); free(tmpNative); (*env)->DeleteLocalRef(env, tmp); } // netInBuffer.flip(); // SSLEngineResult result = sslEngine.unwrap(netInBuffer, appInBuffer); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferFlipMethod)); - jobject sslEngineResult = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineUnwrapMethod, sslStream->netInBuffer, sslStream->appInBuffer); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferFlip)); + jobject result = (*env)->CallObjectMethod( + env, sslStream->sslEngine, g_SSLEngineUnwrap, sslStream->netInBuffer, sslStream->appInBuffer); if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; // netInBuffer.compact(); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompactMethod)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferCompact)); // handshakeStatus = result.getHandshakeStatus(); // SSLEngineResult.Status status = result.getStatus(); - *handshakeStatus = getHandshakeStatus(env, sslStream, sslEngineResult); - int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslEngineResult, g_SSLEngineResultGetStatusMethod)); + *handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetHandshakeStatus)); + int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); switch (status) { case STATUS__OK: @@ -213,22 +201,22 @@ static PAL_SSLStreamStatus doUnwrap(JNIEnv* env, SSLStream* sslStream, int *hand } case STATUS__CLOSED: { - return close(env, sslStream); + return Close(env, sslStream); } case STATUS__BUFFER_UNDERFLOW: { // Expand buffer // int newRemaining = sslSession.getPacketBufferSize(); - int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); - sslStream->netInBuffer = ensureRemaining(env, sslStream, sslStream->netInBuffer, newRemaining); + int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); + sslStream->netInBuffer = EnsureRemaining(env, sslStream, sslStream->netInBuffer, newRemaining); return SSLStreamStatus_OK; } case STATUS__BUFFER_OVERFLOW: { // Expand buffer // int newRemaining = sslSession.getApplicationBufferSize(); - int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod); - sslStream->appInBuffer = ensureRemaining(env, sslStream, sslStream->appInBuffer, newRemaining); + int newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize); + sslStream->appInBuffer = EnsureRemaining(env, sslStream, sslStream->appInBuffer, newRemaining); return SSLStreamStatus_OK; } default: @@ -245,16 +233,17 @@ static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream) assert(sslStream != NULL); PAL_SSLStreamStatus status = SSLStreamStatus_OK; - int handshakeStatus = getHandshakeStatus(env, sslStream, NULL); + int handshakeStatus = + GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); while (IsHandshaking(handshakeStatus) && status == SSLStreamStatus_OK) { switch (handshakeStatus) { case HANDSHAKE_STATUS__NEED_WRAP: - status = doWrap(env, sslStream, &handshakeStatus); + status = DoWrap(env, sslStream, &handshakeStatus); break; case HANDSHAKE_STATUS__NEED_UNWRAP: - status = doUnwrap(env, sslStream, &handshakeStatus); + status = DoUnwrap(env, sslStream, &handshakeStatus); break; case HANDSHAKE_STATUS__NOT_HANDSHAKING: case HANDSHAKE_STATUS__FINISHED: @@ -268,7 +257,7 @@ static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream) return status; } -static void FreeSSLStream(JNIEnv *env, SSLStream *sslStream) +static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) { assert(sslStream != NULL); ReleaseGRef(env, sslStream->sslContext); @@ -281,12 +270,10 @@ static void FreeSSLStream(JNIEnv *env, SSLStream *sslStream) free(sslStream); } -SSLStream* AndroidCryptoNative_SSLStreamCreate( - bool isServer, - STREAM_READER streamReader, - STREAM_WRITER streamWriter, - int appOutBufferSize, - int appInBufferSize) +SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, + STREAM_READER streamReader, + STREAM_WRITER streamWriter, + int appBufferSize) { JNIEnv* env = GetJNIEnv(); @@ -306,27 +293,31 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( // sslEngine.setUseClientMode(!isServer); jobject sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod); ON_EXCEPTION_PRINT_AND_GOTO(fail); - sslStream->sslEngine = ToGRef(env, sslEngine); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientModeMethod, !isServer); + sslStream->sslEngine = ToGRef(env, sslEngine); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientMode, !isServer); ON_EXCEPTION_PRINT_AND_GOTO(fail); // SSLSession sslSession = sslEngine.getSession(); - sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSessionMethod)); + sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession)); - // final int applicationBufferSize = sslSession.getApplicationBufferSize(); - // final int packetBufferSize = sslSession.getPacketBufferSize(); - int applicationBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod); - int packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); + // int applicationBufferSize = sslSession.getApplicationBufferSize(); + // int packetBufferSize = sslSession.getPacketBufferSize(); + int applicationBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize); + int packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); - // ByteBuffer appOutBuffer = ByteBuffer.allocate(appOutBufferSize); + // ByteBuffer appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appBufferSize)); + // ByteBuffer appOutBuffer = ByteBuffer.allocate(appBufferSize); // ByteBuffer netOutBuffer = ByteBuffer.allocate(packetBufferSize); // ByteBuffer netInBuffer = ByteBuffer.allocate(packetBufferSize); - // ByteBuffer appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appInBufferSize)); - sslStream->appOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, appOutBufferSize)); - sslStream->netOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); - sslStream->appInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, - applicationBufferSize > appInBufferSize ? applicationBufferSize : appInBufferSize)); - sslStream->netInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); + int appInBufferSize = applicationBufferSize > appBufferSize ? applicationBufferSize : appBufferSize; + sslStream->appInBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, appInBufferSize)); + sslStream->appOutBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, appBufferSize)); + sslStream->netOutBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, packetBufferSize)); + sslStream->netInBuffer = + ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocate, packetBufferSize)); sslStream->streamReader = streamReader; sslStream->streamWriter = streamWriter; @@ -340,7 +331,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate( return NULL; } -int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, char* targetHost) +int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost) { assert(sslStream != NULL); assert(targetHost != NULL); @@ -364,9 +355,9 @@ int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, c // SSLParameters params = new SSLParameters(); // params.setServerNames(nameList); // sslEngine.setSSLParameters(params); - loc[params] = (*env)->NewObject(env, g_sslParamsClass, g_sslParamsCtor); + loc[params] = (*env)->NewObject(env, g_SSLParametersClass, g_SSLParametersCtor); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - (*env)->CallVoidMethod(env, loc[params], g_sslParamsSetServerNames, loc[nameList]); + (*env)->CallVoidMethod(env, loc[params], g_SSLParametersSetServerNames, loc[nameList]); (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetSSLParameters, loc[params]); ret = SUCCESS; @@ -376,82 +367,19 @@ int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, c return ret; } -PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream) +PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream) { assert(sslStream != NULL); JNIEnv* env = GetJNIEnv(); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshakeMethod); + // sslEngine.beginHandshake(); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshake); if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; return DoHandshake(env, sslStream); } -SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake( - STREAM_READER streamReader, - STREAM_WRITER streamWriter, - int tlsVersion, - int appOutBufferSize, - int appInBufferSize) -{ - JNIEnv* env = GetJNIEnv(); - /* - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); - sslContext.init(null, new TrustManage r[]{trustAllCerts}, null); - this.sslEngine = sslContext.createSSLEngine(); - this.sslEngine.setUseClientMode(true); - SSLSession sslSession = sslEngine.getSession(); - final int applicationBufferSize = sslSession.getApplicationBufferSize(); - final int packetBufferSize = sslSession.getPacketBufferSize(); - this.appOutBuffer = ByteBuffer.allocate(appOutBufferSize); - this.netOutBuffer = ByteBuffer.allocate(packetBufferSize); - this.netInBuffer = ByteBuffer.allocate(packetBufferSize); - this.appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appInBufferSize)); - sslEngine.beginHandshake(); - */ - - SSLStream* sslStream = malloc(sizeof(SSLStream)); - - jobject tlsVerStr = NULL; - if (tlsVersion == 11) - tlsVerStr = JSTRING("TLSv1.1"); - else if (tlsVersion == 12) - tlsVerStr = JSTRING("TLSv1.2"); - else if (tlsVersion == 13) - tlsVerStr = JSTRING("TLSv1.3"); - else - assert(0 && "unknown tlsVersion"); - - sslStream->sslContext = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, tlsVerStr)); - - // TODO: set TrustManager[] argument to be able to intercept cert validation process (and callback to C#). - (*env)->CallVoidMethod(env, sslStream->sslContext, g_SSLContextInitMethod, NULL, NULL, NULL); - sslStream->sslEngine = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod)); - sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSessionMethod)); - - int applicationBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSizeMethod); - int packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSizeMethod); - - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientModeMethod, true); - - sslStream->appOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, appOutBufferSize)); - sslStream->netOutBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); - sslStream->appInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, - applicationBufferSize > appInBufferSize ? applicationBufferSize : appInBufferSize)); - sslStream->netInBuffer = ToGRef(env, (*env)->CallStaticObjectMethod(env, g_ByteBuffer, g_ByteBufferAllocateMethod, packetBufferSize)); - - sslStream->streamReader = streamReader; - sslStream->streamWriter = streamWriter; - - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshakeMethod); - - DoHandshake(env, sslStream); - (*env)->DeleteLocalRef(env, tlsVerStr); - AssertOnJNIExceptions(env); - return sslStream; -} - PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int length, int* read) { assert(sslStream != NULL); @@ -466,7 +394,7 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint appInBuffer.flip(); if (appInBuffer.remaining() == 0) { appInBuffer.compact(); - doUnwrap(); + DoUnwrap(); appInBuffer.flip(); } if (appInBuffer.remaining() > 0) { @@ -479,20 +407,22 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint } */ - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlipMethod)); - int rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlip)); + int rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); if (rem == 0) { - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompact)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + int handshakeStatus; - PAL_SSLStreamStatus unwrapStatus = doUnwrap(env, sslStream, &handshakeStatus); + PAL_SSLStreamStatus unwrapStatus = DoUnwrap(env, sslStream, &handshakeStatus); if (unwrapStatus != SSLStreamStatus_OK) { ret = unwrapStatus; goto cleanup; } - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlipMethod)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferFlip)); if (IsHandshaking(handshakeStatus)) { @@ -500,17 +430,17 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint goto cleanup; } - rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemainingMethod); + rem = (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); } if (rem > 0) { data = (*env)->NewByteArray(env, rem); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferGetMethod, data)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferGet, data)); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompactMethod)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appInBuffer, g_ByteBufferCompact)); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - (*env)->GetByteArrayRegion(env, data, 0, rem, (jbyte*) buffer); + (*env)->GetByteArrayRegion(env, data, 0, rem, (jbyte*)buffer); *read = rem; ret = SSLStreamStatus_OK; } @@ -535,11 +465,11 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uin // appOutBuffer.put(data); jbyteArray data = (*env)->NewByteArray(env, length); (*env)->SetByteArrayRegion(env, data, 0, length, (jbyte*)buffer); - (*env)->DeleteLocalRef(env, (*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPutWithByteArray, data)); + IGNORE_RETURN((*env)->CallObjectMethod(env, sslStream->appOutBuffer, g_ByteBufferPutByteArray, data)); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); int handshakeStatus; - ret = doWrap(env, sslStream, &handshakeStatus); + ret = DoWrap(env, sslStream, &handshakeStatus); if (ret == SSLStreamStatus_OK && IsHandshaking(handshakeStatus)) { ret = SSLStreamStatus_Renegotiate; @@ -585,7 +515,7 @@ int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream return ret; } -int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream *sslStream, uint16_t** out) +int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream* sslStream, uint16_t** out) { assert(sslStream != NULL); assert(out != NULL); @@ -606,7 +536,7 @@ int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream *sslStream, uint16 return ret; } -int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t** out) +int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t** out) { assert(sslStream != NULL); assert(out != NULL); @@ -627,7 +557,7 @@ int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t* return ret; } -int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jobject* out) +int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream* sslStream, jobject* out) { assert(sslStream != NULL); assert(out != NULL); @@ -637,13 +567,14 @@ int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jo *out = NULL; // Certificate[] certs = sslSession.getPeerCertificates(); + // out = certs[0]; jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); jsize len = (*env)->GetArrayLength(env, certs); if (len > 0) { // First element is the peer's own certificate - jobject cert =(*env)->GetObjectArrayElement(env, certs, 0); + jobject cert = (*env)->GetObjectArrayElement(env, certs, 0); *out = ToGRef(env, cert); } @@ -654,7 +585,7 @@ int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jo return ret; } -int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, jobject** out, int* outLen) +int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobject** out, int* outLen) { assert(sslStream != NULL); assert(out != NULL); @@ -677,7 +608,7 @@ int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, j *out = malloc(sizeof(jobject) * (size_t)len); for (int i = 0; i < len; i++) { - jobject cert =(*env)->GetObjectArrayElement(env, certs, i); + jobject cert = (*env)->GetObjectArrayElement(env, certs, i); (*out)[i] = ToGRef(env, cert); } } @@ -689,7 +620,7 @@ int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, j return ret; } -bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hostname) +bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hostname) { assert(sslStream != NULL); assert(hostname != NULL); @@ -701,36 +632,24 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hos // HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); // return verifier.verify(hostname, sslSession); loc[name] = JSTRING(hostname); - loc[verifier] = (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); + loc[verifier] = + (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], sslStream->sslSession); RELEASE_LOCALS(loc, env); return ret; } -bool AndroidCryptoNative_SSLStreamShutdown(SSLStream *sslStream) +bool AndroidCryptoNative_SSLStreamShutdown(SSLStream* sslStream) { assert(sslStream != NULL); JNIEnv* env = GetJNIEnv(); - PAL_SSLStreamStatus status = close(env, sslStream); + PAL_SSLStreamStatus status = Close(env, sslStream); return status == SSLStreamStatus_Closed; } -static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) -{ - jsize bytesLen = (*env)->GetArrayLength(env, source); - - bool insufficientBuffer = *len < bytesLen; - *len = bytesLen; - if (insufficientBuffer) - return INSUFFICIENT_BUFFER; - - (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - -static uint16_t* AllocateString(JNIEnv *env, jstring source) +static uint16_t* AllocateString(JNIEnv* env, jstring source) { if (source == NULL) return NULL; 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 4f7fc33c27b0e3..aa8a88dfab4e4b 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 @@ -6,7 +6,7 @@ #include "pal_jni.h" typedef void (*STREAM_WRITER)(uint8_t*, int32_t); -typedef int (*STREAM_READER)(uint8_t*, int32_t*); +typedef int (*STREAM_READER)(uint8_t*, int32_t*); typedef struct SSLStream { @@ -32,22 +32,107 @@ enum }; typedef int32_t PAL_SSLStreamStatus; -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, STREAM_READER streamReader, STREAM_WRITER streamWriter, int appOutBufferSize, int appInBufferSize); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream *sslStream, char* targetHost); -PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream *sslStream); +/* +Create an SSL context + - isServer : true if the context should be created in server mode + - streamReader : callback for reading data from the connection + - streamWriter : callback for writing data to the connection + - appBufferSize : initial buffer size for applicaiton data -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateAndStartHandshake(STREAM_READER streamReader, STREAM_WRITER streamWriter, int tlsVersion, int appOutBufferSize, int appInBufferSize); -PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, uint8_t* buffer, int length, int* read); +Returns NULL on failure +*/ +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, + STREAM_READER streamReader, + STREAM_WRITER streamWriter, + int appBufferSize); + +/* +Set configuration parameters + - targetHost : SNI host name + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost); + +/* +Start or continue the TLS handshake +*/ +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream); + +/* +Read bytes from the connection into a buffer + - buffer : buffer to populate with the bytes read from the connection + - length : maximum number of bytes to read + - read : [out] number of bytes read from the connection and written into the buffer + +Unless data from a previous incomplete read is present, this will invoke the STREAM_READER callback. +*/ +PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamRead(SSLStream* sslStream, + uint8_t* buffer, + int length, + int* read); +/* +Encodes bytes from a buffer + - buffer : data to encode + - length : length of buffer + +This will invoke the STREAM_WRITER callback with the processed data. +*/ PALEXPORT PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamWrite(SSLStream* sslStream, uint8_t* buffer, int length); + +/* +Release the SSL context +*/ PALEXPORT void AndroidCryptoNative_SSLStreamRelease(SSLStream* sslStream); +/* +Get the negotiated application protocol for the current session + +Returns 1 on success, 0 otherwise +*/ PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream, uint8_t* out, int* outLen); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream *sslStream, uint16_t** out); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream *sslStream, uint16_t** out); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream *sslStream, jobject* out); -PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream *sslStream, jobject** out, int* outLen); +/* +Get the name of the cipher suite for the current session + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream* sslStream, uint16_t** out); + +/* +Get the standard name of the protocol for the current session (e.g. TLSv1.2) + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t** out); + +/* +Get the peer certificate for the current session + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream* sslStream, + jobject* /*X509Certificate*/ out); + +/* +Get the peer certificates for the current session + +The peer's own certificate will be first, followed by any certificate authorities. + +Returns 1 on success, 0 otherwise +*/ +PALEXPORT int32_t AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, + jobject** /*X509Certificate[]*/ out, + int* outLen); + +/* +Verify hostname using the peer certificate for the current session -PALEXPORT bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream *sslStream, char* hostname); +Returns true if hostname matches, false otherwise +*/ +PALEXPORT bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hostname); -PALEXPORT bool AndroidCryptoNative_SSLStreamShutdown(SSLStream *sslStream); +/* +Shut down the session +*/ +PALEXPORT bool AndroidCryptoNative_SSLStreamShutdown(SSLStream* sslStream); diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c index 9cd92dadecdab2..350353da4fb03f 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c @@ -12,10 +12,6 @@ #include #include -#define INSUFFICIENT_BUFFER -1 - -static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len); - static void FindCertStart(const uint8_t** buffer, int32_t* len); // Handles both DER and PEM formats @@ -270,19 +266,6 @@ void* AndroidCryptoNative_X509PublicKey(jobject /*X509Certificate*/ cert, PAL_Ke return keyHandle; } -static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) -{ - jsize bytesLen = (*env)->GetArrayLength(env, source); - - bool insufficientBuffer = *len < bytesLen; - *len = bytesLen; - if (insufficientBuffer) - return INSUFFICIENT_BUFFER; - - (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - static void FindCertStart(const uint8_t** buffer, int32_t* len) { assert(buffer != NULL && *buffer != NULL); 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 d8f51469c45982..93b7b6c02ff9a7 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 @@ -49,39 +49,6 @@ public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthentication } } - private static SafeSslHandle CreateSslContext( - Interop.AndroidCrypto.SSLReadCallback readCallback, - Interop.AndroidCrypto.SSLWriteCallback writeCallback, - SafeFreeSslCredentials credential, - SslAuthenticationOptions authOptions) - { - bool isServer = authOptions.IsServer; - - if (authOptions.ApplicationProtocols != null - || authOptions.CipherSuitesPolicy != null - || credential.Protocols != SslProtocols.None - || (isServer && authOptions.RemoteCertRequired) - || (credential.CertificateContext != null)) - { - // TODO: [AndroidCrypto] Handle non-system-default options - throw new NotImplementedException(); - } - - SafeSslHandle handle = Interop.AndroidCrypto.SSLStreamCreate( - isServer, - readCallback, - writeCallback, - InitialBufferSize, - InitialBufferSize); - - if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) - { - Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); - } - - return handle; - } - public override bool IsInvalid => _sslContext?.IsInvalid ?? true; protected override void Dispose(bool disposing) @@ -166,5 +133,37 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) return limit; } + + private static SafeSslHandle CreateSslContext( + Interop.AndroidCrypto.SSLReadCallback readCallback, + Interop.AndroidCrypto.SSLWriteCallback writeCallback, + SafeFreeSslCredentials credential, + SslAuthenticationOptions authOptions) + { + bool isServer = authOptions.IsServer; + + if (authOptions.ApplicationProtocols != null + || authOptions.CipherSuitesPolicy != null + || credential.Protocols != SslProtocols.None + || (isServer && authOptions.RemoteCertRequired) + || (credential.CertificateContext != null)) + { + // TODO: [AndroidCrypto] Handle certificate context, non-system-default options + throw new NotImplementedException(nameof(SafeDeleteSslContext)); + } + + SafeSslHandle handle = Interop.AndroidCrypto.SSLStreamCreate( + isServer, + readCallback, + writeCallback, + InitialBufferSize); + + if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) + { + Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); + } + + return handle; + } } } From 1d3b41af75537c8ac708b2818751068f3119e1dd Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Wed, 31 Mar 2021 03:05:42 -0700 Subject: [PATCH 10/13] Handle creating SSL context with certificates --- .../Interop.Ssl.cs | 34 +++- .../pal_jni.c | 20 +++ .../pal_jni.h | 11 ++ .../pal_sslstream.c | 162 ++++++++++++++++-- .../pal_sslstream.h | 26 ++- .../Pal.Android/SafeDeleteSslContext.cs | 71 ++++++-- 6 files changed, 290 insertions(+), 34 deletions(-) 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 668ed010c0303f..cfee134ff4baef 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 @@ -26,11 +26,43 @@ internal enum PAL_SSLStreamStatus }; [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] - internal static extern SafeSslHandle SSLStreamCreate( + internal static extern SafeSslHandle SSLStreamCreate(); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateWithCertificates")] + private static extern SafeSslHandle SSLStreamCreateWithCertificates( + ref byte pkcs8PrivateKey, + int pkcs8PrivateKeyLen, + PAL_KeyAlgorithm algorithm, + IntPtr[] certs, + int certsLen); + internal static SafeSslHandle SSLStreamCreateWithCertificates(ReadOnlySpan pkcs8PrivateKey, PAL_KeyAlgorithm algorithm, IntPtr[] certificates) + { + return SSLStreamCreateWithCertificates( + ref MemoryMarshal.GetReference(pkcs8PrivateKey), + pkcs8PrivateKey.Length, + algorithm, + certificates, + certificates.Length); + } + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] + private static extern int SSLStreamInitializeImpl( + SafeSslHandle sslHandle, [MarshalAs(UnmanagedType.U1)] bool isServer, SSLReadCallback streamRead, SSLWriteCallback streamWrite, int appBufferSize); + internal static void SSLStreamInitialize( + SafeSslHandle sslHandle, + [MarshalAs(UnmanagedType.U1)] bool isServer, + SSLReadCallback streamRead, + SSLWriteCallback streamWrite, + int appBufferSize) + { + int ret = SSLStreamInitializeImpl(sslHandle, isServer, streamRead, streamWrite, appBufferSize); + if (ret != SUCCESS) + throw new SslException(); + } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamConfigureParameters")] private static extern int SSLStreamConfigureParametersImpl( 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 251d3b15a6d3ff..36915a6c6e3b29 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 @@ -120,6 +120,7 @@ jmethodID g_keyPairGenGenKeyPairMethod; // java/security/KeyStore jclass g_KeyStoreClass; +jmethodID g_KeyStoreGetDefaultType; jmethodID g_KeyStoreGetInstance; jmethodID g_KeyStoreAliases; jmethodID g_KeyStoreContainsAlias; @@ -328,6 +329,10 @@ jmethodID g_EllipticCurveGetB; jmethodID g_EllipticCurveGetField; jmethodID g_EllipticCurveGetSeed; +// java/security/spec/PKCS8EncodedKeySpec +jclass g_PKCS8EncodedKeySpec; +jmethodID g_PKCS8EncodedKeySpecCtor; + // java/security/spec/X509EncodedKeySpec jclass g_X509EncodedKeySpecClass; jmethodID g_X509EncodedKeySpecCtor; @@ -379,6 +384,12 @@ jmethodID g_HostnameVerifierVerify; jclass g_HttpsURLConnection; jmethodID g_HttpsURLConnectionGetDefaultHostnameVerifier; +// javax/net/ssl/KeyManagerFactory +jclass g_KeyManagerFactory; +jmethodID g_KeyManagerFactoryGetInstance; +jmethodID g_KeyManagerFactoryInit; +jmethodID g_KeyManagerFactoryGetKeyManagers; + // javax/net/ssl/SNIHostName jclass g_SNIHostName; jmethodID g_SNIHostNameCtor; @@ -784,6 +795,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_keyPairGenGenKeyPairMethod = GetMethod(env, false, g_keyPairGenClass, "genKeyPair", "()Ljava/security/KeyPair;"); g_KeyStoreClass = GetClassGRef(env, "java/security/KeyStore"); + g_KeyStoreGetDefaultType = GetMethod(env, true, g_KeyStoreClass, "getDefaultType", "()Ljava/lang/String;"); g_KeyStoreGetInstance = GetMethod(env, true, g_KeyStoreClass, "getInstance", "(Ljava/lang/String;)Ljava/security/KeyStore;"); g_KeyStoreAliases = GetMethod(env, false, g_KeyStoreClass, "aliases", "()Ljava/util/Enumeration;"); g_KeyStoreContainsAlias = GetMethod(env, false, g_KeyStoreClass, "containsAlias", "(Ljava/lang/String;)Z"); @@ -891,6 +903,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_EllipticCurveGetField = GetMethod(env, false, g_EllipticCurveClass, "getField", "()Ljava/security/spec/ECField;"); g_EllipticCurveGetSeed = GetMethod(env, false, g_EllipticCurveClass, "getSeed", "()[B"); + g_PKCS8EncodedKeySpec = GetClassGRef(env, "java/security/spec/PKCS8EncodedKeySpec"); + g_PKCS8EncodedKeySpecCtor = GetMethod(env, false, g_PKCS8EncodedKeySpec, "", "([B)V"); + g_X509EncodedKeySpecClass = GetClassGRef(env, "java/security/spec/X509EncodedKeySpec"); g_X509EncodedKeySpecCtor = GetMethod(env, false, g_X509EncodedKeySpecClass, "", "([B)V"); @@ -931,6 +946,11 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_HttpsURLConnection = GetClassGRef(env, "javax/net/ssl/HttpsURLConnection"); g_HttpsURLConnectionGetDefaultHostnameVerifier = GetMethod(env, true, g_HttpsURLConnection, "getDefaultHostnameVerifier", "()Ljavax/net/ssl/HostnameVerifier;"); + g_KeyManagerFactory = GetClassGRef(env, "javax/net/ssl/KeyManagerFactory"); + g_KeyManagerFactoryGetInstance = GetMethod(env, true, g_KeyManagerFactory, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/KeyManagerFactory;"); + g_KeyManagerFactoryInit = GetMethod(env, false, g_KeyManagerFactory, "init", "(Ljava/security/KeyStore;[C)V"); + g_KeyManagerFactoryGetKeyManagers = GetMethod(env, false, g_KeyManagerFactory, "getKeyManagers", "()[Ljavax/net/ssl/KeyManager;"); + g_SNIHostName = GetClassGRef(env, "javax/net/ssl/SNIHostName"); g_SNIHostNameCtor = GetMethod(env, false, g_SNIHostName, "", "(Ljava/lang/String;)V"); 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 fa751087bbe498..d76fd79295c39f 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 @@ -211,6 +211,7 @@ extern jmethodID g_keyPairGenGenKeyPairMethod; // java/security/KeyStore extern jclass g_KeyStoreClass; +extern jmethodID g_KeyStoreGetDefaultType; extern jmethodID g_KeyStoreGetInstance; extern jmethodID g_KeyStoreAliases; extern jmethodID g_KeyStoreContainsAlias; @@ -338,6 +339,10 @@ extern jmethodID g_EllipticCurveGetB; extern jmethodID g_EllipticCurveGetField; extern jmethodID g_EllipticCurveGetSeed; +// java/security/spec/PKCS8EncodedKeySpec +extern jclass g_PKCS8EncodedKeySpec; +extern jmethodID g_PKCS8EncodedKeySpecCtor; + // java/security/spec/X509EncodedKeySpec extern jclass g_X509EncodedKeySpecClass; extern jmethodID g_X509EncodedKeySpecCtor; @@ -394,6 +399,12 @@ extern jmethodID g_HostnameVerifierVerify; extern jclass g_HttpsURLConnection; extern jmethodID g_HttpsURLConnectionGetDefaultHostnameVerifier; +// javax/net/ssl/KeyManagerFactory +extern jclass g_KeyManagerFactory; +extern jmethodID g_KeyManagerFactoryGetInstance; +extern jmethodID g_KeyManagerFactoryInit; +extern jmethodID g_KeyManagerFactoryGetKeyManagers; + // javax/net/ssl/SNIHostName extern jclass g_SNIHostName; extern jmethodID g_SNIHostNameCtor; 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 1b41a19e0a9035..b277c62c634972 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 @@ -270,32 +270,165 @@ static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) free(sslStream); } -SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, - STREAM_READER streamReader, - STREAM_WRITER streamWriter, - int appBufferSize) +SSLStream* AndroidCryptoNative_SSLStreamCreate(void) { JNIEnv* env = GetJNIEnv(); - SSLStream* sslStream = malloc(sizeof(SSLStream)); - memset(sslStream, 0, sizeof(SSLStream)); - // 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); - ON_EXCEPTION_PRINT_AND_GOTO(fail); + if (CheckJNIExceptions(env)) + return NULL; + + SSLStream* sslStream = malloc(sizeof(SSLStream)); + memset(sslStream, 0, sizeof(SSLStream)); sslStream->sslContext = ToGRef(env, sslContext); + return sslStream; +} + +static int32_t AddCertChainToStore( + JNIEnv* env, + jobject store, + uint8_t* pkcs8PrivateKey, + int32_t pkcs8PrivateKeyLen, + PAL_KeyAlgorithm algorithm, + jobject* /*X509Certificate[]*/ certs, + int32_t certsLen) +{ + int32_t ret = FAIL; + INIT_LOCALS(loc, keyBytes, keySpec, algorithmName, keyFactory, privateKey, certArray, alias); + + // byte[] keyBytes = new byte[] { }; + // PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + loc[keyBytes] = (*env)->NewByteArray(env, pkcs8PrivateKeyLen); + (*env)->SetByteArrayRegion(env, loc[keyBytes], 0, pkcs8PrivateKeyLen, (jbyte*)pkcs8PrivateKey); + loc[keySpec] = (*env)->NewObject(env, g_PKCS8EncodedKeySpec, g_PKCS8EncodedKeySpecCtor, loc[keyBytes]); + + switch (algorithm) + { + case PAL_DSA: + loc[algorithmName] = JSTRING("DSA"); + break; + case PAL_EC: + loc[algorithmName] = JSTRING("EC"); + break; + case PAL_RSA: + loc[algorithmName] = JSTRING("RSA"); + break; + default: + LOG_ERROR("Unknown key algorithm: %d", algorithm); + goto cleanup; + } + + // KeyFactory keyFactory = KeyFactory.getInstance(algorithmName); + // PrivateKey privateKey = keyFactory.generatePrivate(spec); + loc[keyFactory] = (*env)->CallStaticObjectMethod(env, g_KeyFactoryClass, g_KeyFactoryGetInstanceMethod, loc[algorithmName]); + loc[privateKey] = (*env)->CallObjectMethod(env, loc[keyFactory], g_KeyFactoryGenPrivateMethod, loc[keySpec]); + + // X509Certificate[] certArray = new X509Certificate[certsLen]; + loc[certArray] = (*env)->NewObjectArray(env, certsLen, g_X509CertClass, NULL); + for (int i = 0; i < certsLen; ++i) + { + (*env)->SetObjectArrayElement(env, loc[certArray], i, certs[i]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + } + + // store.setKeyEntry("SSLCertificateContext", privateKey, null, certArray); + loc[alias] = JSTRING("SSLCertificateContext"); + (*env)->CallVoidMethod(env, store, g_KeyStoreSetKeyEntry, loc[alias], loc[privateKey], NULL, loc[certArray]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ret = SUCCESS; + +cleanup: + RELEASE_LOCALS(loc, env); + return ret; +} + +SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8PrivateKey, int32_t pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, jobject* /*X509Certificate[]*/ certs, int32_t certsLen) +{ + SSLStream* sslStream = NULL; + JNIEnv* env = GetJNIEnv(); + + INIT_LOCALS(loc, tls13, sslContext, ksType, keyStore, kmfType, kmf, keyManagers); + + // SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); + loc[tls13] = JSTRING("TLSv1.3"); + loc[sslContext] = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, loc[tls13]); + if (TryClearJNIExceptions(env)) + { + // TLSv1.3 is only supported on API level 29+ - fall back to TLSv1.2 (which is supported on API level 16+) + // sslContext = SSLContext.getInstance("TLSv1.2"); + jobject tls12 = JSTRING("TLSv1.2"); + loc[sslContext] = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, tls12); + ReleaseLRef(env, tls12); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + } + + // String ksType = KeyStore.getDefaultType(); + // KeyStore keyStore = KeyStore.getInstance(ksType); + // keyStore.load(null, null); + loc[ksType] = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetDefaultType); + loc[keyStore] = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, loc[ksType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[keyStore], g_KeyStoreLoad, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + int32_t status = AddCertChainToStore(env, loc[keyStore], pkcs8PrivateKey, pkcs8PrivateKeyLen, algorithm, certs, certsLen); + if (status != SUCCESS) + goto cleanup; + + // String kmfType = "PKIX"; + // KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfType); + loc[kmfType] = JSTRING("PKIX"); + loc[kmf] = (*env)->CallStaticObjectMethod(env, g_KeyManagerFactory, g_KeyManagerFactoryGetInstance, loc[kmfType]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // kmf.init(keyStore, null); + (*env)->CallVoidMethod(env, loc[kmf], g_KeyManagerFactoryInit, loc[keyStore], NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // KeyManager[] keyManagers = kmf.getKeyManagers(); + // sslContext.init(keyManagers, null, null); + loc[keyManagers] = (*env)->CallObjectMethod(env, loc[kmf], g_KeyManagerFactoryGetKeyManagers); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, loc[keyManagers], NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + sslStream = malloc(sizeof(SSLStream)); + memset(sslStream, 0, sizeof(SSLStream)); + sslStream->sslContext = ToGRef(env, loc[sslContext]); + loc[sslContext] = NULL; + +cleanup: + RELEASE_LOCALS(loc, env); + return sslStream; +} + +int32_t AndroidCryptoNative_SSLStreamInitialize(SSLStream* sslStream, + bool isServer, + STREAM_READER streamReader, + STREAM_WRITER streamWriter, + int appBufferSize) +{ + assert(sslStream != NULL); + assert(sslStream->sslContext != NULL); + assert(sslStream->sslEngine == NULL); + assert(sslStream->sslSession == NULL); + + int32_t ret = FAIL; + JNIEnv* env = GetJNIEnv(); // SSLEngine sslEngine = sslContext.createSSLEngine(); // sslEngine.setUseClientMode(!isServer); jobject sslEngine = (*env)->CallObjectMethod(env, sslStream->sslContext, g_SSLContextCreateSSLEngineMethod); - ON_EXCEPTION_PRINT_AND_GOTO(fail); + ON_EXCEPTION_PRINT_AND_GOTO(exit); sslStream->sslEngine = ToGRef(env, sslEngine); (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineSetUseClientMode, !isServer); - ON_EXCEPTION_PRINT_AND_GOTO(fail); + ON_EXCEPTION_PRINT_AND_GOTO(exit); // SSLSession sslSession = sslEngine.getSession(); sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession)); @@ -322,13 +455,10 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, sslStream->streamReader = streamReader; sslStream->streamWriter = streamWriter; - return sslStream; - -fail: - if (sslStream != NULL) - FreeSSLStream(env, sslStream); + ret = SUCCESS; - return NULL; +exit: + return ret; } int32_t AndroidCryptoNative_SSLStreamConfigureParameters(SSLStream* sslStream, char* targetHost) 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 aa8a88dfab4e4b..afe966935c9484 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 @@ -4,6 +4,7 @@ #pragma once #include "pal_jni.h" +#include "pal_x509.h" typedef void (*STREAM_WRITER)(uint8_t*, int32_t); typedef int (*STREAM_READER)(uint8_t*, int32_t*); @@ -34,17 +35,32 @@ typedef int32_t PAL_SSLStreamStatus; /* Create an SSL context + +Returns NULL on failure +*/ +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(void); + +/* +Create an SSL context with the specified certificates + +Returns NULL on failure +*/ +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8PrivateKey, int32_t pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, jobject* /*X509Certificate[]*/ certs, int32_t certsLen); + +/* +Initialize an SSL context - isServer : true if the context should be created in server mode - streamReader : callback for reading data from the connection - streamWriter : callback for writing data to the connection - appBufferSize : initial buffer size for applicaiton data -Returns NULL on failure +Returns 1 on success, 0 otherwise */ -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(bool isServer, - STREAM_READER streamReader, - STREAM_WRITER streamWriter, - int appBufferSize); +PALEXPORT int32_t AndroidCryptoNative_SSLStreamInitialize(SSLStream* sslStream, + bool isServer, + STREAM_READER streamReader, + STREAM_WRITER streamWriter, + int appBufferSize); /* Set configuration parameters 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 93b7b6c02ff9a7..776bd6358ca7db 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 @@ -6,9 +6,11 @@ using System.Net.Http; using System.Net.Security; using System.Security.Authentication; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Microsoft.Win32.SafeHandles; +using PAL_KeyAlgorithm = Interop.AndroidCrypto.PAL_KeyAlgorithm; using PAL_SSLStreamStatus = Interop.AndroidCrypto.PAL_SSLStreamStatus; namespace System.Net @@ -39,7 +41,8 @@ public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthentication _writeCallback = WriteToConnection; } - _sslContext = CreateSslContext(_readCallback, _writeCallback, credential, authOptions); + _sslContext = CreateSslContext(credential); + InitializeSslContext(_sslContext, _readCallback, _writeCallback, credential, authOptions); } catch (Exception ex) { @@ -134,7 +137,58 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) return limit; } - private static SafeSslHandle CreateSslContext( + private static SafeSslHandle CreateSslContext(SafeFreeSslCredentials credential) + { + if (credential.CertificateContext == null) + { + return Interop.AndroidCrypto.SSLStreamCreate(); + } + + SslStreamCertificateContext context = credential.CertificateContext; + X509Certificate2 cert = context.Certificate; + Debug.Assert(context.Certificate.HasPrivateKey); + + PAL_KeyAlgorithm algorithm; + byte[] keyBytes; + using (AsymmetricAlgorithm key = GetPrivateKeyAlgorithm(cert, out algorithm)) + { + keyBytes = key.ExportPkcs8PrivateKey(); + } + IntPtr[] ptrs = new IntPtr[context.IntermediateCertificates.Length + 1]; + ptrs[0] = cert.Handle; + for (int i = 0; i < context.IntermediateCertificates.Length; i++) + { + ptrs[i + 1] = context.IntermediateCertificates[i].Handle; + } + + return Interop.AndroidCrypto.SSLStreamCreateWithCertificates(keyBytes, algorithm, ptrs); + } + + private static AsymmetricAlgorithm GetPrivateKeyAlgorithm(X509Certificate2 cert, out PAL_KeyAlgorithm algorithm) + { + AsymmetricAlgorithm? key = cert.GetRSAPrivateKey(); + if (key != null) + { + algorithm = PAL_KeyAlgorithm.RSA; + return key; + } + key = cert.GetECDsaPrivateKey(); + if (key != null) + { + algorithm = PAL_KeyAlgorithm.EC; + return key; + } + key = cert.GetDSAPrivateKey(); + if (key != null) + { + algorithm = PAL_KeyAlgorithm.DSA; + return key; + } + throw new NotSupportedException(SR.net_ssl_io_no_server_cert); + } + + private static void InitializeSslContext( + SafeSslHandle handle, Interop.AndroidCrypto.SSLReadCallback readCallback, Interop.AndroidCrypto.SSLWriteCallback writeCallback, SafeFreeSslCredentials credential, @@ -145,25 +199,18 @@ private static SafeSslHandle CreateSslContext( if (authOptions.ApplicationProtocols != null || authOptions.CipherSuitesPolicy != null || credential.Protocols != SslProtocols.None - || (isServer && authOptions.RemoteCertRequired) - || (credential.CertificateContext != null)) + || (isServer && authOptions.RemoteCertRequired)) { - // TODO: [AndroidCrypto] Handle certificate context, non-system-default options + // TODO: [AndroidCrypto] Handle non-system-default options throw new NotImplementedException(nameof(SafeDeleteSslContext)); } - SafeSslHandle handle = Interop.AndroidCrypto.SSLStreamCreate( - isServer, - readCallback, - writeCallback, - InitialBufferSize); + Interop.AndroidCrypto.SSLStreamInitialize(handle, isServer, readCallback, writeCallback, InitialBufferSize); if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) { Interop.AndroidCrypto.SSLStreamConfigureParameters(handle, authOptions.TargetHost); } - - return handle; } } } From e49cf546e11742b77b6ecbc1307db436da8dcf27 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Mon, 5 Apr 2021 10:15:47 -0700 Subject: [PATCH 11/13] Apply suggestions from code review Co-authored-by: Jeremy Barton --- .../Interop.Ssl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 cfee134ff4baef..148924beb0414d 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 @@ -54,7 +54,7 @@ private static extern int SSLStreamInitializeImpl( int appBufferSize); internal static void SSLStreamInitialize( SafeSslHandle sslHandle, - [MarshalAs(UnmanagedType.U1)] bool isServer, + bool isServer, SSLReadCallback streamRead, SSLWriteCallback streamWrite, int appBufferSize) @@ -70,7 +70,7 @@ private static extern int SSLStreamConfigureParametersImpl( [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost); internal static void SSLStreamConfigureParameters( SafeSslHandle sslHandle, - [MarshalAs(UnmanagedType.LPUTF8Str)] string targetHost) + string targetHost) { int ret = SSLStreamConfigureParametersImpl(sslHandle, targetHost); if (ret != SUCCESS) From 8c23ae419f126eb98395e737b3e9c32f12b7f3a1 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 6 Apr 2021 09:34:51 -0700 Subject: [PATCH 12/13] PR feedback --- .../Interop.Ssl.cs | 24 ++++++++- .../Net/Security/SslStreamPal.Android.cs | 52 ++++++------------- 2 files changed, 38 insertions(+), 38 deletions(-) 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 148924beb0414d..b9aed3973f845a 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 @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Buffers; using System.Runtime.InteropServices; using System.Security.Cryptography.X509Certificates; @@ -98,17 +99,36 @@ internal static void SSLStreamConfigureParameters( } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRead")] - internal static unsafe extern PAL_SSLStreamStatus SSLStreamRead( + private static unsafe extern PAL_SSLStreamStatus SSLStreamRead( SafeSslHandle sslHandle, byte* buffer, int length, out int bytesRead); + internal static unsafe PAL_SSLStreamStatus SSLStreamRead( + SafeSslHandle sslHandle, + Span buffer, + out int bytesRead) + { + fixed (byte* bufferPtr = buffer) + { + return SSLStreamRead(sslHandle, bufferPtr, buffer.Length, out bytesRead); + } + } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamWrite")] - internal static unsafe extern PAL_SSLStreamStatus SSLStreamWrite( + private static unsafe extern PAL_SSLStreamStatus SSLStreamWrite( SafeSslHandle sslHandle, byte* buffer, int length); + internal static unsafe PAL_SSLStreamStatus SSLStreamWrite( + SafeSslHandle sslHandle, + ReadOnlyMemory buffer) + { + using (MemoryHandle memHandle = buffer.Pin()) + { + return SSLStreamWrite(sslHandle, (byte*)memHandle.Pointer, buffer.Length); + } + } [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamRelease")] internal static extern void SSLStreamRelease(IntPtr ptr); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index 0cda2ef2b761d0..5890e1ccc14618 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers; using System.Diagnostics; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; @@ -79,20 +78,7 @@ public static SecurityStatusPal EncryptMessage( SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext; SafeSslHandle sslHandle = sslContext.SslContext; - PAL_SSLStreamStatus ret; - unsafe - { - MemoryHandle memHandle = input.Pin(); - try - { - ret = Interop.AndroidCrypto.SSLStreamWrite(sslHandle, (byte*)memHandle.Pointer, input.Length); - } - finally - { - memHandle.Dispose(); - } - } - + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamWrite(sslHandle, input); SecurityStatusPalErrorCode statusCode = ret switch { PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, @@ -133,28 +119,22 @@ public static SecurityStatusPal DecryptMessage( sslContext.Write(buffer.AsSpan(offset, count)); - unsafe + PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer.AsSpan(offset, count), out int read); + if (ret == PAL_SSLStreamStatus.Error) + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + + count = read; + + SecurityStatusPalErrorCode statusCode = ret switch { - fixed (byte* offsetInput = &buffer[offset]) - { - PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, offsetInput, count, out int read); - if (ret == PAL_SSLStreamStatus.Error) - return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); - - count = read; - - SecurityStatusPalErrorCode statusCode = ret switch - { - PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, - PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.OK, - PAL_SSLStreamStatus.Renegotiate => SecurityStatusPalErrorCode.Renegotiate, - PAL_SSLStreamStatus.Closed => SecurityStatusPalErrorCode.ContextExpired, - _ => SecurityStatusPalErrorCode.InternalError - }; - - return new SecurityStatusPal(statusCode); - } - } + PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.OK, + PAL_SSLStreamStatus.Renegotiate => SecurityStatusPalErrorCode.Renegotiate, + PAL_SSLStreamStatus.Closed => SecurityStatusPalErrorCode.ContextExpired, + _ => SecurityStatusPalErrorCode.InternalError + }; + + return new SecurityStatusPal(statusCode); } catch (Exception e) { From 58274104008d586867a41bb5d64e66d642de95d9 Mon Sep 17 00:00:00 2001 From: Elinor Fung Date: Tue, 6 Apr 2021 13:35:41 -0700 Subject: [PATCH 13/13] PR feedback - use GetStringUTFRegion --- .../pal_jni.c | 13 ----------- .../pal_jni.h | 11 ++++++---- .../pal_sslstream.c | 22 +++++++++---------- .../pal_x509.c | 15 +++++++++++++ 4 files changed, 33 insertions(+), 28 deletions(-) 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 36915a6c6e3b29..dea66dc52d4f1d 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 @@ -610,19 +610,6 @@ int GetEnumAsInt(JNIEnv *env, jobject enumObj) return value; } -int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) -{ - jsize bytesLen = (*env)->GetArrayLength(env, source); - - bool insufficientBuffer = *len < bytesLen; - *len = bytesLen; - if (insufficientBuffer) - return INSUFFICIENT_BUFFER; - - (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); - return CheckJNIExceptions(env) ? FAIL : SUCCESS; -} - JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { 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 d76fd79295c39f..6095640415b864 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 @@ -363,7 +363,7 @@ extern jclass g_CollectionClass; extern jmethodID g_CollectionIterator; extern jmethodID g_CollectionSize; -// java/util/ArrayListe +// java/util/ArrayList extern jclass g_ArrayList; extern jmethodID g_ArrayListCtor; extern jmethodID g_ArrayListAdd; @@ -462,13 +462,18 @@ extern jmethodID g_KeyAgreementInit; extern jmethodID g_KeyAgreementDoPhase; extern jmethodID g_KeyAgreementGenerateSecret; -// JNI helpers +// Logging helpers #define LOG_DEBUG(fmt, ...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)) #define LOG_INFO(fmt, ...) ((void)__android_log_print(ANDROID_LOG_INFO, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)) #define LOG_ERROR(fmt, ...) ((void)__android_log_print(ANDROID_LOG_ERROR, "DOTNET", "%s: " fmt, __FUNCTION__, ## __VA_ARGS__)) + +// JNI helpers - assume there is a JNIEnv* variable named env #define JSTRING(str) ((jstring)(*env)->NewStringUTF(env, str)) #define ON_EXCEPTION_PRINT_AND_GOTO(label) if (CheckJNIExceptions(env)) goto label +// Explicitly ignore jobject return value +#define IGNORE_RETURN(retval) (*env)->DeleteLocalRef(env, retval) + #define INIT_LOCALS(name, ...) \ enum { __VA_ARGS__, count_##name }; \ jobject name[count_##name] = { 0 } \ @@ -512,5 +517,3 @@ jmethodID GetOptionalMethod(JNIEnv *env, bool isStatic, jclass klass, const char jfieldID GetField(JNIEnv *env, bool isStatic, jclass klass, const char* name, const char* sig); JNIEnv* GetJNIEnv(void); int GetEnumAsInt(JNIEnv *env, jobject enumObj); - -int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len); 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 b277c62c634972..6f1c8b238fc717 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 @@ -3,9 +3,6 @@ #include "pal_sslstream.h" -// Explicitly ignore jobject return value - assumes there is a JNIEnv* variable named env -#define IGNORE_RETURN(retval) (*env)->DeleteLocalRef(env, retval) - // javax/net/ssl/SSLEngineResult$HandshakeStatus enum { @@ -624,24 +621,27 @@ int32_t AndroidCryptoNative_SSLStreamGetApplicationProtocol(SSLStream* sslStream assert(sslStream != NULL); JNIEnv* env = GetJNIEnv(); - int32_t ret = FAIL; - INIT_LOCALS(loc, protocol, bytes); // String protocol = sslEngine.getApplicationProtocol(); - loc[protocol] = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetApplicationProtocol); + jstring protocol = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetApplicationProtocol); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - if (loc[protocol] == NULL) + if (protocol == NULL) goto cleanup; - // byte[] bytes = protocol.getBytes(); - loc[bytes] = (*env)->CallObjectMethod(env, loc[protocol], g_StringGetBytes); + jsize len = (*env)->GetStringUTFLength(env, protocol); + bool insufficientBuffer = *outLen < len; + *outLen = len; + if (insufficientBuffer) + return INSUFFICIENT_BUFFER; + + (*env)->GetStringUTFRegion(env, protocol, 0, len, (char*)out); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - ret = PopulateByteArray(env, loc[bytes], out, outLen); + ret = SUCCESS; cleanup: - RELEASE_LOCALS(loc, env); + (*env)->DeleteLocalRef(env, protocol); return ret; } diff --git a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c index 350353da4fb03f..f2468882674a28 100644 --- a/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c +++ b/src/libraries/Native/Unix/System.Security.Cryptography.Native.Android/pal_x509.c @@ -12,6 +12,8 @@ #include #include +static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len); + static void FindCertStart(const uint8_t** buffer, int32_t* len); // Handles both DER and PEM formats @@ -266,6 +268,19 @@ void* AndroidCryptoNative_X509PublicKey(jobject /*X509Certificate*/ cert, PAL_Ke return keyHandle; } +static int32_t PopulateByteArray(JNIEnv* env, jbyteArray source, uint8_t* dest, int32_t* len) +{ + jsize bytesLen = (*env)->GetArrayLength(env, source); + + bool insufficientBuffer = *len < bytesLen; + *len = bytesLen; + if (insufficientBuffer) + return INSUFFICIENT_BUFFER; + + (*env)->GetByteArrayRegion(env, source, 0, bytesLen, (jbyte*)dest); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + static void FindCertStart(const uint8_t** buffer, int32_t* len) { assert(buffer != NULL && *buffer != NULL);