Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] PoC: Use Java TrustManager to call RemoteCertificateValidationCallback in SslStream #77220

Closed
wants to merge 44 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2f0f324
WIP: getting something to work
simonrozsival Oct 4, 2022
1728ce8
Renaming
simonrozsival Oct 5, 2022
217fdd7
Various code improvements
simonrozsival Oct 5, 2022
f6585fc
Verify hostname mismatch
simonrozsival Oct 5, 2022
fd0f49d
Fix C# code
simonrozsival Oct 5, 2022
eb5e8e1
Fix passing errors from Java to C#
simonrozsival Oct 5, 2022
708c070
Shuffle code around
simonrozsival Oct 6, 2022
d4cd5f9
Enable a bunch of tests
simonrozsival Oct 6, 2022
5efc0b4
Remove passing target hostname and errors between C# and Java
simonrozsival Oct 7, 2022
efb0bce
Fix crash in System.Net.Http functional tests
simonrozsival Oct 7, 2022
4d18289
Add AndroidDexBuilder
simonrozsival Oct 10, 2022
c50f3d6
Include dex in apk
simonrozsival Oct 10, 2022
f8f013e
Revert "[Mono] Implement eager finalization of WeakReference (#76173)"
simonrozsival Oct 11, 2022
cbd87be
Bunch of code improvements
simonrozsival Oct 11, 2022
83b34d8
Fix missing sdcardfs enum item
simonrozsival Oct 13, 2022
eb4171d
Improve naming
simonrozsival Oct 13, 2022
17a494e
Remove commented-out code
simonrozsival Oct 13, 2022
001d6ed
Fix hostname verification
simonrozsival Oct 13, 2022
3431f08
Fix setting SNI hostname on Android
simonrozsival Oct 13, 2022
bd883b3
Temporarily disable failing tests
simonrozsival Oct 14, 2022
28d4f53
Improve dex builder
simonrozsival Oct 15, 2022
1ea9191
Code improvements
simonrozsival Oct 16, 2022
f5d7a6d
Prevent exceptions leaving the C# code
simonrozsival Oct 17, 2022
0fc2744
Allow selecting TLS version based on the settings
simonrozsival Oct 17, 2022
3822378
Include client certificate
simonrozsival Oct 18, 2022
c950eac
Revert "Include client certificate"
simonrozsival Oct 18, 2022
9710732
Revert "Allow selecting TLS version based on the settings"
simonrozsival Oct 18, 2022
ab1ccb9
Fix getting remote certificate
simonrozsival Oct 18, 2022
48ccb41
Remove active issue attribute
simonrozsival Oct 18, 2022
be040e4
Disable failing System.Net.Http outerloop tests
simonrozsival Oct 18, 2022
20dc5ff
Improve exception catching in the TrustManagerProxy
simonrozsival Oct 18, 2022
bfd6397
Bugfix
simonrozsival Oct 18, 2022
f91748e
Disable failing System.Net.Security tests
simonrozsival Oct 19, 2022
75942a2
Fix build
simonrozsival Oct 19, 2022
eece1ce
Revert "Revert "[Mono] Implement eager finalization of WeakReference …
simonrozsival Oct 19, 2022
5d23009
Merge branch 'main' of https://github.com/dotnet/runtime into poc-tru…
simonrozsival Oct 19, 2022
507e7fa
Fix build
simonrozsival Oct 19, 2022
1a37b0e
Code cleanup
simonrozsival Oct 19, 2022
728783b
Code improvements
simonrozsival Oct 20, 2022
0eb28f5
Simplify dex builder
simonrozsival Oct 20, 2022
64d7b7a
Fix building tests apks
simonrozsival Oct 20, 2022
ed8aba9
Fix sslstream server mode verification
simonrozsival Oct 20, 2022
1c33b30
Fix tests
simonrozsival Oct 21, 2022
36ca706
Merge branch 'main' of https://github.com/dotnet/runtime into poc-tru…
simonrozsival Oct 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions eng/liveBuilds.targets
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@
$(LibrariesNativeArtifactsPath)*.pdb"
IsNative="true"
Exclude="@(ExcludeNativeLibrariesRuntimeFiles)" />
<LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'Android'"
Include="
$(LibrariesNativeArtifactsPath)*.dex;"
IsNative="true" />
<LibrariesRuntimeFiles Condition="'$(TargetOS)' == 'Browser'"
Include="
$(LibrariesNativeArtifactsPath)dotnet.js;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Apple.dylib" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Android.a" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Android.so" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.Android.dex" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.OpenSsl.a" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.OpenSsl.dylib" IsNative="true" />
<PlatformManifestFileEntry Include="libSystem.Security.Cryptography.Native.OpenSsl.so" IsNative="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,35 @@ 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<byte> pkcs8PrivateKey, PAL_KeyAlgorithm algorithm, IntPtr[] certificates)
internal static SafeSslHandle SSLStreamCreateWithCertificates(
IntPtr trustManagerProxyHandle,
ReadOnlySpan<byte> pkcs8PrivateKey,
PAL_KeyAlgorithm algorithm,
IntPtr[] certificates)
{
return SSLStreamCreateWithCertificates(
trustManagerProxyHandle,
ref MemoryMarshal.GetReference(pkcs8PrivateKey),
pkcs8PrivateKey.Length,
algorithm,
certificates,
certificates.Length);
}

[LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_RegisterTrustManagerValidationCallback")]
internal static unsafe partial void RegisterTrustManagerValidationCallback(
delegate* unmanaged<IntPtr, int, int*, byte**, bool> validateCertificates);

[LibraryImport(Interop.Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_SSLStreamInitialize")]
private static unsafe partial int SSLStreamInitializeImpl(
SafeSslHandle sslHandle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ internal enum UnixFileSystemTypes : uint
rootfs = 0x53464846,
rpc_pipefs = 0x67596969,
samba = 0x517B,
sdcardfs = 0x5DCA2DF5,
securityfs = 0x73636673,
selinux = 0xF97CFF8C,
sffs = 0x786F4256, // same as vboxfs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ private HttpClient CreateHttpClientWithCert(X509Certificate2 cert)
[InlineData(1, true)]
[InlineData(2, true)]
[InlineData(3, false)]
[ActiveIssue("TODO", TestPlatforms.Android)]
public async Task Manual_CertificateOnlySentWhenValid_Success(int certIndex, bool serverExpectsClientCertificate)
{
// [ActiveIssue("https://github.com/dotnet/runtime/issues/69238")]
Expand Down Expand Up @@ -132,6 +133,7 @@ await TestHelper.WhenAllCompletedOrAnyFailed(
[Theory]
[InlineData(6, false)]
[InlineData(3, true)]
[ActiveIssue("TODO", TestPlatforms.Android)]
public async Task Manual_CertificateSentMatchesCertificateReceived_Success(
int numberOfRequests,
bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ public static IEnumerable<object[]> UseCallback_ValidCertificate_ExpectedValuesD
[OuterLoop("Uses external servers")]
[Theory]
[MemberData(nameof(UseCallback_ValidCertificate_ExpectedValuesDuringCallback_Urls))]
[ActiveIssue("TODO", TestPlatforms.Android)]
public async Task UseCallback_ValidCertificate_ExpectedValuesDuringCallback(Configuration.Http.RemoteServer remoteServer, Uri url, bool checkRevocation)
{
HttpClientHandler handler = CreateHttpClientHandler();
Expand Down Expand Up @@ -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# to Java and back to C#...
public async Task UseCallback_CallbackThrowsException_ExceptionPropagatesAsBaseException()
{
HttpClientHandler handler = CreateHttpClientHandler();
Expand Down Expand Up @@ -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);
Expand All @@ -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())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
//
// <IncludeNetworkSecurityConfig Condition="'$(TargetOS)' == 'Android'">true</IncludeNetworkSecurityConfig>
//

handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ public Task BadRttPingResponse_RequestShouldFail(int mode)

[OuterLoop("Runs long")]
[Fact]
[ActiveIssue("TODO", TestPlatforms.Android)]
public async Task HighBandwidthDelayProduct_ClientStreamReceiveWindowWindowScalesUp()
{
int maxCredit = await TestClientWindowScalingAsync(
Expand Down Expand Up @@ -228,7 +229,7 @@ private static async Task<int> 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];
Expand Down Expand Up @@ -315,7 +316,7 @@ async Task ProcessIncomingFramesAsync(CancellationToken cancellationToken)
catch (OperationCanceledException)
{
}


output?.WriteLine("ProcessIncomingFramesAsync finished");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Linux;$(NetCoreAppCurrent)-Browser;$(NetCoreAppCurrent)-OSX</TargetFrameworks>
<EnableLibraryImportGenerator>true</EnableLibraryImportGenerator>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<!-- the res/xml/network_security_config.xml file comes from the System.Net.TestData package -->
<IncludeNetworkSecurityConfig Condition="'$(TargetOS)' == 'Android'">true</IncludeNetworkSecurityConfig>
<EventSourceSupport Condition="'$(TestNativeAot)' == 'true'">true</EventSourceSupport>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
<IgnoreForCI Condition="'$(TargetOS)' == 'Browser'">true</IgnoreForCI>
<!-- SYSLIB0014: WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead. -->
<NoWarn>$(NoWarn);SYSLIB0014</NoWarn>
<!-- the res/xml/network_security_config.xml file comes from the System.Net.TestData package -->
<IncludeNetworkSecurityConfig Condition="'$(TargetOS)' == 'Android'">true</IncludeNetworkSecurityConfig>
<EnablePreviewFeatures>true</EnablePreviewFeatures>
<EventSourceSupport Condition="'$(TestNativeAot)' == 'true'">true</EventSourceSupport>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>
<GeneratePlatformNotSupportedAssemblyMessage Condition="'$(TargetPlatformIdentifier)' == ''">SR.SystemNetSecurity_PlatformNotSupported</GeneratePlatformNotSupportedAssemblyMessage>
<DefineConstants Condition="'$(TargetPlatformIdentifier)' == 'windows'">$(DefineConstants);TARGET_WINDOWS</DefineConstants>
<DefineConstants Condition="'$(TargetPlatformIdentifier)' == 'Android'">$(DefineConstants);TARGET_ANDROID</DefineConstants>
<UseAndroidCrypto Condition="'$(TargetPlatformIdentifier)' == 'Android'">true</UseAndroidCrypto>
<UseAppleCrypto Condition="'$(TargetPlatformIdentifier)' == 'OSX' or '$(TargetPlatformIdentifier)' == 'iOS' or '$(TargetPlatformIdentifier)' == 'tvOS'">true</UseAppleCrypto>
<UseManagedNtlm Condition="'$(TargetPlatformIdentifier)' == 'Android' or '$(TargetPlatformIdentifier)' == 'tvOS'">true</UseManagedNtlm>
Expand Down Expand Up @@ -41,6 +42,7 @@
<Compile Include="System\Net\Security\SslSessionsCache.cs" />
<Compile Include="System\Net\Security\SslStream.cs" />
<Compile Include="System\Net\Security\SslStream.IO.cs" />
<Compile Include="System\Net\Security\RemoteCertificateVerification.cs" />
<Compile Include="System\Net\Security\SslStream.Protocol.cs" />
<Compile Include="System\Net\Security\SslStreamCertificateContext.cs" />
<Compile Include="System\Net\Security\SslConnectionInfo.cs" />
Expand Down Expand Up @@ -102,7 +104,7 @@
Link="Common\System\Net\SecurityStatusPal.cs" />
<Compile Include="$(CommonPath)System\HexConverter.cs"
Link="Common\System\HexConverter.cs" />
<Compile Include="$(CommonPath)System\Obsoletions.cs"
<Compile Include="$(CommonPath)System\Obsoletions.cs"
Link="Common\System\Obsoletions.cs" />
</ItemGroup>
<!-- This file depends on IANA registry. We do not want anyone's build to break after the update -->
Expand Down Expand Up @@ -380,6 +382,7 @@
Link="Common\Interop\Android\System.Security.Cryptography.Native.Android\Interop.X509.cs" />
<Compile Include="System\Net\CertificateValidationPal.Android.cs" />
<Compile Include="System\Net\Security\Pal.Android\SafeDeleteSslContext.cs" />
<Compile Include="System\Net\Security\Pal.Android\TrustManagerProxy.cs" />
<Compile Include="System\Net\Security\Pal.Managed\SslProtocolsValidation.cs" />
<Compile Include="System\Net\Security\SslConnectionInfo.Android.cs" />
<Compile Include="System\Net\Security\SslStreamCertificateContext.Android.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,24 @@ internal sealed class SafeDeleteSslContext : SafeDeleteContext
private static readonly Lazy<SslProtocols> s_supportedSslProtocols = new Lazy<SslProtocols>(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)
Expand All @@ -65,6 +71,8 @@ protected override void Dispose(bool disposing)
_outputBuffer.Dispose();
sslContext.Dispose();
}

_trustManagerProxy?.Dispose();
}

base.Dispose(disposing);
Expand Down Expand Up @@ -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? validatorPtr, SslAuthenticationOptions authOptions)
{
if (authOptions.CertificateContext == null)
{
return Interop.AndroidCrypto.SSLStreamCreate();
return Interop.AndroidCrypto.SSLStreamCreate(validatorPtr ?? IntPtr.Zero);
}

SslStreamCertificateContext context = authOptions.CertificateContext;
Expand All @@ -169,7 +177,7 @@ private static SafeSslHandle CreateSslContext(SslAuthenticationOptions authOptio
ptrs[i + 1] = context.IntermediateCertificates[i].Handle;
}

return Interop.AndroidCrypto.SSLStreamCreateWithCertificates(keyBytes, algorithm, ptrs);
return Interop.AndroidCrypto.SSLStreamCreateWithCertificates(validatorPtr ?? IntPtr.Zero, keyBytes, algorithm, ptrs);
}

private static AsymmetricAlgorithm GetPrivateKeyAlgorithm(X509Certificate2 cert, out PAL_KeyAlgorithm algorithm)
Expand Down Expand Up @@ -249,7 +257,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);
}
}
}
}
Expand Down
Loading