diff --git a/Packages.props b/Packages.props
index 76fc75462..6234daee4 100644
--- a/Packages.props
+++ b/Packages.props
@@ -5,7 +5,9 @@
+
+
diff --git a/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs
new file mode 100644
index 000000000..7b5a02d13
--- /dev/null
+++ b/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs
@@ -0,0 +1,159 @@
+namespace DotNet.Testcontainers.Builders
+{
+ using System;
+ using System.IO;
+ using System.Linq;
+ using System.Net.Security;
+ using System.Text.RegularExpressions;
+ using Docker.DotNet.X509;
+ using DotNet.Testcontainers.Configurations;
+ using Org.BouncyCastle.Asn1;
+ using Org.BouncyCastle.Asn1.Pkcs;
+ using Org.BouncyCastle.Crypto;
+ using Org.BouncyCastle.Crypto.Parameters;
+ using Org.BouncyCastle.Pkcs;
+ using Org.BouncyCastle.Security;
+ using Org.BouncyCastle.X509;
+ using MSX509 = System.Security.Cryptography.X509Certificates;
+
+ ///
+ internal sealed class MTlsEndpointAuthenticationProvider : DockerEndpointAuthenticationProvider
+ {
+ private const string DefaultCertPath = ".docker";
+ private const string DefaultCaCertFileName = "ca.pem";
+ private const string DefaultClientCertFileName = "cert.pem";
+ private const string DefaultClientKeyFileName = "key.pem";
+ private static readonly Regex PemData = new Regex("-----BEGIN (.*)-----(.*)-----END (.*)-----", RegexOptions.Multiline);
+
+ private readonly Uri dockerEngine;
+ private readonly bool dockerTlsVerifyEnabled;
+ private readonly string dockerCertPath;
+ private readonly Lazy caCertificate;
+ private readonly MSX509.X509Certificate2 clientCertificate;
+
+ public MTlsEndpointAuthenticationProvider()
+ {
+ var dockerHostValue = Environment.GetEnvironmentVariable("DOCKER_HOST");
+ var dockerTlsVerifyValue = Environment.GetEnvironmentVariable("DOCKER_TLS_VERIFY");
+ var dockerCertPathValue = Environment.GetEnvironmentVariable("DOCKER_CERT_PATH");
+
+ this.dockerEngine = Uri.TryCreate(dockerHostValue, UriKind.RelativeOrAbsolute, out var dockerHost) ? dockerHost : null;
+ this.dockerTlsVerifyEnabled = int.TryParse(dockerTlsVerifyValue, out var dockerTlsVerify) && dockerTlsVerify == 1;
+ this.dockerCertPath = dockerCertPathValue ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), DefaultCertPath);
+ this.caCertificate = new Lazy(() => new MSX509.X509Certificate2(Path.Combine(this.dockerCertPath, DefaultCaCertFileName)));
+ this.clientCertificate = this.GetClientCertificate();
+ }
+
+ ///
+ public override bool IsApplicable()
+ {
+ return this.dockerEngine != null && this.dockerTlsVerifyEnabled;
+ }
+
+ ///
+ public override IDockerEndpointAuthenticationConfiguration GetAuthConfig()
+ {
+ return new DockerEndpointAuthenticationConfiguration(this.dockerEngine, new CertificateCredentials(this.clientCertificate)
+ {
+ ServerCertificateValidationCallback = this.ServerCertificateValidationCallback,
+ });
+ }
+
+ private static MSX509.X509Certificate2 GetCertificateWithKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
+ {
+ var store = new Pkcs12StoreBuilder()
+ .SetUseDerEncoding(true)
+ .Build();
+
+ var certEntry = new X509CertificateEntry(certificate);
+ store.SetKeyEntry(certificate.SubjectDN.ToString(), new AsymmetricKeyEntry(privateKey), new[] { certEntry });
+
+ byte[] pfxBytes;
+ var password = Guid.NewGuid().ToString("N");
+
+ using (var stream = new MemoryStream())
+ {
+ store.Save(stream, password.ToCharArray(), new SecureRandom());
+ pfxBytes = stream.ToArray();
+ }
+
+ var result = Pkcs12Utilities.ConvertToDefiniteLength(pfxBytes);
+
+ return new MSX509.X509Certificate2(result, password, MSX509.X509KeyStorageFlags.Exportable);
+ }
+
+ private static X509Certificate ReadPemCert(string pathToPemFile)
+ {
+ var x509CertificateParser = new X509CertificateParser();
+ var cert = x509CertificateParser.ReadCertificate(File.ReadAllBytes(pathToPemFile));
+
+ return cert;
+ }
+
+ private static AsymmetricKeyParameter ReadPemRsaPrivateKey(string pathToPemFile)
+ {
+ var keyData = File.ReadAllText(pathToPemFile)
+ .Replace("\n", string.Empty);
+
+ var keyMatch = PemData.Match(keyData);
+ if (!keyMatch.Success)
+ {
+ throw new NotSupportedException("Not supported key content");
+ }
+
+ if (keyMatch.Groups[1].Value != "RSA PRIVATE KEY")
+ {
+ throw new NotSupportedException("Not supported key type. Only RSA PRIVATE KEY is supported.");
+ }
+
+ var keyContent = keyMatch.Groups[2].Value;
+ var keyRawData = Convert.FromBase64String(keyContent);
+ var seq = Asn1Sequence.GetInstance(keyRawData);
+
+ if (seq.Count != 9)
+ {
+ throw new NotSupportedException("Invalid RSA Private Key ASN1 sequence.");
+ }
+
+ var keyStructure = RsaPrivateKeyStructure.GetInstance(seq);
+ var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(new RsaPrivateCrtKeyParameters(keyStructure));
+ var key = PrivateKeyFactory.CreateKey(privateKeyInfo);
+
+ return key;
+ }
+
+ private MSX509.X509Certificate2 GetClientCertificate()
+ {
+ var certFilePath = Path.Combine(this.dockerCertPath, DefaultClientCertFileName);
+ var certificate = ReadPemCert(certFilePath);
+
+ var keyFilePath = Path.Combine(this.dockerCertPath, DefaultClientKeyFileName);
+ var key = ReadPemRsaPrivateKey(keyFilePath);
+
+ return GetCertificateWithKey(certificate, key);
+ }
+
+ private bool ServerCertificateValidationCallback(object sender, MSX509.X509Certificate certificate, MSX509.X509Chain chain, SslPolicyErrors sslPolicyErrors)
+ {
+ switch (sslPolicyErrors)
+ {
+ case SslPolicyErrors.None:
+ return true;
+ case SslPolicyErrors.RemoteCertificateNotAvailable:
+ case SslPolicyErrors.RemoteCertificateNameMismatch:
+ return false;
+ case SslPolicyErrors.RemoteCertificateChainErrors:
+ default:
+ var validationChain = new MSX509.X509Chain();
+ validationChain.ChainPolicy.RevocationMode = MSX509.X509RevocationMode.NoCheck;
+ validationChain.ChainPolicy.VerificationFlags = MSX509.X509VerificationFlags.AllowUnknownCertificateAuthority;
+ validationChain.ChainPolicy.ExtraStore.Add(this.caCertificate.Value);
+ validationChain.ChainPolicy.ExtraStore.AddRange(chain.ChainElements.OfType().Select(element => element.Certificate).ToArray());
+ var isVerified = validationChain.Build(certificate as MSX509.X509Certificate2 ?? new MSX509.X509Certificate2(certificate));
+ var isSignedByExpectedRoot = validationChain.ChainElements[validationChain.ChainElements.Count - 1].Certificate.RawData.SequenceEqual(this.caCertificate.Value.RawData);
+ var isSuccess = isVerified && isSignedByExpectedRoot;
+ return isSuccess;
+ }
+ }
+ }
+}
diff --git a/src/Testcontainers/Configurations/TestcontainersSettings.cs b/src/Testcontainers/Configurations/TestcontainersSettings.cs
index b4e6a821b..e4f508725 100644
--- a/src/Testcontainers/Configurations/TestcontainersSettings.cs
+++ b/src/Testcontainers/Configurations/TestcontainersSettings.cs
@@ -27,6 +27,7 @@ public static class TestcontainersSettings
private static readonly IDockerEndpointAuthenticationConfiguration DockerEndpointAuthConfig =
new IDockerEndpointAuthenticationProvider[]
{
+ new MTlsEndpointAuthenticationProvider(),
new TlsEndpointAuthenticationProvider(),
new EnvironmentEndpointAuthenticationProvider(),
new NpipeEndpointAuthenticationProvider(),
diff --git a/src/Testcontainers/Testcontainers.csproj b/src/Testcontainers/Testcontainers.csproj
index 1c67783b2..37b0cf66c 100644
--- a/src/Testcontainers/Testcontainers.csproj
+++ b/src/Testcontainers/Testcontainers.csproj
@@ -9,8 +9,10 @@
+
+