This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 4136fcfcd9 IGNITE-19045 .NET: Refactor SslStreamFactory to use
SslClientAuthenticationOptions (#1817)
4136fcfcd9 is described below
commit 4136fcfcd96be236b4c50719800339a841934d76
Author: Pavel Tupitsyn <[email protected]>
AuthorDate: Tue Mar 21 09:53:16 2023 +0300
IGNITE-19045 .NET: Refactor SslStreamFactory to use
SslClientAuthenticationOptions (#1817)
* Replace a custom and potentially incomplete (e.g. ciphers could not be
set before) set of options in `SslStreamFactory` with
`SslClientAuthenticationOptions`. This way our API is simpler, and full set of
options is available to the users.
* Make `ISslStreamFactory` asynchronous.
[SslClientAuthenticationOptions](https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslclientauthenticationoptions?view=net-7.0)
is a property bag that can be passed to `SslStream.AuthenticateAsClientAsync`.
This API is not available in "classic" .NET or .NET standard, so we could not
use it in Ignite 2.x.
---
.../dotnet/Apache.Ignite.Tests/SslTests.cs | 65 +++++++++++++++----
.../dotnet/Apache.Ignite/ISslStreamFactory.cs | 3 +-
.../dotnet/Apache.Ignite/Internal/ClientSocket.cs | 2 +-
.../dotnet/Apache.Ignite/SslStreamFactory.cs | 72 +++-------------------
4 files changed, 63 insertions(+), 79 deletions(-)
diff --git a/modules/platforms/dotnet/Apache.Ignite.Tests/SslTests.cs
b/modules/platforms/dotnet/Apache.Ignite.Tests/SslTests.cs
index 335733d086..2a17f930a3 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Tests/SslTests.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Tests/SslTests.cs
@@ -23,6 +23,7 @@ using System.IO;
using System.Linq;
using System.Net.Security;
using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Log;
using NUnit.Framework;
@@ -30,6 +31,7 @@ using NUnit.Framework;
/// <summary>
/// SSL tests.
/// </summary>
+[SuppressMessage("Security", "CA5359:Do Not Disable Certificate Validation",
Justification = "Tests.")]
public class SslTests : IgniteTestsBase
{
private const string CertificatePassword = "123456";
@@ -51,7 +53,13 @@ public class SslTests : IgniteTestsBase
var cfg = new IgniteClientConfiguration
{
Endpoints = { SslEndpoint },
- SslStreamFactory = new SslStreamFactory {
SkipServerCertificateValidation = true }
+ SslStreamFactory = new SslStreamFactory
+ {
+ SslClientAuthenticationOptions = new
SslClientAuthenticationOptions
+ {
+ RemoteCertificateValidationCallback = (_, _, _, _) => true
+ }
+ }
};
using var client = await IgniteClient.StartAsync(cfg);
@@ -76,9 +84,11 @@ public class SslTests : IgniteTestsBase
Endpoints = { SslEndpointWithClientAuth },
SslStreamFactory = new SslStreamFactory
{
- SkipServerCertificateValidation = true,
- CertificatePath = CertificatePath,
- CertificatePassword = CertificatePassword
+ SslClientAuthenticationOptions = new()
+ {
+ RemoteCertificateValidationCallback = (_, _, _, _) => true,
+ ClientCertificates = new X509Certificate2Collection(new
X509Certificate2(CertificatePath, CertificatePassword))
+ }
},
Logger = new ConsoleLogger { MinLevel = LogLevel.Trace }
};
@@ -101,10 +111,7 @@ public class SslTests : IgniteTestsBase
public void TestSslOnClientWithoutSslOnServerThrows()
{
var cfg = GetConfig();
- cfg.SslStreamFactory = new SslStreamFactory
- {
- SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12
- };
+ cfg.SslStreamFactory = new SslStreamFactory();
var ex = Assert.ThrowsAsync<AggregateException>(async () => await
IgniteClient.StartAsync(cfg));
Assert.IsInstanceOf<IgniteClientConnectionException>(ex?.InnerException);
@@ -130,8 +137,11 @@ public class SslTests : IgniteTestsBase
Endpoints = { SslEndpointWithClientAuth },
SslStreamFactory = new SslStreamFactory
{
- SkipServerCertificateValidation = true,
- CheckCertificateRevocation = true
+ SslClientAuthenticationOptions = new()
+ {
+ RemoteCertificateValidationCallback = (_, _, _, _) => true,
+ CertificateRevocationCheckMode = X509RevocationMode.NoCheck
+ }
}
};
@@ -166,14 +176,43 @@ public class SslTests : IgniteTestsBase
Assert.IsNull(client.GetConnections().Single().SslInfo);
}
+ [Test]
+ public async Task TestCustomCipherSuite()
+ {
+ var cfg = new IgniteClientConfiguration
+ {
+ Endpoints = { SslEndpoint },
+ SslStreamFactory = new SslStreamFactory
+ {
+ SslClientAuthenticationOptions = new
SslClientAuthenticationOptions
+ {
+ RemoteCertificateValidationCallback = (_, _, _, _) => true,
+ CipherSuitesPolicy = new CipherSuitesPolicy(new[]
+ {
+ TlsCipherSuite.TLS_AES_128_GCM_SHA256
+ })
+ }
+ }
+ };
+
+ using var client = await IgniteClient.StartAsync(cfg);
+
+ var connection = client.GetConnections().Single();
+ var sslInfo = connection.SslInfo;
+
+ Assert.IsNotNull(sslInfo);
+ Assert.IsFalse(sslInfo!.IsMutuallyAuthenticated);
+ Assert.AreEqual(TlsCipherSuite.TLS_AES_128_GCM_SHA256.ToString(),
sslInfo.NegotiatedCipherSuiteName);
+ }
+
private class NullSslStreamFactory : ISslStreamFactory
{
- public SslStream? Create(Stream stream, string targetHost) => null;
+ public Task<SslStream?> CreateAsync(Stream stream, string targetHost)
=> Task.FromResult<SslStream?>(null);
}
private class CustomSslStreamFactory : ISslStreamFactory
{
- public SslStream Create(Stream stream, string targetHost)
+ public async Task<SslStream?> CreateAsync(Stream stream, string
targetHost)
{
var sslStream = new SslStream(
innerStream: stream,
@@ -181,7 +220,7 @@ public class SslTests : IgniteTestsBase
userCertificateValidationCallback: (_, certificate, _, _) =>
certificate!.Issuer.Contains("ignite"),
userCertificateSelectionCallback: null);
- sslStream.AuthenticateAsClient(targetHost, null,
SslProtocols.None, false);
+ await sslStream.AuthenticateAsClientAsync(targetHost, null,
SslProtocols.None, false);
return sslStream;
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/ISslStreamFactory.cs
b/modules/platforms/dotnet/Apache.Ignite/ISslStreamFactory.cs
index 6cd692b9ac..9bc51c3ceb 100644
--- a/modules/platforms/dotnet/Apache.Ignite/ISslStreamFactory.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/ISslStreamFactory.cs
@@ -19,6 +19,7 @@ namespace Apache.Ignite;
using System.IO;
using System.Net.Security;
+using System.Threading.Tasks;
/// <summary>
/// SSL Stream Factory defines how SSL connection is established.
@@ -35,5 +36,5 @@ public interface ISslStreamFactory
/// <returns>
/// SSL stream, or null if SSL is not enabled.
/// </returns>
- SslStream? Create(Stream stream, string targetHost);
+ Task<SslStream?> CreateAsync(Stream stream, string targetHost);
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
index e525862fe3..f9630ee4e0 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
@@ -181,7 +181,7 @@ namespace Apache.Ignite.Internal
Stream stream = new NetworkStream(socket, ownsSocket: true);
if (configuration.SslStreamFactory is { } sslStreamFactory &&
- sslStreamFactory.Create(stream, endPoint.Host) is { }
sslStream)
+ await sslStreamFactory.CreateAsync(stream,
endPoint.Host).ConfigureAwait(false) is { } sslStream)
{
stream = sslStream;
diff --git a/modules/platforms/dotnet/Apache.Ignite/SslStreamFactory.cs
b/modules/platforms/dotnet/Apache.Ignite/SslStreamFactory.cs
index 02f552260d..cbdc43a4ee 100644
--- a/modules/platforms/dotnet/Apache.Ignite/SslStreamFactory.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/SslStreamFactory.cs
@@ -17,11 +17,9 @@
namespace Apache.Ignite;
-using System.ComponentModel;
using System.IO;
using System.Net.Security;
-using System.Security.Authentication;
-using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
using Internal.Common;
/// <summary>
@@ -30,76 +28,22 @@ using Internal.Common;
public sealed class SslStreamFactory : ISslStreamFactory
{
/// <summary>
- /// Default SSL protocols.
+ /// Gets or sets client authentication options.
/// </summary>
- public const SslProtocols DefaultSslProtocols = SslProtocols.None;
-
- /// <summary>
- /// Gets or sets the certificate file path (see <see
cref="X509Certificate2"/>).
- /// </summary>
- public string? CertificatePath { get; set; }
-
- /// <summary>
- /// Gets or sets the certificate file password (see <see
cref="X509Certificate2"/>).
- /// </summary>
- public string? CertificatePassword { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether to ignore invalid remote
(server) certificates.
- /// This may be useful for testing with self-signed certificates.
- /// </summary>
- public bool SkipServerCertificateValidation { get; set; }
-
- /// <summary>
- /// Gets or sets a value indicating whether the certificate revocation
list is checked during authentication.
- /// </summary>
- public bool CheckCertificateRevocation { get; set; }
-
- /// <summary>
- /// Gets or sets the SSL protocols.
- /// </summary>
- [DefaultValue(DefaultSslProtocols)]
- public SslProtocols SslProtocols { get; set; } = DefaultSslProtocols;
+ public SslClientAuthenticationOptions? SslClientAuthenticationOptions {
get; set; }
/// <inheritdoc />
- public SslStream Create(Stream stream, string targetHost)
+ public async Task<SslStream?> CreateAsync(Stream stream, string targetHost)
{
IgniteArgumentCheck.NotNull(stream, "stream");
- var sslStream = new SslStream(stream, false,
ValidateServerCertificate, null);
+ var sslStream = new SslStream(stream, false, null, null);
- var cert = string.IsNullOrEmpty(CertificatePath)
- ? null
- : new X509Certificate2(CertificatePath, CertificatePassword);
+ var options = SslClientAuthenticationOptions ?? new
SslClientAuthenticationOptions();
+ options.TargetHost ??= targetHost;
- var certs = cert == null
- ? null
- : new X509CertificateCollection(new X509Certificate[] { cert });
-
- sslStream.AuthenticateAsClient(targetHost, certs, SslProtocols,
CheckCertificateRevocation);
+ await
sslStream.AuthenticateAsClientAsync(options).ConfigureAwait(false);
return sslStream;
}
-
- /// <summary>
- /// Validates the server certificate.
- /// </summary>
- private bool ValidateServerCertificate(
- object sender,
- X509Certificate? certificate,
- X509Chain? chain,
- SslPolicyErrors sslPolicyErrors)
- {
- if (SkipServerCertificateValidation)
- {
- return true;
- }
-
- if (sslPolicyErrors == SslPolicyErrors.None)
- {
- return true;
- }
-
- return false;
- }
}