diff --git a/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs b/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs
index 3f203c01d..f106b7599 100644
--- a/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs
+++ b/src/Testcontainers/Builders/MTlsEndpointAuthenticationProvider.cs
@@ -7,19 +7,11 @@ namespace DotNet.Testcontainers.Builders
using Docker.DotNet.X509;
using DotNet.Testcontainers.Configurations;
using JetBrains.Annotations;
- using Org.BouncyCastle.Crypto;
- using Org.BouncyCastle.Crypto.Parameters;
- using Org.BouncyCastle.OpenSsl;
- using Org.BouncyCastle.Pkcs;
- using Org.BouncyCastle.Security;
- using Org.BouncyCastle.X509;
///
[PublicAPI]
internal sealed class MTlsEndpointAuthenticationProvider : TlsEndpointAuthenticationProvider
{
- private static readonly X509CertificateParser CertificateParser = new X509CertificateParser();
-
///
/// Initializes a new instance of the class.
///
@@ -57,57 +49,15 @@ protected override X509Certificate2 GetClientCertificate()
{
var clientCertificateFilePath = Path.Combine(CertificatesDirectoryPath, ClientCertificateFileName);
var clientCertificateKeyFilePath = Path.Combine(CertificatesDirectoryPath, ClientCertificateKeyFileName);
- return CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath);
- }
-
- private static X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath)
- {
- if (!File.Exists(certPemFilePath))
- {
- throw new FileNotFoundException(certPemFilePath);
- }
-
- if (!File.Exists(keyPemFilePath))
- {
- throw new FileNotFoundException(keyPemFilePath);
- }
-
- using (var keyPairStream = new StreamReader(keyPemFilePath))
- {
- var store = new Pkcs12StoreBuilder().Build();
-
- var certificate = CertificateParser.ReadCertificate(File.ReadAllBytes(certPemFilePath));
-
- var password = Guid.NewGuid().ToString("D");
- var keyObject = new PemReader(keyPairStream).ReadObject();
-
- var certificateEntry = new X509CertificateEntry(certificate);
-
- var keyParameter = ResolveKeyParameter(keyObject);
-
- var keyEntry = new AsymmetricKeyEntry(keyParameter);
- store.SetKeyEntry(certificate.SubjectDN + "_key", keyEntry, new[] { certificateEntry });
-
- using (var certificateStream = new MemoryStream())
- {
- store.Save(certificateStream, password.ToCharArray(), new SecureRandom());
- return new X509Certificate2(Pkcs12Utilities.ConvertToDefiniteLength(certificateStream.ToArray()), password);
- }
- }
- }
-
- private static AsymmetricKeyParameter ResolveKeyParameter(object keyObject)
- {
- switch (keyObject)
- {
- case AsymmetricCipherKeyPair ackp:
- return ackp.Private;
- case RsaPrivateCrtKeyParameters rpckp:
- return rpckp;
- default:
- throw new ArgumentOutOfRangeException(nameof(keyObject), $"Unsupported asymmetric key entry encountered while trying to resolve key from input object '{keyObject.GetType()}'.");
- }
+#if NETSTANDARD
+ return Polyfills.X509Certificate2.CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath);
+#else
+ var certificate = X509Certificate2.CreateFromPemFile(clientCertificateFilePath, clientCertificateKeyFilePath);
+ // The certificate must be exported to PFX on Windows to avoid "No credentials are available in the security package":
+ // https://stackoverflow.com/questions/72096812/loading-x509certificate2-from-pem-file-results-in-no-credentials-are-available/72101855#72101855.
+ return OperatingSystem.IsWindows() ? new X509Certificate2(certificate.Export(X509ContentType.Pfx)) : certificate;
+#endif
}
}
}
diff --git a/src/Testcontainers/Polyfills/X509Certificate2.cs b/src/Testcontainers/Polyfills/X509Certificate2.cs
new file mode 100644
index 000000000..bcbb0da00
--- /dev/null
+++ b/src/Testcontainers/Polyfills/X509Certificate2.cs
@@ -0,0 +1,68 @@
+#if NETSTANDARD
+namespace DotNet.Testcontainers.Polyfills
+{
+ using System;
+ using System.IO;
+ using Org.BouncyCastle.Crypto;
+ using Org.BouncyCastle.Crypto.Parameters;
+ using Org.BouncyCastle.OpenSsl;
+ using Org.BouncyCastle.Pkcs;
+ using Org.BouncyCastle.Security;
+ using Org.BouncyCastle.X509;
+
+ public static class X509Certificate2
+ {
+ private static readonly X509CertificateParser CertificateParser = new X509CertificateParser();
+
+ public static System.Security.Cryptography.X509Certificates.X509Certificate2 CreateFromPemFile(string certPemFilePath, string keyPemFilePath)
+ {
+ if (!File.Exists(certPemFilePath))
+ {
+ throw new FileNotFoundException(certPemFilePath);
+ }
+
+ if (!File.Exists(keyPemFilePath))
+ {
+ throw new FileNotFoundException(keyPemFilePath);
+ }
+
+ using (var keyPairStream = new StreamReader(keyPemFilePath))
+ {
+ var store = new Pkcs12StoreBuilder().Build();
+
+ var certificate = CertificateParser.ReadCertificate(File.ReadAllBytes(certPemFilePath));
+
+ var password = Guid.NewGuid().ToString("D");
+
+ var keyObject = new PemReader(keyPairStream).ReadObject();
+
+ var certificateEntry = new X509CertificateEntry(certificate);
+
+ var keyParameter = ResolveKeyParameter(keyObject);
+
+ var keyEntry = new AsymmetricKeyEntry(keyParameter);
+ store.SetKeyEntry(certificate.SubjectDN + "_key", keyEntry, new[] { certificateEntry });
+
+ using (var certificateStream = new MemoryStream())
+ {
+ store.Save(certificateStream, password.ToCharArray(), new SecureRandom());
+ return new System.Security.Cryptography.X509Certificates.X509Certificate2(Pkcs12Utilities.ConvertToDefiniteLength(certificateStream.ToArray()), password);
+ }
+ }
+ }
+
+ private static AsymmetricKeyParameter ResolveKeyParameter(object keyObject)
+ {
+ switch (keyObject)
+ {
+ case AsymmetricCipherKeyPair ackp:
+ return ackp.Private;
+ case RsaPrivateCrtKeyParameters rpckp:
+ return rpckp;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(keyObject), $"Unsupported asymmetric key entry encountered while trying to resolve key from input object '{keyObject.GetType()}'.");
+ }
+ }
+ }
+}
+#endif
diff --git a/src/Testcontainers/Testcontainers.csproj b/src/Testcontainers/Testcontainers.csproj
index 12f06cd47..7280cbb10 100644
--- a/src/Testcontainers/Testcontainers.csproj
+++ b/src/Testcontainers/Testcontainers.csproj
@@ -6,14 +6,16 @@
-
-
-
+
+
+
+
+
diff --git a/tests/Testcontainers.Tests/Testcontainers.Tests.csproj b/tests/Testcontainers.Tests/Testcontainers.Tests.csproj
index 21b9b100f..caa03761c 100644
--- a/tests/Testcontainers.Tests/Testcontainers.Tests.csproj
+++ b/tests/Testcontainers.Tests/Testcontainers.Tests.csproj
@@ -11,6 +11,7 @@
+