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

add CertificateChainPolicy to ssl options #71961

Merged
merged 4 commits into from
Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions src/libraries/System.Net.Security/ref/System.Net.Security.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ public SslClientAuthenticationOptions() { }
public System.Net.Security.LocalCertificateSelectionCallback? LocalCertificateSelectionCallback { get { throw null; } set { } }
public System.Net.Security.RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get { throw null; } set { } }
public string? TargetHost { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509ChainPolicy? CertificateChainPolicy { get { throw null; } set { } }
}
public readonly partial struct SslClientHelloInfo
{
Expand All @@ -218,6 +219,7 @@ public SslServerAuthenticationOptions() { }
public System.Security.Cryptography.X509Certificates.X509Certificate? ServerCertificate { get { throw null; } set { } }
public System.Net.Security.SslStreamCertificateContext? ServerCertificateContext { get { throw null; } set { } }
public System.Net.Security.ServerCertificateSelectionCallback? ServerCertificateSelectionCallback { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509ChainPolicy? CertificateChainPolicy { get { throw null; } set { } }
}
public partial class SslStream : System.Net.Security.AuthenticatedStream
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ internal static SslPolicyErrors VerifyCertificateProperties(
private static X509Certificate2? GetRemoteCertificate(
SafeDeleteContext? securityContext,
bool retrieveChainCertificates,
ref X509Chain? chain)
ref X509Chain? chain,
X509ChainPolicy? chainPolicy)
{
SafeSslHandle? sslContext = ((SafeDeleteSslContext?)securityContext)?.SslContext;
if (sslContext == null)
Expand All @@ -65,6 +66,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
else
{
chain ??= new X509Chain();
if (chainPolicy != null)
{
chain.ChainPolicy = chainPolicy;
}
IntPtr[]? ptrs = Interop.AndroidCrypto.SSLStreamGetPeerCertificates(sslContext);
if (ptrs != null && ptrs.Length > 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ internal static SslPolicyErrors VerifyCertificateProperties(
private static X509Certificate2? GetRemoteCertificate(
SafeDeleteContext? securityContext,
bool retrieveChainCertificates,
ref X509Chain? chain)
ref X509Chain? chain,
X509ChainPolicy? chainPolicy)
{
if (securityContext == null)
{
Expand All @@ -73,6 +74,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
if (retrieveChainCertificates)
{
chain ??= new X509Chain();
if (chainPolicy != null)
{
chain.ChainPolicy = chainPolicy;
}

for (int i = 0; i < chainSize; i++)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ internal static SslPolicyErrors VerifyCertificateProperties(
//
// Extracts a remote certificate upon request.
//
private static X509Certificate2? GetRemoteCertificate(SafeDeleteContext? securityContext, bool retrieveChainCertificates, ref X509Chain? chain)
private static X509Certificate2? GetRemoteCertificate(
SafeDeleteContext? securityContext,
bool retrieveChainCertificates,
ref X509Chain? chain,
X509ChainPolicy? chainPolicy)
{
bool gotReference = false;

Expand All @@ -48,6 +52,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
if (retrieveChainCertificates)
{
chain ??= new X509Chain();
if (chainPolicy != null)
{
chain.ChainPolicy = chainPolicy;
}

using (SafeSharedX509StackHandle chainStack =
Interop.OpenSsl.GetPeerCertificateChain(((SafeDeleteSslContext)securityContext).SslContext))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
//

private static X509Certificate2? GetRemoteCertificate(
SafeDeleteContext? securityContext, bool retrieveChainCertificates, ref X509Chain? chain)
SafeDeleteContext? securityContext,
bool retrieveChainCertificates,
ref X509Chain? chain,
X509ChainPolicy? chainPolicy)
{
if (securityContext == null)
{
Expand Down Expand Up @@ -67,6 +70,10 @@ internal static SslPolicyErrors VerifyCertificateProperties(
if (retrieveChainCertificates)
{
chain ??= new X509Chain();
if (chainPolicy != null)
{
chain.ChainPolicy = chainPolicy;
}

UnmanagedCertificateContext.GetRemoteCertificatesFromStoreContext(remoteContext, chain.ChainPolicy.ExtraStore);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ internal static partial class CertificateValidationPal
private static X509Chain? s_chain;

internal static X509Certificate2? GetRemoteCertificate(SafeDeleteContext? securityContext) =>
GetRemoteCertificate(securityContext, retrieveChainCertificates: false, ref s_chain);
GetRemoteCertificate(securityContext, retrieveChainCertificates: false, ref s_chain, null);

internal static X509Certificate2? GetRemoteCertificate(SafeDeleteContext? securityContext, ref X509Chain? chain) =>
GetRemoteCertificate(securityContext, retrieveChainCertificates: true, ref chain);
internal static X509Certificate2? GetRemoteCertificate(SafeDeleteContext? securityContext, ref X509Chain? chain, X509ChainPolicy? chainPolicy) =>
GetRemoteCertificate(securityContext, retrieveChainCertificates: true, ref chain, chainPolicy);

static partial void CheckSupportsStore(StoreLocation storeLocation, ref bool hasSupport);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ internal void UpdateOptions(SslClientAuthenticationOptions sslClientAuthenticati
CertificateRevocationCheckMode = sslClientAuthenticationOptions.CertificateRevocationCheckMode;
ClientCertificates = sslClientAuthenticationOptions.ClientCertificates;
CipherSuitesPolicy = sslClientAuthenticationOptions.CipherSuitesPolicy;

if (sslClientAuthenticationOptions.CertificateChainPolicy != null)
{
CertificateChainPolicy = sslClientAuthenticationOptions.CertificateChainPolicy.Clone();
}
}

internal void UpdateOptions(ServerOptionsSelectionCallback optionCallback, object? state)
Expand Down Expand Up @@ -135,6 +140,11 @@ internal void UpdateOptions(SslServerAuthenticationOptions sslServerAuthenticati
{
ServerCertSelectionDelegate = sslServerAuthenticationOptions.ServerCertificateSelectionCallback;
}

if (sslServerAuthenticationOptions.CertificateChainPolicy != null)
{
CertificateChainPolicy = sslServerAuthenticationOptions.CertificateChainPolicy.Clone();
}
}

private static SslProtocols FilterOutIncompatibleSslProtocols(SslProtocols protocols)
Expand Down Expand Up @@ -170,5 +180,6 @@ private static SslProtocols FilterOutIncompatibleSslProtocols(SslProtocols proto
internal CipherSuitesPolicy? CipherSuitesPolicy { get; set; }
internal object? UserState { get; set; }
internal ServerOptionsSelectionCallback? ServerOptionDelegate { get; set; }
internal X509ChainPolicy? CertificateChainPolicy { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,13 @@ public SslProtocols EnabledSslProtocols
/// Use extreme caution when changing this setting.
/// </summary>
public CipherSuitesPolicy? CipherSuitesPolicy { get; set; }

/// <summary>
/// Gets or sets an optional customized policy for remote certificate
/// validation. If not <see langword="null"/>,
/// <see cref="CertificateRevocationCheckMode"/> and <see cref="SslCertificateTrust"/>
/// are ignored.
/// </summary>
public X509ChainPolicy? CertificateChainPolicy { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,13 @@ public EncryptionPolicy EncryptionPolicy
/// Use extreme caution when changing this setting.
/// </summary>
public CipherSuitesPolicy? CipherSuitesPolicy { get; set; }

/// <summary>
/// Gets or sets an optional customized policy for remote certificate
/// validation. If not <see langword="null"/>,
/// <see cref="CertificateRevocationCheckMode"/> and <see cref="SslCertificateTrust"/>
/// are ignored.
/// </summary>
public X509ChainPolicy? CertificateChainPolicy { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -925,7 +925,7 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot

try
{
X509Certificate2? certificate = CertificateValidationPal.GetRemoteCertificate(_securityContext, ref chain);
X509Certificate2? certificate = CertificateValidationPal.GetRemoteCertificate(_securityContext, ref chain, _sslAuthenticationOptions.CertificateChainPolicy);
if (_remoteCertificate != null && certificate != null &&
certificate.RawDataMemory.Span.SequenceEqual(_remoteCertificate.RawDataMemory.Span))
{
Expand All @@ -944,25 +944,37 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot
else
{
chain ??= new X509Chain();
chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;

// Authenticate the remote party: (e.g. when operating in server mode, authenticate the client).
chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid);

if (trust != null)
if (_sslAuthenticationOptions.CertificateChainPolicy != null)
{
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
if (trust._store != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
}
if (trust._trustList != null)
chain.ChainPolicy = _sslAuthenticationOptions.CertificateChainPolicy;
}
else
{
chain.ChainPolicy.RevocationMode = _sslAuthenticationOptions.CertificateRevocationCheckMode;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;

if (trust != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
if (trust._store != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
}
if (trust._trustList != null)
{
chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
}
}
}

// set ApplicationPolicy unless already provided.
if (chain.ChainPolicy.ApplicationPolicy.Count == 0)
{
// Authenticate the remote party: (e.g. when operating in server mode, authenticate the client).
chain.ChainPolicy.ApplicationPolicy.Add(_sslAuthenticationOptions.IsServer ? s_clientAuthOid : s_serverAuthOid);
}

sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties(
_securityContext!,
chain,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
#pragma warning restore SYSLIB0040
RemoteCertificateValidationCallback serverRemoteCallback = new RemoteCertificateValidationCallback(delegate { return true; });
SslStreamCertificateContext certificateContext = SslStreamCertificateContext.Create(serverCert, null, false);
X509ChainPolicy policy = new X509ChainPolicy();

(Stream stream1, Stream stream2) = TestHelper.GetConnectedStreams();
using (var client = new SslStream(stream1))
Expand All @@ -65,7 +66,8 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
EncryptionPolicy = clientEncryption,
LocalCertificateSelectionCallback = clientLocalCallback,
RemoteCertificateValidationCallback = clientRemoteCallback,
TargetHost = clientHost
TargetHost = clientHost,
CertificateChainPolicy = policy,
};

// Create server options
Expand All @@ -80,6 +82,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
RemoteCertificateValidationCallback = serverRemoteCallback,
ServerCertificate = serverCert,
ServerCertificateContext = certificateContext,
CertificateChainPolicy = policy,
};

// Authenticate
Expand All @@ -99,6 +102,8 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
Assert.Same(clientLocalCallback, clientOptions.LocalCertificateSelectionCallback);
Assert.Same(clientRemoteCallback, clientOptions.RemoteCertificateValidationCallback);
Assert.Same(clientHost, clientOptions.TargetHost);
Assert.Same(clientHost, clientOptions.TargetHost);
Assert.Same(policy, clientOptions.CertificateChainPolicy);

// Validate that server options are unchanged
Assert.Equal(serverAllowRenegotiation, serverOptions.AllowRenegotiation);
Expand All @@ -111,6 +116,7 @@ public async Task ClientOptions_ServerOptions_NotMutatedDuringAuthentication()
Assert.Same(serverRemoteCallback, serverOptions.RemoteCertificateValidationCallback);
Assert.Same(serverCert, serverOptions.ServerCertificate);
Assert.Same(certificateContext, serverOptions.ServerCertificateContext);
Assert.Same(policy, serverOptions.CertificateChainPolicy);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,32 +745,23 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task SslStream_UntrustedCaWithCustomCallback_OK(bool usePartialChain)
public async Task SslStream_UntrustedCaWithCustomTrust_OK(bool usePartialChain)
{
int split = Random.Shared.Next(0, certificates.serverChain.Count - 1);

var clientOptions = new SslClientAuthenticationOptions() { TargetHost = "localhost" };
clientOptions.RemoteCertificateValidationCallback =
(sender, certificate, chain, sslPolicyErrors) =>
clientOptions.CertificateChainPolicy = new X509ChainPolicy() { RevocationMode = X509RevocationMode.NoCheck,
TrustMode = X509ChainTrustMode.CustomRootTrust };
clientOptions.CertificateChainPolicy.CustomTrustStore.Add(certificates.serverChain[certificates.serverChain.Count - 1]);
// Add only one CA to verify that peer did send intermediate CA cert.
// In case of partial chain, we need to make missing certs available.
if (usePartialChain)
{
for (int i = split; i < certificates.serverChain.Count - 1; i++)
{
// add our custom root CA
chain.ChainPolicy.CustomTrustStore.Add(certificates.serverChain[certificates.serverChain.Count - 1]);
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
// Add only one CA to verify that peer did send intermediate CA cert.
// In case of partial chain, we need to make missing certs available.
if (usePartialChain)
{
for (int i = split; i < certificates.serverChain.Count - 1; i++)
{
chain.ChainPolicy.ExtraStore.Add(certificates.serverChain[i]);
}
}

bool result = chain.Build((X509Certificate2)certificate);
Assert.True(result);

return result;
};
clientOptions.CertificateChainPolicy.ExtraStore.Add(certificates.serverChain[i]);
}
}

var serverOptions = new SslServerAuthenticationOptions();
X509Certificate2Collection serverChain;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2916,6 +2916,8 @@ public X509ChainPolicy() { }
public System.TimeSpan UrlRetrievalTimeout { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509VerificationFlags VerificationFlags { get { throw null; } set { } }
public System.DateTime VerificationTime { get { throw null; } set { } }
public bool VerificationTimeIgnored { get { throw null; } set { } }
public System.Security.Cryptography.X509Certificates.X509ChainPolicy Clone() { throw null; }
public void Reset() { }
}
public partial struct X509ChainStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ internal bool Build(X509Certificate2 certificate, bool throwOnException)
chainPolicy.RevocationFlag,
chainPolicy._customTrustStore,
chainPolicy.TrustMode,
chainPolicy.VerificationTime,
chainPolicy.VerificationTimeIgnored ? DateTime.Now : chainPolicy.VerificationTime,
chainPolicy.UrlRetrievalTimeout,
chainPolicy.DisableCertificateDownloads);

Expand Down
Loading