From 4fdd70a13f5a8c4e52361aeea667d0b0b26d0fe3 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 12:07:32 +0200 Subject: [PATCH 01/59] Extract existing validation code into a separate class --- .../src/System.Net.Security.csproj | 1 + .../Security/RemoteCertificateVerification.cs | 169 ++++++++++++++++++ .../src/System/Net/Security/SslStream.IO.cs | 2 +- .../System/Net/Security/SslStream.Protocol.cs | 145 ++------------- 4 files changed, 183 insertions(+), 134 deletions(-) create mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs 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 3ff9a0bf97a19..7e6e35b28f69a 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -41,6 +41,7 @@ + diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs b/src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs new file mode 100644 index 0000000000000..03497bc5f7af6 --- /dev/null +++ b/src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs @@ -0,0 +1,169 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using System.Net.Security; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace System.Net.Security +{ + internal sealed class RemoteCertificateVerification + { + private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); + private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); + + private readonly SslStream _sslStream; + private readonly SslAuthenticationOptions _sslAuthenticationOptions; + + public RemoteCertificateVerification(SslStream sslStream, SslAuthenticationOptions sslAuthenticationOptions) + { + _sslStream = sslStream; + _sslAuthenticationOptions = sslAuthenticationOptions; + } + + internal bool Verify( + X509Certificate2? remoteCertificate, + SafeDeleteSslContext securityContext, + SslCertificateTrust? trust, + ref X509Chain? chain, + out SslPolicyErrors sslPolicyErrors, + out X509ChainStatus[] chainStatus) + { + bool success = false; + sslPolicyErrors = SslPolicyErrors.None; + chainStatus = Array.Empty(); + + if (remoteCertificate == null) + { + if (NetEventSource.Log.IsEnabled() && _sslAuthenticationOptions.RemoteCertRequired) + NetEventSource.Error(_sslStream, $"Remote certificate required, but no remote certificate received"); + + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; + } + else + { + chain ??= new X509Chain(); + + if (_sslAuthenticationOptions.CertificateChainPolicy != null) + { + chain.ChainPolicy = _sslAuthenticationOptions.CertificateChainPolicy; + } + else + { + chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode; + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; + + if (trust != null) + { + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + if (trust._store != null) + { + chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates); + } + if (trust._trustList != null) + { + chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList); + } + } + } + + // set ApplicationPolicy unless already provided. + if (chain.ChainPolicy.ApplicationPolicy.Count == 0) + { + // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). + chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid); + } + + sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties( + securityContext, + chain, + remoteCertificate, + _sslAuthenticationOptions.CheckCertName, + _sslAuthenticationOptions.IsServer, + _sslAuthenticationOptions.TargetHost); + } + + var remoteCertValidationCallback = _sslAuthenticationOptions.CertValidationDelegate; + if (remoteCertValidationCallback != null) + { + // the validation callback has already been called by the trust manager + success = remoteCertValidationCallback(_sslStream, remoteCertificate, chain, sslPolicyErrors); + } + else + { + if (!_sslAuthenticationOptions.RemoteCertRequired) + { + sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable; + } + + success = (sslPolicyErrors == SslPolicyErrors.None); + } + + LogCertificateValidationResult(remoteCertificate, chain, success, sslPolicyErrors, remoteCertValidationCallback); + + if (!success && chain != null) + { + chainStatus = chain.ChainStatus; + } + + return success; + } + + private void LogCertificateValidationResult( + X509Certificate2? remoteCertificate, + X509Chain? chain, + bool success, + SslPolicyErrors sslPolicyErrors, + RemoteCertificateValidationCallback? remoteCertValidationCallback) + { + if (!NetEventSource.Log.IsEnabled()) + return; + + if (sslPolicyErrors != SslPolicyErrors.None) + { + NetEventSource.Log.RemoteCertificateError(_sslStream, SR.net_log_remote_cert_has_errors); + if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) + { + NetEventSource.Log.RemoteCertificateError(_sslStream, SR.net_log_remote_cert_not_available); + } + + if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) + { + NetEventSource.Log.RemoteCertificateError(_sslStream, SR.net_log_remote_cert_name_mismatch); + } + + if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 && chain is not null) + { + string chainStatusString = "ChainStatus: "; + foreach (X509ChainStatus chainStatus in chain.ChainStatus) + { + chainStatusString += "\t" + chainStatus.StatusInformation; + } + NetEventSource.Log.RemoteCertificateError(_sslStream, chainStatusString); + } + } + + if (success) + { + if (remoteCertValidationCallback != null) + { + NetEventSource.Log.RemoteCertDeclaredValid(_sslStream); + } + else + { + NetEventSource.Log.RemoteCertHasNoErrors(_sslStream); + } + } + else + { + if (remoteCertValidationCallback != null) + { + NetEventSource.Log.RemoteCertUserDeclaredInvalid(_sslStream); + } + } + + NetEventSource.Info(_sslStream, $"Cert validation, remote cert = {remoteCertificate}"); + } + } +} diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 38b097ebd961b..44698862727c4 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -503,7 +503,7 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError return true; } - if (!VerifyRemoteCertificate(_sslAuthenticationOptions.CertValidationDelegate, _sslAuthenticationOptions.CertificateContext?.Trust, ref alertToken, out sslPolicyErrors, out chainStatus)) + if (!VerifyRemoteCertificate(ref alertToken, out sslPolicyErrors, out chainStatus)) { _handshakeCompleted = false; return false; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index ac8db67ba2775..0932f663d291a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -19,6 +19,7 @@ public partial class SslStream { private SafeFreeCredentials? _credentialsHandle; private SafeDeleteSslContext? _securityContext; + private RemoteCertificateVerification? _remoteCertificateVerifier; private SslConnectionInfo _connectionInfo; private X509Certificate? _selectedClientCertificate; @@ -32,9 +33,6 @@ public partial class SslStream private bool _refreshCredentialNeeded = true; - private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); - private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); - // // Protocol properties // @@ -954,12 +952,11 @@ internal SecurityStatusPal Decrypt(Span buffer, out int outputOffset, out --*/ //This method validates a remote certificate. - internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remoteCertValidationCallback, SslCertificateTrust? trust, ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus) + internal bool VerifyRemoteCertificate(ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatusFlags) { sslPolicyErrors = SslPolicyErrors.None; - chainStatus = X509ChainStatusFlags.NoError; + chainStatusFlags = X509ChainStatusFlags.NoError; - // We don't catch exceptions in this method, so it's safe for "accepted" be initialized with true. bool success = false; X509Chain? chain = null; @@ -977,84 +974,15 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot } _remoteCertificate = certificate; + _remoteCertificateVerifier ??= new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions); - if (_remoteCertificate == null) - { - if (NetEventSource.Log.IsEnabled() && RemoteCertRequired) NetEventSource.Error(this, $"Remote certificate required, but no remote certificate received"); - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; - } - else - { - chain ??= new X509Chain(); - - if (_sslAuthenticationOptions.CertificateChainPolicy != null) - { - chain.ChainPolicy = _sslAuthenticationOptions.CertificateChainPolicy; - } - else - { - chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode; - chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; - - if (trust != null) - { - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - if (trust._store != null) - { - chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates); - } - if (trust._trustList != null) - { - chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList); - } - } - } - - // set ApplicationPolicy unless already provided. - if (chain.ChainPolicy.ApplicationPolicy.Count == 0) - { - // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). - chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid); - } - - sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties( - _securityContext!, - chain, - _remoteCertificate, - _sslAuthenticationOptions.CheckCertName, - _sslAuthenticationOptions.IsServer, - _sslAuthenticationOptions.TargetHost); - } - - if (remoteCertValidationCallback != null) - { - success = remoteCertValidationCallback(this, _remoteCertificate, chain, sslPolicyErrors); - } - else - { - if (!RemoteCertRequired) - { - sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable; - } - - success = (sslPolicyErrors == SslPolicyErrors.None); - } - - if (NetEventSource.Log.IsEnabled()) - { - LogCertificateValidation(remoteCertValidationCallback, sslPolicyErrors, success, chain!); - NetEventSource.Info(this, $"Cert validation, remote cert = {_remoteCertificate}"); - } - + success = _remoteCertificateVerifier.Verify(_remoteCertificate, _securityContext!, _sslAuthenticationOptions.CertificateContext?.Trust, ref chain, out sslPolicyErrors, out X509ChainStatus[] chainStatus); if (!success) { - alertToken = CreateFatalHandshakeAlertToken(sslPolicyErrors, chain!); - if (chain != null) + alertToken = CreateFatalHandshakeAlertToken(sslPolicyErrors, chainStatus); + foreach (X509ChainStatus status in chainStatus) { - foreach (X509ChainStatus status in chain.ChainStatus) - { - chainStatus |= status.Status; - } + chainStatusFlags |= status.Status; } } } @@ -1078,14 +1006,14 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot return success; } - private ProtocolToken? CreateFatalHandshakeAlertToken(SslPolicyErrors sslPolicyErrors, X509Chain chain) + private ProtocolToken? CreateFatalHandshakeAlertToken(SslPolicyErrors sslPolicyErrors, X509ChainStatus[] chainStatus) { TlsAlertMessage alertMessage; switch (sslPolicyErrors) { case SslPolicyErrors.RemoteCertificateChainErrors: - alertMessage = GetAlertMessageFromChain(chain); + alertMessage = GetAlertMessageFromChain(chainStatus); break; case SslPolicyErrors.RemoteCertificateNameMismatch: alertMessage = TlsAlertMessage.BadCertificate; @@ -1149,9 +1077,9 @@ private ProtocolToken GenerateAlertToken() return new ProtocolToken(nextmsg, status); } - private static TlsAlertMessage GetAlertMessageFromChain(X509Chain chain) + private static TlsAlertMessage GetAlertMessageFromChain(X509ChainStatus[] chainStates) { - foreach (X509ChainStatus chainStatus in chain.ChainStatus) + foreach (X509ChainStatus chainStatus in chainStates) { if (chainStatus.Status == X509ChainStatusFlags.NoError) { @@ -1197,55 +1125,6 @@ private static TlsAlertMessage GetAlertMessageFromChain(X509Chain chain) return TlsAlertMessage.BadCertificate; } - - private void LogCertificateValidation(RemoteCertificateValidationCallback? remoteCertValidationCallback, SslPolicyErrors sslPolicyErrors, bool success, X509Chain chain) - { - if (!NetEventSource.Log.IsEnabled()) - return; - - if (sslPolicyErrors != SslPolicyErrors.None) - { - NetEventSource.Log.RemoteCertificateError(this, SR.net_log_remote_cert_has_errors); - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) - { - NetEventSource.Log.RemoteCertificateError(this, SR.net_log_remote_cert_not_available); - } - - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) - { - NetEventSource.Log.RemoteCertificateError(this, SR.net_log_remote_cert_name_mismatch); - } - - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0) - { - string chainStatusString = "ChainStatus: "; - foreach (X509ChainStatus chainStatus in chain.ChainStatus) - { - chainStatusString += "\t" + chainStatus.StatusInformation; - } - NetEventSource.Log.RemoteCertificateError(this, chainStatusString); - } - } - - if (success) - { - if (remoteCertValidationCallback != null) - { - NetEventSource.Log.RemoteCertDeclaredValid(this); - } - else - { - NetEventSource.Log.RemoteCertHasNoErrors(this); - } - } - else - { - if (remoteCertValidationCallback != null) - { - NetEventSource.Log.RemoteCertUserDeclaredInvalid(this); - } - } - } } // ProtocolToken - used to process and handle the return codes from the SSPI wrapper From a4726a6fbf5a0a5b07cff255b42213429ef5b7a1 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 12:09:55 +0200 Subject: [PATCH 02/59] Implement AndroidDexBuilderTask --- .../AndroidAppBuilder.csproj | 4 + .../AndroidDexBuilderTask.cs | 59 ++++++++++++++ src/tasks/AndroidAppBuilder/ApkBuilder.cs | 15 +++- src/tasks/Common/AndroidSdkHelper.cs | 81 +++++++++++++++++++ src/tasks/Common/DexBuilder.cs | 59 ++++++++++++++ src/tasks/Common/JavaCompiler.cs | 29 +++++++ 6 files changed, 245 insertions(+), 2 deletions(-) create mode 100644 src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs create mode 100644 src/tasks/Common/AndroidSdkHelper.cs create mode 100644 src/tasks/Common/DexBuilder.cs create mode 100644 src/tasks/Common/JavaCompiler.cs diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj index 139d5672ee01a..3705b168a52ab 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj @@ -17,6 +17,10 @@ + + + + diff --git a/src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs b/src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs new file mode 100644 index 0000000000000..24bcd41078e2d --- /dev/null +++ b/src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +public class AndroidDexBuilderTask : Task +{ + public ITaskItem[] JavaFiles { get; set; } = Array.Empty(); + + [Required] + public string OutputDir { get; set; } = ""!; + + [Required] + public string DexFileName { get; set; } = ""!; + + public string? AndroidSdk { get; set; } + + public string? BuildApiLevel { get; set; } + + public string? BuildToolsVersion { get; set; } + + [Output] + public string DexFilePath { get; set; } = ""!; + + public override bool Execute() + { + var androidSdk = new AndroidSdkHelper( + androidSdkPath: AndroidSdk, + buildApiLevel: BuildApiLevel, + buildToolsVersion: BuildToolsVersion); + + var compiler = new JavaCompiler(Log, androidSdk, workingDir: OutputDir); + var dexBuilder = new DexBuilder(Log, androidSdk, workingDir: OutputDir); + + var objDir = "obj"; + var objPath = Path.Combine(OutputDir, objDir); + Directory.CreateDirectory(objPath); + + try + { + foreach (var file in JavaFiles) + { + compiler.Compile(file.ItemSpec, outputDir: objDir); + } + + dexBuilder.Build(inputDir: objDir, outputFileName: DexFileName); + + DexFilePath = Path.Combine(OutputDir, DexFileName); + return true; + } + finally + { + Directory.Delete(objPath, recursive: true); + } + } +} diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 4cfc8d0d1cac9..7fdd6f958f10d 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -182,7 +182,7 @@ public ApkBuilder(TaskLoggingHelper logger) Directory.CreateDirectory(Path.Combine(OutputDir, "assets")); Directory.CreateDirectory(Path.Combine(OutputDir, "res")); - var extensionsToIgnore = new List { ".so", ".a" }; + var extensionsToIgnore = new List { ".so", ".a", ".dex" }; if (StripDebugSymbols) { extensionsToIgnore.Add(".pdb"); @@ -247,7 +247,7 @@ public ApkBuilder(TaskLoggingHelper logger) if (!File.Exists(androidJar)) throw new ArgumentException($"API level={BuildApiLevel} is not downloaded in Android SDK"); - // 1. Build libmonodroid.so` via cmake + // 1. Build libmonodroid.so via cmake string nativeLibraries = ""; string monoRuntimeLib = ""; @@ -507,6 +507,17 @@ public ApkBuilder(TaskLoggingHelper logger) } Utils.RunProcess(logger, aapt, $"add {apkFile} classes.dex", workingDir: OutputDir); + // Include prebuilt .dex files + int sequence = 2; + var dexFiles = Directory.GetFiles(AppDir, "*.dex"); + foreach (var dexFile in dexFiles) + { + var classesFileName = $"classes{sequence++}.dex"; + File.Copy(dexFile, Path.Combine(OutputDir, classesFileName)); + logger.LogMessage(MessageImportance.High, $"Adding dex file {Path.GetFileName(dexFile)} as {classesFileName}"); + Utils.RunProcess(logger, aapt, $"add {apkFile} {classesFileName}", workingDir: OutputDir); + } + // 4. Align APK string alignedApk = Path.Combine(OutputDir, "bin", $"{ProjectName}.apk"); diff --git a/src/tasks/Common/AndroidSdkHelper.cs b/src/tasks/Common/AndroidSdkHelper.cs new file mode 100644 index 0000000000000..e56e7d65699d2 --- /dev/null +++ b/src/tasks/Common/AndroidSdkHelper.cs @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; + +internal sealed class AndroidSdkHelper +{ + private readonly string _androidSdkPath; + private readonly string _buildToolsPath; + private readonly string _buildApiLevel; + + public AndroidSdkHelper( + string? androidSdkPath, + string? buildApiLevel, + string? buildToolsVersion) + { + if (string.IsNullOrEmpty(androidSdkPath)) + androidSdkPath = Environment.GetEnvironmentVariable("ANDROID_SDK_ROOT"); + + if (string.IsNullOrEmpty(androidSdkPath) || !Directory.Exists(androidSdkPath)) + throw new ArgumentException($"Android SDK='{androidSdkPath}' was not found or empty (can be set via ANDROID_SDK_ROOT envvar)."); + + _androidSdkPath = androidSdkPath; + + // Try to get the latest API level if not specified + if (string.IsNullOrEmpty(buildApiLevel)) + buildApiLevel = GetLatestApiLevel(_androidSdkPath); + + _buildApiLevel = buildApiLevel; + + // Try to get the latest build-tools version if not specified + if (string.IsNullOrEmpty(buildToolsVersion)) + buildToolsVersion = GetLatestBuildTools(_androidSdkPath); + + _buildToolsPath = Path.Combine(_androidSdkPath, "build-tools", buildToolsVersion); + + if (!Directory.Exists(_buildToolsPath)) + throw new ArgumentException($"{_buildToolsPath} was not found."); + } + + public string AndroidJarPath => Path.Combine(_androidSdkPath, "platforms", $"android-{_buildApiLevel}", "android.jar"); + + public bool HasD8 => File.Exists(D8Path); + public string D8Path => getToolPath("d8"); + public string DxPath => getToolPath("dx"); + + private string getToolPath(string tool) + => Path.Combine(_buildToolsPath, tool); + + /// + /// Scan android SDK for api levels (ignore preview versions) + /// + private static string GetLatestApiLevel(string androidSdkDir) + { + return Directory.GetDirectories(Path.Combine(androidSdkDir, "platforms")) + .Select(file => int.TryParse(Path.GetFileName(file).Replace("android-", ""), out int apiLevel) ? apiLevel : -1) + .OrderByDescending(v => v) + .FirstOrDefault() + .ToString(); + } + + /// + /// Scan android SDK for build tools (ignore preview versions) + /// + private static string GetLatestBuildTools(string androidSdkPath) + { + string? buildTools = Directory.GetDirectories(Path.Combine(androidSdkPath, "build-tools")) + .Select(Path.GetFileName) + .Where(file => !file!.Contains('-')) + .Select(file => { Version.TryParse(Path.GetFileName(file), out Version? version); return version; }) + .OrderByDescending(v => v) + .FirstOrDefault()?.ToString(); + + if (string.IsNullOrEmpty(buildTools)) + throw new ArgumentException($"Android SDK ({androidSdkPath}) doesn't contain build-tools."); + + return buildTools; + } +} diff --git a/src/tasks/Common/DexBuilder.cs b/src/tasks/Common/DexBuilder.cs new file mode 100644 index 0000000000000..e7076d3d8c829 --- /dev/null +++ b/src/tasks/Common/DexBuilder.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using Microsoft.Build.Utilities; + +internal sealed class DexBuilder +{ + private readonly string _workingDir; + private readonly AndroidSdkHelper _androidSdk; + private readonly TaskLoggingHelper _logger; + + public DexBuilder( + TaskLoggingHelper logger, + AndroidSdkHelper buildTools, + string workingDir) + { + _androidSdk = buildTools; + _workingDir = workingDir; + _logger = logger; + } + + public void Build(string inputDir, string outputFileName) + { + if (_androidSdk.HasD8) + { + BuildUsingD8(inputDir, outputFileName); + } + else + { + BuildUsingDx(inputDir, outputFileName); + } + } + + private void BuildUsingD8(string inputDir, string outputFileName) + { + string[] classFiles = Directory.GetFiles(Path.Combine(_workingDir, inputDir), "*.class", SearchOption.AllDirectories); + + if (!classFiles.Any()) + throw new InvalidOperationException("Didn't find any .class files"); + + Utils.RunProcess(_logger, _androidSdk.D8Path, $"--no-desugaring {string.Join(" ", classFiles)}", workingDir: _workingDir); + + if (outputFileName != "classes.dex") + { + File.Move( + sourceFileName: Path.Combine(_workingDir, "classes.dex"), + destFileName: Path.Combine(_workingDir, outputFileName), + overwrite: true); + } + } + + private void BuildUsingDx(string inputDir, string outputFileName) + { + Utils.RunProcess(_logger, _androidSdk.DxPath, $"--dex --output={outputFileName} {inputDir}", workingDir: _workingDir); + } +} diff --git a/src/tasks/Common/JavaCompiler.cs b/src/tasks/Common/JavaCompiler.cs new file mode 100644 index 0000000000000..abeb7c6fd9155 --- /dev/null +++ b/src/tasks/Common/JavaCompiler.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using Microsoft.Build.Utilities; + +internal sealed class JavaCompiler +{ + private readonly string _javaCompilerArgs; + private readonly string _workingDir; + private readonly TaskLoggingHelper _logger; + + public JavaCompiler( + TaskLoggingHelper logger, + AndroidSdkHelper androidSdk, + string workingDir, + string javaVersion = "1.8") + { + _javaCompilerArgs = $"-classpath src -bootclasspath {androidSdk.AndroidJarPath} -source {javaVersion} -target {javaVersion}"; + _workingDir = workingDir; + _logger = logger; + } + + public void Compile(string javaSourceFile, string outputDir) + { + Utils.RunProcess(_logger, "javac", $"{_javaCompilerArgs} -d {outputDir} {javaSourceFile}", workingDir: _workingDir); + } +} From 18214ea4987f748aa63eb54c90adf7f0d9890c2b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 12:18:22 +0200 Subject: [PATCH 03/59] Implement TrustManager proxy --- .../Directory.Build.props | 1 + .../Interop.Ssl.cs | 14 +- .../src/System.Net.Security.csproj | 1 + .../Pal.Android/SafeDeleteSslContext.cs | 27 ++- .../Security/Pal.Android/TrustManagerProxy.cs | 126 ++++++++++++ .../Net/Security/SslStreamPal.Android.cs | 7 +- src/libraries/native-binplace.proj | 3 +- src/libraries/pretest.proj | 1 + .../CMakeLists.txt | 1 + ...tificateVerificationProxyTrustManager.java | 34 ++++ .../pal_jni.c | 34 ++++ .../pal_jni.h | 19 ++ .../pal_sslstream.c | 186 ++++++++++++++---- .../pal_sslstream.h | 5 +- .../pal_trust_manager.c | 134 +++++++++++++ .../pal_trust_manager.h | 13 ++ src/native/libs/build-native.proj | 22 ++- 17 files changed, 570 insertions(+), 58 deletions(-) create mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs create mode 100644 src/native/libs/System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java create mode 100644 src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c create mode 100644 src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 07d77161002bc..00f4c1da4ea64 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -95,6 +95,7 @@ + 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 ba9fbd297c93b..cc34543708cfc 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 @@ -29,18 +29,24 @@ internal enum PAL_SSLStreamStatus }; [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] - internal static partial SafeSslHandle SSLStreamCreate(); + internal static partial SafeSslHandle SSLStreamCreate(IntPtr trustManagerProxyHandle); [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateWithCertificates")] private static partial SafeSslHandle SSLStreamCreateWithCertificates( + IntPtr trustManagerProxyHandle, ref byte pkcs8PrivateKey, int pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, IntPtr[] certs, int certsLen); - internal static SafeSslHandle SSLStreamCreateWithCertificates(ReadOnlySpan pkcs8PrivateKey, PAL_KeyAlgorithm algorithm, IntPtr[] certificates) + internal static SafeSslHandle SSLStreamCreateWithCertificates( + IntPtr trustManagerProxyHandle, + ReadOnlySpan pkcs8PrivateKey, + PAL_KeyAlgorithm algorithm, + IntPtr[] certificates) { return SSLStreamCreateWithCertificates( + trustManagerProxyHandle, ref MemoryMarshal.GetReference(pkcs8PrivateKey), pkcs8PrivateKey.Length, algorithm, @@ -48,6 +54,10 @@ ref MemoryMarshal.GetReference(pkcs8PrivateKey), certificates.Length); } + [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterTrustManagerCallback")] + internal static unsafe partial void RegisterTrustManagerCallback( + delegate* unmanaged verifyRemoteCertificate); + [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] private static unsafe partial int SSLStreamInitializeImpl( SafeSslHandle sslHandle, 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 7e6e35b28f69a..ad6d702bf6162 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -381,6 +381,7 @@ Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\Interop.X509.cs" /> + 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 e4a2ee35c53d8..30f224f549b1b 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 @@ -30,18 +30,24 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private static readonly Lazy s_supportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); private readonly SafeSslHandle _sslContext; + private readonly TrustManagerProxy? _trustManagerProxy; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SafeSslHandle SslContext => _sslContext; - public SafeDeleteSslContext(SslAuthenticationOptions authOptions) + public SafeDeleteSslContext(RemoteCertificateVerification? verifier, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) { + if (verifier is not null) + { + _trustManagerProxy = new TrustManagerProxy(verifier, securityContext: this); + } + try { - _sslContext = CreateSslContext(authOptions); + _sslContext = CreateSslContext(_trustManagerProxy?.Handle, authOptions); InitializeSslContext(_sslContext, authOptions); } catch (Exception ex) @@ -65,6 +71,8 @@ protected override void Dispose(bool disposing) _outputBuffer.Dispose(); sslContext.Dispose(); } + + _trustManagerProxy?.Dispose(); } base.Dispose(disposing); @@ -145,11 +153,11 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) return limit; } - private static SafeSslHandle CreateSslContext(SslAuthenticationOptions authOptions) + private static SafeSslHandle CreateSslContext(IntPtr? trustManagerProxyHandle, SslAuthenticationOptions authOptions) { if (authOptions.CertificateContext == null) { - return Interop.AndroidCrypto.SSLStreamCreate(); + return Interop.AndroidCrypto.SSLStreamCreate(trustManagerProxyHandle ?? IntPtr.Zero); } SslStreamCertificateContext context = authOptions.CertificateContext; @@ -169,7 +177,8 @@ private static SafeSslHandle CreateSslContext(SslAuthenticationOptions authOptio ptrs[i + 1] = context.IntermediateCertificates[i].Handle; } - return Interop.AndroidCrypto.SSLStreamCreateWithCertificates(keyBytes, algorithm, ptrs); + return Interop.AndroidCrypto.SSLStreamCreateWithCertificates( + trustManagerProxyHandle ?? IntPtr.Zero, keyBytes, algorithm, ptrs); } private static AsymmetricAlgorithm GetPrivateKeyAlgorithm(X509Certificate2 cert, out PAL_KeyAlgorithm algorithm) @@ -249,7 +258,13 @@ private unsafe void InitializeSslContext( if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) { - Interop.AndroidCrypto.SSLStreamSetTargetHost(handle, authOptions.TargetHost); + // the Java SNIHostName class that's used internally to wrap the hostname + // doesn't support IPv6 addresses + var containsUnsupportedCharacters = authOptions.TargetHost.Contains(':'); + if (!containsUnsupportedCharacters) + { + Interop.AndroidCrypto.SSLStreamSetTargetHost(handle, authOptions.TargetHost); + } } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs new file mode 100644 index 0000000000000..9bc6d5e5c91ff --- /dev/null +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Net.Security; +using System.Threading; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; + +namespace System.Net +{ + internal sealed class TrustManagerProxy : IDisposable + { + private static object s_initializationLock = new(); + private static bool s_initialized; + + private readonly RemoteCertificateVerification _remoteCertificateVerifier; + private readonly SafeDeleteSslContext _securityContext; + private GCHandle? _handle; + + public unsafe TrustManagerProxy( + RemoteCertificateVerification remoteCertificateVerifier, + SafeDeleteSslContext securityContext) + { + RegisterTrustManagerCallback(); + + _remoteCertificateVerifier = remoteCertificateVerifier; + _securityContext = securityContext; + _handle = GCHandle.Alloc(this); + } + + public IntPtr Handle + => _handle is GCHandle handle + ? GCHandle.ToIntPtr(handle) + : throw new ObjectDisposedException(nameof(TrustManagerProxy)); + + private static unsafe void RegisterTrustManagerCallback() + { + lock (s_initializationLock) + { + if (!s_initialized) + { + Interop.AndroidCrypto.RegisterTrustManagerCallback(&VerifyRemoteCertificate); + s_initialized = true; + } + } + } + + public void Dispose() + { + _handle?.Free(); + _handle = null; + } + + [UnmanagedCallersOnly] + private static unsafe bool VerifyRemoteCertificate( + IntPtr trustManagerProxyHandle, + int certificatesCount, + int* certificateLengths, + byte** rawCertificates) + { + var proxy = (TrustManagerProxy?)GCHandle.FromIntPtr(trustManagerProxyHandle).Target; + Debug.Assert(proxy is not null); + + X509Certificate2[] certificates = ConvertCertificates(certificatesCount, certificateLengths, rawCertificates); + try + { + return proxy.Verify(certificates); + } + catch (Exception exception) + { + Debug.WriteLine($"Remote certificate verification has thrown an exception: {exception}"); + Debug.WriteLine(exception.StackTrace); + return false; + } + finally + { + foreach (var certificate in certificates) + certificate.Dispose(); + } + } + + private bool Verify(X509Certificate2[] certificates) + { + X509Certificate2? certificate = certificates.Length > 0 ? certificates[0] : null; + X509Chain? chain = null; + if (certificates.Length > 1) + { + chain = new X509Chain(); + chain.ChainPolicy.ExtraStore.AddRange(certificates[1..]); + } + + try + { + return _remoteCertificateVerifier.Verify(certificate, _securityContext, trust: null, ref chain, out _, out _); + } + finally + { + if (chain != null) + { + int elementsCount = chain.ChainElements.Count; + for (int i = 0; i < elementsCount; i++) + { + chain.ChainElements[i].Certificate.Dispose(); + } + + chain.Dispose(); + } + } + } + + private static unsafe X509Certificate2[] ConvertCertificates(int count, int* lengths, byte** rawData) + { + var certificates = new X509Certificate2[count]; + + for (int i = 0; i < count; i++) + { + var rawCertificate = new ReadOnlySpan(rawData[i], lengths[i]); + certificates[i] = new X509Certificate2(rawCertificate); + } + + return certificates; + } + } +} 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 f01dd68e294b2..cd2aafa88d6ba 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 @@ -31,7 +31,7 @@ public static SecurityStatusPal AcceptSecurityContext( ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(verifier: null, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal InitializeSecurityContext( @@ -43,7 +43,7 @@ public static SecurityStatusPal InitializeSecurityContext( SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate? clientCertificateSelectionCallback) { - return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(verifier: null, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal Renegotiate( @@ -168,6 +168,7 @@ public static void QueryContextConnectionInfo( } private static SecurityStatusPal HandshakeInternal( + RemoteCertificateVerification? verifier, SafeFreeCredentials credential, ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, @@ -180,7 +181,7 @@ private static SecurityStatusPal HandshakeInternal( if ((context == null) || context.IsInvalid) { - context = new SafeDeleteSslContext(sslAuthenticationOptions); + context = new SafeDeleteSslContext(verifier, sslAuthenticationOptions); sslContext = context; } diff --git a/src/libraries/native-binplace.proj b/src/libraries/native-binplace.proj index 82427d8f27b01..7946258980992 100644 --- a/src/libraries/native-binplace.proj +++ b/src/libraries/native-binplace.proj @@ -22,10 +22,11 @@ + - \ No newline at end of file + diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index ac4f3c9021c0c..9943a10fe38bb 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -44,6 +44,7 @@ + diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/CMakeLists.txt b/src/native/libs/System.Security.Cryptography.Native.Android/CMakeLists.txt index c4136a588f856..36a0827e25d13 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/CMakeLists.txt +++ b/src/native/libs/System.Security.Cryptography.Native.Android/CMakeLists.txt @@ -24,6 +24,7 @@ set(NATIVECRYPTO_SOURCES pal_signature.c pal_ssl.c pal_sslstream.c + pal_trust_manager.c pal_x509.c pal_x509chain.c pal_x509store.c diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java new file mode 100644 index 0000000000000..810343b74e069 --- /dev/null +++ b/src/native/libs/System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java @@ -0,0 +1,34 @@ +package net.dot.android.crypto; + +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import javax.net.ssl.X509TrustManager; + +class RemoteCertificateVerificationProxyTrustManager implements X509TrustManager { + private int dotnetHandle; + + public RemoteCertificateVerificationProxyTrustManager(int dotnetHandle) + { + this.dotnetHandle = dotnetHandle; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (!verifyRemoteCertificate(dotnetHandle, chain)) { + throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); + } + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + if (!verifyRemoteCertificate(dotnetHandle, chain)) { + throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); + } + } + + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + + static native boolean verifyRemoteCertificate(int dotnetHandle, X509Certificate[] chain); +} diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index f61d91060e806..feaf0a054528f 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -418,6 +418,7 @@ jclass g_SSLEngine; jmethodID g_SSLEngineBeginHandshake; jmethodID g_SSLEngineCloseOutbound; jmethodID g_SSLEngineGetApplicationProtocol; +jmethodID g_SSLEngineGetHandshakeSession; jmethodID g_SSLEngineGetHandshakeStatus; jmethodID g_SSLEngineGetSession; jmethodID g_SSLEngineGetSSLParameters; @@ -469,6 +470,24 @@ jmethodID g_KeyAgreementInit; jmethodID g_KeyAgreementDoPhase; jmethodID g_KeyAgreementGenerateSecret; +// javax/net/ssl/TrustManagerFactory +jclass g_TrustManagerFactory; +jmethodID g_TrustManagerFactoryGetDefaultAlgorithm; +jmethodID g_TrustManagerFactoryGetInstance; +jmethodID g_TrustManagerFactoryInit; +jmethodID g_TrustManagerFactoryGetTrustManagers; + +// javax/net/ssl/X509TrustManager +jclass g_X509TrustManager; + +// java/security/cert/Certificate +jclass g_Certificate; +jmethodID g_CertificateGetEncoded; + +// net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager +jclass g_RemoteCertificateVerificationProxyTrustManager; +jmethodID g_RemoteCertificateVerificationProxyTrustManagerCtor; + jobject ToGRef(JNIEnv *env, jobject lref) { if (lref) @@ -999,6 +1018,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); g_SSLEngineBeginHandshake = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); g_SSLEngineCloseOutbound = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); + g_SSLEngineGetHandshakeSession = GetMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetApplicationProtocol = GetOptionalMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); @@ -1046,5 +1066,19 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_KeyAgreementDoPhase = GetMethod(env, false, g_KeyAgreementClass, "doPhase", "(Ljava/security/Key;Z)Ljava/security/Key;"); g_KeyAgreementGenerateSecret = GetMethod(env, false, g_KeyAgreementClass, "generateSecret", "()[B"); + g_TrustManagerFactory = GetClassGRef(env, "javax/net/ssl/TrustManagerFactory"); + g_TrustManagerFactoryGetDefaultAlgorithm = GetMethod(env, true, g_TrustManagerFactory, "getDefaultAlgorithm", "()Ljava/lang/String;"); + g_TrustManagerFactoryGetInstance = GetMethod(env, true, g_TrustManagerFactory, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/TrustManagerFactory;"); + g_TrustManagerFactoryInit = GetMethod(env, false, g_TrustManagerFactory, "init", "(Ljava/security/KeyStore;)V"); + g_TrustManagerFactoryGetTrustManagers = GetMethod(env, false, g_TrustManagerFactory, "getTrustManagers", "()[Ljavax/net/ssl/TrustManager;"); + + g_X509TrustManager = GetClassGRef(env, "javax/net/ssl/X509TrustManager"); + + g_Certificate = GetClassGRef(env, "java/security/cert/Certificate"); + g_CertificateGetEncoded = GetMethod(env, false, g_Certificate, "getEncoded", "()[B"); + + g_RemoteCertificateVerificationProxyTrustManager = GetClassGRef(env, "net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager"); + g_RemoteCertificateVerificationProxyTrustManagerCtor = GetMethod(env, false, g_RemoteCertificateVerificationProxyTrustManager, "", "(I)V"); + return JNI_VERSION_1_6; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index 9294c0e13cb34..d9af003bbbeb6 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -432,6 +432,7 @@ extern jclass g_SSLEngine; extern jmethodID g_SSLEngineBeginHandshake; extern jmethodID g_SSLEngineCloseOutbound; extern jmethodID g_SSLEngineGetApplicationProtocol; +extern jmethodID g_SSLEngineGetHandshakeSession; extern jmethodID g_SSLEngineGetHandshakeStatus; extern jmethodID g_SSLEngineGetSession; extern jmethodID g_SSLEngineGetSSLParameters; @@ -484,6 +485,24 @@ extern jmethodID g_KeyAgreementInit; extern jmethodID g_KeyAgreementDoPhase; extern jmethodID g_KeyAgreementGenerateSecret; +// javax/net/ssl/TrustManagerFactory +extern jclass g_TrustManagerFactory; +extern jmethodID g_TrustManagerFactoryGetDefaultAlgorithm; +extern jmethodID g_TrustManagerFactoryGetInstance; +extern jmethodID g_TrustManagerFactoryInit; +extern jmethodID g_TrustManagerFactoryGetTrustManagers; + +// javax/net/ssl/X509TrustManager +extern jclass g_X509TrustManager; + +// java/security/cert/Certificate +extern jclass g_Certificate; +extern jmethodID g_CertificateGetEncoded; + +// net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager +extern jclass g_RemoteCertificateVerificationProxyTrustManager; +extern jmethodID g_RemoteCertificateVerificationProxyTrustManagerCtor; + // Compatibility macros #if !defined (__mallocfunc) #if defined (__clang__) || defined (__GNUC__) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 430edc20edf28..2286647035949 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -3,6 +3,7 @@ #include "pal_sslstream.h" #include "pal_ssl.h" +#include "pal_trust_manager.h" // javax/net/ssl/SSLEngineResult$HandshakeStatus enum @@ -283,17 +284,79 @@ ARGS_NON_NULL_ALL static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) free(sslStream); } -SSLStream* AndroidCryptoNative_SSLStreamCreate(void) +static jobject GetSSLContextInstance(JNIEnv* env) { + jobject sslContext = NULL; + + // sslContext = SSLContext.getInstance("TLSv1.3"); + jstring tls13 = make_java_string(env, "TLSv1.3"); + sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, 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"); + jstring tls12 = make_java_string(env, "TLSv1.2"); + sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, tls12); + ReleaseLRef(env, tls12); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + } + +cleanup: + ReleaseLRef(env, tls13); + return sslContext; +} + +static jobject GetKeyStoreInstance(JNIEnv* env) +{ + jobject keyStore = NULL; + jstring ksType = NULL; + + // String ksType = KeyStore.getDefaultType(); + // KeyStore keyStore = KeyStore.getInstance(ksType); + // keyStore.load(null, null); + ksType = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetDefaultType); + keyStore = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, ksType); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, keyStore, g_KeyStoreLoad, NULL, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + +cleanup: + ReleaseLRef(env, ksType); + return keyStore; +} + +SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t trustManagerProxyHandle) +{ + SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); - // SSLContext sslContext = SSLContext.getDefault(); - jobject sslContext = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetDefault); - if (CheckJNIExceptions(env)) - return NULL; + INIT_LOCALS(loc, sslContext, keyStore, trustManagers); + + loc[sslContext] = GetSSLContextInstance(env); + if (loc[sslContext] == NULL) + goto cleanup; - SSLStream* sslStream = xcalloc(1, sizeof(SSLStream)); - sslStream->sslContext = ToGRef(env, sslContext); + // We only need to init the key store, we don't use it + IGNORE_RETURN(GetKeyStoreInstance(env)); + + if (trustManagerProxyHandle != 0) + { + // Init trust managers + loc[trustManagers] = initTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); + if (loc[trustManagers] == NULL) + goto cleanup; + } + + // Init the SSLContext + (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, NULL, loc[trustManagers], NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + sslStream = xcalloc(1, sizeof(SSLStream)); + sslStream->sslContext = ToGRef(env, loc[sslContext]); + loc[sslContext] = NULL; + +cleanup: + RELEASE_LOCALS(loc, env); return sslStream; } @@ -360,7 +423,8 @@ static int32_t AddCertChainToStore(JNIEnv* env, return ret; } -SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8PrivateKey, +SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustManagerProxyHandle, + uint8_t* pkcs8PrivateKey, int32_t pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, jobject* /*X509Certificate[]*/ certs, @@ -369,29 +433,16 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8Pri SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); - INIT_LOCALS(loc, tls13, sslContext, ksType, keyStore, kmfType, kmf, keyManagers); + INIT_LOCALS(loc, sslContext, keyStore, kmfType, kmf, keyManagers, trustManagers); // SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); - loc[tls13] = make_java_string(env, "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 = make_java_string(env, "TLSv1.2"); - loc[sslContext] = (*env)->CallStaticObjectMethod(env, g_SSLContext, g_SSLContextGetInstanceMethod, tls12); - ReleaseLRef(env, tls12); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - } + loc[sslContext] = GetSSLContextInstance(env); + if (loc[sslContext] == NULL) + 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); + loc[keyStore] = GetKeyStoreInstance(env); + if (loc[keyStore] == NULL) + goto cleanup; int32_t status = AddCertChainToStore(env, loc[keyStore], pkcs8PrivateKey, pkcs8PrivateKeyLen, algorithm, certs, certsLen); @@ -409,10 +460,19 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8Pri 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); + + if (trustManagerProxyHandle != 0) + { + // TrustManager[] trustMangers = initTrustManagersWithCustomValidatorProxy(trustManagerProxyHandle); + loc[trustManagers] = initTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); + if (loc[trustManagers] == NULL) + goto cleanup; + } + + // sslContext.init(keyManagers, trustManagers, null); + (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, loc[keyManagers], loc[trustManagers], NULL); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); sslStream = xcalloc(1, sizeof(SSLStream)); @@ -737,6 +797,34 @@ int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t* return ret; } +static jobject getPeerCertificates(JNIEnv* env, SSLStream* sslStream) +{ + jobject certificates = NULL; + jobject sslSession = NULL; + bool isHandshaking = false; + + // During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates + // which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLession. + + int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + isHandshaking = IsHandshaking(handshakeStatus); + sslSession = isHandshaking + ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) + : sslStream->sslSession; + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + certificates = (*env)->CallObjectMethod(env, sslSession, g_SSLSessionGetPeerCertificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + +cleanup: + if (isHandshaking) + ReleaseLRef(env, sslSession); + + return certificates; +} + jobject /*X509Certificate*/ AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLStream* sslStream) { abort_if_invalid_pointer_argument (sslStream); @@ -745,13 +833,11 @@ jobject /*X509Certificate*/ AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLS jobject ret = NULL; // Certificate[] certs = sslSession.getPeerCertificates(); - // out = certs[0]; - jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); - - // If there are no peer certificates, getPeerCertificates will throw. Return null to indicate no certificate. - if (TryClearJNIExceptions(env)) + jobjectArray certs = getPeerCertificates(env, sslStream); + if (certs == NULL) goto cleanup; + // out = certs[0]; jsize len = (*env)->GetArrayLength(env, certs); if (len > 0) { @@ -761,7 +847,7 @@ jobject /*X509Certificate*/ AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLS } cleanup: - (*env)->DeleteLocalRef(env, certs); + ReleaseLRef(env, certs); return ret; } @@ -776,15 +862,13 @@ void AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobj *outLen = 0; // Certificate[] certs = sslSession.getPeerCertificates(); + jobjectArray certs = getPeerCertificates(env, sslStream); + if (certs == NULL) + goto cleanup; + // for (int i = 0; i < certs.length; i++) { // out[i] = certs[i]; // } - jobjectArray certs = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetPeerCertificates); - - // If there are no peer certificates, getPeerCertificates will throw. Return null and length of zero to indicate no certificates. - if (TryClearJNIExceptions(env)) - goto cleanup; - jsize len = (*env)->GetArrayLength(env, certs); *outLen = len; if (len > 0) @@ -914,14 +998,30 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hos bool ret = false; INIT_LOCALS(loc, name, verifier); + // During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates + // which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLession. + + int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); + bool isHandshaking = IsHandshaking(handshakeStatus); + + jobject sslSession = isHandshaking + ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) + : sslStream->sslSession; + + if (CheckJNIExceptions(env)) + return false; + // HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); // return verifier.verify(hostname, sslSession); loc[name] = make_java_string(env, hostname); loc[verifier] = (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); - ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], sslStream->sslSession); + ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], sslSession); RELEASE_LOCALS(loc, env); + if (isHandshaking) + ReleaseLRef(env, sslSession); + return ret; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h index cc7a7b52b6f25..4332b76c6a92c 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -44,14 +44,15 @@ Create an SSL context Returns NULL on failure */ -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(void); +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t trustManagerProxyHandle); /* Create an SSL context with the specified certificates Returns NULL on failure */ -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(uint8_t* pkcs8PrivateKey, +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustManagerProxyHandle, + uint8_t* pkcs8PrivateKey, int32_t pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, jobject* /*X509Certificate[]*/ certs, diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c new file mode 100644 index 0000000000000..85cca3ebd8516 --- /dev/null +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -0,0 +1,134 @@ +#include "pal_trust_manager.h" + +static RemoteCertificateValidationCallback verifyRemoteCertificate; + +void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback) +{ + verifyRemoteCertificate = callback; +} + +jobjectArray initTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle) +{ + abort_unless(dotnetHandle != 0, "invalid pointer to the .NET remote certificate validator"); + + jobjectArray trustManagers = NULL; + INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, trustManagerProxy); + + // string defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + loc[defaultAlgorithm] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetDefaultAlgorithm); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // TrustManagerFactory tmf = TrustManagerFactory.getInstance(defaultAlgorithm); + loc[tmf] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetInstance, loc[defaultAlgorithm]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // tmf.init(); + (*env)->CallVoidMethod(env, loc[tmf], g_TrustManagerFactoryInit, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // TrustManager[] trustManagers = tmf.getTrustManagers(); + trustManagers = (*env)->CallObjectMethod(env, loc[tmf], g_TrustManagerFactoryGetTrustManagers); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + // boolean foundAndReplaced = false; + // for (int i = 0; i < trustManagers.length; i++) { + // if (trustManagers[i] instanceof X509TrustManager) { + // trustManagers[i] = new RemoteCertificateVerificationProxyTrustManager(dotnetHandle, trustManagers[i]); + // foundAndReplaced = true; + // break; + // } + // } + + bool foundAndReplaced = false; + size_t length = (size_t)(*env)->GetArrayLength(env, trustManagers); + for (size_t i = 0; i < length; i++) + { + loc[trustManager] = (*env)->GetObjectArrayElement(env, trustManagers, (jsize)i); + + if ((*env)->IsInstanceOf(env, loc[trustManager], g_X509TrustManager)) + { + loc[trustManagerProxy] = (*env)->NewObject(env, g_RemoteCertificateVerificationProxyTrustManager, g_RemoteCertificateVerificationProxyTrustManagerCtor, (int)dotnetHandle); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[trustManagerProxy]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + foundAndReplaced = true; + break; + } + + ReleaseLRef(env, loc[trustManager]); + loc[trustManager] = NULL; + } + + abort_unless(foundAndReplaced, "no X509 trust managers"); +cleanup: + RELEASE_LOCALS(loc, env); + return trustManagers; +} + +jboolean Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustManager_verifyRemoteCertificate( + JNIEnv* env, + jobject handle, + intptr_t dotnetHandle, + jobjectArray certificates) +{ + abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); + + INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, trustManagerProxy, certificate, encodedCertificate); + + bool isAccepted = false; + uint8_t** rawData = NULL; + int32_t* lengths = NULL; + + size_t certificateCount = (size_t)(*env)->GetArrayLength(env, certificates); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + rawData = (uint8_t**)xcalloc(certificateCount, sizeof(uint8_t*)); + lengths = (int*)xcalloc(certificateCount, sizeof(int32_t)); + + for (size_t i = 0; i < certificateCount; i++) + { + // X509Certificate certificate = certificates[i]; + // byte[] encodedCertificate = certificate.getEncoded(); + // int length = encodedCertificate.length; + + loc[certificate] = (*env)->GetObjectArrayElement(env, certificates, (jsize)i); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + loc[encodedCertificate] = (*env)->CallObjectMethod(env, loc[certificate], g_CertificateGetEncoded); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + jsize length = (*env)->GetArrayLength(env, loc[encodedCertificate]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + lengths[i] = (int32_t)length; + rawData[i] = (uint8_t*)xmalloc((size_t)length * sizeof(uint8_t)); + (*env)->GetByteArrayRegion(env, loc[encodedCertificate], 0, length, (jbyte*)rawData[i]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + ReleaseLRef(env, loc[certificate]); + ReleaseLRef(env, loc[encodedCertificate]); + loc[certificate] = NULL; + loc[encodedCertificate] = NULL; + } + + isAccepted = verifyRemoteCertificate(dotnetHandle, (int32_t)certificateCount, lengths, rawData); + +cleanup: + if (rawData != NULL) + { + for (size_t i = 0; i < certificateCount; i++) + { + if (rawData[i] != NULL) + free(rawData[i]); + } + } + + if (lengths != NULL) + free(lengths); + + RELEASE_LOCALS(loc, env); + + return isAccepted ? JNI_TRUE : JNI_FALSE; +} diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h new file mode 100644 index 0000000000000..c9897e1f69118 --- /dev/null +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -0,0 +1,13 @@ +#include "pal_jni.h" + +typedef bool (*RemoteCertificateValidationCallback)(intptr_t, int32_t, int32_t*, uint8_t**); + +PALEXPORT void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback); + +jobjectArray initTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle); + +JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustManager_verifyRemoteCertificate( + JNIEnv *env, + jobject handle, + intptr_t dotnetHandle, + jobjectArray certificates); diff --git a/src/native/libs/build-native.proj b/src/native/libs/build-native.proj index b102a6b841c92..26f8f0fd6e9c5 100644 --- a/src/native/libs/build-native.proj +++ b/src/native/libs/build-native.proj @@ -6,7 +6,8 @@ .NET Runtime <_BuildNativeTargetOS>$(TargetOS) <_BuildNativeTargetOS Condition="'$(TargetsLinuxBionic)' == 'true'">linux-bionic - <_BuildNativeArgs>$(TargetArchitecture) $(Configuration) outconfig $(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture) -os $(_BuildNativeTargetOS) + <_BuildNativeOutConfig>$(NetCoreAppCurrent)-$(TargetOS)-$(Configuration)-$(TargetArchitecture) + <_BuildNativeArgs>$(TargetArchitecture) $(Configuration) outconfig $(_BuildNativeOutConfig) -os $(_BuildNativeTargetOS) <_BuildNativeArgs Condition="'$(OfficialBuildId)' != ''">$(_BuildNativeArgs) /p:OfficialBuildId="$(OfficialBuildId)" @@ -53,4 +54,23 @@ + + + + + <_JavaFile Include="$(MSBuildThisFileDirectory)System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java" /> + + + + + + + + From 193f3c6f037b78b9eb84cecc740b9ce6e395e17d Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 12:46:22 +0200 Subject: [PATCH 04/59] Integrate the trust manager proxy with SslStream on Android --- .../src/System.Net.Security.csproj | 3 ++- .../src/System/Net/Security/SslStream.IO.cs | 12 ++++++++++++ .../src/System/Net/Security/SslStream.Protocol.cs | 6 ++++++ .../src/System/Net/Security/SslStreamPal.Android.cs | 3 ++- 4 files changed, 22 insertions(+), 2 deletions(-) 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 ad6d702bf6162..440211a55cfe1 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -11,6 +11,7 @@ $([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) SR.SystemNetSecurity_PlatformNotSupported $(DefineConstants);TARGET_WINDOWS + $(DefineConstants);TARGET_ANDROID true true true @@ -103,7 +104,7 @@ Link="Common\System\Net\SecurityStatusPal.cs" /> - diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 44698862727c4..060b5aebf4004 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -503,6 +503,18 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError return true; } +#if TARGET_ANDROID + // Client streams perform the verification during the handshake via Java's TrustManager callbacks + if (!_sslAuthenticationOptions.IsServer) + { + sslPolicyErrors = SslPolicyErrors.None; + chainStatus = X509ChainStatusFlags.NoError; + + _handshakeCompleted = true; + return true; + } +#endif + if (!VerifyRemoteCertificate(ref alertToken, out sslPolicyErrors, out chainStatus)) { _handshakeCompleted = false; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 0932f663d291a..050c29a2ea7ef 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -818,7 +818,13 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte } else { +#if TARGET_ANDROID + _remoteCertificateVerifier ??= new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions); +#endif status = SslStreamPal.InitializeSecurityContext( +#if TARGET_ANDROID + _remoteCertificateVerifier, +#endif ref _credentialsHandle!, ref _securityContext, _sslAuthenticationOptions.TargetHost, 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 cd2aafa88d6ba..f31cb6b12afd3 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 @@ -35,6 +35,7 @@ public static SecurityStatusPal AcceptSecurityContext( } public static SecurityStatusPal InitializeSecurityContext( + RemoteCertificateVerification verifier, ref SafeFreeCredentials credential, ref SafeDeleteSslContext? context, string? targetName, @@ -43,7 +44,7 @@ public static SecurityStatusPal InitializeSecurityContext( SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate? clientCertificateSelectionCallback) { - return HandshakeInternal(verifier: null, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(verifier, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal Renegotiate( From 6e8659fde240e1a159e3122afe8c2e4e90c0fe12 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 13:36:40 +0200 Subject: [PATCH 05/59] Update tests --- .../HttpClientHandlerTestBase.SocketsHttpHandler.cs | 7 ------- .../System.Net.Http.Functional.Tests.csproj | 2 -- .../tests/System.Net.Requests.Tests.csproj | 2 -- .../FunctionalTests/System.Net.Security.Tests.csproj | 2 -- .../tests/System.Net.WebSockets.Client.Tests.csproj | 2 -- src/tests/Directory.Build.targets | 6 +++++- .../gRPC/Android.Device_Emulator.gRPC.Test.csproj | 10 +--------- src/tests/build.proj | 2 +- 8 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs index a372c7d372dc1..602d177f5f1be 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTestBase.SocketsHttpHandler.cs @@ -37,13 +37,6 @@ protected static HttpClientHandler CreateHttpClientHandler(Version useVersion = // Browser doesn't support ServerCertificateCustomValidationCallback if (allowAllCertificates && PlatformDetection.IsNotBrowser) { - // On Android, it is not enough to set the custom validation callback, the certificates also need to be trusted by the OS. - // The public keys of our self-signed certificates that are used by the loopback server are part of the System.Net.TestData - // package and they can be included in a the Android test apk by adding the following property to the test's .csproj: - // - // true - // - handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates; } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj index 83b3bb8da2808..6192b97251aab 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/System.Net.Http.Functional.Tests.csproj @@ -7,8 +7,6 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX true true - - true true diff --git a/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj b/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj index 8e509bd356a8d..15da31062df06 100644 --- a/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj +++ b/src/libraries/System.Net.Requests/tests/System.Net.Requests.Tests.csproj @@ -7,8 +7,6 @@ true $(NoWarn);SYSLIB0014 - - true true true diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 7c3d432f34cb4..c5b626773100b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -5,8 +5,6 @@ $(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS true true - - true true true diff --git a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj index 7aff0244108ee..5bb4cb782a449 100644 --- a/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj +++ b/src/libraries/System.Net.WebSockets.Client/tests/System.Net.WebSockets.Client.Tests.csproj @@ -5,8 +5,6 @@ $(DefineConstants);NETSTANDARD false - - true diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 369abf945a373..e6d6acc9bdde0 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -169,12 +169,16 @@ + + + + @@ -297,7 +301,7 @@ Lines="NoMonoAot" Overwrite="true" WriteOnlyWhenDifferent="true" /> - + true true - + enable enable @@ -21,16 +21,8 @@ CS8981;SYSLIB0039 - - true - - - - - PreserveNewest - diff --git a/src/tests/build.proj b/src/tests/build.proj index 37236bad27175..3070d74adc62c 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -206,7 +206,7 @@ - + From c84e3557d5f882d90c0abbc8644321839ee21c1c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 15:37:46 +0200 Subject: [PATCH 06/59] Update System.Net.Http tests --- .../System/Net/Http/HttpClientHandlerTest.AcceptAllCerts.cs | 1 - .../Net/Http/HttpClientHandlerTest.ClientCertificates.cs | 2 ++ .../Net/Http/HttpClientHandlerTest.ServerCertificates.cs | 4 ++-- .../Common/tests/System/Net/Http/HttpClientHandlerTest.cs | 1 - .../SocketsHttpHandlerTest.Http2FlowControl.cs | 4 ++-- .../tests/FunctionalTests/SocketsHttpHandlerTest.cs | 3 --- 6 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AcceptAllCerts.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AcceptAllCerts.cs index cf9af00134217..b449f5025276d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AcceptAllCerts.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.AcceptAllCerts.cs @@ -96,7 +96,6 @@ await TestHelper.WhenAllCompletedOrAnyFailed( [OuterLoop] [ConditionalTheory(nameof(ClientSupportsDHECipherSuites))] [MemberData(nameof(InvalidCertificateServers))] - [SkipOnPlatform(TestPlatforms.Android, "Android rejects the certificate, the custom validation callback in .NET cannot override OS behavior in the current implementation")] public async Task InvalidCertificateServers_CertificateValidationDisabled_Succeeds(string url) { using (HttpClientHandler handler = CreateHttpClientHandler(allowAllCertificates: true)) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs index 04a7414d04695..4385cc45d19f4 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs @@ -81,6 +81,7 @@ private HttpClient CreateHttpClientWithCert(X509Certificate2 cert) [InlineData(1, true)] [InlineData(2, true)] [InlineData(3, false)] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Client certificate isn't sent public async Task Manual_CertificateOnlySentWhenValid_Success(int certIndex, bool serverExpectsClientCertificate) { // [ActiveIssue("https://github.com/dotnet/runtime/issues/69238")] @@ -132,6 +133,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( [Theory] [InlineData(6, false)] [InlineData(3, true)] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: client certificate isn't sent public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index 90de3cdee7b93..f733419bbe4cc 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -141,6 +141,7 @@ public static IEnumerable UseCallback_ValidCertificate_ExpectedValuesD [OuterLoop("Uses external servers")] [Theory] [MemberData(nameof(UseCallback_ValidCertificate_ExpectedValuesDuringCallback_Urls))] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Exception of type 'Interop+AndroidCrypto+SslException' was thrown. public async Task UseCallback_ValidCertificate_ExpectedValuesDuringCallback(Configuration.Http.RemoteServer remoteServer, Uri url, bool checkRevocation) { HttpClientHandler handler = CreateHttpClientHandler(); @@ -195,6 +196,7 @@ public async Task UseCallback_CallbackReturnsFailure_ThrowsException() [OuterLoop("Uses external servers")] [Fact] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: right now the exception can't propagate from C# -> C -> Java -> C# public async Task UseCallback_CallbackThrowsException_ExceptionPropagatesAsBaseException() { HttpClientHandler handler = CreateHttpClientHandler(); @@ -284,7 +286,6 @@ private async Task UseCallback_BadCertificate_ExpectedPolicyErrors_Helper(string [OuterLoop("Uses external servers")] [Theory] [MemberData(nameof(CertificateValidationServersAndExpectedPolicies))] - [SkipOnPlatform(TestPlatforms.Android, "Android rejects the certificate, the custom validation callback in .NET cannot override OS behavior in the current implementation")] public async Task UseCallback_BadCertificate_ExpectedPolicyErrors(string url, SslPolicyErrors expectedErrors) { const int SEC_E_BUFFER_TOO_SMALL = unchecked((int)0x80090321); @@ -308,7 +309,6 @@ public async Task UseCallback_BadCertificate_ExpectedPolicyErrors(string url, Ss } [Fact] - [SkipOnPlatform(TestPlatforms.Android, "Android rejects the certificate, the custom validation callback in .NET cannot override OS behavior in the current implementation")] public async Task UseCallback_SelfSignedCertificate_ExpectedPolicyErrors() { using (HttpClientHandler handler = CreateHttpClientHandler()) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 91ea0ae621bff..8cb4ef5fdc104 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -153,7 +153,6 @@ public void Properties_AddItemToDictionary_ItemPresent() [ConditionalFact] [SkipOnPlatform(TestPlatforms.Browser, "ServerCertificateCustomValidationCallback not supported on Browser")] - [SkipOnPlatform(TestPlatforms.Android, "IPv6 loopback with SSL doesn't work on Android")] public async Task GetAsync_IPv6LinkLocalAddressUri_Success() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs index 4862c0a4ae52c..5bbe67521932f 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs @@ -228,7 +228,7 @@ private static async Task TestClientWindowScalingAsync( bool pingReceivedAfterReachingMaxWindow = false; bool unexpectedFrameReceived = false; CancellationTokenSource stopFrameProcessingCts = new CancellationTokenSource(); - + CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(stopFrameProcessingCts.Token, timeoutCts.Token); Task processFramesTask = ProcessIncomingFramesAsync(linkedCts.Token); byte[] buffer = new byte[16384]; @@ -315,7 +315,7 @@ async Task ProcessIncomingFramesAsync(CancellationToken cancellationToken) catch (OperationCanceledException) { } - + output?.WriteLine("ProcessIncomingFramesAsync finished"); } diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs index 4e51d356fc655..96bca93ab6eb8 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs @@ -3931,7 +3931,6 @@ public abstract class SocketsHttpHandler_SecurityTest : HttpClientHandlerTestBas public SocketsHttpHandler_SecurityTest(ITestOutputHelper output) : base(output) { } [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] - [SkipOnPlatform(TestPlatforms.Android, "Self-signed certificates are rejected by Android before the .NET validation is reached")] public async Task SslOptions_CustomTrust_Ok() { X509Certificate2Collection caCerts = new X509Certificate2Collection(); @@ -3968,7 +3967,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync( } [Fact] - [SkipOnPlatform(TestPlatforms.Android, "Self-signed certificates are rejected by Android before the .NET validation is reached")] public async Task SslOptions_InvalidName_Throws() { X509Certificate2Collection caCerts = new X509Certificate2Collection(); @@ -3999,7 +3997,6 @@ await LoopbackServerFactory.CreateClientAndServerAsync( } [Fact] - [SkipOnPlatform(TestPlatforms.Android, "Self-signed certificates are rejected by Android before the .NET validation is reached")] public async Task SslOptions_CustomPolicy_IgnoresNameMismatch() { X509Certificate2Collection caCerts = new X509Certificate2Collection(); From f1d429f723bd100e7c4c6ad682ea0db9d0843d1b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 17:36:06 +0200 Subject: [PATCH 07/59] Update System.Net.Security tests --- .../FunctionalTests/CertificateValidationClientServer.cs | 1 + .../FunctionalTests/CertificateValidationRemoteServer.cs | 2 +- .../tests/FunctionalTests/SslStreamNetworkStreamTest.cs | 5 ++--- .../tests/FunctionalTests/SslStreamSniTest.cs | 4 ++-- .../tests/FunctionalTests/SslStreamSystemDefaultsTest.cs | 1 + 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 494bcf86fc226..2a384c98ef57f 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -108,6 +108,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [Theory] [InlineData(false)] [InlineData(true)] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Client certificate is not sent to the server public async Task CertificateValidationClientServer_EndToEnd_Ok(bool useClientSelectionCallback) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 0); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs index 1530d5a33b7b1..b26728b0d0c03 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs @@ -95,7 +95,7 @@ public async Task DefaultConnect_EndToEnd_Ok(string host) [Theory] [InlineData(true)] [InlineData(false)] - [SkipOnPlatform(TestPlatforms.Android, "The invalid certificate is rejected by Android and the .NET validation code isn't reached")] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Exception of type 'Interop+AndroidCrypto+SslException' was thrown. [ActiveIssue("https://github.com/dotnet/runtime/issues/70981", TestPlatforms.OSX)] public Task ConnectWithRevocation_WithCallback(bool checkRevocation) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 70777d0a447be..195d5ed04cb51 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -753,7 +753,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [Theory] [InlineData(true)] [InlineData(false)] - [SkipOnPlatform(TestPlatforms.Android, "Self-signed certificates are rejected by Android before the .NET validation is reached")] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Exception of type 'Interop+AndroidCrypto+SslException' was thrown. public async Task SslStream_UntrustedCaWithCustomTrust_OK(bool usePartialChain) { int split = Random.Shared.Next(0, _certificates.serverChain.Count - 1); @@ -810,7 +810,7 @@ public async Task SslStream_UntrustedCaWithCustomTrust_OK(bool usePartialChain) [PlatformSpecific(TestPlatforms.AnyUnix)] [InlineData(true)] [InlineData(false)] - [SkipOnPlatform(TestPlatforms.Android, "Self-signed certificates are rejected by Android before the .NET validation is reached")] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: `await t2` blocks indefinitely public async Task SslStream_UntrustedCaWithCustomCallback_Throws(bool customCallback) { string errorMessage; @@ -857,7 +857,6 @@ public async Task SslStream_UntrustedCaWithCustomCallback_Throws(bool customCall } [Fact] - [SkipOnPlatform(TestPlatforms.Android, "Self-signed certificates are rejected by Android before the .NET validation is reached")] [ActiveIssue("https://github.com/dotnet/runtime/issues/73862")] public async Task SslStream_ClientCertificate_SendsChain() { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs index 9d4aa07ff9299..acbebdbd3c762 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs @@ -18,7 +18,7 @@ public class SslStreamSniTest { [Theory] [MemberData(nameof(HostNameData))] - [SkipOnPlatform(TestPlatforms.Android, "Host name is not sent on Android")] + [SkipOnPlatform(TestPlatforms.Android, "SNI isn't set for localhost communication")] public async Task SslStream_ClientSendsSNIServerReceives_Ok(string hostName) { using X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate(); @@ -96,7 +96,7 @@ public async Task SslStream_ServerCallbackAndLocalCertificateSelectionSet_Throws [Theory] [MemberData(nameof(HostNameData))] - [SkipOnPlatform(TestPlatforms.Android, "TODO: this test would work with GetServerCertificate(). Is there something wrong with the PEMs?")] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: client certificate isn't sent to the server public async Task SslStream_ServerCallbackNotSet_UsesLocalCertificateSelection(string hostName) { using X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate(); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs index 51b28f5b26f60..7e1d8eaf61b56 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs @@ -76,6 +76,7 @@ public static IEnumerable OneOrBothUseDefaulData() [ConditionalTheory] [MemberData(nameof(OneOrBothUseDefaulData))] + [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: System.Security.Authentication.AuthenticationException : The remote certificate was rejected by the provided RemoteCertificateValidationCallback. public async Task ClientAndServer_OneOrBothUseDefault_Ok(SslProtocols? clientProtocols, SslProtocols? serverProtocols) { using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) From 3e72ff3e8519208161caf3c136cb2c4cb1cee686 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Oct 2022 18:42:39 +0200 Subject: [PATCH 08/59] Fix packaging --- eng/liveBuilds.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 3006fd92e756e..bcbb6110e3549 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -172,6 +172,7 @@ $(LibrariesNativeArtifactsPath)*.dylib; $(LibrariesNativeArtifactsPath)*.a; $(LibrariesNativeArtifactsPath)*.so; + $(LibrariesNativeArtifactsPath)*.dex; $(LibrariesNativeArtifactsPath)*.dbg; $(LibrariesNativeArtifactsPath)*.dwarf; $(LibrariesNativeArtifactsPath)*.pdb" From 04da7c9819c9654cf857168b12f11c7edefdf169 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 26 Oct 2022 14:05:35 +0200 Subject: [PATCH 09/59] Propagate caught exceptions --- .../Net/Http/HttpClientHandlerTest.ServerCertificates.cs | 1 - .../Net/Security/Pal.Android/SafeDeleteSslContext.cs | 1 + .../System/Net/Security/Pal.Android/TrustManagerProxy.cs | 4 ++++ .../src/System/Net/Security/SslStream.IO.cs | 7 +++++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index f733419bbe4cc..ffd87af5f38ae 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -196,7 +196,6 @@ public async Task UseCallback_CallbackReturnsFailure_ThrowsException() [OuterLoop("Uses external servers")] [Fact] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: right now the exception can't propagate from C# -> C -> Java -> C# public async Task UseCallback_CallbackThrowsException_ExceptionPropagatesAsBaseException() { HttpClientHandler handler = CreateHttpClientHandler(); 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 30f224f549b1b..302cd82882164 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 @@ -36,6 +36,7 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SafeSslHandle SslContext => _sslContext; + public Exception? CaughtException => _trustManagerProxy?.CaughtException; public SafeDeleteSslContext(RemoteCertificateVerification? verifier, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs index 9bc6d5e5c91ff..f7b8d30c41109 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs @@ -35,6 +35,8 @@ public IntPtr Handle ? GCHandle.ToIntPtr(handle) : throw new ObjectDisposedException(nameof(TrustManagerProxy)); + public Exception? CaughtException { get; private set; } + private static unsafe void RegisterTrustManagerCallback() { lock (s_initializationLock) @@ -72,6 +74,8 @@ private static unsafe bool VerifyRemoteCertificate( { Debug.WriteLine($"Remote certificate verification has thrown an exception: {exception}"); Debug.WriteLine(exception.StackTrace); + + proxy.CaughtException = exception; return false; } finally diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 060b5aebf4004..9e86741b7e5d6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -313,6 +313,13 @@ private async Task ForceAuthenticationAsync(bool receiveFirst, byte[ { if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, message.Status); +#if TARGET_ANDROID + if (_securityContext?.CaughtException is Exception caughtException) + { + throw new AuthenticationException(SR.net_auth_SSPI, caughtException); + } +#endif + if (_lastFrame.Header.Type == TlsContentType.Alert && _lastFrame.AlertDescription != TlsAlertDescription.CloseNotify && message.Status.ErrorCode == SecurityStatusPalErrorCode.IllegalMessage) { From 28c360b4d49b3c16c1157e25dd1c3db6e4ae5274 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 26 Oct 2022 15:44:50 +0200 Subject: [PATCH 10/59] Build and pack .jar --- eng/liveBuilds.targets | 1 + .../Directory.Build.props | 1 + src/libraries/native-binplace.proj | 1 + src/libraries/pretest.proj | 1 + src/native/libs/build-native.proj | 7 +++--- .../AndroidAppBuilder.csproj | 3 ++- ...uilderTask.cs => AndroidLibBuilderTask.cs} | 13 ++++++++-- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 2 +- src/tasks/Common/JarBuilder.cs | 25 +++++++++++++++++++ src/tests/Directory.Build.targets | 3 ++- src/tests/build.proj | 2 +- 11 files changed, 50 insertions(+), 9 deletions(-) rename src/tasks/AndroidAppBuilder/{AndroidDexBuilderTask.cs => AndroidLibBuilderTask.cs} (80%) create mode 100644 src/tasks/Common/JarBuilder.cs diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index bcbb6110e3549..78bcbf538f8f3 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -173,6 +173,7 @@ $(LibrariesNativeArtifactsPath)*.a; $(LibrariesNativeArtifactsPath)*.so; $(LibrariesNativeArtifactsPath)*.dex; + $(LibrariesNativeArtifactsPath)*.jar; $(LibrariesNativeArtifactsPath)*.dbg; $(LibrariesNativeArtifactsPath)*.dwarf; $(LibrariesNativeArtifactsPath)*.pdb" diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 00f4c1da4ea64..8670b22716cc9 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -96,6 +96,7 @@ + diff --git a/src/libraries/native-binplace.proj b/src/libraries/native-binplace.proj index 7946258980992..867fadd3c4637 100644 --- a/src/libraries/native-binplace.proj +++ b/src/libraries/native-binplace.proj @@ -23,6 +23,7 @@ + diff --git a/src/libraries/pretest.proj b/src/libraries/pretest.proj index aa5e712b1e0f8..156de2e38337a 100644 --- a/src/libraries/pretest.proj +++ b/src/libraries/pretest.proj @@ -48,6 +48,7 @@ + diff --git a/src/native/libs/build-native.proj b/src/native/libs/build-native.proj index 26f8f0fd6e9c5..7ac4b7a657e54 100644 --- a/src/native/libs/build-native.proj +++ b/src/native/libs/build-native.proj @@ -55,7 +55,7 @@ - - - + diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj index 3705b168a52ab..3dd38caa3c880 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.csproj @@ -17,9 +17,10 @@ - + + diff --git a/src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs b/src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs similarity index 80% rename from src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs rename to src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs index 24bcd41078e2d..b5c017da71e19 100644 --- a/src/tasks/AndroidAppBuilder/AndroidDexBuilderTask.cs +++ b/src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs @@ -6,7 +6,7 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; -public class AndroidDexBuilderTask : Task +public class AndroidLibBuilderTask : Task { public ITaskItem[] JavaFiles { get; set; } = Array.Empty(); @@ -16,6 +16,9 @@ public class AndroidDexBuilderTask : Task [Required] public string DexFileName { get; set; } = ""!; + [Required] + public string JarFileName { get; set; } = ""!; + public string? AndroidSdk { get; set; } public string? BuildApiLevel { get; set; } @@ -25,6 +28,9 @@ public class AndroidDexBuilderTask : Task [Output] public string DexFilePath { get; set; } = ""!; + [Output] + public string JarFilePath { get; set; } = ""!; + public override bool Execute() { var androidSdk = new AndroidSdkHelper( @@ -33,6 +39,7 @@ public override bool Execute() buildToolsVersion: BuildToolsVersion); var compiler = new JavaCompiler(Log, androidSdk, workingDir: OutputDir); + var jarBuilder = new JarBuilder(Log, workingDir: OutputDir); var dexBuilder = new DexBuilder(Log, androidSdk, workingDir: OutputDir); var objDir = "obj"; @@ -46,8 +53,10 @@ public override bool Execute() compiler.Compile(file.ItemSpec, outputDir: objDir); } - dexBuilder.Build(inputDir: objDir, outputFileName: DexFileName); + jarBuilder.Build(inputDir: objDir, outputFileName: JarFileName); + JarFilePath = Path.Combine(OutputDir, JarFileName); + dexBuilder.Build(inputDir: objDir, outputFileName: DexFileName); DexFilePath = Path.Combine(OutputDir, DexFileName); return true; } diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 7fdd6f958f10d..e38a8ac54dc3e 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -182,7 +182,7 @@ public ApkBuilder(TaskLoggingHelper logger) Directory.CreateDirectory(Path.Combine(OutputDir, "assets")); Directory.CreateDirectory(Path.Combine(OutputDir, "res")); - var extensionsToIgnore = new List { ".so", ".a", ".dex" }; + var extensionsToIgnore = new List { ".so", ".a", ".dex", ".jar" }; if (StripDebugSymbols) { extensionsToIgnore.Add(".pdb"); diff --git a/src/tasks/Common/JarBuilder.cs b/src/tasks/Common/JarBuilder.cs new file mode 100644 index 0000000000000..22fa18a0126d5 --- /dev/null +++ b/src/tasks/Common/JarBuilder.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Linq; +using Microsoft.Build.Utilities; + +internal sealed class JarBuilder +{ + private readonly string _workingDir; + private readonly TaskLoggingHelper _logger; + + public JarBuilder(TaskLoggingHelper logger, string workingDir) + { + _workingDir = workingDir; + _logger = logger; + } + + public void Build(string inputDir, string outputFileName) + { + string[] classFiles = Directory.GetFiles(Path.Combine(_workingDir, inputDir), "*.class", SearchOption.AllDirectories); + Utils.RunProcess(_logger, "jar", $"-cf {outputFileName} {string.Join(" ", classFiles)}", workingDir: _workingDir); + } +} diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index e6d6acc9bdde0..7f15281bf534d 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -169,8 +169,9 @@ - + + diff --git a/src/tests/build.proj b/src/tests/build.proj index 3070d74adc62c..e64ebd43504b7 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -206,7 +206,7 @@ - + From 220a83e10b11dcf487e843ac668fe22b0319bd56 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 27 Oct 2022 13:57:20 +0200 Subject: [PATCH 11/59] Optimize allocation and deallocation of memory for certificate data --- .../Interop.Ssl.cs | 2 +- .../Security/Pal.Android/TrustManagerProxy.cs | 9 ++-- .../pal_jni.c | 5 ++ .../pal_jni.h | 3 ++ .../pal_sslstream.c | 27 +++++----- .../pal_trust_manager.c | 49 ++++++++++++------- .../pal_trust_manager.h | 9 ++-- 7 files changed, 62 insertions(+), 42 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 cc34543708cfc..b81df7da8c80a 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 @@ -56,7 +56,7 @@ ref MemoryMarshal.GetReference(pkcs8PrivateKey), [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterTrustManagerCallback")] internal static unsafe partial void RegisterTrustManagerCallback( - delegate* unmanaged verifyRemoteCertificate); + delegate* unmanaged verifyRemoteCertificate); [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] private static unsafe partial int SSLStreamInitializeImpl( diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs index f7b8d30c41109..c89c6d01f0652 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs @@ -60,7 +60,7 @@ private static unsafe bool VerifyRemoteCertificate( IntPtr trustManagerProxyHandle, int certificatesCount, int* certificateLengths, - byte** rawCertificates) + byte* rawCertificates) { var proxy = (TrustManagerProxy?)GCHandle.FromIntPtr(trustManagerProxyHandle).Target; Debug.Assert(proxy is not null); @@ -114,13 +114,16 @@ private bool Verify(X509Certificate2[] certificates) } } - private static unsafe X509Certificate2[] ConvertCertificates(int count, int* lengths, byte** rawData) + private static unsafe X509Certificate2[] ConvertCertificates(int count, int* lengths, byte* rawData) { var certificates = new X509Certificate2[count]; + int offset = 0; for (int i = 0; i < count; i++) { - var rawCertificate = new ReadOnlySpan(rawData[i], lengths[i]); + var rawCertificate = new ReadOnlySpan(rawData, offset + lengths[i]).Slice(offset); + offset += lengths[i]; + certificates[i] = new X509Certificate2(rawCertificate); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index feaf0a054528f..541cbf7dd3397 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -6,6 +6,9 @@ JavaVM* gJvm; +// byte[] +jclass g_ByteArray; + // java/io/ByteArrayInputStream jclass g_ByteArrayInputStreamClass; jmethodID g_ByteArrayInputStreamCtor; @@ -676,6 +679,8 @@ JNI_OnLoad(JavaVM *vm, void *reserved) JNIEnv* env = GetJNIEnv(); // cache some classes and methods while we're in the thread-safe JNI_OnLoad + g_ByteArray = GetClassGRef(env, "[B"); + g_ByteArrayInputStreamClass = GetClassGRef(env, "java/io/ByteArrayInputStream"); g_ByteArrayInputStreamCtor = GetMethod(env, false, g_ByteArrayInputStreamClass, "", "([B)V"); g_ByteArrayInputStreamReset = GetMethod(env, false, g_ByteArrayInputStreamClass, "reset", "()V"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index d9af003bbbeb6..65afe35193470 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -15,6 +15,9 @@ extern JavaVM* gJvm; +// byte[] +extern jclass g_ByteArray; + // java/io/ByteArrayInputStream extern jclass g_ByteArrayInputStreamClass; extern jmethodID g_ByteArrayInputStreamCtor; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 2286647035949..1f5aadee70cd4 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -284,7 +284,7 @@ ARGS_NON_NULL_ALL static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) free(sslStream); } -static jobject GetSSLContextInstance(JNIEnv* env) +ARGS_NON_NULL_ALL static jobject GetSSLContextInstance(JNIEnv* env) { jobject sslContext = NULL; @@ -306,7 +306,7 @@ static jobject GetSSLContextInstance(JNIEnv* env) return sslContext; } -static jobject GetKeyStoreInstance(JNIEnv* env) +ARGS_NON_NULL_ALL static jobject GetKeyStoreInstance(JNIEnv* env) { jobject keyStore = NULL; jstring ksType = NULL; @@ -333,17 +333,14 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t trustManagerProxyHandle) INIT_LOCALS(loc, sslContext, keyStore, trustManagers); loc[sslContext] = GetSSLContextInstance(env); - if (loc[sslContext] == NULL) + if (!loc[sslContext]) goto cleanup; - // We only need to init the key store, we don't use it - IGNORE_RETURN(GetKeyStoreInstance(env)); - if (trustManagerProxyHandle != 0) { // Init trust managers - loc[trustManagers] = initTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); - if (loc[trustManagers] == NULL) + loc[trustManagers] = InitTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); + if (!loc[trustManagers]) goto cleanup; } @@ -437,11 +434,11 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustMan // SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); loc[sslContext] = GetSSLContextInstance(env); - if (loc[sslContext] == NULL) + if (!loc[sslContext]) goto cleanup; loc[keyStore] = GetKeyStoreInstance(env); - if (loc[keyStore] == NULL) + if (!loc[keyStore]) goto cleanup; int32_t status = @@ -465,9 +462,9 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustMan if (trustManagerProxyHandle != 0) { - // TrustManager[] trustMangers = initTrustManagersWithCustomValidatorProxy(trustManagerProxyHandle); - loc[trustManagers] = initTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); - if (loc[trustManagers] == NULL) + // TrustManager[] trustMangers = InitTrustManagersWithCustomValidatorProxy(trustManagerProxyHandle); + loc[trustManagers] = InitTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); + if (!loc[trustManagers]) goto cleanup; } @@ -797,7 +794,7 @@ int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t* return ret; } -static jobject getPeerCertificates(JNIEnv* env, SSLStream* sslStream) +ARGS_NON_NULL_ALL static jobject getPeerCertificates(JNIEnv* env, SSLStream* sslStream) { jobject certificates = NULL; jobject sslSession = NULL; @@ -863,7 +860,7 @@ void AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobj // Certificate[] certs = sslSession.getPeerCertificates(); jobjectArray certs = getPeerCertificates(env, sslStream); - if (certs == NULL) + if (!certs) goto cleanup; // for (int i = 0; i < certs.length; i++) { diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index 85cca3ebd8516..a6a9aa2adc849 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -7,7 +7,7 @@ void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidatio verifyRemoteCertificate = callback; } -jobjectArray initTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle) +jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle) { abort_unless(dotnetHandle != 0, "invalid pointer to the .NET remote certificate validator"); @@ -68,30 +68,33 @@ jobjectArray initTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot } jboolean Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustManager_verifyRemoteCertificate( - JNIEnv* env, - jobject handle, - intptr_t dotnetHandle, - jobjectArray certificates) + JNIEnv* env, jobject thisHandle, intptr_t dotnetHandle, jobjectArray certificates) { abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); - INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, trustManagerProxy, certificate, encodedCertificate); + INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, trustManagerProxy, certificate, encodedCertificate, encodedCertificates); bool isAccepted = false; - uint8_t** rawData = NULL; + uint8_t* rawData = NULL; int32_t* lengths = NULL; size_t certificateCount = (size_t)(*env)->GetArrayLength(env, certificates); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - rawData = (uint8_t**)xcalloc(certificateCount, sizeof(uint8_t*)); lengths = (int*)xcalloc(certificateCount, sizeof(int32_t)); + size_t totalLength = 0; + + loc[encodedCertificates] = make_java_object_array(env, (int32_t)certificateCount, g_ByteArray, NULL); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); for (size_t i = 0; i < certificateCount; i++) { // X509Certificate certificate = certificates[i]; // byte[] encodedCertificate = certificate.getEncoded(); // int length = encodedCertificate.length; + // lengths[i] = length; + // totalLength += length; + // encodedCertificates[i] = encodedCertificate; loc[certificate] = (*env)->GetObjectArrayElement(env, certificates, (jsize)i); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); @@ -103,8 +106,9 @@ jboolean Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustMana ON_EXCEPTION_PRINT_AND_GOTO(cleanup); lengths[i] = (int32_t)length; - rawData[i] = (uint8_t*)xmalloc((size_t)length * sizeof(uint8_t)); - (*env)->GetByteArrayRegion(env, loc[encodedCertificate], 0, length, (jbyte*)rawData[i]); + totalLength += (size_t)lengths[i]; + + (*env)->SetObjectArrayElement(env, loc[encodedCertificates], (jsize)i, loc[encodedCertificate]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); ReleaseLRef(env, loc[certificate]); @@ -113,17 +117,28 @@ jboolean Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustMana loc[encodedCertificate] = NULL; } + rawData = (uint8_t*)xmalloc(totalLength * sizeof(uint8_t)); + int32_t offset = 0; + + for (size_t i = 0; i < certificateCount; i++) + { + loc[encodedCertificate] = (*env)->GetObjectArrayElement(env, loc[encodedCertificates], (jsize)i); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + (*env)->GetByteArrayRegion(env, loc[encodedCertificate], 0, lengths[i], (jbyte*)&rawData[offset]); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + + offset += lengths[i]; + + ReleaseLRef(env, loc[encodedCertificate]); + loc[encodedCertificate] = NULL; + } + isAccepted = verifyRemoteCertificate(dotnetHandle, (int32_t)certificateCount, lengths, rawData); cleanup: if (rawData != NULL) - { - for (size_t i = 0; i < certificateCount; i++) - { - if (rawData[i] != NULL) - free(rawData[i]); - } - } + free(rawData); if (lengths != NULL) free(lengths); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h index c9897e1f69118..59954d3a10021 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -1,13 +1,10 @@ #include "pal_jni.h" -typedef bool (*RemoteCertificateValidationCallback)(intptr_t, int32_t, int32_t*, uint8_t**); +typedef bool (*RemoteCertificateValidationCallback)(intptr_t, int32_t, int32_t*, uint8_t*); PALEXPORT void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback); -jobjectArray initTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle); +jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle); JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustManager_verifyRemoteCertificate( - JNIEnv *env, - jobject handle, - intptr_t dotnetHandle, - jobjectArray certificates); + JNIEnv *env, jobject thisHandle, intptr_t dotnetHandle, jobjectArray certificates); From e42a67defbe8d211918a47f023b4566d1d6e3ca8 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 3 Nov 2022 14:25:30 +0100 Subject: [PATCH 12/59] Fix building .jar --- ...tificateVerificationProxyTrustManager.java | 0 src/native/libs/build-native.proj | 11 +--- .../AndroidLibBuilderTask.cs | 60 +++++++++++-------- src/tasks/Common/DexBuilder.cs | 15 ++--- src/tasks/Common/JarBuilder.cs | 12 ++-- 5 files changed, 51 insertions(+), 47 deletions(-) rename src/native/libs/System.Security.Cryptography.Native.Android/{ => net/dot/android/crypto}/RemoteCertificateVerificationProxyTrustManager.java (100%) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager.java similarity index 100% rename from src/native/libs/System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java rename to src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager.java diff --git a/src/native/libs/build-native.proj b/src/native/libs/build-native.proj index 7ac4b7a657e54..10062c38d2af3 100644 --- a/src/native/libs/build-native.proj +++ b/src/native/libs/build-native.proj @@ -60,18 +60,11 @@ - - <_JavaFile Include="$(MSBuildThisFileDirectory)System.Security.Cryptography.Native.Android/RemoteCertificateVerificationProxyTrustManager.java" /> - - - + OutputDir="$(ArtifactsBinDir)native/$(_BuildNativeOutConfig)"> - - diff --git a/src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs b/src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs index b5c017da71e19..81d9062c6f0ea 100644 --- a/src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs +++ b/src/tasks/AndroidAppBuilder/AndroidLibBuilderTask.cs @@ -8,7 +8,8 @@ public class AndroidLibBuilderTask : Task { - public ITaskItem[] JavaFiles { get; set; } = Array.Empty(); + [Required] + public string JavaSourceDirectory { get; set; } = ""!; [Required] public string OutputDir { get; set; } = ""!; @@ -25,12 +26,6 @@ public class AndroidLibBuilderTask : Task public string? BuildToolsVersion { get; set; } - [Output] - public string DexFilePath { get; set; } = ""!; - - [Output] - public string JarFilePath { get; set; } = ""!; - public override bool Execute() { var androidSdk = new AndroidSdkHelper( @@ -38,31 +33,48 @@ public override bool Execute() buildApiLevel: BuildApiLevel, buildToolsVersion: BuildToolsVersion); - var compiler = new JavaCompiler(Log, androidSdk, workingDir: OutputDir); - var jarBuilder = new JarBuilder(Log, workingDir: OutputDir); - var dexBuilder = new DexBuilder(Log, androidSdk, workingDir: OutputDir); - - var objDir = "obj"; - var objPath = Path.Combine(OutputDir, objDir); - Directory.CreateDirectory(objPath); + var objDir = Path.Combine(OutputDir, "obj"); + Directory.CreateDirectory(objDir); try { - foreach (var file in JavaFiles) - { - compiler.Compile(file.ItemSpec, outputDir: objDir); - } + CompileJava(objDir, androidSdk); + BuildJar(objDir); + BuildDex(objDir, androidSdk); - jarBuilder.Build(inputDir: objDir, outputFileName: JarFileName); - JarFilePath = Path.Combine(OutputDir, JarFileName); - - dexBuilder.Build(inputDir: objDir, outputFileName: DexFileName); - DexFilePath = Path.Combine(OutputDir, DexFileName); return true; } finally { - Directory.Delete(objPath, recursive: true); + Directory.Delete(objDir, recursive: true); + } + } + + private void CompileJava(string objDir, AndroidSdkHelper androidSdk) + { + var compiler = new JavaCompiler(Log, androidSdk, workingDir: JavaSourceDirectory); + string[] javaFiles = Directory.GetFiles(JavaSourceDirectory, "*.java", SearchOption.AllDirectories); + foreach (var file in javaFiles) + { + compiler.Compile(file, outputDir: objDir); } } + + private void BuildJar(string objDir) + { + var jarBuilder = new JarBuilder(Log); + var jarFilePath = Path.Combine(OutputDir, JarFileName); + jarBuilder.Build(inputDir: objDir, outputFileName: jarFilePath); + + Log.LogMessage(MessageImportance.High, $"Built {jarFilePath}"); + } + + private void BuildDex(string objDir, AndroidSdkHelper androidSdk) + { + var dexBuilder = new DexBuilder(Log, androidSdk, workingDir: OutputDir); + var dexFilePath = Path.Combine(OutputDir, DexFileName); + dexBuilder.Build(inputDir: objDir, outputFileName: dexFilePath); + + Log.LogMessage(MessageImportance.High, $"Built {dexFilePath}"); + } } diff --git a/src/tasks/Common/DexBuilder.cs b/src/tasks/Common/DexBuilder.cs index e7076d3d8c829..a98da4e218e93 100644 --- a/src/tasks/Common/DexBuilder.cs +++ b/src/tasks/Common/DexBuilder.cs @@ -34,22 +34,19 @@ public void Build(string inputDir, string outputFileName) } } - private void BuildUsingD8(string inputDir, string outputFileName) + private void BuildUsingD8(string inputDir, string outputFilePath) { - string[] classFiles = Directory.GetFiles(Path.Combine(_workingDir, inputDir), "*.class", SearchOption.AllDirectories); + string[] classFiles = Directory.GetFiles(inputDir, "*.class", SearchOption.AllDirectories); if (!classFiles.Any()) throw new InvalidOperationException("Didn't find any .class files"); Utils.RunProcess(_logger, _androidSdk.D8Path, $"--no-desugaring {string.Join(" ", classFiles)}", workingDir: _workingDir); - if (outputFileName != "classes.dex") - { - File.Move( - sourceFileName: Path.Combine(_workingDir, "classes.dex"), - destFileName: Path.Combine(_workingDir, outputFileName), - overwrite: true); - } + File.Move( + sourceFileName: Path.Combine(_workingDir, "classes.dex"), + destFileName: outputFilePath, + overwrite: true); } private void BuildUsingDx(string inputDir, string outputFileName) diff --git a/src/tasks/Common/JarBuilder.cs b/src/tasks/Common/JarBuilder.cs index 22fa18a0126d5..f5e2a03215b3f 100644 --- a/src/tasks/Common/JarBuilder.cs +++ b/src/tasks/Common/JarBuilder.cs @@ -4,22 +4,24 @@ using System; using System.IO; using System.Linq; +using System.Collections.Generic; using Microsoft.Build.Utilities; internal sealed class JarBuilder { - private readonly string _workingDir; private readonly TaskLoggingHelper _logger; - public JarBuilder(TaskLoggingHelper logger, string workingDir) + public JarBuilder(TaskLoggingHelper logger) { - _workingDir = workingDir; _logger = logger; } public void Build(string inputDir, string outputFileName) { - string[] classFiles = Directory.GetFiles(Path.Combine(_workingDir, inputDir), "*.class", SearchOption.AllDirectories); - Utils.RunProcess(_logger, "jar", $"-cf {outputFileName} {string.Join(" ", classFiles)}", workingDir: _workingDir); + IEnumerable classFiles = + Directory.GetFiles(inputDir, "*.class", SearchOption.AllDirectories) + .Select(classFile => Path.GetRelativePath(inputDir, classFile)); + + Utils.RunProcess(_logger, "jar", $"-cf {outputFileName} {string.Join(" ", classFiles)}", workingDir: inputDir); } } From 476bd8402670483e5af00f2e4c4baa121879ed95 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 12:35:38 +0100 Subject: [PATCH 13/59] Cleanup --- .../src/System/Net/Security/SslStream.Protocol.cs | 10 +++------- .../pal_trust_manager.c | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 050c29a2ea7ef..3ff704881007a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -19,7 +19,6 @@ public partial class SslStream { private SafeFreeCredentials? _credentialsHandle; private SafeDeleteSslContext? _securityContext; - private RemoteCertificateVerification? _remoteCertificateVerifier; private SslConnectionInfo _connectionInfo; private X509Certificate? _selectedClientCertificate; @@ -818,12 +817,9 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte } else { -#if TARGET_ANDROID - _remoteCertificateVerifier ??= new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions); -#endif status = SslStreamPal.InitializeSecurityContext( #if TARGET_ANDROID - _remoteCertificateVerifier, + verifier: new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions), #endif ref _credentialsHandle!, ref _securityContext, @@ -980,9 +976,9 @@ internal bool VerifyRemoteCertificate(ref ProtocolToken? alertToken, out SslPoli } _remoteCertificate = certificate; - _remoteCertificateVerifier ??= new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions); - success = _remoteCertificateVerifier.Verify(_remoteCertificate, _securityContext!, _sslAuthenticationOptions.CertificateContext?.Trust, ref chain, out sslPolicyErrors, out X509ChainStatus[] chainStatus); + var remoteCertificateVerifier = new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions); + success = remoteCertificateVerifier.Verify(_remoteCertificate, _securityContext!, _sslAuthenticationOptions.CertificateContext?.Trust, ref chain, out sslPolicyErrors, out X509ChainStatus[] chainStatus); if (!success) { alertToken = CreateFatalHandshakeAlertToken(sslPolicyErrors, chainStatus); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index a6a9aa2adc849..f481df0d63dfd 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -33,7 +33,7 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot // boolean foundAndReplaced = false; // for (int i = 0; i < trustManagers.length; i++) { // if (trustManagers[i] instanceof X509TrustManager) { - // trustManagers[i] = new RemoteCertificateVerificationProxyTrustManager(dotnetHandle, trustManagers[i]); + // trustManagers[i] = new RemoteCertificateVerificationProxyTrustManager(dotnetHandle); // foundAndReplaced = true; // break; // } From f3fb8c57964ca6040a7938040555ed97ed330682 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 13:24:10 +0100 Subject: [PATCH 14/59] Remove complicated certificate copying --- .../Interop.Ssl.cs | 2 +- .../Security/Pal.Android/TrustManagerProxy.cs | 17 ++--- .../pal_trust_manager.c | 76 ++----------------- .../pal_trust_manager.h | 2 +- 4 files changed, 14 insertions(+), 83 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 b81df7da8c80a..481fad1b4d770 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 @@ -56,7 +56,7 @@ ref MemoryMarshal.GetReference(pkcs8PrivateKey), [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterTrustManagerCallback")] internal static unsafe partial void RegisterTrustManagerCallback( - delegate* unmanaged verifyRemoteCertificate); + delegate* unmanaged verifyRemoteCertificate); [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] private static unsafe partial int SSLStreamInitializeImpl( diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs index c89c6d01f0652..76a572cdd4bb1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs @@ -58,14 +58,13 @@ public void Dispose() [UnmanagedCallersOnly] private static unsafe bool VerifyRemoteCertificate( IntPtr trustManagerProxyHandle, - int certificatesCount, - int* certificateLengths, - byte* rawCertificates) + int certificateCount, + IntPtr* certificatePtrs) { var proxy = (TrustManagerProxy?)GCHandle.FromIntPtr(trustManagerProxyHandle).Target; Debug.Assert(proxy is not null); - X509Certificate2[] certificates = ConvertCertificates(certificatesCount, certificateLengths, rawCertificates); + X509Certificate2[] certificates = ConvertCertificates(certificateCount, certificatePtrs); try { return proxy.Verify(certificates); @@ -114,17 +113,13 @@ private bool Verify(X509Certificate2[] certificates) } } - private static unsafe X509Certificate2[] ConvertCertificates(int count, int* lengths, byte* rawData) + private static unsafe X509Certificate2[] ConvertCertificates(int count, IntPtr* certificatePtrs) { var certificates = new X509Certificate2[count]; - - int offset = 0; for (int i = 0; i < count; i++) { - var rawCertificate = new ReadOnlySpan(rawData, offset + lengths[i]).Slice(offset); - offset += lengths[i]; - - certificates[i] = new X509Certificate2(rawCertificate); + using var handle = new SafeX509Handle(certificatePtrs[i]); + certificates[i] = new X509Certificate2(handle.DangerousGetHandle()); } return certificates; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index f481df0d63dfd..5a6d982e48b65 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -72,78 +72,14 @@ jboolean Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustMana { abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); - INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, trustManagerProxy, certificate, encodedCertificate, encodedCertificates); + size_t count = (size_t)(*env)->GetArrayLength(env, certificates); + jobject* ptrs = xcalloc(count, sizeof(jobject)); - bool isAccepted = false; - uint8_t* rawData = NULL; - int32_t* lengths = NULL; + for (size_t i = 0; i < count; i++) + ptrs[i] = ToGRef(env, (*env)->GetObjectArrayElement(env, certificates, (jsize)i)); - size_t certificateCount = (size_t)(*env)->GetArrayLength(env, certificates); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - lengths = (int*)xcalloc(certificateCount, sizeof(int32_t)); - size_t totalLength = 0; - - loc[encodedCertificates] = make_java_object_array(env, (int32_t)certificateCount, g_ByteArray, NULL); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - for (size_t i = 0; i < certificateCount; i++) - { - // X509Certificate certificate = certificates[i]; - // byte[] encodedCertificate = certificate.getEncoded(); - // int length = encodedCertificate.length; - // lengths[i] = length; - // totalLength += length; - // encodedCertificates[i] = encodedCertificate; - - loc[certificate] = (*env)->GetObjectArrayElement(env, certificates, (jsize)i); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - loc[encodedCertificate] = (*env)->CallObjectMethod(env, loc[certificate], g_CertificateGetEncoded); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - jsize length = (*env)->GetArrayLength(env, loc[encodedCertificate]); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - lengths[i] = (int32_t)length; - totalLength += (size_t)lengths[i]; - - (*env)->SetObjectArrayElement(env, loc[encodedCertificates], (jsize)i, loc[encodedCertificate]); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - ReleaseLRef(env, loc[certificate]); - ReleaseLRef(env, loc[encodedCertificate]); - loc[certificate] = NULL; - loc[encodedCertificate] = NULL; - } - - rawData = (uint8_t*)xmalloc(totalLength * sizeof(uint8_t)); - int32_t offset = 0; - - for (size_t i = 0; i < certificateCount; i++) - { - loc[encodedCertificate] = (*env)->GetObjectArrayElement(env, loc[encodedCertificates], (jsize)i); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - (*env)->GetByteArrayRegion(env, loc[encodedCertificate], 0, lengths[i], (jbyte*)&rawData[offset]); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - offset += lengths[i]; - - ReleaseLRef(env, loc[encodedCertificate]); - loc[encodedCertificate] = NULL; - } - - isAccepted = verifyRemoteCertificate(dotnetHandle, (int32_t)certificateCount, lengths, rawData); - -cleanup: - if (rawData != NULL) - free(rawData); - - if (lengths != NULL) - free(lengths); - - RELEASE_LOCALS(loc, env); + bool isAccepted = verifyRemoteCertificate(dotnetHandle, (int32_t)count, ptrs); + free(ptrs); return isAccepted ? JNI_TRUE : JNI_FALSE; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h index 59954d3a10021..a17584797fed3 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -1,6 +1,6 @@ #include "pal_jni.h" -typedef bool (*RemoteCertificateValidationCallback)(intptr_t, int32_t, int32_t*, uint8_t*); +typedef bool (*RemoteCertificateValidationCallback)(intptr_t, int32_t, jobject*); PALEXPORT void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback); From f579d386df53916bb607f8f8128f569ecbb5f8e3 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 13:32:22 +0100 Subject: [PATCH 15/59] Remove unnecessary JNI classes and methods --- .../pal_jni.c | 12 ------------ .../pal_jni.h | 7 ------- 2 files changed, 19 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index 541cbf7dd3397..cda5c04e28608 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -6,9 +6,6 @@ JavaVM* gJvm; -// byte[] -jclass g_ByteArray; - // java/io/ByteArrayInputStream jclass g_ByteArrayInputStreamClass; jmethodID g_ByteArrayInputStreamCtor; @@ -483,10 +480,6 @@ jmethodID g_TrustManagerFactoryGetTrustManagers; // javax/net/ssl/X509TrustManager jclass g_X509TrustManager; -// java/security/cert/Certificate -jclass g_Certificate; -jmethodID g_CertificateGetEncoded; - // net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager jclass g_RemoteCertificateVerificationProxyTrustManager; jmethodID g_RemoteCertificateVerificationProxyTrustManagerCtor; @@ -679,8 +672,6 @@ JNI_OnLoad(JavaVM *vm, void *reserved) JNIEnv* env = GetJNIEnv(); // cache some classes and methods while we're in the thread-safe JNI_OnLoad - g_ByteArray = GetClassGRef(env, "[B"); - g_ByteArrayInputStreamClass = GetClassGRef(env, "java/io/ByteArrayInputStream"); g_ByteArrayInputStreamCtor = GetMethod(env, false, g_ByteArrayInputStreamClass, "", "([B)V"); g_ByteArrayInputStreamReset = GetMethod(env, false, g_ByteArrayInputStreamClass, "reset", "()V"); @@ -1079,9 +1070,6 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_X509TrustManager = GetClassGRef(env, "javax/net/ssl/X509TrustManager"); - g_Certificate = GetClassGRef(env, "java/security/cert/Certificate"); - g_CertificateGetEncoded = GetMethod(env, false, g_Certificate, "getEncoded", "()[B"); - g_RemoteCertificateVerificationProxyTrustManager = GetClassGRef(env, "net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager"); g_RemoteCertificateVerificationProxyTrustManagerCtor = GetMethod(env, false, g_RemoteCertificateVerificationProxyTrustManager, "", "(I)V"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index 65afe35193470..ba77b86e41230 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -15,9 +15,6 @@ extern JavaVM* gJvm; -// byte[] -extern jclass g_ByteArray; - // java/io/ByteArrayInputStream extern jclass g_ByteArrayInputStreamClass; extern jmethodID g_ByteArrayInputStreamCtor; @@ -498,10 +495,6 @@ extern jmethodID g_TrustManagerFactoryGetTrustManagers; // javax/net/ssl/X509TrustManager extern jclass g_X509TrustManager; -// java/security/cert/Certificate -extern jclass g_Certificate; -extern jmethodID g_CertificateGetEncoded; - // net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager extern jclass g_RemoteCertificateVerificationProxyTrustManager; extern jmethodID g_RemoteCertificateVerificationProxyTrustManagerCtor; From e68908b3e16d75e394d79d97aec2575395ce47de Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 17:28:47 +0100 Subject: [PATCH 16/59] Simplify and fix the core implementation --- .../Interop.Ssl.cs | 2 +- ...ttpClientHandlerTest.ClientCertificates.cs | 11 +- ...ttpClientHandlerTest.ServerCertificates.cs | 1 - .../src/System.Net.Security.csproj | 3 +- .../Pal.Android/SafeDeleteSslContext.cs | 17 +- .../Security/Pal.Android/TrustManagerProxy.cs | 128 ------------- .../Security/RemoteCertificateVerification.cs | 169 ------------------ .../System/Net/Security/SslStream.Android.cs | 80 +++++++++ .../src/System/Net/Security/SslStream.IO.cs | 21 +-- .../System/Net/Security/SslStream.Protocol.cs | 156 ++++++++++++++-- .../Net/Security/SslStreamPal.Android.cs | 11 +- ...ager.java => DotnetProxyTrustManager.java} | 11 +- .../pal_jni.c | 10 +- .../pal_jni.h | 6 +- .../pal_trust_manager.c | 24 +-- .../pal_trust_manager.h | 6 +- 16 files changed, 276 insertions(+), 380 deletions(-) delete mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs delete mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs create mode 100644 src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs rename src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/{RemoteCertificateVerificationProxyTrustManager.java => DotnetProxyTrustManager.java} (75%) 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 481fad1b4d770..86ecfa6873174 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 @@ -56,7 +56,7 @@ ref MemoryMarshal.GetReference(pkcs8PrivateKey), [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterTrustManagerCallback")] internal static unsafe partial void RegisterTrustManagerCallback( - delegate* unmanaged verifyRemoteCertificate); + delegate* unmanaged verifyRemoteCertificate); [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] private static unsafe partial int SSLStreamInitializeImpl( diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs index 4385cc45d19f4..769e76a6402cc 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs @@ -81,7 +81,6 @@ private HttpClient CreateHttpClientWithCert(X509Certificate2 cert) [InlineData(1, true)] [InlineData(2, true)] [InlineData(3, false)] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Client certificate isn't sent public async Task Manual_CertificateOnlySentWhenValid_Success(int certIndex, bool serverExpectsClientCertificate) { // [ActiveIssue("https://github.com/dotnet/runtime/issues/69238")] @@ -114,9 +113,12 @@ await TestHelper.WhenAllCompletedOrAnyFailed( SslStream sslStream = Assert.IsType(connection.Stream); if (serverExpectsClientCertificate) { - _output.WriteLine( - "Client cert: {0}", - new X509Certificate2(sslStream.RemoteCertificate.Export(X509ContentType.Cert)).GetNameInfo(X509NameType.SimpleName, false)); + if (sslStream.RemoteCertificate is not null) { + _output.WriteLine( + "Client cert: {0}", + new X509Certificate2(sslStream.RemoteCertificate.Export(X509ContentType.Cert)).GetNameInfo(X509NameType.SimpleName, false)); + } + Assert.Equal(cert, sslStream.RemoteCertificate); } else @@ -133,7 +135,6 @@ await TestHelper.WhenAllCompletedOrAnyFailed( [Theory] [InlineData(6, false)] [InlineData(3, true)] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: client certificate isn't sent public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index ffd87af5f38ae..7efd4ed9d5ea2 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -141,7 +141,6 @@ public static IEnumerable UseCallback_ValidCertificate_ExpectedValuesD [OuterLoop("Uses external servers")] [Theory] [MemberData(nameof(UseCallback_ValidCertificate_ExpectedValuesDuringCallback_Urls))] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Exception of type 'Interop+AndroidCrypto+SslException' was thrown. public async Task UseCallback_ValidCertificate_ExpectedValuesDuringCallback(Configuration.Http.RemoteServer remoteServer, Uri url, bool checkRevocation) { HttpClientHandler handler = CreateHttpClientHandler(); 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 440211a55cfe1..15c3a558f38da 100644 --- a/src/libraries/System.Net.Security/src/System.Net.Security.csproj +++ b/src/libraries/System.Net.Security/src/System.Net.Security.csproj @@ -42,7 +42,6 @@ - @@ -382,9 +381,9 @@ Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\Interop.X509.cs" /> - + 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 302cd82882164..929afac6039ba 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 @@ -30,25 +30,21 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private static readonly Lazy s_supportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); private readonly SafeSslHandle _sslContext; - private readonly TrustManagerProxy? _trustManagerProxy; + private readonly SslStream.JavaProxy? _sslStreamProxy; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SafeSslHandle SslContext => _sslContext; - public Exception? CaughtException => _trustManagerProxy?.CaughtException; + public Exception? CaughtException => _sslStreamProxy?.CaughtException; - public SafeDeleteSslContext(RemoteCertificateVerification? verifier, SslAuthenticationOptions authOptions) + public SafeDeleteSslContext(SslStream.JavaProxy? sslStreamProxy, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) { - if (verifier is not null) - { - _trustManagerProxy = new TrustManagerProxy(verifier, securityContext: this); - } - + _sslStreamProxy = sslStreamProxy; try { - _sslContext = CreateSslContext(_trustManagerProxy?.Handle, authOptions); + _sslContext = CreateSslContext(_sslStreamProxy?.Handle, authOptions); InitializeSslContext(_sslContext, authOptions); } catch (Exception ex) @@ -73,7 +69,8 @@ protected override void Dispose(bool disposing) sslContext.Dispose(); } - _trustManagerProxy?.Dispose(); + + _sslStreamProxy?.Dispose(); } base.Dispose(disposing); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs b/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs deleted file mode 100644 index 76a572cdd4bb1..0000000000000 --- a/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Android/TrustManagerProxy.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Diagnostics; -using System.Net.Security; -using System.Threading; -using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; - -namespace System.Net -{ - internal sealed class TrustManagerProxy : IDisposable - { - private static object s_initializationLock = new(); - private static bool s_initialized; - - private readonly RemoteCertificateVerification _remoteCertificateVerifier; - private readonly SafeDeleteSslContext _securityContext; - private GCHandle? _handle; - - public unsafe TrustManagerProxy( - RemoteCertificateVerification remoteCertificateVerifier, - SafeDeleteSslContext securityContext) - { - RegisterTrustManagerCallback(); - - _remoteCertificateVerifier = remoteCertificateVerifier; - _securityContext = securityContext; - _handle = GCHandle.Alloc(this); - } - - public IntPtr Handle - => _handle is GCHandle handle - ? GCHandle.ToIntPtr(handle) - : throw new ObjectDisposedException(nameof(TrustManagerProxy)); - - public Exception? CaughtException { get; private set; } - - private static unsafe void RegisterTrustManagerCallback() - { - lock (s_initializationLock) - { - if (!s_initialized) - { - Interop.AndroidCrypto.RegisterTrustManagerCallback(&VerifyRemoteCertificate); - s_initialized = true; - } - } - } - - public void Dispose() - { - _handle?.Free(); - _handle = null; - } - - [UnmanagedCallersOnly] - private static unsafe bool VerifyRemoteCertificate( - IntPtr trustManagerProxyHandle, - int certificateCount, - IntPtr* certificatePtrs) - { - var proxy = (TrustManagerProxy?)GCHandle.FromIntPtr(trustManagerProxyHandle).Target; - Debug.Assert(proxy is not null); - - X509Certificate2[] certificates = ConvertCertificates(certificateCount, certificatePtrs); - try - { - return proxy.Verify(certificates); - } - catch (Exception exception) - { - Debug.WriteLine($"Remote certificate verification has thrown an exception: {exception}"); - Debug.WriteLine(exception.StackTrace); - - proxy.CaughtException = exception; - return false; - } - finally - { - foreach (var certificate in certificates) - certificate.Dispose(); - } - } - - private bool Verify(X509Certificate2[] certificates) - { - X509Certificate2? certificate = certificates.Length > 0 ? certificates[0] : null; - X509Chain? chain = null; - if (certificates.Length > 1) - { - chain = new X509Chain(); - chain.ChainPolicy.ExtraStore.AddRange(certificates[1..]); - } - - try - { - return _remoteCertificateVerifier.Verify(certificate, _securityContext, trust: null, ref chain, out _, out _); - } - finally - { - if (chain != null) - { - int elementsCount = chain.ChainElements.Count; - for (int i = 0; i < elementsCount; i++) - { - chain.ChainElements[i].Certificate.Dispose(); - } - - chain.Dispose(); - } - } - } - - private static unsafe X509Certificate2[] ConvertCertificates(int count, IntPtr* certificatePtrs) - { - var certificates = new X509Certificate2[count]; - for (int i = 0; i < count; i++) - { - using var handle = new SafeX509Handle(certificatePtrs[i]); - certificates[i] = new X509Certificate2(handle.DangerousGetHandle()); - } - - return certificates; - } - } -} diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs b/src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs deleted file mode 100644 index 03497bc5f7af6..0000000000000 --- a/src/libraries/System.Net.Security/src/System/Net/Security/RemoteCertificateVerification.cs +++ /dev/null @@ -1,169 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Net; -using System.Net.Security; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; - -namespace System.Net.Security -{ - internal sealed class RemoteCertificateVerification - { - private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); - private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); - - private readonly SslStream _sslStream; - private readonly SslAuthenticationOptions _sslAuthenticationOptions; - - public RemoteCertificateVerification(SslStream sslStream, SslAuthenticationOptions sslAuthenticationOptions) - { - _sslStream = sslStream; - _sslAuthenticationOptions = sslAuthenticationOptions; - } - - internal bool Verify( - X509Certificate2? remoteCertificate, - SafeDeleteSslContext securityContext, - SslCertificateTrust? trust, - ref X509Chain? chain, - out SslPolicyErrors sslPolicyErrors, - out X509ChainStatus[] chainStatus) - { - bool success = false; - sslPolicyErrors = SslPolicyErrors.None; - chainStatus = Array.Empty(); - - if (remoteCertificate == null) - { - if (NetEventSource.Log.IsEnabled() && _sslAuthenticationOptions.RemoteCertRequired) - NetEventSource.Error(_sslStream, $"Remote certificate required, but no remote certificate received"); - - sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; - } - else - { - chain ??= new X509Chain(); - - if (_sslAuthenticationOptions.CertificateChainPolicy != null) - { - chain.ChainPolicy = _sslAuthenticationOptions.CertificateChainPolicy; - } - else - { - chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode; - chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; - - if (trust != null) - { - chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; - if (trust._store != null) - { - chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates); - } - if (trust._trustList != null) - { - chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList); - } - } - } - - // set ApplicationPolicy unless already provided. - if (chain.ChainPolicy.ApplicationPolicy.Count == 0) - { - // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). - chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid); - } - - sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties( - securityContext, - chain, - remoteCertificate, - _sslAuthenticationOptions.CheckCertName, - _sslAuthenticationOptions.IsServer, - _sslAuthenticationOptions.TargetHost); - } - - var remoteCertValidationCallback = _sslAuthenticationOptions.CertValidationDelegate; - if (remoteCertValidationCallback != null) - { - // the validation callback has already been called by the trust manager - success = remoteCertValidationCallback(_sslStream, remoteCertificate, chain, sslPolicyErrors); - } - else - { - if (!_sslAuthenticationOptions.RemoteCertRequired) - { - sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable; - } - - success = (sslPolicyErrors == SslPolicyErrors.None); - } - - LogCertificateValidationResult(remoteCertificate, chain, success, sslPolicyErrors, remoteCertValidationCallback); - - if (!success && chain != null) - { - chainStatus = chain.ChainStatus; - } - - return success; - } - - private void LogCertificateValidationResult( - X509Certificate2? remoteCertificate, - X509Chain? chain, - bool success, - SslPolicyErrors sslPolicyErrors, - RemoteCertificateValidationCallback? remoteCertValidationCallback) - { - if (!NetEventSource.Log.IsEnabled()) - return; - - if (sslPolicyErrors != SslPolicyErrors.None) - { - NetEventSource.Log.RemoteCertificateError(_sslStream, SR.net_log_remote_cert_has_errors); - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) - { - NetEventSource.Log.RemoteCertificateError(_sslStream, SR.net_log_remote_cert_not_available); - } - - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) - { - NetEventSource.Log.RemoteCertificateError(_sslStream, SR.net_log_remote_cert_name_mismatch); - } - - if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 && chain is not null) - { - string chainStatusString = "ChainStatus: "; - foreach (X509ChainStatus chainStatus in chain.ChainStatus) - { - chainStatusString += "\t" + chainStatus.StatusInformation; - } - NetEventSource.Log.RemoteCertificateError(_sslStream, chainStatusString); - } - } - - if (success) - { - if (remoteCertValidationCallback != null) - { - NetEventSource.Log.RemoteCertDeclaredValid(_sslStream); - } - else - { - NetEventSource.Log.RemoteCertHasNoErrors(_sslStream); - } - } - else - { - if (remoteCertValidationCallback != null) - { - NetEventSource.Log.RemoteCertUserDeclaredInvalid(_sslStream); - } - } - - NetEventSource.Info(_sslStream, $"Cert validation, remote cert = {remoteCertificate}"); - } - } -} diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs new file mode 100644 index 0000000000000..faaa5aa7a9f33 --- /dev/null +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -0,0 +1,80 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Net.Security; +using System.Threading; +using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; + +namespace System.Net.Security +{ + public partial class SslStream + { + internal sealed class JavaProxy : IDisposable + { + private static object s_initializationLock = new(); + private static bool s_initialized; + + private readonly SslStream _sslStream; + private GCHandle? _handle; + + public unsafe JavaProxy(SslStream sslStream) + { + RegisterTrustManagerCallback(); + + _sslStream = sslStream; + _handle = GCHandle.Alloc(this); + } + + public IntPtr Handle + => _handle is GCHandle handle + ? GCHandle.ToIntPtr(handle) + : throw new ObjectDisposedException(nameof(JavaProxy)); + + public Exception? CaughtException { get; private set; } + + private static unsafe void RegisterTrustManagerCallback() + { + lock (s_initializationLock) + { + if (!s_initialized) + { + Interop.AndroidCrypto.RegisterTrustManagerCallback(&VerifyRemoteCertificate); + s_initialized = true; + } + } + } + + public void Dispose() + { + _handle?.Free(); + _handle = null; + } + + [UnmanagedCallersOnly] + private static unsafe bool VerifyRemoteCertificate(IntPtr sslStreamProxyHandle) + { + var proxy = (JavaProxy?)GCHandle.FromIntPtr(sslStreamProxyHandle).Target; + Debug.Assert(proxy is not null); + + try + { + SslStream sslStream = proxy._sslStream; + SslAuthenticationOptions options = proxy._sslStream._sslAuthenticationOptions; + ProtocolToken? alertToken = null; + return sslStream.VerifyRemoteCertificate(options.CertValidationDelegate, options.CertificateContext?.Trust, ref alertToken, out _, out _); + } + catch (Exception exception) + { + Debug.WriteLine($"Remote certificate verification has thrown an exception: {exception}"); + Debug.WriteLine(exception.StackTrace); + + proxy.CaughtException = exception; + return false; + } + } + } + } +} diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 9e86741b7e5d6..00c3f5c4b1705 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -510,24 +510,17 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError return true; } -#if TARGET_ANDROID - // Client streams perform the verification during the handshake via Java's TrustManager callbacks - if (!_sslAuthenticationOptions.IsServer) - { - sslPolicyErrors = SslPolicyErrors.None; - chainStatus = X509ChainStatusFlags.NoError; - - _handshakeCompleted = true; - return true; - } -#endif - - if (!VerifyRemoteCertificate(ref alertToken, out sslPolicyErrors, out chainStatus)) +#if !TARGET_ANDROID + if (!VerifyRemoteCertificate(_sslAuthenticationOptions.CertValidationDelegate, _sslAuthenticationOptions.CertificateContext?.Trust, ref alertToken, out sslPolicyErrors, out chainStatus)) { _handshakeCompleted = false; return false; } - +#else + // on Android, the verification has already been called from Java's TrustManager callbacks + sslPolicyErrors = SslPolicyErrors.None; + chainStatus = X509ChainStatusFlags.NoError; +#endif _handshakeCompleted = true; return true; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 719a36ab4df38..df8a340705189 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -32,6 +32,9 @@ public partial class SslStream private bool _refreshCredentialNeeded = true; + private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); + private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); + // // Protocol properties // @@ -808,7 +811,18 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte if (_sslAuthenticationOptions.IsServer) { +// #if TARGET_ANDROID +// // TODO remove +// _sslAuthenticationOptions.CertValidationDelegate = (sender, cert, chain, errors) => { +// Console.WriteLine($"server's CertValidationDelegate: {errors}, {cert}"); +// if (errors == SslPolicyErrors.RemoteCertificateNotAvailable) return false; +// else return true; +// }; +// #endif status = SslStreamPal.AcceptSecurityContext( +#if TARGET_ANDROID + new SslStream.JavaProxy(this), +#endif ref _credentialsHandle!, ref _securityContext, inputBuffer, @@ -819,7 +833,7 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte { status = SslStreamPal.InitializeSecurityContext( #if TARGET_ANDROID - verifier: new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions), + new SslStream.JavaProxy(this), #endif ref _credentialsHandle!, ref _securityContext, @@ -954,10 +968,10 @@ internal SecurityStatusPal Decrypt(Span buffer, out int outputOffset, out --*/ //This method validates a remote certificate. - internal bool VerifyRemoteCertificate(ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatusFlags) + internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remoteCertValidationCallback, SslCertificateTrust? trust, ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus) { sslPolicyErrors = SslPolicyErrors.None; - chainStatusFlags = X509ChainStatusFlags.NoError; + chainStatus = X509ChainStatusFlags.NoError; bool success = false; X509Chain? chain = null; @@ -977,14 +991,83 @@ internal bool VerifyRemoteCertificate(ref ProtocolToken? alertToken, out SslPoli _remoteCertificate = certificate; - var remoteCertificateVerifier = new RemoteCertificateVerification(sslStream: this, _sslAuthenticationOptions); - success = remoteCertificateVerifier.Verify(_remoteCertificate, _securityContext!, _sslAuthenticationOptions.CertificateContext?.Trust, ref chain, out sslPolicyErrors, out X509ChainStatus[] chainStatus); + if (_remoteCertificate == null) + { + if (NetEventSource.Log.IsEnabled() && RemoteCertRequired) NetEventSource.Error(this, $"Remote certificate required, but no remote certificate received"); + sslPolicyErrors |= SslPolicyErrors.RemoteCertificateNotAvailable; + } + else + { + chain ??= new X509Chain(); + + if (_sslAuthenticationOptions.CertificateChainPolicy != null) + { + chain.ChainPolicy = _sslAuthenticationOptions.CertificateChainPolicy; + } + else + { + chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode; + chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot; + + if (trust != null) + { + chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust; + if (trust._store != null) + { + chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates); + } + if (trust._trustList != null) + { + chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList); + } + } + } + + // set ApplicationPolicy unless already provided. + if (chain.ChainPolicy.ApplicationPolicy.Count == 0) + { + // Authenticate the remote party: (e.g. when operating in server mode, authenticate the client). + chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid); + } + + sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties( + _securityContext!, + chain, + _remoteCertificate, + _sslAuthenticationOptions.CheckCertName, + _sslAuthenticationOptions.IsServer, + _sslAuthenticationOptions.TargetHost); + } + + if (remoteCertValidationCallback != null) + { + success = remoteCertValidationCallback(this, _remoteCertificate, chain, sslPolicyErrors); + } + else + { + if (!RemoteCertRequired) + { + sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable; + } + + success = (sslPolicyErrors == SslPolicyErrors.None); + } + + if (NetEventSource.Log.IsEnabled()) + { + LogCertificateValidation(remoteCertValidationCallback, sslPolicyErrors, success, chain!); + NetEventSource.Info(this, $"Cert validation, remote cert = {_remoteCertificate}"); + } + if (!success) { - alertToken = CreateFatalHandshakeAlertToken(sslPolicyErrors, chainStatus); - foreach (X509ChainStatus status in chainStatus) + alertToken = CreateFatalHandshakeAlertToken(sslPolicyErrors, chain!); + if (chain != null) { - chainStatusFlags |= status.Status; + foreach (X509ChainStatus status in chain.ChainStatus) + { + chainStatus |= status.Status; + } } } } @@ -1008,14 +1091,14 @@ internal bool VerifyRemoteCertificate(ref ProtocolToken? alertToken, out SslPoli return success; } - private ProtocolToken? CreateFatalHandshakeAlertToken(SslPolicyErrors sslPolicyErrors, X509ChainStatus[] chainStatus) + private ProtocolToken? CreateFatalHandshakeAlertToken(SslPolicyErrors sslPolicyErrors, X509Chain chain) { TlsAlertMessage alertMessage; switch (sslPolicyErrors) { case SslPolicyErrors.RemoteCertificateChainErrors: - alertMessage = GetAlertMessageFromChain(chainStatus); + alertMessage = GetAlertMessageFromChain(chain); break; case SslPolicyErrors.RemoteCertificateNameMismatch: alertMessage = TlsAlertMessage.BadCertificate; @@ -1079,9 +1162,9 @@ private ProtocolToken GenerateAlertToken() return new ProtocolToken(nextmsg, status); } - private static TlsAlertMessage GetAlertMessageFromChain(X509ChainStatus[] chainStates) + private static TlsAlertMessage GetAlertMessageFromChain(X509Chain chain) { - foreach (X509ChainStatus chainStatus in chainStates) + foreach (X509ChainStatus chainStatus in chain.ChainStatus) { if (chainStatus.Status == X509ChainStatusFlags.NoError) { @@ -1127,6 +1210,55 @@ private static TlsAlertMessage GetAlertMessageFromChain(X509ChainStatus[] chainS return TlsAlertMessage.BadCertificate; } + + private void LogCertificateValidation(RemoteCertificateValidationCallback? remoteCertValidationCallback, SslPolicyErrors sslPolicyErrors, bool success, X509Chain chain) + { + if (!NetEventSource.Log.IsEnabled()) + return; + + if (sslPolicyErrors != SslPolicyErrors.None) + { + NetEventSource.Log.RemoteCertificateError(this, SR.net_log_remote_cert_has_errors); + if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable) != 0) + { + NetEventSource.Log.RemoteCertificateError(this, SR.net_log_remote_cert_not_available); + } + + if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0) + { + NetEventSource.Log.RemoteCertificateError(this, SR.net_log_remote_cert_name_mismatch); + } + + if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0) + { + string chainStatusString = "ChainStatus: "; + foreach (X509ChainStatus chainStatus in chain.ChainStatus) + { + chainStatusString += "\t" + chainStatus.StatusInformation; + } + NetEventSource.Log.RemoteCertificateError(this, chainStatusString); + } + } + + if (success) + { + if (remoteCertValidationCallback != null) + { + NetEventSource.Log.RemoteCertDeclaredValid(this); + } + else + { + NetEventSource.Log.RemoteCertHasNoErrors(this); + } + } + else + { + if (remoteCertValidationCallback != null) + { + NetEventSource.Log.RemoteCertUserDeclaredInvalid(this); + } + } + } } // ProtocolToken - used to process and handle the return codes from the SSPI wrapper 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 777e011f2dad6..f1ec95aae45e2 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 @@ -27,17 +27,18 @@ public static void VerifyPackageInfo() } public static SecurityStatusPal AcceptSecurityContext( + SslStream.JavaProxy? sslStreamProxy, ref SafeFreeCredentials credential, ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(verifier: null, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(sslStreamProxy, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal InitializeSecurityContext( - RemoteCertificateVerification verifier, + SslStream.JavaProxy? sslStreamProxy, ref SafeFreeCredentials credential, ref SafeDeleteSslContext? context, string? targetName, @@ -46,7 +47,7 @@ public static SecurityStatusPal InitializeSecurityContext( SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate? clientCertificateSelectionCallback) { - return HandshakeInternal(verifier, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(sslStreamProxy, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal Renegotiate( @@ -171,7 +172,7 @@ public static void QueryContextConnectionInfo( } private static SecurityStatusPal HandshakeInternal( - RemoteCertificateVerification? verifier, + SslStream.JavaProxy? sslStreamProxy, SafeFreeCredentials credential, ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, @@ -184,7 +185,7 @@ private static SecurityStatusPal HandshakeInternal( if ((context == null) || context.IsInvalid) { - context = new SafeDeleteSslContext(verifier, sslAuthenticationOptions); + context = new SafeDeleteSslContext(sslStreamProxy, sslAuthenticationOptions); sslContext = context; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java similarity index 75% rename from src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager.java rename to src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java index 810343b74e069..afd70bd63c61e 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager.java +++ b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java @@ -4,16 +4,17 @@ import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; -class RemoteCertificateVerificationProxyTrustManager implements X509TrustManager { +class DotnetProxyTrustManager implements X509TrustManager { private int dotnetHandle; - public RemoteCertificateVerificationProxyTrustManager(int dotnetHandle) + public DotnetProxyTrustManager(int dotnetHandle) { this.dotnetHandle = dotnetHandle; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if (!verifyRemoteCertificate(dotnetHandle, chain)) { + android.util.Log.d("DOTNET", "checkClientTrusted"); + if (!verifyRemoteCertificate(dotnetHandle)) { throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); } } @@ -21,7 +22,7 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) throws public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if (!verifyRemoteCertificate(dotnetHandle, chain)) { + if (!verifyRemoteCertificate(dotnetHandle)) { throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); } } @@ -30,5 +31,5 @@ public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - static native boolean verifyRemoteCertificate(int dotnetHandle, X509Certificate[] chain); + static native boolean verifyRemoteCertificate(int dotnetHandle); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index cda5c04e28608..ecb3f9ba1265f 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -480,9 +480,9 @@ jmethodID g_TrustManagerFactoryGetTrustManagers; // javax/net/ssl/X509TrustManager jclass g_X509TrustManager; -// net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager -jclass g_RemoteCertificateVerificationProxyTrustManager; -jmethodID g_RemoteCertificateVerificationProxyTrustManagerCtor; +// net/dot/android/crypto/DotnetProxyTrustManager +jclass g_DotnetProxyTrustManager; +jmethodID g_DotnetProxyTrustManagerCtor; jobject ToGRef(JNIEnv *env, jobject lref) { @@ -1070,8 +1070,8 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_X509TrustManager = GetClassGRef(env, "javax/net/ssl/X509TrustManager"); - g_RemoteCertificateVerificationProxyTrustManager = GetClassGRef(env, "net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager"); - g_RemoteCertificateVerificationProxyTrustManagerCtor = GetMethod(env, false, g_RemoteCertificateVerificationProxyTrustManager, "", "(I)V"); + g_DotnetProxyTrustManager = GetClassGRef(env, "net/dot/android/crypto/DotnetProxyTrustManager"); + g_DotnetProxyTrustManagerCtor = GetMethod(env, false, g_DotnetProxyTrustManager, "", "(I)V"); return JNI_VERSION_1_6; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index ba77b86e41230..df1e303c925f5 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -495,9 +495,9 @@ extern jmethodID g_TrustManagerFactoryGetTrustManagers; // javax/net/ssl/X509TrustManager extern jclass g_X509TrustManager; -// net/dot/android/crypto/RemoteCertificateVerificationProxyTrustManager -extern jclass g_RemoteCertificateVerificationProxyTrustManager; -extern jmethodID g_RemoteCertificateVerificationProxyTrustManagerCtor; +// net/dot/android/crypto/DotnetProxyTrustManager +extern jclass g_DotnetProxyTrustManager; +extern jmethodID g_DotnetProxyTrustManagerCtor; // Compatibility macros #if !defined (__mallocfunc) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index 5a6d982e48b65..1d7bc5fc464da 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -12,7 +12,7 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot abort_unless(dotnetHandle != 0, "invalid pointer to the .NET remote certificate validator"); jobjectArray trustManagers = NULL; - INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, trustManagerProxy); + INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, dotnetProxyTrustManager); // string defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); loc[defaultAlgorithm] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetDefaultAlgorithm); @@ -33,7 +33,7 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot // boolean foundAndReplaced = false; // for (int i = 0; i < trustManagers.length; i++) { // if (trustManagers[i] instanceof X509TrustManager) { - // trustManagers[i] = new RemoteCertificateVerificationProxyTrustManager(dotnetHandle); + // trustManagers[i] = new DotnetProxyTrustManager(dotnetHandle); // foundAndReplaced = true; // break; // } @@ -47,10 +47,10 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot if ((*env)->IsInstanceOf(env, loc[trustManager], g_X509TrustManager)) { - loc[trustManagerProxy] = (*env)->NewObject(env, g_RemoteCertificateVerificationProxyTrustManager, g_RemoteCertificateVerificationProxyTrustManagerCtor, (int)dotnetHandle); + loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (int)dotnetHandle); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[trustManagerProxy]); + (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[dotnetProxyTrustManager]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); foundAndReplaced = true; @@ -67,19 +67,9 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot return trustManagers; } -jboolean Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustManager_verifyRemoteCertificate( - JNIEnv* env, jobject thisHandle, intptr_t dotnetHandle, jobjectArray certificates) +jboolean Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( + JNIEnv* env, jobject thisHandle, intptr_t dotnetHandle) { abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); - - size_t count = (size_t)(*env)->GetArrayLength(env, certificates); - jobject* ptrs = xcalloc(count, sizeof(jobject)); - - for (size_t i = 0; i < count; i++) - ptrs[i] = ToGRef(env, (*env)->GetObjectArrayElement(env, certificates, (jsize)i)); - - bool isAccepted = verifyRemoteCertificate(dotnetHandle, (int32_t)count, ptrs); - - free(ptrs); - return isAccepted ? JNI_TRUE : JNI_FALSE; + return verifyRemoteCertificate(dotnetHandle); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h index a17584797fed3..c53abfb7b68fd 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -1,10 +1,10 @@ #include "pal_jni.h" -typedef bool (*RemoteCertificateValidationCallback)(intptr_t, int32_t, jobject*); +typedef bool (*RemoteCertificateValidationCallback)(intptr_t); PALEXPORT void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback); jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle); -JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_RemoteCertificateVerificationProxyTrustManager_verifyRemoteCertificate( - JNIEnv *env, jobject thisHandle, intptr_t dotnetHandle, jobjectArray certificates); +JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( + JNIEnv *env, jobject thisHandle, intptr_t dotnetHandle); From 9cfd08bca3dc2ad5b9ace0d7dd185e34bc4d8e35 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 18:00:49 +0100 Subject: [PATCH 17/59] Update enabled and disabled tests --- .../System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs | 1 + .../tests/FunctionalTests/CertificateValidationClientServer.cs | 1 - .../tests/FunctionalTests/CertificateValidationRemoteServer.cs | 1 - .../tests/FunctionalTests/ServerAsyncAuthenticateTest.cs | 2 ++ .../tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs | 1 + .../tests/FunctionalTests/SslStreamNetworkStreamTest.cs | 2 +- .../tests/FunctionalTests/SslStreamSniTest.cs | 2 +- .../tests/FunctionalTests/SslStreamSystemDefaultsTest.cs | 1 - 8 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index 7efd4ed9d5ea2..ae44393dbc69e 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -228,6 +228,7 @@ public async Task NoCallback_BadCertificate_ThrowsException(string url) [OuterLoop("Uses external servers")] [ConditionalFact(nameof(ClientSupportsDHECipherSuites))] + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task NoCallback_RevokedCertificate_NoRevocationChecking_Succeeds() { using (HttpClient client = CreateHttpClient()) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 2a384c98ef57f..494bcf86fc226 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -108,7 +108,6 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [Theory] [InlineData(false)] [InlineData(true)] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Client certificate is not sent to the server public async Task CertificateValidationClientServer_EndToEnd_Ok(bool useClientSelectionCallback) { IPEndPoint endPoint = new IPEndPoint(IPAddress.Loopback, 0); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs index b26728b0d0c03..2d4b1ba09ff08 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs @@ -95,7 +95,6 @@ public async Task DefaultConnect_EndToEnd_Ok(string host) [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Exception of type 'Interop+AndroidCrypto+SslException' was thrown. [ActiveIssue("https://github.com/dotnet/runtime/issues/70981", TestPlatforms.OSX)] public Task ConnectWithRevocation_WithCallback(bool checkRevocation) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 3ba7978bde1eb..d724da5dd7d13 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -197,6 +197,7 @@ public async Task ServerAsyncAuthenticate_FailingOptionCallback_Throws(bool useA } [Fact] + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task ServerAsyncAuthenticate_VerificationDelegate_Success() { bool validationCallbackCalled = false; @@ -229,6 +230,7 @@ public async Task ServerAsyncAuthenticate_VerificationDelegate_Success() } [Fact] + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task ServerAsyncAuthenticate_ConstructorVerificationDelegate_Success() { bool validationCallbackCalled = false; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs index 233f18267f23b..6e8d9d68623c6 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs @@ -70,6 +70,7 @@ public async Task SslStream_AllowRenegotiation_True_Succeeds() [Fact] [OuterLoop] // Test hits external azure server. + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task SslStream_AllowRenegotiation_False_Throws() { Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 195d5ed04cb51..67c7b80f02a1f 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -753,7 +753,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( [Theory] [InlineData(true)] [InlineData(false)] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: Exception of type 'Interop+AndroidCrypto+SslException' was thrown. + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task SslStream_UntrustedCaWithCustomTrust_OK(bool usePartialChain) { int split = Random.Shared.Next(0, _certificates.serverChain.Count - 1); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs index acbebdbd3c762..a3af787fdcce1 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs @@ -96,7 +96,7 @@ public async Task SslStream_ServerCallbackAndLocalCertificateSelectionSet_Throws [Theory] [MemberData(nameof(HostNameData))] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: client certificate isn't sent to the server + [ActiveIssue("TODO", TestPlatforms.Android)] // only one test case is failing - the one with hostname 'żółć gęślą jaźń. 红烧. 照り焼き' public async Task SslStream_ServerCallbackNotSet_UsesLocalCertificateSelection(string hostName) { using X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate(); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs index 7e1d8eaf61b56..51b28f5b26f60 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSystemDefaultsTest.cs @@ -76,7 +76,6 @@ public static IEnumerable OneOrBothUseDefaulData() [ConditionalTheory] [MemberData(nameof(OneOrBothUseDefaulData))] - [ActiveIssue("TODO", TestPlatforms.Android)] // TODO: System.Security.Authentication.AuthenticationException : The remote certificate was rejected by the provided RemoteCertificateValidationCallback. public async Task ClientAndServer_OneOrBothUseDefault_Ok(SslProtocols? clientProtocols, SslProtocols? serverProtocols) { using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate()) From 1bc237ebf87481d0964025f39675dec205490510 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 18:01:18 +0100 Subject: [PATCH 18/59] Cleanup --- .../src/System/Net/Security/SslStream.Protocol.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index df8a340705189..77531495518b9 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -811,14 +811,6 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte if (_sslAuthenticationOptions.IsServer) { -// #if TARGET_ANDROID -// // TODO remove -// _sslAuthenticationOptions.CertValidationDelegate = (sender, cert, chain, errors) => { -// Console.WriteLine($"server's CertValidationDelegate: {errors}, {cert}"); -// if (errors == SslPolicyErrors.RemoteCertificateNotAvailable) return false; -// else return true; -// }; -// #endif status = SslStreamPal.AcceptSecurityContext( #if TARGET_ANDROID new SslStream.JavaProxy(this), From 7b04be0e3d289238ff4afa69e552359a4700e1c1 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 18:34:28 +0100 Subject: [PATCH 19/59] Renaming --- .../Interop.Ssl.cs | 14 ++++++++------ .../Security/Pal.Android/SafeDeleteSslContext.cs | 11 +++++------ .../src/System/Net/Security/SslStream.Android.cs | 6 +++--- .../System/Net/Security/SslStreamPal.Android.cs | 6 +++--- .../android/crypto/DotnetProxyTrustManager.java | 12 ++++++------ .../pal_sslstream.c | 14 +++++++------- .../pal_sslstream.h | 4 ++-- .../pal_trust_manager.c | 14 +++++++------- .../pal_trust_manager.h | 6 +++--- 9 files changed, 44 insertions(+), 43 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 86ecfa6873174..452de55392217 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 @@ -29,24 +29,26 @@ internal enum PAL_SSLStreamStatus }; [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreate")] - internal static partial SafeSslHandle SSLStreamCreate(IntPtr trustManagerProxyHandle); + private static partial SafeSslHandle SSLStreamCreate(IntPtr sslStreamProxyHandle); + internal static SafeSslHandle SSLStreamCreate(SslStream.JavaProxy sslStreamProxy) + => SSLStreamCreate(sslStreamProxy.Handle); [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamCreateWithCertificates")] private static partial SafeSslHandle SSLStreamCreateWithCertificates( - IntPtr trustManagerProxyHandle, + IntPtr sslStreamProxyHandle, ref byte pkcs8PrivateKey, int pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, IntPtr[] certs, int certsLen); internal static SafeSslHandle SSLStreamCreateWithCertificates( - IntPtr trustManagerProxyHandle, + SslStream.JavaProxy sslStreamProxy, ReadOnlySpan pkcs8PrivateKey, PAL_KeyAlgorithm algorithm, IntPtr[] certificates) { return SSLStreamCreateWithCertificates( - trustManagerProxyHandle, + sslStreamProxy.Handle, ref MemoryMarshal.GetReference(pkcs8PrivateKey), pkcs8PrivateKey.Length, algorithm, @@ -54,8 +56,8 @@ ref MemoryMarshal.GetReference(pkcs8PrivateKey), certificates.Length); } - [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterTrustManagerCallback")] - internal static unsafe partial void RegisterTrustManagerCallback( + [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterRemoteCertificateValidationCallback")] + internal static unsafe partial void RegisterRemoteCertificateValidationCallback( delegate* unmanaged verifyRemoteCertificate); [LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")] 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 929afac6039ba..becae2f9db141 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 @@ -38,13 +38,13 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext public SafeSslHandle SslContext => _sslContext; public Exception? CaughtException => _sslStreamProxy?.CaughtException; - public SafeDeleteSslContext(SslStream.JavaProxy? sslStreamProxy, SslAuthenticationOptions authOptions) + public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) { _sslStreamProxy = sslStreamProxy; try { - _sslContext = CreateSslContext(_sslStreamProxy?.Handle, authOptions); + _sslContext = CreateSslContext(_sslStreamProxy, authOptions); InitializeSslContext(_sslContext, authOptions); } catch (Exception ex) @@ -151,11 +151,11 @@ internal int ReadPendingWrites(byte[] buf, int offset, int count) return limit; } - private static SafeSslHandle CreateSslContext(IntPtr? trustManagerProxyHandle, SslAuthenticationOptions authOptions) + private static SafeSslHandle CreateSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) { if (authOptions.CertificateContext == null) { - return Interop.AndroidCrypto.SSLStreamCreate(trustManagerProxyHandle ?? IntPtr.Zero); + return Interop.AndroidCrypto.SSLStreamCreate(sslStreamProxy); } SslStreamCertificateContext context = authOptions.CertificateContext; @@ -175,8 +175,7 @@ private static SafeSslHandle CreateSslContext(IntPtr? trustManagerProxyHandle, S ptrs[i + 1] = context.IntermediateCertificates[i].Handle; } - return Interop.AndroidCrypto.SSLStreamCreateWithCertificates( - trustManagerProxyHandle ?? IntPtr.Zero, keyBytes, algorithm, ptrs); + return Interop.AndroidCrypto.SSLStreamCreateWithCertificates(sslStreamProxy, keyBytes, algorithm, ptrs); } private static AsymmetricAlgorithm GetPrivateKeyAlgorithm(X509Certificate2 cert, out PAL_KeyAlgorithm algorithm) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index faaa5aa7a9f33..189e45faf5b32 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -22,7 +22,7 @@ internal sealed class JavaProxy : IDisposable public unsafe JavaProxy(SslStream sslStream) { - RegisterTrustManagerCallback(); + RegisterRemoteCertificateValidationCallback(); _sslStream = sslStream; _handle = GCHandle.Alloc(this); @@ -35,13 +35,13 @@ public IntPtr Handle public Exception? CaughtException { get; private set; } - private static unsafe void RegisterTrustManagerCallback() + private static unsafe void RegisterRemoteCertificateValidationCallback() { lock (s_initializationLock) { if (!s_initialized) { - Interop.AndroidCrypto.RegisterTrustManagerCallback(&VerifyRemoteCertificate); + Interop.AndroidCrypto.RegisterRemoteCertificateValidationCallback(&VerifyRemoteCertificate); s_initialized = true; } } 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 f1ec95aae45e2..29a93c78226e9 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 @@ -27,7 +27,7 @@ public static void VerifyPackageInfo() } public static SecurityStatusPal AcceptSecurityContext( - SslStream.JavaProxy? sslStreamProxy, + SslStream.JavaProxy sslStreamProxy, ref SafeFreeCredentials credential, ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, @@ -38,7 +38,7 @@ public static SecurityStatusPal AcceptSecurityContext( } public static SecurityStatusPal InitializeSecurityContext( - SslStream.JavaProxy? sslStreamProxy, + SslStream.JavaProxy sslStreamProxy, ref SafeFreeCredentials credential, ref SafeDeleteSslContext? context, string? targetName, @@ -172,7 +172,7 @@ public static void QueryContextConnectionInfo( } private static SecurityStatusPal HandshakeInternal( - SslStream.JavaProxy? sslStreamProxy, + SslStream.JavaProxy sslStreamProxy, SafeFreeCredentials credential, ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java index afd70bd63c61e..29839066f5c38 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java +++ b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java @@ -5,16 +5,16 @@ import javax.net.ssl.X509TrustManager; class DotnetProxyTrustManager implements X509TrustManager { - private int dotnetHandle; + private int sslStreamProxyHandle; - public DotnetProxyTrustManager(int dotnetHandle) + public DotnetProxyTrustManager(int sslStreamProxyHandle) { - this.dotnetHandle = dotnetHandle; + this.sslStreamProxyHandle = sslStreamProxyHandle; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { android.util.Log.d("DOTNET", "checkClientTrusted"); - if (!verifyRemoteCertificate(dotnetHandle)) { + if (!verifyRemoteCertificate(sslStreamProxyHandle)) { throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); } } @@ -22,7 +22,7 @@ public void checkClientTrusted(X509Certificate[] chain, String authType) throws public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if (!verifyRemoteCertificate(dotnetHandle)) { + if (!verifyRemoteCertificate(sslStreamProxyHandle)) { throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); } } @@ -31,5 +31,5 @@ public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - static native boolean verifyRemoteCertificate(int dotnetHandle); + static native boolean verifyRemoteCertificate(int sslStreamProxyHandle); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 1f5aadee70cd4..b43578f57b683 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -325,7 +325,7 @@ ARGS_NON_NULL_ALL static jobject GetKeyStoreInstance(JNIEnv* env) return keyStore; } -SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t trustManagerProxyHandle) +SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t sslStreamProxyHandle) { SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); @@ -336,10 +336,10 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t trustManagerProxyHandle) if (!loc[sslContext]) goto cleanup; - if (trustManagerProxyHandle != 0) + if (sslStreamProxyHandle != 0) { // Init trust managers - loc[trustManagers] = InitTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); + loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); if (!loc[trustManagers]) goto cleanup; } @@ -420,7 +420,7 @@ static int32_t AddCertChainToStore(JNIEnv* env, return ret; } -SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustManagerProxyHandle, +SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStreamProxyHandle, uint8_t* pkcs8PrivateKey, int32_t pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, @@ -460,10 +460,10 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustMan loc[keyManagers] = (*env)->CallObjectMethod(env, loc[kmf], g_KeyManagerFactoryGetKeyManagers); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - if (trustManagerProxyHandle != 0) + if (sslStreamProxyHandle != 0) { - // TrustManager[] trustMangers = InitTrustManagersWithCustomValidatorProxy(trustManagerProxyHandle); - loc[trustManagers] = InitTrustManagersWithCustomValidatorProxy(env, trustManagerProxyHandle); + // TrustManager[] trustMangers = InitTrustManagersWithDotnetProxy(sslStreamProxyHandle); + loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); if (!loc[trustManagers]) goto cleanup; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h index 4332b76c6a92c..01a5a7b766c99 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -44,14 +44,14 @@ Create an SSL context Returns NULL on failure */ -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t trustManagerProxyHandle); +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t sslStreamProxyHandle); /* Create an SSL context with the specified certificates Returns NULL on failure */ -PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t trustManagerProxyHandle, +PALEXPORT SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStreamProxyHandle, uint8_t* pkcs8PrivateKey, int32_t pkcs8PrivateKeyLen, PAL_KeyAlgorithm algorithm, diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index 1d7bc5fc464da..c2c0ebdc92758 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -2,14 +2,14 @@ static RemoteCertificateValidationCallback verifyRemoteCertificate; -void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback) +void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback) { verifyRemoteCertificate = callback; } -jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle) +jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle) { - abort_unless(dotnetHandle != 0, "invalid pointer to the .NET remote certificate validator"); + abort_unless(sslStreamProxyHandle != 0, "invalid pointer to the .NET remote certificate validator"); jobjectArray trustManagers = NULL; INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, dotnetProxyTrustManager); @@ -33,7 +33,7 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot // boolean foundAndReplaced = false; // for (int i = 0; i < trustManagers.length; i++) { // if (trustManagers[i] instanceof X509TrustManager) { - // trustManagers[i] = new DotnetProxyTrustManager(dotnetHandle); + // trustManagers[i] = new DotnetProxyTrustManager(sslStreamProxyHandle); // foundAndReplaced = true; // break; // } @@ -47,7 +47,7 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot if ((*env)->IsInstanceOf(env, loc[trustManager], g_X509TrustManager)) { - loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (int)dotnetHandle); + loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (int)sslStreamProxyHandle); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[dotnetProxyTrustManager]); @@ -68,8 +68,8 @@ jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dot } jboolean Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( - JNIEnv* env, jobject thisHandle, intptr_t dotnetHandle) + JNIEnv* env, jobject thisHandle, intptr_t sslStreamProxyHandle) { abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); - return verifyRemoteCertificate(dotnetHandle); + return verifyRemoteCertificate(sslStreamProxyHandle); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h index c53abfb7b68fd..e64dc1fce56f6 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -2,9 +2,9 @@ typedef bool (*RemoteCertificateValidationCallback)(intptr_t); -PALEXPORT void AndroidCryptoNative_RegisterTrustManagerCallback(RemoteCertificateValidationCallback callback); +PALEXPORT void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback); -jobjectArray InitTrustManagersWithCustomValidatorProxy(JNIEnv* env, intptr_t dotnetHandle); +jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle); JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( - JNIEnv *env, jobject thisHandle, intptr_t dotnetHandle); + JNIEnv *env, jobject thisHandle, intptr_t sslStreamProxyHandle); From 2f70a5d9783649ac7d6c4c4f378712ab3d81fabe Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 19:00:47 +0100 Subject: [PATCH 20/59] Remove unnecessary changes --- .../Net/Http/HttpClientHandlerTest.ClientCertificates.cs | 9 +++------ .../SocketsHttpHandlerTest.Http2FlowControl.cs | 4 ++-- .../Net/Security/Pal.Android/SafeDeleteSslContext.cs | 1 - .../src/System/Net/Security/SslStream.Protocol.cs | 1 + 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs index 769e76a6402cc..04a7414d04695 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ClientCertificates.cs @@ -113,12 +113,9 @@ await TestHelper.WhenAllCompletedOrAnyFailed( SslStream sslStream = Assert.IsType(connection.Stream); if (serverExpectsClientCertificate) { - if (sslStream.RemoteCertificate is not null) { - _output.WriteLine( - "Client cert: {0}", - new X509Certificate2(sslStream.RemoteCertificate.Export(X509ContentType.Cert)).GetNameInfo(X509NameType.SimpleName, false)); - } - + _output.WriteLine( + "Client cert: {0}", + new X509Certificate2(sslStream.RemoteCertificate.Export(X509ContentType.Cert)).GetNameInfo(X509NameType.SimpleName, false)); Assert.Equal(cert, sslStream.RemoteCertificate); } else diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs index 5bbe67521932f..4862c0a4ae52c 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.Http2FlowControl.cs @@ -228,7 +228,7 @@ private static async Task TestClientWindowScalingAsync( bool pingReceivedAfterReachingMaxWindow = false; bool unexpectedFrameReceived = false; CancellationTokenSource stopFrameProcessingCts = new CancellationTokenSource(); - + CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(stopFrameProcessingCts.Token, timeoutCts.Token); Task processFramesTask = ProcessIncomingFramesAsync(linkedCts.Token); byte[] buffer = new byte[16384]; @@ -315,7 +315,7 @@ async Task ProcessIncomingFramesAsync(CancellationToken cancellationToken) catch (OperationCanceledException) { } - + output?.WriteLine("ProcessIncomingFramesAsync finished"); } 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 becae2f9db141..42b17d6a9b83c 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 @@ -69,7 +69,6 @@ protected override void Dispose(bool disposing) sslContext.Dispose(); } - _sslStreamProxy?.Dispose(); } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 77531495518b9..ebf1e6a97fad4 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -965,6 +965,7 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot sslPolicyErrors = SslPolicyErrors.None; chainStatus = X509ChainStatusFlags.NoError; + // We don't catch exceptions in this method, so it's safe for "accepted" be initialized with true. bool success = false; X509Chain? chain = null; From af18da72777190d67defef307fc529c45364d161 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 9 Nov 2022 22:54:39 +0100 Subject: [PATCH 21/59] Fix invoking validation even when the Java callbacks aren't called (no peer certificate to validate) --- .../Pal.Android/SafeDeleteSslContext.cs | 2 +- .../System/Net/Security/SslStream.Android.cs | 42 ++++++++++++++++--- .../src/System/Net/Security/SslStream.IO.cs | 25 +++++++---- .../ServerAsyncAuthenticateTest.cs | 2 - 4 files changed, 55 insertions(+), 16 deletions(-) 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 42b17d6a9b83c..b683172044ec1 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 @@ -36,7 +36,7 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SafeSslHandle SslContext => _sslContext; - public Exception? CaughtException => _sslStreamProxy?.CaughtException; + public SslStream.JavaProxy.RemoteCertificateValidationResult? ValidationResult => _sslStreamProxy?.ValidationResult; public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index 189e45faf5b32..6b39af9c2c15a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -33,7 +33,7 @@ public IntPtr Handle ? GCHandle.ToIntPtr(handle) : throw new ObjectDisposedException(nameof(JavaProxy)); - public Exception? CaughtException { get; private set; } + public RemoteCertificateValidationResult? ValidationResult { get; private set; } private static unsafe void RegisterRemoteCertificateValidationCallback() { @@ -59,21 +59,51 @@ private static unsafe bool VerifyRemoteCertificate(IntPtr sslStreamProxyHandle) var proxy = (JavaProxy?)GCHandle.FromIntPtr(sslStreamProxyHandle).Target; Debug.Assert(proxy is not null); + return proxy.VerifyRemoteCertificate(); + } + + private bool VerifyRemoteCertificate() + { try { - SslStream sslStream = proxy._sslStream; - SslAuthenticationOptions options = proxy._sslStream._sslAuthenticationOptions; ProtocolToken? alertToken = null; - return sslStream.VerifyRemoteCertificate(options.CertValidationDelegate, options.CertificateContext?.Trust, ref alertToken, out _, out _); + var isValid = _sslStream.VerifyRemoteCertificate( + _sslStream._sslAuthenticationOptions.CertValidationDelegate, + _sslStream._sslAuthenticationOptions.CertificateContext?.Trust, + ref alertToken, + out SslPolicyErrors sslPolicyErrors, + out X509ChainStatusFlags chainStatus); + + ValidationResult = new() + { + IsValid = isValid, + SslPolicyErrors = sslPolicyErrors, + ChainStatus = chainStatus, + }; } catch (Exception exception) { Debug.WriteLine($"Remote certificate verification has thrown an exception: {exception}"); Debug.WriteLine(exception.StackTrace); - proxy.CaughtException = exception; - return false; + ValidationResult = new() + { + IsValid = false, + CaughtException = exception, + SslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors, + ChainStatus = X509ChainStatusFlags.NoError, + }; } + + return ValidationResult.IsValid; + } + + internal sealed class RemoteCertificateValidationResult + { + public bool IsValid { get; init; } + public Exception? CaughtException { get; init; } + public SslPolicyErrors SslPolicyErrors { get; init; } + public X509ChainStatusFlags ChainStatus { get; init; } } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 00c3f5c4b1705..91db1510e6d1f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -314,7 +314,7 @@ private async Task ForceAuthenticationAsync(bool receiveFirst, byte[ if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, message.Status); #if TARGET_ANDROID - if (_securityContext?.CaughtException is Exception caughtException) + if (_securityContext?.ValidationResult?.CaughtException is Exception caughtException) { throw new AuthenticationException(SR.net_auth_SSPI, caughtException); } @@ -510,17 +510,27 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError return true; } -#if !TARGET_ANDROID +#if TARGET_ANDROID + // If the remote certificate verification has already been invoked from Java TrustManager's callback + // we shouldn't run it repeatedly and honor the existing validation result. + // It's possible that the validation result isn't set (for example the client didn't send a certificate) + // in which case we still need to run the verification. + if (_securityContext?.ValidationResult is SslStream.JavaProxy.RemoteCertificateValidationResult result) + { + sslPolicyErrors = result.SslPolicyErrors; + chainStatus = result.ChainStatus; + + _handshakeCompleted = result.IsValid; + return result.IsValid; + } +#endif + if (!VerifyRemoteCertificate(_sslAuthenticationOptions.CertValidationDelegate, _sslAuthenticationOptions.CertificateContext?.Trust, ref alertToken, out sslPolicyErrors, out chainStatus)) { _handshakeCompleted = false; return false; } -#else - // on Android, the verification has already been called from Java's TrustManager callbacks - sslPolicyErrors = SslPolicyErrors.None; - chainStatus = X509ChainStatusFlags.NoError; -#endif + _handshakeCompleted = true; return true; } @@ -831,6 +841,7 @@ private async ValueTask ReadAsyncInternal(Memory buffer, } SecurityStatusPal status = DecryptData(payloadBytes); + Console.WriteLine($"status.ErrorCode = {status.ErrorCode}"); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { byte[]? extraBuffer = null; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index d724da5dd7d13..3ba7978bde1eb 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -197,7 +197,6 @@ public async Task ServerAsyncAuthenticate_FailingOptionCallback_Throws(bool useA } [Fact] - [ActiveIssue("TODO", TestPlatforms.Android)] public async Task ServerAsyncAuthenticate_VerificationDelegate_Success() { bool validationCallbackCalled = false; @@ -230,7 +229,6 @@ public async Task ServerAsyncAuthenticate_VerificationDelegate_Success() } [Fact] - [ActiveIssue("TODO", TestPlatforms.Android)] public async Task ServerAsyncAuthenticate_ConstructorVerificationDelegate_Success() { bool validationCallbackCalled = false; From 4a7c785bb0e2a2937c5867058ea797debb1f3238 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 10 Nov 2022 09:14:52 +0100 Subject: [PATCH 22/59] Minor refactoring --- .../System/Net/Security/SslStream.Android.cs | 71 +++++++++---------- .../src/System/Net/Security/SslStream.IO.cs | 1 - .../crypto/DotnetProxyTrustManager.java | 12 ++-- 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index 6b39af9c2c15a..ccc71059ad4c1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -12,6 +12,37 @@ namespace System.Net.Security { public partial class SslStream { + private JavaProxy.RemoteCertificateValidationResult VerifyRemoteCertificate() + { + try + { + ProtocolToken? alertToken = null; + var isValid = VerifyRemoteCertificate( + _sslAuthenticationOptions.CertValidationDelegate, + _sslAuthenticationOptions.CertificateContext?.Trust, + ref alertToken, + out SslPolicyErrors sslPolicyErrors, + out X509ChainStatusFlags chainStatus); + + return new JavaProxy.RemoteCertificateValidationResult + { + IsValid = isValid, + SslPolicyErrors = sslPolicyErrors, + ChainStatus = chainStatus, + }; + } + catch (Exception exception) + { + return new JavaProxy.RemoteCertificateValidationResult + { + IsValid = false, + CaughtException = exception, + SslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors, + ChainStatus = X509ChainStatusFlags.NoError, + }; + } + } + internal sealed class JavaProxy : IDisposable { private static object s_initializationLock = new(); @@ -58,44 +89,10 @@ private static unsafe bool VerifyRemoteCertificate(IntPtr sslStreamProxyHandle) { var proxy = (JavaProxy?)GCHandle.FromIntPtr(sslStreamProxyHandle).Target; Debug.Assert(proxy is not null); + Debug.Assert(proxy.ValidationResult is null); - return proxy.VerifyRemoteCertificate(); - } - - private bool VerifyRemoteCertificate() - { - try - { - ProtocolToken? alertToken = null; - var isValid = _sslStream.VerifyRemoteCertificate( - _sslStream._sslAuthenticationOptions.CertValidationDelegate, - _sslStream._sslAuthenticationOptions.CertificateContext?.Trust, - ref alertToken, - out SslPolicyErrors sslPolicyErrors, - out X509ChainStatusFlags chainStatus); - - ValidationResult = new() - { - IsValid = isValid, - SslPolicyErrors = sslPolicyErrors, - ChainStatus = chainStatus, - }; - } - catch (Exception exception) - { - Debug.WriteLine($"Remote certificate verification has thrown an exception: {exception}"); - Debug.WriteLine(exception.StackTrace); - - ValidationResult = new() - { - IsValid = false, - CaughtException = exception, - SslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors, - ChainStatus = X509ChainStatusFlags.NoError, - }; - } - - return ValidationResult.IsValid; + proxy.ValidationResult = proxy._sslStream.VerifyRemoteCertificate(); + return proxy.ValidationResult.IsValid; } internal sealed class RemoteCertificateValidationResult diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 91db1510e6d1f..eefbbc614e646 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -841,7 +841,6 @@ private async ValueTask ReadAsyncInternal(Memory buffer, } SecurityStatusPal status = DecryptData(payloadBytes); - Console.WriteLine($"status.ErrorCode = {status.ErrorCode}"); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { byte[]? extraBuffer = null; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java index 29839066f5c38..e6c185e657eac 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java +++ b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java @@ -7,23 +7,19 @@ class DotnetProxyTrustManager implements X509TrustManager { private int sslStreamProxyHandle; - public DotnetProxyTrustManager(int sslStreamProxyHandle) - { + public DotnetProxyTrustManager(int sslStreamProxyHandle) { this.sslStreamProxyHandle = sslStreamProxyHandle; } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - android.util.Log.d("DOTNET", "checkClientTrusted"); if (!verifyRemoteCertificate(sslStreamProxyHandle)) { - throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); + throw new CertificateException(); } } - public void checkServerTrusted(X509Certificate[] chain, String authType) - throws CertificateException - { + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { if (!verifyRemoteCertificate(sslStreamProxyHandle)) { - throw new CertificateException("The remote certificate was rejected by the provided RemoteCertificateValidationCallback."); + throw new CertificateException(); } } From 21a69ed4ba3270cbc6520c524fca88eae068bc44 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 10 Nov 2022 11:14:06 +0100 Subject: [PATCH 23/59] Enable more unnecessarily disabled tests --- .../tests/FunctionalTests/ServerAsyncAuthenticateTest.cs | 1 - .../tests/FunctionalTests/SslStreamCredentialCacheTest.cs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 3ba7978bde1eb..2b4e3c9befab8 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -45,7 +45,6 @@ public async Task ServerAsyncAuthenticate_EachSupportedProtocol_Success(SslProto [Theory] [MemberData(nameof(ProtocolMismatchData))] - [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails( SslProtocols clientProtocol, SslProtocols serverProtocol) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs index d8746a5d81afe..f135095476320 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCredentialCacheTest.cs @@ -17,7 +17,6 @@ namespace System.Net.Security.Tests public class SslStreamCredentialCacheTest { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public async Task SslStream_SameCertUsedForClientAndServer_Ok() { (Stream stream1, Stream stream2) = TestHelper.GetConnectedStreams(); From 6cb71224f66a30ca717e6073385e20f75b13f4f8 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 11 Nov 2022 15:17:35 +0100 Subject: [PATCH 24/59] Refactor exception handling --- .../Pal.Android/SafeDeleteSslContext.cs | 1 + .../System/Net/Security/SslStream.Android.cs | 51 +++++++++---------- .../src/System/Net/Security/SslStream.IO.cs | 11 ++-- 3 files changed, 27 insertions(+), 36 deletions(-) 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 b683172044ec1..dde0425e3997d 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 @@ -36,6 +36,7 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SafeSslHandle SslContext => _sslContext; + public Exception? ValidationException => _sslStreamProxy?.ValidationException; public SslStream.JavaProxy.RemoteCertificateValidationResult? ValidationResult => _sslStreamProxy?.ValidationResult; public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index ccc71059ad4c1..351f820c6c3e6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -14,33 +14,20 @@ public partial class SslStream { private JavaProxy.RemoteCertificateValidationResult VerifyRemoteCertificate() { - try - { - ProtocolToken? alertToken = null; - var isValid = VerifyRemoteCertificate( - _sslAuthenticationOptions.CertValidationDelegate, - _sslAuthenticationOptions.CertificateContext?.Trust, - ref alertToken, - out SslPolicyErrors sslPolicyErrors, - out X509ChainStatusFlags chainStatus); + ProtocolToken? alertToken = null; + var isValid = VerifyRemoteCertificate( + _sslAuthenticationOptions.CertValidationDelegate, + _sslAuthenticationOptions.CertificateContext?.Trust, + ref alertToken, + out SslPolicyErrors sslPolicyErrors, + out X509ChainStatusFlags chainStatus); - return new JavaProxy.RemoteCertificateValidationResult - { - IsValid = isValid, - SslPolicyErrors = sslPolicyErrors, - ChainStatus = chainStatus, - }; - } - catch (Exception exception) + return new() { - return new JavaProxy.RemoteCertificateValidationResult - { - IsValid = false, - CaughtException = exception, - SslPolicyErrors = SslPolicyErrors.RemoteCertificateChainErrors, - ChainStatus = X509ChainStatusFlags.NoError, - }; - } + IsValid = isValid, + SslPolicyErrors = sslPolicyErrors, + ChainStatus = chainStatus, + }; } internal sealed class JavaProxy : IDisposable @@ -64,6 +51,7 @@ public IntPtr Handle ? GCHandle.ToIntPtr(handle) : throw new ObjectDisposedException(nameof(JavaProxy)); + public Exception? ValidationException { get; private set; } public RemoteCertificateValidationResult? ValidationResult { get; private set; } private static unsafe void RegisterRemoteCertificateValidationCallback() @@ -91,14 +79,21 @@ private static unsafe bool VerifyRemoteCertificate(IntPtr sslStreamProxyHandle) Debug.Assert(proxy is not null); Debug.Assert(proxy.ValidationResult is null); - proxy.ValidationResult = proxy._sslStream.VerifyRemoteCertificate(); - return proxy.ValidationResult.IsValid; + try + { + proxy.ValidationResult = proxy._sslStream.VerifyRemoteCertificate(); + return proxy.ValidationResult.IsValid; + } + catch (Exception exception) + { + proxy.ValidationException = exception; + return false; + } } internal sealed class RemoteCertificateValidationResult { public bool IsValid { get; init; } - public Exception? CaughtException { get; init; } public SslPolicyErrors SslPolicyErrors { get; init; } public X509ChainStatusFlags ChainStatus { get; init; } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index eefbbc614e646..8277cc3b74a75 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -313,13 +313,6 @@ private async Task ForceAuthenticationAsync(bool receiveFirst, byte[ { if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, message.Status); -#if TARGET_ANDROID - if (_securityContext?.ValidationResult?.CaughtException is Exception caughtException) - { - throw new AuthenticationException(SR.net_auth_SSPI, caughtException); - } -#endif - if (_lastFrame.Header.Type == TlsContentType.Alert && _lastFrame.AlertDescription != TlsAlertDescription.CloseNotify && message.Status.ErrorCode == SecurityStatusPalErrorCode.IllegalMessage) { @@ -515,11 +508,13 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError // we shouldn't run it repeatedly and honor the existing validation result. // It's possible that the validation result isn't set (for example the client didn't send a certificate) // in which case we still need to run the verification. + if (_securityContext?.ValidationException is Exception validationException) + throw new AuthenticationException(SR.net_auth_SSPI, validationException); + if (_securityContext?.ValidationResult is SslStream.JavaProxy.RemoteCertificateValidationResult result) { sslPolicyErrors = result.SslPolicyErrors; chainStatus = result.ChainStatus; - _handshakeCompleted = result.IsValid; return result.IsValid; } From 7aa11a90d901327ee009cc6b7c07a8b8b78b9c99 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 11 Nov 2022 15:19:37 +0100 Subject: [PATCH 25/59] Update disabled tests --- .../CertificateValidationClientServer.cs | 2 +- .../CertificateValidationRemoteServer.cs | 1 + .../tests/FunctionalTests/SslStreamAlpnTests.cs | 2 +- .../FunctionalTests/SslStreamNetworkStreamTest.cs | 15 ++++++++++----- .../tests/FunctionalTests/SslStreamSniTest.cs | 4 ++-- .../tests/FunctionalTests/TransportContextTest.cs | 2 +- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 494bcf86fc226..6c7972b5ed4cc 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -40,7 +40,7 @@ public void Dispose() [InlineData(false, true)] [InlineData(true, false)] [InlineData(false, false)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task CertificateSelectionCallback_DelayedCertificate_OK(bool delayCertificate, bool sendClientCertificate) { X509Certificate? remoteCertificate = null; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs index 2d4b1ba09ff08..88b1c6755d8e1 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs @@ -96,6 +96,7 @@ public async Task DefaultConnect_EndToEnd_Ok(string host) [InlineData(true)] [InlineData(false)] [ActiveIssue("https://github.com/dotnet/runtime/issues/70981", TestPlatforms.OSX)] + [ActiveIssue("TODO", TestPlatforms.Android)] public Task ConnectWithRevocation_WithCallback(bool checkRevocation) { X509RevocationMode mode = checkRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs index 8b0e9ab6165ff..9b9c80ae57fb1 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs @@ -130,7 +130,7 @@ public async Task SslStream_StreamToStream_Alpn_Success(List(() => t1); Assert.Contains(errorMessage, e.Message); // Server side should finish since we run custom callback after handshake is done. - await t2; + await t2.WaitAsync(TestConfiguration.PassingTestTimeout); } } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs index a3af787fdcce1..e6032f1be899d 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamSniTest.cs @@ -18,7 +18,7 @@ public class SslStreamSniTest { [Theory] [MemberData(nameof(HostNameData))] - [SkipOnPlatform(TestPlatforms.Android, "SNI isn't set for localhost communication")] + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task SslStream_ClientSendsSNIServerReceives_Ok(string hostName) { using X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate(); @@ -96,7 +96,7 @@ public async Task SslStream_ServerCallbackAndLocalCertificateSelectionSet_Throws [Theory] [MemberData(nameof(HostNameData))] - [ActiveIssue("TODO", TestPlatforms.Android)] // only one test case is failing - the one with hostname 'żółć gęślą jaźń. 红烧. 照り焼き' + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task SslStream_ServerCallbackNotSet_UsesLocalCertificateSelection(string hostName) { using X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate(); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs index 4535fcaf7db26..1d148cd4fe431 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs @@ -14,7 +14,7 @@ namespace System.Net.Security.Tests public class TransportContextTest { [Fact] - [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] + [ActiveIssue("TODO", TestPlatforms.Android)] public async Task TransportContext_ConnectToServerWithSsl_GetExpectedChannelBindings() { (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams(); From 405dd0c6a4eb244f1d07b8885d923de4658a6108 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 14 Nov 2022 11:33:28 +0100 Subject: [PATCH 26/59] Renaming --- src/tests/Directory.Build.targets | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tests/Directory.Build.targets b/src/tests/Directory.Build.targets index 5607643538d80..4826a4b8594ce 100644 --- a/src/tests/Directory.Build.targets +++ b/src/tests/Directory.Build.targets @@ -170,8 +170,8 @@ - - + + @@ -179,7 +179,7 @@ - + From 0e86a019043dee985e5b1b701733b0c9bec618b9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 14 Nov 2022 12:13:58 +0100 Subject: [PATCH 27/59] Remove network security config workarounds --- .../msbuild/android/build/AndroidApp.targets | 13 ++++++------- src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs | 3 --- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 16 +--------------- .../Templates/AndroidManifest.xml | 5 ++--- src/tests/build.proj | 1 - 5 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/mono/msbuild/android/build/AndroidApp.targets b/src/mono/msbuild/android/build/AndroidApp.targets index 6fd26641cd1e1..e3e2b0e4b9357 100644 --- a/src/mono/msbuild/android/build/AndroidApp.targets +++ b/src/mono/msbuild/android/build/AndroidApp.targets @@ -1,5 +1,5 @@ - @@ -45,7 +45,7 @@ - + @@ -63,7 +63,7 @@ - <_AotInputAssemblies Include="@(_AndroidAssembliesInternal)" + <_AotInputAssemblies Include="@(_AndroidAssembliesInternal)" Condition="'%(_AndroidAssembliesInternal._InternalForceInterpret)' != 'true'"> $(AotArguments) $(ProcessArguments) @@ -72,7 +72,7 @@ <_AOT_InternalForceInterpretAssemblies Include="@(_AndroidAssembliesInternal->WithMetadataValue('_InternalForceInterpret', 'true'))" /> <_AndroidAssembliesInternal Remove="@(_AndroidAssembliesInternal)" /> - + @@ -127,7 +127,6 @@ MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackNativeDir)include\mono-2.0" Assemblies="@(_AndroidAssembliesInternal)" MainLibraryFileName="$(MainLibraryFileName)" - IncludeNetworkSecurityConfig="$(IncludeNetworkSecurityConfig)" EnvironmentVariables="@(AndroidEnv)" ForceAOT="$(RunAOTCompilation)" ForceFullAOT="$(ForceFullAOT)" @@ -147,7 +146,7 @@ - + - \ No newline at end of file + diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs index 0f409c1745f7c..bf10dc962c942 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs @@ -85,8 +85,6 @@ public class AndroidAppBuilderTask : Task /// public string? NativeMainSource { get; set; } - public bool IncludeNetworkSecurityConfig { get; set; } - public string? KeyStorePath { get; set; } public bool ForceInterpreter { get; set; } @@ -112,7 +110,6 @@ public override bool Execute() apkBuilder.BuildToolsVersion = BuildToolsVersion; apkBuilder.StripDebugSymbols = StripDebugSymbols; apkBuilder.NativeMainSource = NativeMainSource; - apkBuilder.IncludeNetworkSecurityConfig = IncludeNetworkSecurityConfig; apkBuilder.KeyStorePath = KeyStorePath; apkBuilder.ForceInterpreter = ForceInterpreter; apkBuilder.ForceAOT = ForceAOT; diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index e38a8ac54dc3e..057591c9d3812 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -24,7 +24,6 @@ public class ApkBuilder public string OutputDir { get; set; } = ""!; public bool StripDebugSymbols { get; set; } public string? NativeMainSource { get; set; } - public bool IncludeNetworkSecurityConfig { get; set; } public string? KeyStorePath { get; set; } public bool ForceInterpreter { get; set; } public bool ForceAOT { get; set; } @@ -59,12 +58,6 @@ public ApkBuilder(TaskLoggingHelper logger) throw new ArgumentException($"MainLibraryFileName='{mainLibraryFileName}' was not found in AppDir='{AppDir}'"); } - var networkSecurityConfigFilePath = Path.Combine(AppDir, "res", "xml", "network_security_config.xml"); - if (IncludeNetworkSecurityConfig && !File.Exists(networkSecurityConfigFilePath)) - { - throw new ArgumentException($"IncludeNetworkSecurityConfig is set but the file '{networkSecurityConfigFilePath}' was not found"); - } - if (string.IsNullOrEmpty(abi)) { throw new ArgumentException("abi should not be empty (e.g. x86, x86_64, armeabi-v7a or arm64-v8a"); @@ -399,11 +392,6 @@ public ApkBuilder(TaskLoggingHelper logger) if (!string.IsNullOrEmpty(NativeMainSource)) File.Copy(NativeMainSource, javaActivityPath, true); - string networkSecurityConfigAttribute = - IncludeNetworkSecurityConfig - ? "a:networkSecurityConfig=\"@xml/network_security_config\"" - : string.Empty; - string envVariables = ""; foreach (ITaskItem item in EnvironmentVariables) { @@ -421,7 +409,6 @@ public ApkBuilder(TaskLoggingHelper logger) File.WriteAllText(Path.Combine(OutputDir, "AndroidManifest.xml"), Utils.GetEmbeddedResource("AndroidManifest.xml") .Replace("%PackageName%", packageId) - .Replace("%NetworkSecurityConfig%", networkSecurityConfigAttribute) .Replace("%MinSdkLevel%", MinApiLevel)); string javaCompilerArgs = $"-d obj -classpath src -bootclasspath {androidJar} -source 1.8 -target 1.8 "; @@ -446,8 +433,7 @@ public ApkBuilder(TaskLoggingHelper logger) string debugModeArg = StripDebugSymbols ? string.Empty : "--debug-mode"; string apkFile = Path.Combine(OutputDir, "bin", $"{ProjectName}.unaligned.apk"); - string resources = IncludeNetworkSecurityConfig ? "-S res" : string.Empty; - Utils.RunProcess(logger, aapt, $"package -f -m -F {apkFile} -A assets {resources} -M AndroidManifest.xml -I {androidJar} {debugModeArg}", workingDir: OutputDir); + Utils.RunProcess(logger, aapt, $"package -f -m -F {apkFile} -A assets -M AndroidManifest.xml -I {androidJar} {debugModeArg}", workingDir: OutputDir); var dynamicLibs = new List(); dynamicLibs.Add(Path.Combine(OutputDir, "monodroid", "libmonodroid.so")); diff --git a/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml b/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml index befd2e446a650..30ded9e136048 100644 --- a/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml +++ b/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml @@ -1,5 +1,5 @@ - @@ -8,7 +8,6 @@ @@ -21,4 +20,4 @@ - \ No newline at end of file + diff --git a/src/tests/build.proj b/src/tests/build.proj index ddf5bc302e1e1..da4e54296bdc9 100644 --- a/src/tests/build.proj +++ b/src/tests/build.proj @@ -234,7 +234,6 @@ RuntimeIdentifier="$(RuntimeIdentifier)" ProjectName="$(Category)" MonoRuntimeHeaders="$(MicrosoftNetCoreAppRuntimePackDir)/native/include/mono-2.0" - IncludeNetworkSecurityConfig="$(IncludeNetworkSecurityConfig)" RuntimeComponents="$(RuntimeComponents)" DiagnosticPorts="$(DiagnosticPorts)" StripDebugSymbols="$(StripDebugSymbols)" From b4f0e81b7c309d2a38bfc133da90738d106d7701 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 14 Nov 2022 12:31:17 +0100 Subject: [PATCH 28/59] Keep existing active issue --- .../FunctionalTests/CertificateValidationClientServer.cs | 2 +- .../FunctionalTests/CertificateValidationRemoteServer.cs | 2 +- .../FunctionalTests/SslStreamAllowRenegotiationTests.cs | 2 +- .../tests/FunctionalTests/SslStreamAlpnTests.cs | 2 +- .../tests/FunctionalTests/SslStreamNetworkStreamTest.cs | 6 +++--- .../tests/FunctionalTests/SslStreamSniTest.cs | 4 ++-- .../tests/FunctionalTests/TransportContextTest.cs | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 6c7972b5ed4cc..494bcf86fc226 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -40,7 +40,7 @@ public void Dispose() [InlineData(false, true)] [InlineData(true, false)] [InlineData(false, false)] - [ActiveIssue("TODO", TestPlatforms.Android)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public async Task CertificateSelectionCallback_DelayedCertificate_OK(bool delayCertificate, bool sendClientCertificate) { X509Certificate? remoteCertificate = null; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs index 88b1c6755d8e1..1b0f91c934145 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs @@ -96,7 +96,7 @@ public async Task DefaultConnect_EndToEnd_Ok(string host) [InlineData(true)] [InlineData(false)] [ActiveIssue("https://github.com/dotnet/runtime/issues/70981", TestPlatforms.OSX)] - [ActiveIssue("TODO", TestPlatforms.Android)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public Task ConnectWithRevocation_WithCallback(bool checkRevocation) { X509RevocationMode mode = checkRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs index 6e8d9d68623c6..b32a424abfde8 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs @@ -70,7 +70,7 @@ public async Task SslStream_AllowRenegotiation_True_Succeeds() [Fact] [OuterLoop] // Test hits external azure server. - [ActiveIssue("TODO", TestPlatforms.Android)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public async Task SslStream_AllowRenegotiation_False_Throws() { Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs index 9b9c80ae57fb1..8b0e9ab6165ff 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlpnTests.cs @@ -130,7 +130,7 @@ public async Task SslStream_StreamToStream_Alpn_Success(List Date: Mon, 14 Nov 2022 12:36:08 +0100 Subject: [PATCH 29/59] Remove unnecessary changes --- .../FunctionalTests/SslStreamNetworkStreamTest.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs index 9034336cdc55b..4ebb5d05cdce8 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamNetworkStreamTest.cs @@ -829,17 +829,12 @@ public async Task SslStream_UntrustedCaWithCustomCallback_Throws(bool customCall return false; }; - errorMessage = PlatformDetection.IsAndroid ? "Authentication failed, see inner exception." : "RemoteCertificateValidationCallback"; + errorMessage = "RemoteCertificateValidationCallback"; } else { // On Windows we hand whole chain to OS so they can always see the root CA. - errorMessage = - PlatformDetection.IsWindows - ? "UntrustedRoot" - : PlatformDetection.IsAndroid - ? "Authentication failed, see inner exception." - : "PartialChain"; + errorMessage = PlatformDetection.IsWindows ? "UntrustedRoot" : "PartialChain"; } var serverOptions = new SslServerAuthenticationOptions(); @@ -857,7 +852,7 @@ public async Task SslStream_UntrustedCaWithCustomCallback_Throws(bool customCall var e = await Assert.ThrowsAsync(() => t1); Assert.Contains(errorMessage, e.Message); // Server side should finish since we run custom callback after handshake is done. - await t2.WaitAsync(TestConfiguration.PassingTestTimeout); + await t2; } } From 90ea1480c45dff1827dc071fd927af808e7d52ce Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 14 Nov 2022 14:02:19 +0100 Subject: [PATCH 30/59] Remove unnecessary code --- .../Common/tests/System/Net/Http/TestHelper.cs | 3 --- src/tasks/AndroidAppBuilder/ApkBuilder.cs | 12 ------------ 2 files changed, 15 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/TestHelper.cs b/src/libraries/Common/tests/System/Net/Http/TestHelper.cs index 8525ed8c1b297..fd19ace2704b3 100644 --- a/src/libraries/Common/tests/System/Net/Http/TestHelper.cs +++ b/src/libraries/Common/tests/System/Net/Http/TestHelper.cs @@ -173,9 +173,6 @@ public static SocketsHttpHandler CreateSocketsHttpHandler(bool allowAllCertifica // Browser doesn't support ServerCertificateCustomValidationCallback if (allowAllCertificates && PlatformDetection.IsNotBrowser) { - // On Android, it is not enough to set the custom validation callback, the certificates also need to be trusted by the OS. - // See HttpClientHandlerTestBase.SocketsHttpHandler.cs:CreateHttpClientHandler for more details. - handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; }; } diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 057591c9d3812..ba1b7b3a8cdf0 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -173,7 +173,6 @@ public ApkBuilder(TaskLoggingHelper logger) Directory.CreateDirectory(Path.Combine(OutputDir, "obj")); Directory.CreateDirectory(Path.Combine(OutputDir, "assets-tozip")); Directory.CreateDirectory(Path.Combine(OutputDir, "assets")); - Directory.CreateDirectory(Path.Combine(OutputDir, "res")); var extensionsToIgnore = new List { ".so", ".a", ".dex", ".jar" }; if (StripDebugSymbols) @@ -202,20 +201,9 @@ public ApkBuilder(TaskLoggingHelper logger) // aapt complains on such files return false; } - if (file.Contains("/res/")) - { - // exclude everything in the `res` folder - return false; - } return true; }); - // copy the res directory as is - if (Directory.Exists(Path.Combine(AppDir, "res"))) - { - Utils.DirectoryCopy(Path.Combine(AppDir, "res"), Path.Combine(OutputDir, "res")); - } - // add AOT .so libraries foreach (var aotlib in aotLibraryFiles) { From 343cc65cd692d612b00f4e519458484643e93c3e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 14 Nov 2022 16:33:50 +0100 Subject: [PATCH 31/59] Enable more disabled tests --- .../HttpClientHandlerTest.ServerCertificates.cs | 1 - .../System/Net/Http/HttpClientHandlerTest.cs | 15 ++------------- .../tests/FunctionalTests/SocksProxyTest.cs | 8 +------- 3 files changed, 3 insertions(+), 21 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index ae44393dbc69e..7efd4ed9d5ea2 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -228,7 +228,6 @@ public async Task NoCallback_BadCertificate_ThrowsException(string url) [OuterLoop("Uses external servers")] [ConditionalFact(nameof(ClientSupportsDHECipherSuites))] - [ActiveIssue("TODO", TestPlatforms.Android)] public async Task NoCallback_RevokedCertificate_NoRevocationChecking_Succeeds() { using (HttpClient client = CreateHttpClient()) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index 8cb4ef5fdc104..d072e850b9fcf 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -183,7 +183,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( }, options: options); } - [ConditionalTheory] + [Theory] [MemberData(nameof(GetAsync_IPBasedUri_Success_MemberData))] public async Task GetAsync_IPBasedUri_Success(IPAddress address) { @@ -201,12 +201,6 @@ public async Task GetAsync_IPBasedUri_Success(IPAddress address) using HttpClient client = CreateHttpClient(handler); var options = new GenericLoopbackOptions { Address = address }; - - if (PlatformDetection.IsAndroid && options.UseSsl && address == IPAddress.IPv6Loopback) - { - throw new SkipTestException("IPv6 loopback with SSL doesn't work on Android"); - } - await LoopbackServerFactory.CreateServerAsync(async (server, url) => { _output.WriteLine(url.ToString()); @@ -274,7 +268,7 @@ from useSsl in BoolValues where PlatformDetection.IsNotBrowser || !useSsl select new object[] { address, useSsl }; - [ConditionalTheory] + [Theory] [MemberData(nameof(SecureAndNonSecure_IPBasedUri_MemberData))] public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAddress address, bool useSsl) { @@ -284,11 +278,6 @@ public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAdd return; } - if (PlatformDetection.IsAndroid && useSsl && address == IPAddress.IPv6Loopback) - { - throw new SkipTestException("IPv6 loopback with SSL doesn't work on Android"); - } - var options = new LoopbackServer.Options { Address = address, UseSsl = useSsl }; bool connectionAccepted = false; string host = ""; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index b7e6995f99980..e5e51c641e621 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net.Test.Common; using System.Threading.Tasks; -using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; @@ -26,7 +25,7 @@ from useAuth in BoolValues from host in Hosts(scheme) select new object[] { scheme, useSsl, useAuth, host }; - [ConditionalTheory] + [Theory] [MemberData(nameof(TestLoopbackAsync_MemberData))] public async Task TestLoopbackAsync(string scheme, bool useSsl, bool useAuth, string host) { @@ -35,11 +34,6 @@ public async Task TestLoopbackAsync(string scheme, bool useSsl, bool useAuth, st return; } - if (PlatformDetection.IsAndroid && useSsl && host == "::1") - { - throw new SkipTestException("IPv6 loopback with SSL doesn't work on Android"); - } - await LoopbackServerFactory.CreateClientAndServerAsync( async uri => { From 2dd1ab7184ae74db0fe509c9100e12ffd8d9c06c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 14 Nov 2022 16:34:09 +0100 Subject: [PATCH 32/59] Fix throwing exception --- .../src/System/Net/Security/SslStream.IO.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 8277cc3b74a75..07b9d44f835e2 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -320,6 +320,11 @@ private async Task ForceAuthenticationAsync(bool receiveFirst, byte[ throw new AuthenticationException(SR.Format(SR.net_auth_tls_alert, _lastFrame.AlertDescription.ToString()), message.GetException()); } +#if TARGET_ANDROID + if (_securityContext?.ValidationException is Exception validationException) + throw new AuthenticationException(SR.net_auth_SSPI, validationException); +#endif + throw new AuthenticationException(SR.net_auth_SSPI, message.GetException()); } else if (message.Status.ErrorCode == SecurityStatusPalErrorCode.OK) @@ -508,9 +513,6 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError // we shouldn't run it repeatedly and honor the existing validation result. // It's possible that the validation result isn't set (for example the client didn't send a certificate) // in which case we still need to run the verification. - if (_securityContext?.ValidationException is Exception validationException) - throw new AuthenticationException(SR.net_auth_SSPI, validationException); - if (_securityContext?.ValidationResult is SslStream.JavaProxy.RemoteCertificateValidationResult result) { sslPolicyErrors = result.SslPolicyErrors; From d7d95e05eea6b234a29b4585599e0cba936ce930 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 15 Nov 2022 17:27:35 +0100 Subject: [PATCH 33/59] Fix intptr_t cast to Java --- .../android/crypto/DotnetProxyTrustManager.java | 14 ++++++++++---- .../pal_jni.c | 2 +- .../pal_trust_manager.c | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java index e6c185e657eac..06a13732d0587 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java +++ b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java @@ -4,10 +4,16 @@ import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; -class DotnetProxyTrustManager implements X509TrustManager { - private int sslStreamProxyHandle; +/** + * This class is meant to replace the built-in X509TrustManager. + * Its sole responsibility is to invoke the C# code in the SslStream + * class during TLS handshakes to perform the validation of the remote + * peer's certificate. + */ +public class DotnetProxyTrustManager implements X509TrustManager { + private long sslStreamProxyHandle; - public DotnetProxyTrustManager(int sslStreamProxyHandle) { + public DotnetProxyTrustManager(long sslStreamProxyHandle) { this.sslStreamProxyHandle = sslStreamProxyHandle; } @@ -27,5 +33,5 @@ public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } - static native boolean verifyRemoteCertificate(int sslStreamProxyHandle); + static native boolean verifyRemoteCertificate(long sslStreamProxyHandle); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index ecb3f9ba1265f..a2411250ed392 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -1071,7 +1071,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_X509TrustManager = GetClassGRef(env, "javax/net/ssl/X509TrustManager"); g_DotnetProxyTrustManager = GetClassGRef(env, "net/dot/android/crypto/DotnetProxyTrustManager"); - g_DotnetProxyTrustManagerCtor = GetMethod(env, false, g_DotnetProxyTrustManager, "", "(I)V"); + g_DotnetProxyTrustManagerCtor = GetMethod(env, false, g_DotnetProxyTrustManager, "", "(J)V"); return JNI_VERSION_1_6; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index c2c0ebdc92758..a0e12de0d19e3 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -47,7 +47,7 @@ jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamPro if ((*env)->IsInstanceOf(env, loc[trustManager], g_X509TrustManager)) { - loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (int)sslStreamProxyHandle); + loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, sslStreamProxyHandle); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[dotnetProxyTrustManager]); From aeb571454d4516e568247096956ce801b3561016 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 15 Nov 2022 17:28:05 +0100 Subject: [PATCH 34/59] Remove initialization lock --- .../src/System/Net/Security/SslStream.Android.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index 351f820c6c3e6..320177e69e2d8 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -32,13 +32,12 @@ private JavaProxy.RemoteCertificateValidationResult VerifyRemoteCertificate() internal sealed class JavaProxy : IDisposable { - private static object s_initializationLock = new(); private static bool s_initialized; private readonly SslStream _sslStream; private GCHandle? _handle; - public unsafe JavaProxy(SslStream sslStream) + public JavaProxy(SslStream sslStream) { RegisterRemoteCertificateValidationCallback(); @@ -56,13 +55,10 @@ public IntPtr Handle private static unsafe void RegisterRemoteCertificateValidationCallback() { - lock (s_initializationLock) + if (!s_initialized) { - if (!s_initialized) - { - Interop.AndroidCrypto.RegisterRemoteCertificateValidationCallback(&VerifyRemoteCertificate); - s_initialized = true; - } + Interop.AndroidCrypto.RegisterRemoteCertificateValidationCallback(&VerifyRemoteCertificate); + s_initialized = true; } } From b4b5589c068d53e405a2e2ec8f8608fac1e9b4e5 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 15 Nov 2022 17:32:59 +0100 Subject: [PATCH 35/59] Update naming --- .../src/System/Net/Security/SslStream.IO.cs | 2 +- .../src/System/Net/Security/SslStream.Protocol.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 07b9d44f835e2..347519f3db92a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -513,7 +513,7 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError // we shouldn't run it repeatedly and honor the existing validation result. // It's possible that the validation result isn't set (for example the client didn't send a certificate) // in which case we still need to run the verification. - if (_securityContext?.ValidationResult is SslStream.JavaProxy.RemoteCertificateValidationResult result) + if (_securityContext?.ValidationResult is JavaProxy.RemoteCertificateValidationResult result) { sslPolicyErrors = result.SslPolicyErrors; chainStatus = result.ChainStatus; diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index ebf1e6a97fad4..86de4965e99a7 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -813,7 +813,7 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte { status = SslStreamPal.AcceptSecurityContext( #if TARGET_ANDROID - new SslStream.JavaProxy(this), + sslStreamProxy: new JavaProxy(this), #endif ref _credentialsHandle!, ref _securityContext, @@ -825,7 +825,7 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte { status = SslStreamPal.InitializeSecurityContext( #if TARGET_ANDROID - new SslStream.JavaProxy(this), + sslStreamProxy: new JavaProxy(this), #endif ref _credentialsHandle!, ref _securityContext, From c16ad798223e45c36cb699933be577318e47f7c2 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 21 Nov 2022 17:19:12 +0100 Subject: [PATCH 36/59] Fix type casting --- .../dot/android/crypto/DotnetProxyTrustManager.java | 10 ++++++---- .../pal_trust_manager.c | 6 +++--- .../pal_trust_manager.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java index 06a13732d0587..38454a3d289d1 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java +++ b/src/native/libs/System.Security.Cryptography.Native.Android/net/dot/android/crypto/DotnetProxyTrustManager.java @@ -10,20 +10,22 @@ * class during TLS handshakes to perform the validation of the remote * peer's certificate. */ -public class DotnetProxyTrustManager implements X509TrustManager { - private long sslStreamProxyHandle; +public final class DotnetProxyTrustManager implements X509TrustManager { + private final long sslStreamProxyHandle; public DotnetProxyTrustManager(long sslStreamProxyHandle) { this.sslStreamProxyHandle = sslStreamProxyHandle; } - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { if (!verifyRemoteCertificate(sslStreamProxyHandle)) { throw new CertificateException(); } } - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { if (!verifyRemoteCertificate(sslStreamProxyHandle)) { throw new CertificateException(); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index a0e12de0d19e3..cfa2583e4d13c 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -47,7 +47,7 @@ jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamPro if ((*env)->IsInstanceOf(env, loc[trustManager], g_X509TrustManager)) { - loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, sslStreamProxyHandle); + loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (jlong)sslStreamProxyHandle); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[dotnetProxyTrustManager]); @@ -68,8 +68,8 @@ jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamPro } jboolean Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( - JNIEnv* env, jobject thisHandle, intptr_t sslStreamProxyHandle) + JNIEnv* env, jobject thisHandle, jlong sslStreamProxyHandle) { abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); - return verifyRemoteCertificate(sslStreamProxyHandle); + return verifyRemoteCertificate((intptr_t)sslStreamProxyHandle); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h index e64dc1fce56f6..bd2375e11fdd4 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -7,4 +7,4 @@ PALEXPORT void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(R jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle); JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( - JNIEnv *env, jobject thisHandle, intptr_t sslStreamProxyHandle); + JNIEnv *env, jobject thisHandle, jlong sslStreamProxyHandle); From 446d4a3628f63a3db6720bba7a1cc3b923b24054 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 22 Nov 2022 13:55:28 +0100 Subject: [PATCH 37/59] Improve throwing validation exception --- .../Pal.Android/SafeDeleteSslContext.cs | 5 ++--- .../src/System/Net/Security/SslStream.IO.cs | 18 ++++++++---------- .../Net/Security/SslStreamPal.Android.cs | 5 ++++- 3 files changed, 14 insertions(+), 14 deletions(-) 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 dde0425e3997d..aa2f74a18b629 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 @@ -30,14 +30,13 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext private static readonly Lazy s_supportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); private readonly SafeSslHandle _sslContext; - private readonly SslStream.JavaProxy? _sslStreamProxy; + private readonly SslStream.JavaProxy _sslStreamProxy; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SafeSslHandle SslContext => _sslContext; - public Exception? ValidationException => _sslStreamProxy?.ValidationException; - public SslStream.JavaProxy.RemoteCertificateValidationResult? ValidationResult => _sslStreamProxy?.ValidationResult; + public SslStream.JavaProxy SslStreamProxy => _sslStreamProxy; public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 347519f3db92a..4eac8bc65cec7 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -320,11 +320,6 @@ private async Task ForceAuthenticationAsync(bool receiveFirst, byte[ throw new AuthenticationException(SR.Format(SR.net_auth_tls_alert, _lastFrame.AlertDescription.ToString()), message.GetException()); } -#if TARGET_ANDROID - if (_securityContext?.ValidationException is Exception validationException) - throw new AuthenticationException(SR.net_auth_SSPI, validationException); -#endif - throw new AuthenticationException(SR.net_auth_SSPI, message.GetException()); } else if (message.Status.ErrorCode == SecurityStatusPalErrorCode.OK) @@ -509,11 +504,14 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError } #if TARGET_ANDROID - // If the remote certificate verification has already been invoked from Java TrustManager's callback - // we shouldn't run it repeatedly and honor the existing validation result. - // It's possible that the validation result isn't set (for example the client didn't send a certificate) - // in which case we still need to run the verification. - if (_securityContext?.ValidationResult is JavaProxy.RemoteCertificateValidationResult result) + // On Android, the remote certificate verification can be invoked from Java TrustManager's callback + // during the handshake process. If that has occurred, we shouldn't run the validation again and + // return the existing validation result. + // + // The Java TrustManager callback is called only when the peer has a certificate. It's possible that + // the peer didn't provide any certificate (for example when the peer is the client) and the validation + // result hasn't been set. In that case we still need to run the verification at this point. + if (_securityContext?.SslStreamProxy.ValidationResult is JavaProxy.RemoteCertificateValidationResult result) { sslPolicyErrors = result.SslPolicyErrors; chainStatus = result.ChainStatus; 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 29a93c78226e9..53df642052b40 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 @@ -124,7 +124,10 @@ public static SecurityStatusPal DecryptMessage( PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer, out int read); if (ret == PAL_SSLStreamStatus.Error) - return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); + { + Exception? validationException = securityContext.SslStreamProxy.ValidationException; + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, validationException); + } count = read; From 4b4af021c1336dc4b484cb9186d491d220da7acc Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 23 Nov 2022 12:51:06 +0100 Subject: [PATCH 38/59] Experiment with code structure --- .../Pal.Android/SafeDeleteSslContext.cs | 48 +++++++++++++++---- .../System/Net/Security/SslStream.Android.cs | 42 ++++++++++------ .../src/System/Net/Security/SslStream.IO.cs | 8 ++-- .../System/Net/Security/SslStream.Protocol.cs | 14 +++--- .../Net/Security/SslStreamPal.Android.cs | 28 +++++------ .../CertificateValidationRemoteServer.cs | 1 - 6 files changed, 91 insertions(+), 50 deletions(-) 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 aa2f74a18b629..ded27fda9c68d 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 @@ -29,22 +29,31 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext }; private static readonly Lazy s_supportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); - private readonly SafeSslHandle _sslContext; - private readonly SslStream.JavaProxy _sslStreamProxy; + private readonly SafeSslHandle? _sslContext; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); - public SafeSslHandle SslContext => _sslContext; - public SslStream.JavaProxy SslStreamProxy => _sslStreamProxy; + public SslStream.JavaProxy SslStreamProxy { get; } + + public SafeSslHandle SslContext + { + get + { + if (_sslContext is null) + throw new InvalidOperationException($"{nameof(SafeDeleteSslContext)} is not initialized"); + + return _sslContext; + } + } public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) : base(IntPtr.Zero) { - _sslStreamProxy = sslStreamProxy; + SslStreamProxy = sslStreamProxy; try { - _sslContext = CreateSslContext(_sslStreamProxy, authOptions); + _sslContext = CreateSslContext(SslStreamProxy, authOptions); InitializeSslContext(_sslContext, authOptions); } catch (Exception ex) @@ -55,21 +64,35 @@ public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticatio } } + public static SafeDeleteSslContext CreateContextStub(SslStream.JavaProxy sslStreamProxy) + { + // This is a stub context that is used to pass the reference to the SslStream + // without changing the existing PAL API (SslStreamPal.AcceptSecurityContext and + // SslStreamPal.InitializeSecurityContext). The stub context cannot be used for any + //other purpose and accessing the SslContext property of the stub will throw if used. + return new SafeDeleteSslContext(sslStreamProxy); + } + + private SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy) : base(IntPtr.Zero) + { + SslStreamProxy = sslStreamProxy; + _sslContext = null; + } + public override bool IsInvalid => _sslContext?.IsInvalid ?? true; protected override void Dispose(bool disposing) { if (disposing) { - SafeSslHandle sslContext = _sslContext; - if (sslContext != null) + if (_sslContext is SafeSslHandle sslContext) { _inputBuffer.Dispose(); _outputBuffer.Dispose(); sslContext.Dispose(); } - _sslStreamProxy?.Dispose(); + SslStreamProxy?.Dispose(); } base.Dispose(disposing); @@ -80,6 +103,7 @@ private static unsafe void WriteToConnection(IntPtr connection, byte* data, int { SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target; Debug.Assert(context != null); + Debug.Assert(context._sslContext != null); var inputBuffer = new ReadOnlySpan(data, dataLength); @@ -93,6 +117,7 @@ private static unsafe PAL_SSLStreamStatus ReadFromConnection(IntPtr connection, { SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target; Debug.Assert(context != null); + Debug.Assert(context._sslContext != null); int toRead = *dataLength; if (toRead == 0) @@ -115,6 +140,8 @@ private static unsafe PAL_SSLStreamStatus ReadFromConnection(IntPtr connection, internal void Write(ReadOnlySpan buf) { + Debug.Assert(_sslContext != null); + _inputBuffer.EnsureAvailableSpace(buf.Length); buf.CopyTo(_inputBuffer.AvailableSpan); _inputBuffer.Commit(buf.Length); @@ -124,6 +151,8 @@ internal void Write(ReadOnlySpan buf) internal byte[]? ReadPendingWrites() { + Debug.Assert(_sslContext != null); + if (_outputBuffer.ActiveLength == 0) { return null; @@ -137,6 +166,7 @@ internal void Write(ReadOnlySpan buf) internal int ReadPendingWrites(byte[] buf, int offset, int count) { + Debug.Assert(_sslContext != null); Debug.Assert(buf != null); Debug.Assert(offset >= 0); Debug.Assert(count >= 0); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index 320177e69e2d8..9a3fbdb9bbfd7 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -12,6 +12,9 @@ namespace System.Net.Security { public partial class SslStream { + private SafeDeleteSslContext CreateAndroidSecurityContextStub() + => SafeDeleteSslContext.CreateContextStub(sslStreamProxy: new JavaProxy(this)); + private JavaProxy.RemoteCertificateValidationResult VerifyRemoteCertificate() { ProtocolToken? alertToken = null; @@ -27,9 +30,20 @@ private JavaProxy.RemoteCertificateValidationResult VerifyRemoteCertificate() IsValid = isValid, SslPolicyErrors = sslPolicyErrors, ChainStatus = chainStatus, + AlertToken = alertToken, }; } + private bool TryGetRemoteCertificateValidationResult(out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus, out ProtocolToken? alertToken, out bool isValid) + { + JavaProxy.RemoteCertificateValidationResult? validationResult = _securityContext?.SslStreamProxy.ValidationResult; + sslPolicyErrors = validationResult?.SslPolicyErrors ?? default; + chainStatus = validationResult?.ChainStatus ?? default; + isValid = validationResult?.IsValid ?? default; + alertToken = validationResult?.AlertToken; + return validationResult is not null; + } + internal sealed class JavaProxy : IDisposable { private static bool s_initialized; @@ -37,14 +51,6 @@ internal sealed class JavaProxy : IDisposable private readonly SslStream _sslStream; private GCHandle? _handle; - public JavaProxy(SslStream sslStream) - { - RegisterRemoteCertificateValidationCallback(); - - _sslStream = sslStream; - _handle = GCHandle.Alloc(this); - } - public IntPtr Handle => _handle is GCHandle handle ? GCHandle.ToIntPtr(handle) @@ -53,13 +59,12 @@ public IntPtr Handle public Exception? ValidationException { get; private set; } public RemoteCertificateValidationResult? ValidationResult { get; private set; } - private static unsafe void RegisterRemoteCertificateValidationCallback() + public JavaProxy(SslStream sslStream) { - if (!s_initialized) - { - Interop.AndroidCrypto.RegisterRemoteCertificateValidationCallback(&VerifyRemoteCertificate); - s_initialized = true; - } + RegisterRemoteCertificateValidationCallback(); + + _sslStream = sslStream; + _handle = GCHandle.Alloc(this); } public void Dispose() @@ -67,6 +72,14 @@ public void Dispose() _handle?.Free(); _handle = null; } + private static unsafe void RegisterRemoteCertificateValidationCallback() + { + if (!s_initialized) + { + Interop.AndroidCrypto.RegisterRemoteCertificateValidationCallback(&VerifyRemoteCertificate); + s_initialized = true; + } + } [UnmanagedCallersOnly] private static unsafe bool VerifyRemoteCertificate(IntPtr sslStreamProxyHandle) @@ -92,6 +105,7 @@ internal sealed class RemoteCertificateValidationResult public bool IsValid { get; init; } public SslPolicyErrors SslPolicyErrors { get; init; } public X509ChainStatusFlags ChainStatus { get; init; } + public ProtocolToken? AlertToken { get; init; } } } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 4eac8bc65cec7..777cbeabbbb02 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -511,12 +511,10 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError // The Java TrustManager callback is called only when the peer has a certificate. It's possible that // the peer didn't provide any certificate (for example when the peer is the client) and the validation // result hasn't been set. In that case we still need to run the verification at this point. - if (_securityContext?.SslStreamProxy.ValidationResult is JavaProxy.RemoteCertificateValidationResult result) + if (TryGetRemoteCertificateValidationResult(out sslPolicyErrors, out chainStatus, out alertToken, out bool isValid)) { - sslPolicyErrors = result.SslPolicyErrors; - chainStatus = result.ChainStatus; - _handshakeCompleted = result.IsValid; - return result.IsValid; + _handshakeCompleted = isValid; + return isValid; } #endif diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 86de4965e99a7..2b651ce723ed1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -809,12 +809,17 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte } } +#if TARGET_ANDROID + if (_securityContext is null) + { + _securityContext = CreateAndroidSecurityContextStub(); + Debug.Assert(_securityContext.IsInvalid); + } +#endif + if (_sslAuthenticationOptions.IsServer) { status = SslStreamPal.AcceptSecurityContext( -#if TARGET_ANDROID - sslStreamProxy: new JavaProxy(this), -#endif ref _credentialsHandle!, ref _securityContext, inputBuffer, @@ -824,9 +829,6 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte else { status = SslStreamPal.InitializeSecurityContext( -#if TARGET_ANDROID - sslStreamProxy: new JavaProxy(this), -#endif ref _credentialsHandle!, ref _securityContext, _sslAuthenticationOptions.TargetHost, 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 53df642052b40..4c758321c9fb2 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 @@ -27,27 +27,25 @@ public static void VerifyPackageInfo() } public static SecurityStatusPal AcceptSecurityContext( - SslStream.JavaProxy sslStreamProxy, ref SafeFreeCredentials credential, - ref SafeDeleteSslContext? context, + ref SafeDeleteSslContext context, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(sslStreamProxy, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal InitializeSecurityContext( - SslStream.JavaProxy sslStreamProxy, ref SafeFreeCredentials credential, - ref SafeDeleteSslContext? context, + ref SafeDeleteSslContext context, string? targetName, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate? clientCertificateSelectionCallback) { - return HandshakeInternal(sslStreamProxy, credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal Renegotiate( @@ -125,8 +123,7 @@ public static SecurityStatusPal DecryptMessage( PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer, out int read); if (ret == PAL_SSLStreamStatus.Error) { - Exception? validationException = securityContext.SslStreamProxy.ValidationException; - return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, validationException); + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); } count = read; @@ -175,20 +172,20 @@ public static void QueryContextConnectionInfo( } private static SecurityStatusPal HandshakeInternal( - SslStream.JavaProxy sslStreamProxy, - SafeFreeCredentials credential, - ref SafeDeleteSslContext? context, + ref SafeDeleteSslContext context, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { + ArgumentNullException.ThrowIfNull(context); + try { - SafeDeleteSslContext? sslContext = ((SafeDeleteSslContext?)context); + SafeDeleteSslContext sslContext = ((SafeDeleteSslContext)context); - if ((context == null) || context.IsInvalid) + if (context.IsInvalid) { - context = new SafeDeleteSslContext(sslStreamProxy, sslAuthenticationOptions); + context = new SafeDeleteSslContext(context.SslStreamProxy, sslAuthenticationOptions); sslContext = context; } @@ -209,7 +206,8 @@ private static SecurityStatusPal HandshakeInternal( outputBuffer = sslContext.ReadPendingWrites(); - return new SecurityStatusPal(statusCode); + Exception? validationException = sslContext?.SslStreamProxy.ValidationException; + return new SecurityStatusPal(statusCode, validationException); } catch (Exception exc) { diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs index 1b0f91c934145..2d4b1ba09ff08 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs @@ -96,7 +96,6 @@ public async Task DefaultConnect_EndToEnd_Ok(string host) [InlineData(true)] [InlineData(false)] [ActiveIssue("https://github.com/dotnet/runtime/issues/70981", TestPlatforms.OSX)] - [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public Task ConnectWithRevocation_WithCallback(bool checkRevocation) { X509RevocationMode mode = checkRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck; From eb41d22feb19d98a064eb5035e0e5b0b578cfb71 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 24 Nov 2022 18:01:20 +0100 Subject: [PATCH 39/59] Fix repeated calls to beginHandshake --- .../pal_jni.c | 2 +- .../pal_sslstream.c | 134 +++++++++++------- .../pal_sslstream.h | 1 - 3 files changed, 80 insertions(+), 57 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index a2411250ed392..859658dc3ed49 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -1014,7 +1014,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); g_SSLEngineBeginHandshake = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); g_SSLEngineCloseOutbound = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); - g_SSLEngineGetHandshakeSession = GetMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); + g_SSLEngineGetHandshakeSession = GetOptionalMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetApplicationProtocol = GetOptionalMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index b43578f57b683..f9b12606efe17 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -41,6 +41,28 @@ static bool IsHandshaking(int handshakeStatus) return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING && handshakeStatus != HANDSHAKE_STATUS__FINISHED; } +static jobject GetSslSession(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) +{ + jobject sslSession = IsHandshaking(handshakeStatus) + ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) + : (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession); + + if (CheckJNIExceptions(env)) + return NULL; + + return sslSession; +} + +static jobject GetCurrentSslSession(JNIEnv* env, SSLStream* sslStream) +{ + int handshakeStatus = + GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); + if (CheckJNIExceptions(env)) + return NULL; + + return GetSslSession(env, sslStream, handshakeStatus); +} + ARGS_NON_NULL_ALL static PAL_SSLStreamStatus Close(JNIEnv* env, SSLStream* sslStream) { // Call wrap to clear any remaining data before closing @@ -149,11 +171,17 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslS } case STATUS__BUFFER_OVERFLOW: { + jobject sslSession = GetSslSession(env, sslStream, *handshakeStatus); + if (sslSession == NULL) + return SSLStreamStatus_Error; + // Expand buffer // int newCapacity = sslSession.getPacketBufferSize() + netOutBuffer.remaining(); - int32_t newCapacity = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize) + + int32_t newCapacity = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetPacketBufferSize) + (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferRemaining); sslStream->netOutBuffer = ExpandBuffer(env, sslStream->netOutBuffer, newCapacity); + + ReleaseLRef(env, sslSession); return SSLStreamStatus_OK; } default: @@ -221,20 +249,32 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss } case STATUS__BUFFER_UNDERFLOW: { + jobject sslSession = GetSslSession(env, sslStream, *handshakeStatus); + if (sslSession == NULL) + return SSLStreamStatus_Error; + // Expand buffer // int newRemaining = sslSession.getPacketBufferSize(); - int32_t newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); + int32_t newRemaining = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetPacketBufferSize); sslStream->netInBuffer = EnsureRemaining(env, sslStream->netInBuffer, newRemaining); + + ReleaseLRef(env, sslSession); return SSLStreamStatus_OK; } case STATUS__BUFFER_OVERFLOW: { + jobject sslSession = GetSslSession(env, sslStream, *handshakeStatus); + if (sslSession == NULL) + return SSLStreamStatus_Error; + // Expand buffer // int newCapacity = sslSession.getApplicationBufferSize() + appInBuffer.remaining(); int32_t newCapacity = - (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize) + + (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetApplicationBufferSize) + (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); sslStream->appInBuffer = ExpandBuffer(env, sslStream->appInBuffer, newCapacity); + + ReleaseLRef(env, sslSession); return SSLStreamStatus_OK; } default: @@ -276,7 +316,6 @@ ARGS_NON_NULL_ALL static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) { ReleaseGRef(env, sslStream->sslContext); ReleaseGRef(env, sslStream->sslEngine); - ReleaseGRef(env, sslStream->sslSession); ReleaseGRef(env, sslStream->appOutBuffer); ReleaseGRef(env, sslStream->netOutBuffer); ReleaseGRef(env, sslStream->netInBuffer); @@ -487,7 +526,6 @@ int32_t AndroidCryptoNative_SSLStreamInitialize( abort_if_invalid_pointer_argument (sslStream); abort_unless(sslStream->sslContext != NULL, "sslContext is NULL in SSL stream"); abort_unless(sslStream->sslEngine == NULL, "sslEngine is NOT NULL in SSL stream"); - abort_unless(sslStream->sslSession == NULL, "sslSession is NOT NULL in SSL stream"); int32_t ret = FAIL; JNIEnv* env = GetJNIEnv(); @@ -501,13 +539,14 @@ int32_t AndroidCryptoNative_SSLStreamInitialize( ON_EXCEPTION_PRINT_AND_GOTO(exit); // SSLSession sslSession = sslEngine.getSession(); - sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession)); + jobject sslSession = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession); // int applicationBufferSize = sslSession.getApplicationBufferSize(); // int packetBufferSize = sslSession.getPacketBufferSize(); - int32_t applicationBufferSize = - (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize); - int32_t packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); + int32_t applicationBufferSize = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetApplicationBufferSize); + int32_t packetBufferSize = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetPacketBufferSize); + + ReleaseLRef(env, sslSession); // ByteBuffer appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appBufferSize)); // ByteBuffer appOutBuffer = ByteBuffer.allocate(appBufferSize); @@ -580,11 +619,18 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream) abort_if_invalid_pointer_argument (sslStream); JNIEnv* env = GetJNIEnv(); - // sslEngine.beginHandshake(); - (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshake); + int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); if (CheckJNIExceptions(env)) return SSLStreamStatus_Error; + if (!IsHandshaking(handshakeStatus)) + { + // sslEngine.beginHandshake(); + (*env)->CallVoidMethod(env, sslStream->sslEngine, g_SSLEngineBeginHandshake); + if (CheckJNIExceptions(env)) + return SSLStreamStatus_Error; + } + return DoHandshake(env, sslStream); } @@ -760,16 +806,18 @@ int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream* sslStream, uint16 JNIEnv* env = GetJNIEnv(); int32_t ret = FAIL; *out = NULL; + INIT_LOCALS(loc, sslSession, cipherSuite); // String cipherSuite = sslSession.getCipherSuite(); - jstring cipherSuite = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetCipherSuite); + loc[sslSession] = GetCurrentSslSession(env, sslStream); + loc[cipherSuite] = (*env)->CallObjectMethod(env, loc[sslSession], g_SSLSessionGetCipherSuite); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - *out = AllocateString(env, cipherSuite); + *out = AllocateString(env, loc[cipherSuite]); ret = SUCCESS; cleanup: - (*env)->DeleteLocalRef(env, cipherSuite); + RELEASE_LOCALS(loc, env); return ret; } @@ -781,44 +829,32 @@ int32_t AndroidCryptoNative_SSLStreamGetProtocol(SSLStream* sslStream, uint16_t* JNIEnv* env = GetJNIEnv(); int32_t ret = FAIL; *out = NULL; + INIT_LOCALS(loc, sslSession, protocol); // String protocol = sslSession.getProtocol(); - jstring protocol = (*env)->CallObjectMethod(env, sslStream->sslSession, g_SSLSessionGetProtocol); + loc[sslSession] = GetCurrentSslSession(env, sslStream); + loc[protocol] = (*env)->CallObjectMethod(env, loc[sslSession], g_SSLSessionGetProtocol); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - *out = AllocateString(env, protocol); + *out = AllocateString(env, loc[protocol]); ret = SUCCESS; cleanup: - (*env)->DeleteLocalRef(env, protocol); + RELEASE_LOCALS(loc, env); return ret; } -ARGS_NON_NULL_ALL static jobject getPeerCertificates(JNIEnv* env, SSLStream* sslStream) +ARGS_NON_NULL_ALL static jobject GetPeerCertificates(JNIEnv* env, SSLStream* sslStream) { - jobject certificates = NULL; - jobject sslSession = NULL; - bool isHandshaking = false; - - // During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates - // which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLession. - - int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - isHandshaking = IsHandshaking(handshakeStatus); - sslSession = isHandshaking - ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) - : sslStream->sslSession; - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + jobject sslSession = GetCurrentSslSession(env, sslStream); + if (sslSession == NULL) + return NULL; - certificates = (*env)->CallObjectMethod(env, sslSession, g_SSLSessionGetPeerCertificates); + jobject certificates = (*env)->CallObjectMethod(env, sslSession, g_SSLSessionGetPeerCertificates); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); cleanup: - if (isHandshaking) - ReleaseLRef(env, sslSession); - + ReleaseLRef(env, sslSession); return certificates; } @@ -830,7 +866,7 @@ jobject /*X509Certificate*/ AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLS jobject ret = NULL; // Certificate[] certs = sslSession.getPeerCertificates(); - jobjectArray certs = getPeerCertificates(env, sslStream); + jobjectArray certs = GetPeerCertificates(env, sslStream); if (certs == NULL) goto cleanup; @@ -859,7 +895,7 @@ void AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobj *outLen = 0; // Certificate[] certs = sslSession.getPeerCertificates(); - jobjectArray certs = getPeerCertificates(env, sslStream); + jobjectArray certs = GetPeerCertificates(env, sslStream); if (!certs) goto cleanup; @@ -993,19 +1029,10 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hos JNIEnv* env = GetJNIEnv(); bool ret = false; - INIT_LOCALS(loc, name, verifier); - - // During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates - // which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLession. + INIT_LOCALS(loc, name, verifier, sslSession); - int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); - bool isHandshaking = IsHandshaking(handshakeStatus); - - jobject sslSession = isHandshaking - ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) - : sslStream->sslSession; - - if (CheckJNIExceptions(env)) + loc[sslSession] = GetCurrentSslSession(env, sslStream); + if (loc[sslSession] == NULL) return false; // HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); @@ -1013,12 +1040,9 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hos loc[name] = make_java_string(env, hostname); loc[verifier] = (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); - ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], sslSession); + ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], loc[sslSession]); RELEASE_LOCALS(loc, env); - if (isHandshaking) - ReleaseLRef(env, sslSession); - return ret; } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h index 01a5a7b766c99..7cd6c4efa86b0 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -16,7 +16,6 @@ typedef struct SSLStream { jobject sslContext; jobject sslEngine; - jobject sslSession; jobject appOutBuffer; jobject netOutBuffer; jobject appInBuffer; From c2038acba6a88d9b5414efd505fdaac5f2ce7cb2 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 29 Nov 2022 15:12:34 +0100 Subject: [PATCH 40/59] Make SslStream proxy mandatory --- .../pal_sslstream.c | 26 +++++++++---------- .../pal_trust_manager.c | 11 ++++---- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index f9b12606efe17..31b529e43c5e1 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -366,6 +366,8 @@ ARGS_NON_NULL_ALL static jobject GetKeyStoreInstance(JNIEnv* env) SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t sslStreamProxyHandle) { + abort_unless(sslStreamProxyHandle != 0, "invalid pointer to the .NET SslStream proxy"); + SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); @@ -375,13 +377,10 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t sslStreamProxyHandle) if (!loc[sslContext]) goto cleanup; - if (sslStreamProxyHandle != 0) - { - // Init trust managers - loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); - if (!loc[trustManagers]) - goto cleanup; - } + // Init trust managers + loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); + if (!loc[trustManagers]) + goto cleanup; // Init the SSLContext (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, NULL, loc[trustManagers], NULL); @@ -466,6 +465,8 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStrea jobject* /*X509Certificate[]*/ certs, int32_t certsLen) { + abort_unless(sslStreamProxyHandle != 0, "invalid pointer to the .NET SslStream proxy"); + SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); @@ -499,13 +500,10 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStrea loc[keyManagers] = (*env)->CallObjectMethod(env, loc[kmf], g_KeyManagerFactoryGetKeyManagers); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - if (sslStreamProxyHandle != 0) - { - // TrustManager[] trustMangers = InitTrustManagersWithDotnetProxy(sslStreamProxyHandle); - loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); - if (!loc[trustManagers]) - goto cleanup; - } + // TrustManager[] trustMangers = InitTrustManagersWithDotnetProxy(sslStreamProxyHandle); + loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); + if (!loc[trustManagers]) + goto cleanup; // sslContext.init(keyManagers, trustManagers, null); (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, loc[keyManagers], loc[trustManagers], NULL); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index cfa2583e4d13c..899f9f7ed9c7d 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -7,26 +7,27 @@ void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCerti verifyRemoteCertificate = callback; } +static + jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle) { - abort_unless(sslStreamProxyHandle != 0, "invalid pointer to the .NET remote certificate validator"); - jobjectArray trustManagers = NULL; INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, dotnetProxyTrustManager); // string defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + // TrustManagerFactory tmf = TrustManagerFactory.getInstance(defaultAlgorithm); + // tmf.init(); + // TrustManager[] trustManagers = tmf.getTrustManagers(); + loc[defaultAlgorithm] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetDefaultAlgorithm); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // TrustManagerFactory tmf = TrustManagerFactory.getInstance(defaultAlgorithm); loc[tmf] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetInstance, loc[defaultAlgorithm]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // tmf.init(); (*env)->CallVoidMethod(env, loc[tmf], g_TrustManagerFactoryInit, NULL); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // TrustManager[] trustManagers = tmf.getTrustManagers(); trustManagers = (*env)->CallObjectMethod(env, loc[tmf], g_TrustManagerFactoryGetTrustManagers); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); From ac728115117b462cc1f234124549473795139c54 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 29 Nov 2022 16:13:21 +0100 Subject: [PATCH 41/59] Add missing attributes --- .../pal_sslstream.c | 4 ++-- .../pal_trust_manager.c | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 31b529e43c5e1..94625aa18f881 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -41,7 +41,7 @@ static bool IsHandshaking(int handshakeStatus) return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING && handshakeStatus != HANDSHAKE_STATUS__FINISHED; } -static jobject GetSslSession(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) +ARGS_NON_NULL(1, 2) static jobject GetSslSession(JNIEnv* env, SSLStream* sslStream, int handshakeStatus) { jobject sslSession = IsHandshaking(handshakeStatus) ? (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeSession) @@ -53,7 +53,7 @@ static jobject GetSslSession(JNIEnv* env, SSLStream* sslStream, int handshakeSta return sslSession; } -static jobject GetCurrentSslSession(JNIEnv* env, SSLStream* sslStream) +ARGS_NON_NULL_ALL static jobject GetCurrentSslSession(JNIEnv* env, SSLStream* sslStream) { int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index 899f9f7ed9c7d..be1bb040e5723 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -2,14 +2,12 @@ static RemoteCertificateValidationCallback verifyRemoteCertificate; -void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback) +ARGS_NON_NULL_ALL void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback) { verifyRemoteCertificate = callback; } -static - -jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle) +ARGS_NON_NULL_ALL jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle) { jobjectArray trustManagers = NULL; INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, dotnetProxyTrustManager); @@ -68,7 +66,7 @@ jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamPro return trustManagers; } -jboolean Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( +ARGS_NON_NULL_ALL jboolean Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( JNIEnv* env, jobject thisHandle, jlong sslStreamProxyHandle) { abort_unless(verifyRemoteCertificate, "verifyRemoteCertificate callback has not been registered"); From 3c7378ab7ba1d79129fa6cc2104aa5a5322b7062 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 30 Nov 2022 15:45:17 +0100 Subject: [PATCH 42/59] Free temporary buffer --- .../System.Security.Cryptography.Native.Android/pal_sslstream.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 94625aa18f881..acbff8d88a865 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -210,6 +210,7 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss PAL_SSLStreamStatus status = sslStream->streamReader(sslStream->managedContextHandle, tmpNative, &count); if (status != SSLStreamStatus_OK) { + free(tmpNative); (*env)->DeleteLocalRef(env, tmp); return status; } @@ -217,6 +218,7 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); IGNORE_RETURN( (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPutByteArrayWithLength, tmp, 0, count)); + free(tmpNative); (*env)->DeleteLocalRef(env, tmp); } From 010516c762c404b7e3e0f7c7d9fcfe3845dbfe7b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 1 Dec 2022 10:02:23 +0100 Subject: [PATCH 43/59] Update src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c Co-authored-by: Elinor Fung --- .../System.Security.Cryptography.Native.Android/pal_sslstream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index b43578f57b683..8f90bee66da9e 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -996,7 +996,7 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hos INIT_LOCALS(loc, name, verifier); // During the initial handshake our sslStream->sslSession doesn't have access to the peer certificates - // which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLession. + // which we need for hostname verification. Luckily, the SSLEngine has a getter for the handshake SSLSession. int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); bool isHandshaking = IsHandshaking(handshakeStatus); From ccae3e23c5027a441e3386f7f18c125b44c4130b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 1 Dec 2022 14:31:21 +0100 Subject: [PATCH 44/59] Refactor creating array of trust managers --- .../pal_jni.c | 19 ++----- .../pal_jni.h | 11 +--- .../pal_sslstream.c | 6 +-- .../pal_trust_manager.c | 52 ++----------------- .../pal_trust_manager.h | 2 +- 5 files changed, 14 insertions(+), 76 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index 57ec2a6b862e5..699e4364c2d8d 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -471,15 +471,8 @@ jmethodID g_KeyAgreementInit; jmethodID g_KeyAgreementDoPhase; jmethodID g_KeyAgreementGenerateSecret; -// javax/net/ssl/TrustManagerFactory -jclass g_TrustManagerFactory; -jmethodID g_TrustManagerFactoryGetDefaultAlgorithm; -jmethodID g_TrustManagerFactoryGetInstance; -jmethodID g_TrustManagerFactoryInit; -jmethodID g_TrustManagerFactoryGetTrustManagers; - -// javax/net/ssl/X509TrustManager -jclass g_X509TrustManager; +// javax/net/ssl/TrustManager +jclass g_TrustManager; // net/dot/android/crypto/DotnetProxyTrustManager jclass g_DotnetProxyTrustManager; @@ -1064,13 +1057,7 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_KeyAgreementDoPhase = GetMethod(env, false, g_KeyAgreementClass, "doPhase", "(Ljava/security/Key;Z)Ljava/security/Key;"); g_KeyAgreementGenerateSecret = GetMethod(env, false, g_KeyAgreementClass, "generateSecret", "()[B"); - g_TrustManagerFactory = GetClassGRef(env, "javax/net/ssl/TrustManagerFactory"); - g_TrustManagerFactoryGetDefaultAlgorithm = GetMethod(env, true, g_TrustManagerFactory, "getDefaultAlgorithm", "()Ljava/lang/String;"); - g_TrustManagerFactoryGetInstance = GetMethod(env, true, g_TrustManagerFactory, "getInstance", "(Ljava/lang/String;)Ljavax/net/ssl/TrustManagerFactory;"); - g_TrustManagerFactoryInit = GetMethod(env, false, g_TrustManagerFactory, "init", "(Ljava/security/KeyStore;)V"); - g_TrustManagerFactoryGetTrustManagers = GetMethod(env, false, g_TrustManagerFactory, "getTrustManagers", "()[Ljavax/net/ssl/TrustManager;"); - - g_X509TrustManager = GetClassGRef(env, "javax/net/ssl/X509TrustManager"); + g_TrustManager = GetClassGRef(env, "javax/net/ssl/TrustManager"); g_DotnetProxyTrustManager = GetClassGRef(env, "net/dot/android/crypto/DotnetProxyTrustManager"); g_DotnetProxyTrustManagerCtor = GetMethod(env, false, g_DotnetProxyTrustManager, "", "(J)V"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index 1c3798a8b0420..88f6aee0e731a 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -486,15 +486,8 @@ extern jmethodID g_KeyAgreementInit; extern jmethodID g_KeyAgreementDoPhase; extern jmethodID g_KeyAgreementGenerateSecret; -// javax/net/ssl/TrustManagerFactory -extern jclass g_TrustManagerFactory; -extern jmethodID g_TrustManagerFactoryGetDefaultAlgorithm; -extern jmethodID g_TrustManagerFactoryGetInstance; -extern jmethodID g_TrustManagerFactoryInit; -extern jmethodID g_TrustManagerFactoryGetTrustManagers; - -// javax/net/ssl/X509TrustManager -extern jclass g_X509TrustManager; +// javax/net/ssl/TrustManager +extern jclass g_TrustManager; // net/dot/android/crypto/DotnetProxyTrustManager extern jclass g_DotnetProxyTrustManager; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 37fe788377294..01254085e0d4a 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -379,7 +379,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t sslStreamProxyHandle) goto cleanup; // Init trust managers - loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); + loc[trustManagers] = GetTrustManagers(env, sslStreamProxyHandle); if (!loc[trustManagers]) goto cleanup; @@ -501,8 +501,8 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStrea loc[keyManagers] = (*env)->CallObjectMethod(env, loc[kmf], g_KeyManagerFactoryGetKeyManagers); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // TrustManager[] trustMangers = InitTrustManagersWithDotnetProxy(sslStreamProxyHandle); - loc[trustManagers] = InitTrustManagersWithDotnetProxy(env, sslStreamProxyHandle); + // Init trust managers + loc[trustManagers] = GetTrustManagers(env, sslStreamProxyHandle); if (!loc[trustManagers]) goto cleanup; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index be1bb040e5723..54142c43bdec2 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -4,63 +4,21 @@ static RemoteCertificateValidationCallback verifyRemoteCertificate; ARGS_NON_NULL_ALL void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback) { + abort_unless(verifyRemoteCertificate == NULL, "AndroidCryptoNative_RegisterRemoteCertificateValidationCallback can only be used once"); verifyRemoteCertificate = callback; } -ARGS_NON_NULL_ALL jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle) +ARGS_NON_NULL_ALL jobjectArray GetTrustManagers(JNIEnv* env, intptr_t sslStreamProxyHandle) { jobjectArray trustManagers = NULL; - INIT_LOCALS(loc, defaultAlgorithm, tmf, trustManager, dotnetProxyTrustManager); + INIT_LOCALS(loc, dotnetProxyTrustManager); - // string defaultAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); - // TrustManagerFactory tmf = TrustManagerFactory.getInstance(defaultAlgorithm); - // tmf.init(); - // TrustManager[] trustManagers = tmf.getTrustManagers(); - - loc[defaultAlgorithm] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetDefaultAlgorithm); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - loc[tmf] = (*env)->CallStaticObjectMethod(env, g_TrustManagerFactory, g_TrustManagerFactoryGetInstance, loc[defaultAlgorithm]); + loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (jlong)sslStreamProxyHandle); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - (*env)->CallVoidMethod(env, loc[tmf], g_TrustManagerFactoryInit, NULL); + trustManagers = make_java_object_array(env, 1, g_TrustManager, loc[dotnetProxyTrustManager]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - trustManagers = (*env)->CallObjectMethod(env, loc[tmf], g_TrustManagerFactoryGetTrustManagers); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - // boolean foundAndReplaced = false; - // for (int i = 0; i < trustManagers.length; i++) { - // if (trustManagers[i] instanceof X509TrustManager) { - // trustManagers[i] = new DotnetProxyTrustManager(sslStreamProxyHandle); - // foundAndReplaced = true; - // break; - // } - // } - - bool foundAndReplaced = false; - size_t length = (size_t)(*env)->GetArrayLength(env, trustManagers); - for (size_t i = 0; i < length; i++) - { - loc[trustManager] = (*env)->GetObjectArrayElement(env, trustManagers, (jsize)i); - - if ((*env)->IsInstanceOf(env, loc[trustManager], g_X509TrustManager)) - { - loc[dotnetProxyTrustManager] = (*env)->NewObject(env, g_DotnetProxyTrustManager, g_DotnetProxyTrustManagerCtor, (jlong)sslStreamProxyHandle); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - (*env)->SetObjectArrayElement(env, trustManagers, (jsize)i, loc[dotnetProxyTrustManager]); - ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - - foundAndReplaced = true; - break; - } - - ReleaseLRef(env, loc[trustManager]); - loc[trustManager] = NULL; - } - - abort_unless(foundAndReplaced, "no X509 trust managers"); cleanup: RELEASE_LOCALS(loc, env); return trustManagers; diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h index bd2375e11fdd4..e4f0911849232 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.h @@ -4,7 +4,7 @@ typedef bool (*RemoteCertificateValidationCallback)(intptr_t); PALEXPORT void AndroidCryptoNative_RegisterRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback); -jobjectArray InitTrustManagersWithDotnetProxy(JNIEnv* env, intptr_t sslStreamProxyHandle); +jobjectArray GetTrustManagers(JNIEnv* env, intptr_t sslStreamProxyHandle); JNIEXPORT jboolean JNICALL Java_net_dot_android_crypto_DotnetProxyTrustManager_verifyRemoteCertificate( JNIEnv *env, jobject thisHandle, jlong sslStreamProxyHandle); From 019a83dbbf1f6fa969055663caab3cad63c7278f Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 1 Dec 2022 15:44:17 +0100 Subject: [PATCH 45/59] Add comments and clean up pal_sslstream.c --- .../pal_sslstream.c | 53 ++++++++++++------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 01254085e0d4a..e7915e504e00a 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -36,6 +36,16 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus); ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* sslStream, int* handshakeStatus); +ARGS_NON_NULL_ALL static int GetHandshakeStatus(JNIEnv* env, SSLStream* sslStream) +{ + // int handshakeStatus = sslEngine.getHandshakeStatus().ordinal(); + int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); + if (CheckJNIExceptions(env)) + return -1; + + return handshakeStatus; +} + static bool IsHandshaking(int handshakeStatus) { return handshakeStatus != HANDSHAKE_STATUS__NOT_HANDSHAKING && handshakeStatus != HANDSHAKE_STATUS__FINISHED; @@ -55,8 +65,8 @@ ARGS_NON_NULL(1, 2) static jobject GetSslSession(JNIEnv* env, SSLStream* sslStre ARGS_NON_NULL_ALL static jobject GetCurrentSslSession(JNIEnv* env, SSLStream* sslStream) { - int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); - if (CheckJNIExceptions(env)) + int handshakeStatus = GetHandshakeStatus(env, sslStream); + if (handshakeStatus == -1) return NULL; return GetSslSession(env, sslStream, handshakeStatus); @@ -289,8 +299,9 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoHandshake(JNIEnv* env, SSLStream* sslStream) { PAL_SSLStreamStatus status = SSLStreamStatus_OK; - int handshakeStatus = - GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); + int handshakeStatus = GetHandshakeStatus(env, sslStream); + assert(handshakeStatus >= 0); + while (IsHandshaking(handshakeStatus) && status == SSLStreamStatus_OK) { switch (handshakeStatus) @@ -354,9 +365,14 @@ ARGS_NON_NULL_ALL static jobject GetKeyStoreInstance(JNIEnv* env) // String ksType = KeyStore.getDefaultType(); // KeyStore keyStore = KeyStore.getInstance(ksType); // keyStore.load(null, null); + // return keyStore; + ksType = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetDefaultType); + ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + keyStore = (*env)->CallStaticObjectMethod(env, g_KeyStoreClass, g_KeyStoreGetInstance, ksType); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + (*env)->CallVoidMethod(env, keyStore, g_KeyStoreLoad, NULL, NULL); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); @@ -372,18 +388,17 @@ SSLStream* AndroidCryptoNative_SSLStreamCreate(intptr_t sslStreamProxyHandle) SSLStream* sslStream = NULL; JNIEnv* env = GetJNIEnv(); - INIT_LOCALS(loc, sslContext, keyStore, trustManagers); + INIT_LOCALS(loc, sslContext, trustManagers); loc[sslContext] = GetSSLContextInstance(env); if (!loc[sslContext]) goto cleanup; - // Init trust managers loc[trustManagers] = GetTrustManagers(env, sslStreamProxyHandle); if (!loc[trustManagers]) goto cleanup; - // Init the SSLContext + // sslContext.init(null, trustManagers, null); (*env)->CallVoidMethod(env, loc[sslContext], g_SSLContextInitMethod, NULL, loc[trustManagers], NULL); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); @@ -473,7 +488,6 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStrea INIT_LOCALS(loc, sslContext, keyStore, kmfType, kmf, keyManagers, trustManagers); - // SSLContext sslContext = SSLContext.getInstance("TLSv1.3"); loc[sslContext] = GetSSLContextInstance(env); if (!loc[sslContext]) goto cleanup; @@ -501,7 +515,7 @@ SSLStream* AndroidCryptoNative_SSLStreamCreateWithCertificates(intptr_t sslStrea loc[keyManagers] = (*env)->CallObjectMethod(env, loc[kmf], g_KeyManagerFactoryGetKeyManagers); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); - // Init trust managers + // TrustManager[] trustManagers = GetTrustManagers(sslStreamProxyHandle); loc[trustManagers] = GetTrustManagers(env, sslStreamProxyHandle); if (!loc[trustManagers]) goto cleanup; @@ -618,8 +632,8 @@ PAL_SSLStreamStatus AndroidCryptoNative_SSLStreamHandshake(SSLStream* sslStream) abort_if_invalid_pointer_argument (sslStream); JNIEnv* env = GetJNIEnv(); - int handshakeStatus = GetEnumAsInt(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetHandshakeStatus)); - if (CheckJNIExceptions(env)) + int handshakeStatus = GetHandshakeStatus(env, sslStream); + if (handshakeStatus == -1) return SSLStreamStatus_Error; if (!IsHandshaking(handshakeStatus)) @@ -807,8 +821,11 @@ int32_t AndroidCryptoNative_SSLStreamGetCipherSuite(SSLStream* sslStream, uint16 *out = NULL; INIT_LOCALS(loc, sslSession, cipherSuite); - // String cipherSuite = sslSession.getCipherSuite(); loc[sslSession] = GetCurrentSslSession(env, sslStream); + if (loc[sslSession] == NULL) + goto cleanup; + + // String cipherSuite = sslSession.getCipherSuite(); loc[cipherSuite] = (*env)->CallObjectMethod(env, loc[sslSession], g_SSLSessionGetCipherSuite); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); *out = AllocateString(env, loc[cipherSuite]); @@ -855,7 +872,9 @@ ARGS_NON_NULL_ALL static jobject GetPeerCertificates(JNIEnv* env, SSLStream* ssl if (loc[sslSession] == NULL) goto cleanup; + // Certificate[] certificates = sslSession.getPeerCertificates(); certificates = (*env)->CallObjectMethod(env, loc[sslSession], g_SSLSessionGetPeerCertificates); + // If there are no peer certificates, getPeerCertificates will throw. Return null to indicate no certificates. ON_EXCEPTION_PRINT_AND_GOTO(cleanup); cleanup: @@ -870,11 +889,8 @@ jobject /*X509Certificate*/ AndroidCryptoNative_SSLStreamGetPeerCertificate(SSLS JNIEnv* env = GetJNIEnv(); jobject ret = NULL; - // Certificate[] certs = sslSession.getPeerCertificates(); jobject certs = GetPeerCertificates(env, sslStream); - - // If there are no peer certificates, getPeerCertificates will throw. Return null to indicate no certificate. - if (TryClearJNIExceptions(env)) + if (certs == NULL) goto cleanup; // out = certs[0]; @@ -901,9 +917,8 @@ void AndroidCryptoNative_SSLStreamGetPeerCertificates(SSLStream* sslStream, jobj *out = NULL; *outLen = 0; - // Certificate[] certs = sslSession.getPeerCertificates(); jobjectArray certs = GetPeerCertificates(env, sslStream); - if (!certs) + if (certs == NULL) goto cleanup; // for (int i = 0; i < certs.length; i++) { @@ -1043,11 +1058,11 @@ bool AndroidCryptoNative_SSLStreamVerifyHostname(SSLStream* sslStream, char* hos goto cleanup; // HostnameVerifier verifier = HttpsURLConnection.getDefaultHostnameVerifier(); - // return verifier.verify(hostname, sslSession); loc[name] = make_java_string(env, hostname); loc[verifier] = (*env)->CallStaticObjectMethod(env, g_HttpsURLConnection, g_HttpsURLConnectionGetDefaultHostnameVerifier); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); + // return verifier.verify(hostname, sslSession); ret = (*env)->CallBooleanMethod(env, loc[verifier], g_HostnameVerifierVerify, loc[name], loc[sslSession]); ON_EXCEPTION_PRINT_AND_GOTO(cleanup); From 5d269485ddf6a960fd2894699ad033da4d01c9bc Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 1 Dec 2022 16:05:52 +0100 Subject: [PATCH 46/59] Revert experimental change --- .../pal_sslstream.c | 34 +++++-------------- .../pal_sslstream.h | 1 + 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index 026867e00acf6..48d87b1ec8a49 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -183,17 +183,11 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslS } case STATUS__BUFFER_OVERFLOW: { - jobject sslSession = GetSslSession(env, sslStream, *handshakeStatus); - if (sslSession == NULL) - return SSLStreamStatus_Error; - // Expand buffer // int newCapacity = sslSession.getPacketBufferSize() + netOutBuffer.remaining(); - int32_t newCapacity = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetPacketBufferSize) + + int32_t newCapacity = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize) + (*env)->CallIntMethod(env, sslStream->netOutBuffer, g_ByteBufferRemaining); sslStream->netOutBuffer = ExpandBuffer(env, sslStream->netOutBuffer, newCapacity); - - ReleaseLRef(env, sslSession); return SSLStreamStatus_OK; } default: @@ -263,32 +257,20 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss } case STATUS__BUFFER_UNDERFLOW: { - jobject sslSession = GetSslSession(env, sslStream, *handshakeStatus); - if (sslSession == NULL) - return SSLStreamStatus_Error; - // Expand buffer // int newRemaining = sslSession.getPacketBufferSize(); - int32_t newRemaining = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetPacketBufferSize); + int32_t newRemaining = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); sslStream->netInBuffer = EnsureRemaining(env, sslStream->netInBuffer, newRemaining); - - ReleaseLRef(env, sslSession); return SSLStreamStatus_OK; } case STATUS__BUFFER_OVERFLOW: { - jobject sslSession = GetSslSession(env, sslStream, *handshakeStatus); - if (sslSession == NULL) - return SSLStreamStatus_Error; - // Expand buffer // int newCapacity = sslSession.getApplicationBufferSize() + appInBuffer.remaining(); int32_t newCapacity = - (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetApplicationBufferSize) + + (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize) + (*env)->CallIntMethod(env, sslStream->appInBuffer, g_ByteBufferRemaining); sslStream->appInBuffer = ExpandBuffer(env, sslStream->appInBuffer, newCapacity); - - ReleaseLRef(env, sslSession); return SSLStreamStatus_OK; } default: @@ -331,6 +313,7 @@ ARGS_NON_NULL_ALL static void FreeSSLStream(JNIEnv* env, SSLStream* sslStream) { ReleaseGRef(env, sslStream->sslContext); ReleaseGRef(env, sslStream->sslEngine); + ReleaseGRef(env, sslStream->sslSession); ReleaseGRef(env, sslStream->appOutBuffer); ReleaseGRef(env, sslStream->netOutBuffer); ReleaseGRef(env, sslStream->netInBuffer); @@ -542,6 +525,7 @@ int32_t AndroidCryptoNative_SSLStreamInitialize( abort_if_invalid_pointer_argument (sslStream); abort_unless(sslStream->sslContext != NULL, "sslContext is NULL in SSL stream"); abort_unless(sslStream->sslEngine == NULL, "sslEngine is NOT NULL in SSL stream"); + abort_unless(sslStream->sslSession == NULL, "sslSession is NOT NULL in SSL stream"); int32_t ret = FAIL; JNIEnv* env = GetJNIEnv(); @@ -555,14 +539,12 @@ int32_t AndroidCryptoNative_SSLStreamInitialize( ON_EXCEPTION_PRINT_AND_GOTO(exit); // SSLSession sslSession = sslEngine.getSession(); - jobject sslSession = (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession); + sslStream->sslSession = ToGRef(env, (*env)->CallObjectMethod(env, sslStream->sslEngine, g_SSLEngineGetSession)); // int applicationBufferSize = sslSession.getApplicationBufferSize(); // int packetBufferSize = sslSession.getPacketBufferSize(); - int32_t applicationBufferSize = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetApplicationBufferSize); - int32_t packetBufferSize = (*env)->CallIntMethod(env, sslSession, g_SSLSessionGetPacketBufferSize); - - ReleaseLRef(env, sslSession); + int32_t applicationBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetApplicationBufferSize); + int32_t packetBufferSize = (*env)->CallIntMethod(env, sslStream->sslSession, g_SSLSessionGetPacketBufferSize); // ByteBuffer appInBuffer = ByteBuffer.allocate(Math.max(applicationBufferSize, appBufferSize)); // ByteBuffer appOutBuffer = ByteBuffer.allocate(appBufferSize); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h index 7cd6c4efa86b0..01a5a7b766c99 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.h @@ -16,6 +16,7 @@ typedef struct SSLStream { jobject sslContext; jobject sslEngine; + jobject sslSession; jobject appOutBuffer; jobject netOutBuffer; jobject appInBuffer; From 2dec0d557319d2419513569607f0977b90376654 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Thu, 1 Dec 2022 18:25:11 +0100 Subject: [PATCH 47/59] Remove special case for IPv6 addresses as hostnames and disable affected tests --- .../System/Net/Http/HttpClientHandlerTest.cs | 16 ++++++++++++++-- .../tests/FunctionalTests/SocksProxyTest.cs | 8 +++++++- .../Security/Pal.Android/SafeDeleteSslContext.cs | 8 +------- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs index d072e850b9fcf..f9f6de4931b9d 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.cs @@ -153,6 +153,7 @@ public void Properties_AddItemToDictionary_ItemPresent() [ConditionalFact] [SkipOnPlatform(TestPlatforms.Browser, "ServerCertificateCustomValidationCallback not supported on Browser")] + [SkipOnPlatform(TestPlatforms.Android, "TargetHost cannot be set to an IPv6 address on Android because the string doesn't conform to the STD 3 ASCII rules")] public async Task GetAsync_IPv6LinkLocalAddressUri_Success() { if (IsWinHttpHandler && UseVersion >= HttpVersion20.Value) @@ -183,7 +184,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed( }, options: options); } - [Theory] + [ConditionalTheory] [MemberData(nameof(GetAsync_IPBasedUri_Success_MemberData))] public async Task GetAsync_IPBasedUri_Success(IPAddress address) { @@ -201,6 +202,12 @@ public async Task GetAsync_IPBasedUri_Success(IPAddress address) using HttpClient client = CreateHttpClient(handler); var options = new GenericLoopbackOptions { Address = address }; + + if (PlatformDetection.IsAndroid && options.UseSsl && address == IPAddress.IPv6Loopback) + { + throw new SkipTestException("TargetHost cannot be set to an IPv6 address on Android because the string doesn't conform to the STD 3 ASCII rules"); + } + await LoopbackServerFactory.CreateServerAsync(async (server, url) => { _output.WriteLine(url.ToString()); @@ -268,7 +275,7 @@ from useSsl in BoolValues where PlatformDetection.IsNotBrowser || !useSsl select new object[] { address, useSsl }; - [Theory] + [ConditionalTheory] [MemberData(nameof(SecureAndNonSecure_IPBasedUri_MemberData))] public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAddress address, bool useSsl) { @@ -278,6 +285,11 @@ public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAdd return; } + if (PlatformDetection.IsAndroid && useSsl && address == IPAddress.IPv6Loopback) + { + throw new SkipTestException("TargetHost cannot be set to an IPv6 address on Android because the string doesn't conform to the STD 3 ASCII rules"); + } + var options = new LoopbackServer.Options { Address = address, UseSsl = useSsl }; bool connectionAccepted = false; string host = ""; diff --git a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs index e5e51c641e621..a70f1c4cb5979 100644 --- a/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs +++ b/src/libraries/System.Net.Http/tests/FunctionalTests/SocksProxyTest.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net.Test.Common; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Abstractions; @@ -25,7 +26,7 @@ from useAuth in BoolValues from host in Hosts(scheme) select new object[] { scheme, useSsl, useAuth, host }; - [Theory] + [ConditionalTheory] [MemberData(nameof(TestLoopbackAsync_MemberData))] public async Task TestLoopbackAsync(string scheme, bool useSsl, bool useAuth, string host) { @@ -34,6 +35,11 @@ public async Task TestLoopbackAsync(string scheme, bool useSsl, bool useAuth, st return; } + if (PlatformDetection.IsAndroid && useSsl && host == "::1") + { + throw new SkipTestException("TargetHost cannot be set to an IPv6 address on Android because the string doesn't conform to the STD 3 ASCII rules"); + } + await LoopbackServerFactory.CreateClientAndServerAsync( async uri => { 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 ded27fda9c68d..9330bf7ba1cd3 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 @@ -284,13 +284,7 @@ private unsafe void InitializeSslContext( if (!isServer && !string.IsNullOrEmpty(authOptions.TargetHost)) { - // the Java SNIHostName class that's used internally to wrap the hostname - // doesn't support IPv6 addresses - var containsUnsupportedCharacters = authOptions.TargetHost.Contains(':'); - if (!containsUnsupportedCharacters) - { - Interop.AndroidCrypto.SSLStreamSetTargetHost(handle, authOptions.TargetHost); - } + Interop.AndroidCrypto.SSLStreamSetTargetHost(handle, authOptions.TargetHost); } } } From 694589be4873db1eea53cbc4a243d96ad9f1ef4b Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 7 Dec 2022 13:55:51 +0100 Subject: [PATCH 48/59] Fix duplicate variable after merge --- .../System.Security.Cryptography.Native.Android/pal_jni.c | 4 +--- .../System.Security.Cryptography.Native.Android/pal_jni.h | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index 38d2ca691be7e..0e204a8bc83a1 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -428,7 +428,6 @@ jmethodID g_SSLEngineCloseOutbound; jmethodID g_SSLEngineGetApplicationProtocol; jmethodID g_SSLEngineGetHandshakeSession; jmethodID g_SSLEngineGetHandshakeStatus; -jmethodID g_SSLEngineGetHandshakeSession; jmethodID g_SSLEngineGetSession; jmethodID g_SSLEngineGetSSLParameters; jmethodID g_SSLEngineGetSupportedProtocols; @@ -1027,11 +1026,10 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_SSLEngine = GetClassGRef(env, "javax/net/ssl/SSLEngine"); g_SSLEngineBeginHandshake = GetMethod(env, false, g_SSLEngine, "beginHandshake", "()V"); g_SSLEngineCloseOutbound = GetMethod(env, false, g_SSLEngine, "closeOutbound", "()V"); - g_SSLEngineGetHandshakeSession = GetOptionalMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetApplicationProtocol = GetOptionalMethod(env, false, g_SSLEngine, "getApplicationProtocol", "()Ljava/lang/String;"); + g_SSLEngineGetHandshakeSession = GetOptionalMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetHandshakeStatus = GetMethod(env, false, g_SSLEngine, "getHandshakeStatus", "()Ljavax/net/ssl/SSLEngineResult$HandshakeStatus;"); g_SSLEngineGetSession = GetMethod(env, false, g_SSLEngine, "getSession", "()Ljavax/net/ssl/SSLSession;"); - g_SSLEngineGetHandshakeSession = GetOptionalMethod(env, false, g_SSLEngine, "getHandshakeSession", "()Ljavax/net/ssl/SSLSession;"); g_SSLEngineGetSSLParameters = GetMethod(env, false, g_SSLEngine, "getSSLParameters", "()Ljavax/net/ssl/SSLParameters;"); g_SSLEngineGetSupportedProtocols = GetMethod(env, false, g_SSLEngine, "getSupportedProtocols", "()[Ljava/lang/String;"); g_SSLEngineSetEnabledProtocols = GetMethod(env, false, g_SSLEngine, "setEnabledProtocols", "([Ljava/lang/String;)V"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index d94be9229743f..8229035cab677 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -442,7 +442,6 @@ extern jmethodID g_SSLEngineCloseOutbound; extern jmethodID g_SSLEngineGetApplicationProtocol; extern jmethodID g_SSLEngineGetHandshakeSession; extern jmethodID g_SSLEngineGetHandshakeStatus; -extern jmethodID g_SSLEngineGetHandshakeSession; extern jmethodID g_SSLEngineGetSession; extern jmethodID g_SSLEngineGetSSLParameters; extern jmethodID g_SSLEngineGetSupportedProtocols; From 315ca06236e5c8469e0efebabdb526e00042d8f3 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 7 Dec 2022 13:56:15 +0100 Subject: [PATCH 49/59] Improve code formatting --- .../pal_sslstream.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c index f72d1571fca34..347c1f2fde1fe 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_sslstream.c @@ -163,8 +163,10 @@ ARGS_NON_NULL_ALL static jobject EnsureRemaining(JNIEnv* env, jobject oldBuffer, // There has been a change in the SSLEngineResult.Status enum between API 23 and 24 that changed // the order/interger values of the enum options. -static int MapLegacySSLEngineResultStatus(int legacyStatus) { - switch (legacyStatus) { +static int MapLegacySSLEngineResultStatus(int legacyStatus) +{ + switch (legacyStatus) + { case LEGACY__STATUS__BUFFER_OVERFLOW: return STATUS__BUFFER_OVERFLOW; case LEGACY__STATUS__BUFFER_UNDERFLOW: @@ -199,7 +201,8 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoWrap(JNIEnv* env, SSLStream* sslS int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); (*env)->DeleteLocalRef(env, result); - if (g_SSLEngineResultStatusLegacyOrder) { + if (g_SSLEngineResultStatusLegacyOrder) + { status = MapLegacySSLEngineResultStatus(status); } @@ -258,7 +261,6 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss (*env)->SetByteArrayRegion(env, tmp, 0, count, (jbyte*)(tmpNative)); IGNORE_RETURN( (*env)->CallObjectMethod(env, sslStream->netInBuffer, g_ByteBufferPutByteArrayWithLength, tmp, 0, count)); - free(tmpNative); (*env)->DeleteLocalRef(env, tmp); } @@ -280,7 +282,8 @@ ARGS_NON_NULL_ALL static PAL_SSLStreamStatus DoUnwrap(JNIEnv* env, SSLStream* ss int status = GetEnumAsInt(env, (*env)->CallObjectMethod(env, result, g_SSLEngineResultGetStatus)); (*env)->DeleteLocalRef(env, result); - if (g_SSLEngineResultStatusLegacyOrder) { + if (g_SSLEngineResultStatusLegacyOrder) + { status = MapLegacySSLEngineResultStatus(status); } @@ -1022,7 +1025,8 @@ int32_t AndroidCryptoNative_SSLStreamSetApplicationProtocols(SSLStream* sslStrea abort_if_invalid_pointer_argument (sslStream); abort_if_invalid_pointer_argument (protocolData); - if (!AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration()) { + if (!AndroidCryptoNative_SSLSupportsApplicationProtocolsConfiguration()) + { LOG_ERROR ("SSL does not support application protocols configuration"); return FAIL; } From b94a12c36f57746a3adfd1ae46bd7679f2a1aff5 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 6 Jan 2023 12:55:47 +0100 Subject: [PATCH 50/59] Remove the hack with SafeDeleteContextStub --- .../Pal.Android/SafeDeleteSslContext.cs | 41 +++---------------- .../Net/Security/SslAuthenticationOptions.cs | 4 ++ .../System/Net/Security/SslStream.Android.cs | 4 +- .../System/Net/Security/SslStream.Protocol.cs | 8 ---- .../src/System/Net/Security/SslStream.cs | 4 ++ .../Net/Security/SslStreamPal.Android.cs | 21 ++++------ 6 files changed, 24 insertions(+), 58 deletions(-) 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 d98c2ab7c1d72..0dcc18dc14d63 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 @@ -29,28 +29,21 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext }; private static readonly Lazy s_supportedSslProtocols = new Lazy(Interop.AndroidCrypto.SSLGetSupportedProtocols); - private readonly SafeSslHandle? _sslContext; + private readonly SafeSslHandle _sslContext; private ArrayBuffer _inputBuffer = new ArrayBuffer(InitialBufferSize); private ArrayBuffer _outputBuffer = new ArrayBuffer(InitialBufferSize); public SslStream.JavaProxy SslStreamProxy { get; } - public SafeSslHandle SslContext - { - get - { - if (_sslContext is null) - throw new InvalidOperationException($"{nameof(SafeDeleteSslContext)} is not initialized"); + public SafeSslHandle SslContext => _sslContext; - return _sslContext; - } - } - - public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticationOptions authOptions) + public SafeDeleteSslContext(SslAuthenticationOptions authOptions) : base(IntPtr.Zero) { - SslStreamProxy = sslStreamProxy; + SslStreamProxy = authOptions.SslStreamProxyFactory?.Invoke() + ?? throw new ArgumentNullException(nameof(authOptions.SslStreamProxyFactory)); + try { _sslContext = CreateSslContext(SslStreamProxy, authOptions); @@ -64,21 +57,6 @@ public SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy, SslAuthenticatio } } - public static SafeDeleteSslContext CreateContextStub(SslStream.JavaProxy sslStreamProxy) - { - // This is a stub context that is used to pass the reference to the SslStream - // without changing the existing PAL API (SslStreamPal.AcceptSecurityContext and - // SslStreamPal.InitializeSecurityContext). The stub context cannot be used for any - //other purpose and accessing the SslContext property of the stub will throw if used. - return new SafeDeleteSslContext(sslStreamProxy); - } - - private SafeDeleteSslContext(SslStream.JavaProxy sslStreamProxy) : base(IntPtr.Zero) - { - SslStreamProxy = sslStreamProxy; - _sslContext = null; - } - public override bool IsInvalid => _sslContext?.IsInvalid ?? true; protected override void Dispose(bool disposing) @@ -103,7 +81,6 @@ private static unsafe void WriteToConnection(IntPtr connection, byte* data, int { SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target; Debug.Assert(context != null); - Debug.Assert(context._sslContext != null); var inputBuffer = new ReadOnlySpan(data, dataLength); @@ -117,7 +94,6 @@ private static unsafe PAL_SSLStreamStatus ReadFromConnection(IntPtr connection, { SafeDeleteSslContext? context = (SafeDeleteSslContext?)GCHandle.FromIntPtr(connection).Target; Debug.Assert(context != null); - Debug.Assert(context._sslContext != null); int toRead = *dataLength; if (toRead == 0) @@ -140,8 +116,6 @@ private static unsafe PAL_SSLStreamStatus ReadFromConnection(IntPtr connection, internal void Write(ReadOnlySpan buf) { - Debug.Assert(_sslContext != null); - _inputBuffer.EnsureAvailableSpace(buf.Length); buf.CopyTo(_inputBuffer.AvailableSpan); _inputBuffer.Commit(buf.Length); @@ -151,8 +125,6 @@ internal void Write(ReadOnlySpan buf) internal byte[]? ReadPendingWrites() { - Debug.Assert(_sslContext != null); - if (_outputBuffer.ActiveLength == 0) { return null; @@ -166,7 +138,6 @@ internal void Write(ReadOnlySpan buf) internal int ReadPendingWrites(byte[] buf, int offset, int count) { - Debug.Assert(_sslContext != null); Debug.Assert(buf != null); Debug.Assert(offset >= 0); Debug.Assert(count >= 0); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index 6113cecff667c..0d0bdd1c7e012 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -181,5 +181,9 @@ private static SslProtocols FilterOutIncompatibleSslProtocols(SslProtocols proto internal object? UserState { get; set; } internal ServerOptionsSelectionCallback? ServerOptionDelegate { get; set; } internal X509ChainPolicy? CertificateChainPolicy { get; set; } + +#if TARGET_ANDROID + internal Func? SslStreamProxyFactory { get; set; } +#endif } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs index 9a3fbdb9bbfd7..f35aafe9a66d2 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Android.cs @@ -12,9 +12,6 @@ namespace System.Net.Security { public partial class SslStream { - private SafeDeleteSslContext CreateAndroidSecurityContextStub() - => SafeDeleteSslContext.CreateContextStub(sslStreamProxy: new JavaProxy(this)); - private JavaProxy.RemoteCertificateValidationResult VerifyRemoteCertificate() { ProtocolToken? alertToken = null; @@ -72,6 +69,7 @@ public void Dispose() _handle?.Free(); _handle = null; } + private static unsafe void RegisterRemoteCertificateValidationCallback() { if (!s_initialized) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 2b651ce723ed1..18c5a7e824573 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -809,14 +809,6 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte } } -#if TARGET_ANDROID - if (_securityContext is null) - { - _securityContext = CreateAndroidSecurityContextStub(); - Debug.Assert(_securityContext.IsInvalid); - } -#endif - if (_sslAuthenticationOptions.IsServer) { status = SslStreamPal.AcceptSecurityContext( diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs index 5ec9c5fcb1cb7..2d1150962813c 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -209,6 +209,10 @@ public SslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificat _sslAuthenticationOptions.CertValidationDelegate = userCertificateValidationCallback; _sslAuthenticationOptions.CertSelectionDelegate = userCertificateSelectionCallback; +#if TARGET_ANDROID + _sslAuthenticationOptions.SslStreamProxyFactory = () => new SslStream.JavaProxy(sslStream: this); +#endif + if (NetEventSource.Log.IsEnabled()) NetEventSource.Log.SslStreamCtor(this, innerStream); } 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 4c758321c9fb2..6278d364be3f2 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 @@ -28,24 +28,24 @@ public static void VerifyPackageInfo() public static SecurityStatusPal AcceptSecurityContext( ref SafeFreeCredentials credential, - ref SafeDeleteSslContext context, + ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal InitializeSecurityContext( ref SafeFreeCredentials credential, - ref SafeDeleteSslContext context, + ref SafeDeleteSslContext? context, string? targetName, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions, SelectClientCertificate? clientCertificateSelectionCallback) { - return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); + return HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions); } public static SecurityStatusPal Renegotiate( @@ -122,9 +122,7 @@ public static SecurityStatusPal DecryptMessage( PAL_SSLStreamStatus ret = Interop.AndroidCrypto.SSLStreamRead(sslHandle, buffer, out int read); if (ret == PAL_SSLStreamStatus.Error) - { return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError); - } count = read; @@ -172,20 +170,19 @@ public static void QueryContextConnectionInfo( } private static SecurityStatusPal HandshakeInternal( - ref SafeDeleteSslContext context, + SafeFreeCredentials credential, + ref SafeDeleteSslContext? context, ReadOnlySpan inputBuffer, ref byte[]? outputBuffer, SslAuthenticationOptions sslAuthenticationOptions) { - ArgumentNullException.ThrowIfNull(context); - try { - SafeDeleteSslContext sslContext = ((SafeDeleteSslContext)context); + SafeDeleteSslContext? sslContext = ((SafeDeleteSslContext?)context); - if (context.IsInvalid) + if (context == null || context.IsInvalid) { - context = new SafeDeleteSslContext(context.SslStreamProxy, sslAuthenticationOptions); + context = new SafeDeleteSslContext(sslAuthenticationOptions); sslContext = context; } From 061285ad5f62cdd0091067e0ac14d7182db87f78 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 6 Jan 2023 12:55:59 +0100 Subject: [PATCH 51/59] Enable passing test --- .../tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs index b32a424abfde8..233f18267f23b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAllowRenegotiationTests.cs @@ -70,7 +70,6 @@ public async Task SslStream_AllowRenegotiation_True_Succeeds() [Fact] [OuterLoop] // Test hits external azure server. - [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public async Task SslStream_AllowRenegotiation_False_Throws() { Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); From d2d695a1445924602499cd107a7fc73a37bc23c9 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 6 Jan 2023 18:12:19 +0100 Subject: [PATCH 52/59] Remove unnecessary factory --- .../System/Net/Security/Pal.Android/SafeDeleteSslContext.cs | 6 ++---- .../src/System/Net/Security/SslAuthenticationOptions.cs | 2 +- .../src/System/Net/Security/SslStream.Protocol.cs | 4 ++++ .../src/System/Net/Security/SslStream.cs | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) 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 0dcc18dc14d63..50b0eff0aab14 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 @@ -41,8 +41,8 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext public SafeDeleteSslContext(SslAuthenticationOptions authOptions) : base(IntPtr.Zero) { - SslStreamProxy = authOptions.SslStreamProxyFactory?.Invoke() - ?? throw new ArgumentNullException(nameof(authOptions.SslStreamProxyFactory)); + SslStreamProxy = authOptions.SslStreamProxy + ?? throw new ArgumentNullException(nameof(authOptions.SslStreamProxy)); try { @@ -69,8 +69,6 @@ protected override void Dispose(bool disposing) _outputBuffer.Dispose(); sslContext.Dispose(); } - - SslStreamProxy?.Dispose(); } base.Dispose(disposing); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs index 0d0bdd1c7e012..2940d94eb6b54 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslAuthenticationOptions.cs @@ -183,7 +183,7 @@ private static SslProtocols FilterOutIncompatibleSslProtocols(SslProtocols proto internal X509ChainPolicy? CertificateChainPolicy { get; set; } #if TARGET_ANDROID - internal Func? SslStreamProxyFactory { get; set; } + internal SslStream.JavaProxy? SslStreamProxy { get; set; } #endif } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 3498550ce60f0..fd735dbb661e1 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -119,6 +119,10 @@ internal void CloseContext() _securityContext?.Dispose(); _credentialsHandle?.Dispose(); + +#if TARGET_ANDROID + _sslAuthenticationOptions.SslStreamProxy?.Dispose(); +#endif } // diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs index 2d1150962813c..b26f685c44e21 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.cs @@ -210,7 +210,7 @@ public SslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificat _sslAuthenticationOptions.CertSelectionDelegate = userCertificateSelectionCallback; #if TARGET_ANDROID - _sslAuthenticationOptions.SslStreamProxyFactory = () => new SslStream.JavaProxy(sslStream: this); + _sslAuthenticationOptions.SslStreamProxy = new SslStream.JavaProxy(sslStream: this); #endif if (NetEventSource.Log.IsEnabled()) NetEventSource.Log.SslStreamCtor(this, innerStream); From b6f53859c8d05004e9e79ad893221cf6c73fd284 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 6 Jan 2023 18:14:48 +0100 Subject: [PATCH 53/59] Move clearing selected client certificate out of the remote certificate verification method --- .../src/System/Net/Security/SslStream.IO.cs | 7 +++++++ .../src/System/Net/Security/SslStream.Protocol.cs | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index 37df9a8b300c7..c651e63ed602a 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -509,6 +509,13 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError return true; } + if (_selectedClientCertificate != null && !CertificateValidationPal.IsLocalCertificateUsed(_securityContext!)) + { + // We may slect client cert but it may not be used. + // This is primarily issue on Windows with credential caching + _selectedClientCertificate = null; + } + #if TARGET_ANDROID // On Android, the remote certificate verification can be invoked from Java TrustManager's callback // during the handshake process. If that has occurred, we shouldn't run the validation again and diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index fd735dbb661e1..ea93c50f96547 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -998,13 +998,6 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot } _remoteCertificate = certificate; - if (_selectedClientCertificate != null && !CertificateValidationPal.IsLocalCertificateUsed(_securityContext!)) - { - // We may slect client cert but it may not be used. - // This is primarily issue on Windows with credential caching - _selectedClientCertificate = null; - } - if (_remoteCertificate == null) { if (NetEventSource.Log.IsEnabled() && RemoteCertRequired) NetEventSource.Error(this, $"Remote certificate required, but no remote certificate received"); From 6a0b8efc1ffff7df144930a97023d6cbf43d44fb Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 9 Jan 2023 11:36:39 +0100 Subject: [PATCH 54/59] Fix typo in comment --- .../src/System/Net/Security/SslStream.IO.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs index c651e63ed602a..c9b214b11d423 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.IO.cs @@ -511,8 +511,8 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError if (_selectedClientCertificate != null && !CertificateValidationPal.IsLocalCertificateUsed(_securityContext!)) { - // We may slect client cert but it may not be used. - // This is primarily issue on Windows with credential caching + // We may select client cert but it may not be used. + // This is primarily an issue on Windows with credential caching. _selectedClientCertificate = null; } From e5d1e8d0213d124be741cde5a0a709b8ce74d3da Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 9 Jan 2023 11:40:53 +0100 Subject: [PATCH 55/59] Add comment with java equivalent --- .../pal_trust_manager.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c index 54142c43bdec2..c0097c9f3b998 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_trust_manager.c @@ -10,6 +10,10 @@ ARGS_NON_NULL_ALL void AndroidCryptoNative_RegisterRemoteCertificateValidationCa ARGS_NON_NULL_ALL jobjectArray GetTrustManagers(JNIEnv* env, intptr_t sslStreamProxyHandle) { + // X509TrustManager dotnetProxyTrustManager = new DotnetProxyTrustManager(sslStreamProxyHandle); + // TrustManager[] trustManagers = new TrustManager[] { dotnetProxyTrustManager }; + // return trustManagers; + jobjectArray trustManagers = NULL; INIT_LOCALS(loc, dotnetProxyTrustManager); From 50befcff23bf2e06020aaaffdecbf64360ca7b1a Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 9 Jan 2023 19:20:09 +0100 Subject: [PATCH 56/59] Move Android specific runtime files into a separate item group --- eng/liveBuilds.targets | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/eng/liveBuilds.targets b/eng/liveBuilds.targets index 85b1a805285dd..fc76b7d477386 100644 --- a/eng/liveBuilds.targets +++ b/eng/liveBuilds.targets @@ -174,13 +174,16 @@ $(LibrariesNativeArtifactsPath)*.dylib; $(LibrariesNativeArtifactsPath)*.a; $(LibrariesNativeArtifactsPath)*.so; - $(LibrariesNativeArtifactsPath)*.dex; - $(LibrariesNativeArtifactsPath)*.jar; $(LibrariesNativeArtifactsPath)*.dbg; $(LibrariesNativeArtifactsPath)*.dwarf; $(LibrariesNativeArtifactsPath)*.pdb" IsNative="true" Exclude="@(ExcludeNativeLibrariesRuntimeFiles)" /> + - Date: Fri, 13 Jan 2023 12:46:47 +0100 Subject: [PATCH 58/59] Update src/native/libs/build-native.proj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- src/native/libs/build-native.proj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/native/libs/build-native.proj b/src/native/libs/build-native.proj index 2004fb1c1f279..7199673717bc3 100644 --- a/src/native/libs/build-native.proj +++ b/src/native/libs/build-native.proj @@ -59,7 +59,7 @@ AssemblyFile="$(AndroidAppBuilderTasksAssemblyPath)" /> + Condition="'$(TargetOS)' == 'android'"> Date: Mon, 16 Jan 2023 11:40:33 +0100 Subject: [PATCH 59/59] Disable test that fails on Android emualtors --- .../tests/FunctionalTests/ServerAsyncAuthenticateTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs index 2b4e3c9befab8..3ba7978bde1eb 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs @@ -45,6 +45,7 @@ public async Task ServerAsyncAuthenticate_EachSupportedProtocol_Success(SslProto [Theory] [MemberData(nameof(ProtocolMismatchData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails( SslProtocols clientProtocol, SslProtocols serverProtocol)