Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
43 changes: 43 additions & 0 deletions src/StackExchange.Redis/ConfigurationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,49 @@ public bool HighIntegrity
/// <param name="issuerCertificatePath">The file system path to find the certificate at.</param>
public void TrustIssuer(string issuerCertificatePath) => CertificateValidationCallback = TrustIssuerCallback(issuerCertificatePath);

#if NET5_0_OR_GREATER
/// <summary>
/// Supply a user certificate from a PEM file pair and enable TLS.
/// </summary>
/// <param name="userCertificatePath">The path for the the user certificate (commonly a .crt file).</param>
/// <param name="userKeyPath">The path for the the user key (commonly a .key file).</param>
public void SetUserPemCertificate(string userCertificatePath, string? userKeyPath = null)
{
CertificateSelectionCallback = CreatePemUserCertificateCallback(userCertificatePath, userKeyPath);
Ssl = true;
}
#endif

/// <summary>
/// Supply a user certificate from a PFX file and optional password and enable TLS.
/// </summary>
/// <param name="userCertificatePath">The path for the the user certificate (commonly a .pfx file).</param>
/// <param name="password">The password for the certificate file.</param>
public void SetUserPfxCertificate(string userCertificatePath, string? password = null)
{
CertificateSelectionCallback = CreatePfxUserCertificateCallback(userCertificatePath, password);
Ssl = true;
}

#if NET5_0_OR_GREATER
internal static LocalCertificateSelectionCallback CreatePemUserCertificateCallback(string userCertificatePath, string? userKeyPath)
{
// PEM handshakes not universally supported and causes a runtime error about ephemeral certificates; to avoid, export as PFX
using var pem = X509Certificate2.CreateFromPemFile(userCertificatePath, userKeyPath);
#pragma warning disable SYSLIB0057 // Type or member is obsolete
var pfx = new X509Certificate2(pem.Export(X509ContentType.Pfx));
#pragma warning restore SYSLIB0057 // Type or member is obsolete

return (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => pfx;
}
#endif

internal static LocalCertificateSelectionCallback CreatePfxUserCertificateCallback(string userCertificatePath, string? password, X509KeyStorageFlags storageFlags = X509KeyStorageFlags.DefaultKeySet)
{
var pfx = new X509Certificate2(userCertificatePath, password ?? "", storageFlags);
return (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) => pfx;
}

/// <summary>
/// Create a certificate validation check that checks against the supplied issuer even when not known by the machine.
/// </summary>
Expand Down
28 changes: 18 additions & 10 deletions src/StackExchange.Redis/PhysicalConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1504,21 +1504,29 @@ public ConnectionStatus GetStatus()
{
try
{
var pfxPath = Environment.GetEnvironmentVariable("SERedis_ClientCertPfxPath");
var pfxPassword = Environment.GetEnvironmentVariable("SERedis_ClientCertPassword");
var pfxStorageFlags = Environment.GetEnvironmentVariable("SERedis_ClientCertStorageFlags");

X509KeyStorageFlags? flags = null;
if (!string.IsNullOrEmpty(pfxStorageFlags))
var certificatePath = Environment.GetEnvironmentVariable("SERedis_ClientCertPfxPath");
if (!string.IsNullOrEmpty(certificatePath) && File.Exists(certificatePath))
{
flags = Enum.Parse(typeof(X509KeyStorageFlags), pfxStorageFlags) as X509KeyStorageFlags?;
var password = Environment.GetEnvironmentVariable("SERedis_ClientCertPassword");
var pfxStorageFlags = Environment.GetEnvironmentVariable("SERedis_ClientCertStorageFlags");
X509KeyStorageFlags storageFlags = X509KeyStorageFlags.DefaultKeySet;
if (!string.IsNullOrEmpty(pfxStorageFlags))
{
var tmp = Enum.Parse(typeof(X509KeyStorageFlags), pfxStorageFlags) as X509KeyStorageFlags?;
if (tmp is not null) storageFlags = tmp.GetValueOrDefault();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use .TryParse a bit simpler here

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: 7a9208f

}

return ConfigurationOptions.CreatePfxUserCertificateCallback(certificatePath, password, storageFlags);
}

if (!string.IsNullOrEmpty(pfxPath) && File.Exists(pfxPath))
#if NET5_0_OR_GREATER
certificatePath = Environment.GetEnvironmentVariable("SERedis_ClientCertPemPath");
if (!string.IsNullOrEmpty(certificatePath) && File.Exists(certificatePath))
{
return (sender, targetHost, localCertificates, remoteCertificate, acceptableIssuers) =>
new X509Certificate2(pfxPath, pfxPassword ?? "", flags ?? X509KeyStorageFlags.DefaultKeySet);
var passwordPath = Environment.GetEnvironmentVariable("SERedis_ClientCertPasswordPath");
return ConfigurationOptions.CreatePemUserCertificateCallback(certificatePath, passwordPath);
}
#endif
}
catch (Exception ex)
{
Expand Down
2 changes: 1 addition & 1 deletion src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1893,4 +1893,4 @@ virtual StackExchange.Redis.RedisResult.Length.get -> int
virtual StackExchange.Redis.RedisResult.this[int index].get -> StackExchange.Redis.RedisResult!
StackExchange.Redis.ConnectionMultiplexer.AddLibraryNameSuffix(string! suffix) -> void
StackExchange.Redis.IConnectionMultiplexer.AddLibraryNameSuffix(string! suffix) -> void

StackExchange.Redis.ConfigurationOptions.SetUserPfxCertificate(string! userCertificatePath, string? password = null) -> void
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
StackExchange.Redis.ConfigurationOptions.SslClientAuthenticationOptions.get -> System.Func<string!, System.Net.Security.SslClientAuthenticationOptions!>?
StackExchange.Redis.ConfigurationOptions.SslClientAuthenticationOptions.set -> void
System.Runtime.CompilerServices.IsExternalInit (forwarded, contained in System.Runtime)
System.Runtime.CompilerServices.IsExternalInit (forwarded, contained in System.Runtime)
StackExchange.Redis.ConfigurationOptions.SetUserPemCertificate(string! userCertificatePath, string? userKeyPath = null) -> void
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
StackExchange.Redis.ConfigurationOptions.SslClientAuthenticationOptions.get -> System.Func<string!, System.Net.Security.SslClientAuthenticationOptions!>?
StackExchange.Redis.ConfigurationOptions.SslClientAuthenticationOptions.set -> void
System.Runtime.CompilerServices.IsExternalInit (forwarded, contained in System.Runtime)
System.Runtime.CompilerServices.IsExternalInit (forwarded, contained in System.Runtime)
StackExchange.Redis.ConfigurationOptions.SetUserPemCertificate(string! userCertificatePath, string? userKeyPath = null) -> void
Loading