Skip to content

Commit cd4df7d

Browse files
authored
Implement part of SslCertificateTrust (#55104)
* snatpshot * snapshot * add csproj' * update * more ckeanup * more cleanup * feedback from review * fix mail tests * feedabck from review * feedback from review * make constructor private
1 parent efd0bb5 commit cd4df7d

16 files changed

Lines changed: 188 additions & 23 deletions

src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ internal enum ContextAttribute
6565
SECPKG_ATTR_LOCAL_CERT_CONTEXT = 0x54, // returns PCCERT_CONTEXT
6666
SECPKG_ATTR_ROOT_STORE = 0x55, // returns HCERTCONTEXT to the root store
6767
SECPKG_ATTR_ISSUER_LIST_EX = 0x59, // returns SecPkgContext_IssuerListInfoEx
68+
SECPKG_ATTR_CLIENT_CERT_POLICY = 0x60, // sets SecPkgCred_ClientCertCtlPolicy
6869
SECPKG_ATTR_CONNECTION_INFO = 0x5A, // returns SecPkgContext_ConnectionInfo
6970
SECPKG_ATTR_CIPHER_INFO = 0x64, // returns SecPkgContext_CipherInfo
7071
SECPKG_ATTR_UI_INFO = 0x68, // sets SEcPkgContext_UiInfo
@@ -315,6 +316,20 @@ public SecBufferDesc(int count)
315316
}
316317
}
317318

319+
[StructLayout(LayoutKind.Sequential)]
320+
internal unsafe struct SecPkgCred_ClientCertPolicy
321+
{
322+
public uint dwFlags;
323+
public Guid guidPolicyId;
324+
public uint dwCertFlags;
325+
public uint dwUrlRetrievalTimeout;
326+
public BOOL fCheckRevocationFreshnessTime;
327+
public uint dwRevocationFreshnessTime;
328+
public BOOL fOmitUsageCheck;
329+
public char* pwszSslCtlStoreName;
330+
public char* pwszSslCtlIdentifier;
331+
}
332+
318333
[DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, SetLastError = true)]
319334
internal static extern int EncryptMessage(
320335
ref CredHandle contextHandle,
@@ -472,5 +487,12 @@ internal static extern SECURITY_STATUS SspiEncodeStringsAsAuthIdentity(
472487
[In] string domainName,
473488
[In] string password,
474489
[Out] out SafeSspiAuthDataHandle authData);
490+
491+
[DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
492+
internal static extern SECURITY_STATUS SetCredentialsAttributesW(
493+
[In] ref CredHandle handlePtr,
494+
[In] long ulAttribute,
495+
[In] ref SecPkgCred_ClientCertPolicy pBuffer,
496+
[In] long cbBuffer);
475497
}
476498
}

src/libraries/System.Net.Http/src/System.Net.Http.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,12 @@
396396
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpNoProxy.cs" />
397397
<Compile Include="System\Net\Http\SocketsHttpHandler\HttpWindowsProxy.cs" />
398398
<Compile Include="System\Net\Http\SocketsHttpHandler\CurrentUserIdentityProvider.Windows.cs" />
399+
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
400+
Link="Common\Interop\Windows\Interop.BOOL.cs" />
399401
<Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs"
400402
Link="Common\Interop\Windows\Interop.Libraries.cs" />
401403
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
402-
Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
404+
Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
403405
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs"
404406
Link="Common\Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs" />
405407
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_INFO.cs"

src/libraries/System.Net.Mail/src/System.Net.Mail.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@
204204
Link="Common\Interop\Windows\Crypt32\Interop.DATA_BLOB.cs" />
205205
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.MsgEncodingType.cs"
206206
Link="Common\Interop\Windows\Crypt32\Interop.Interop.MsgEncodingType.cs" />
207+
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
208+
Link="Common\Interop\Windows\Interop.BOOL.cs" />
207209
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
208210
Link="Common\Interop\Windows\Interop.Libraries.cs" />
209211
<Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecPkgContext_Bindings.cs"

src/libraries/System.Net.Mail/tests/Unit/System.Net.Mail.Unit.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,8 @@
183183
</ItemGroup>
184184
<!-- Windows specific files -->
185185
<ItemGroup Condition="'$(TargetsWindows)'=='true'">
186+
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
187+
Link="Common\Interop\Windows\Interop.BOOL.cs" />
186188
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs"
187189
Link="Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs" />
188190
<Compile Include="$(CommonPath)System\Net\Security\SecurityBuffer.Windows.cs"

src/libraries/System.Net.Security/ref/System.Net.Security.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,25 @@ public readonly struct SslClientHelloInfo
130130
public static bool operator !=(System.Net.Security.SslApplicationProtocol left, System.Net.Security.SslApplicationProtocol right) { throw null; }
131131
public override string ToString() { throw null; }
132132
}
133+
134+
public sealed partial class SslCertificateTrust
135+
{
136+
public static SslCertificateTrust CreateForX509Store(
137+
System.Security.Cryptography.X509Certificates.X509Store store,
138+
bool sendTrustInHandshake = false) { throw null; }
139+
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
140+
public static SslCertificateTrust CreateForX509Collection(
141+
System.Security.Cryptography.X509Certificates.X509Certificate2Collection trustList,
142+
bool sendTrustInHandshake = false) { throw null; }
143+
private SslCertificateTrust() { throw null; }
144+
}
145+
133146
public sealed partial class SslStreamCertificateContext
134147
{
135148
internal SslStreamCertificateContext() { throw null; }
136-
public static SslStreamCertificateContext Create(System.Security.Cryptography.X509Certificates.X509Certificate2 target, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? additionalCertificates, bool offline = false) { throw null; }
149+
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
150+
public static SslStreamCertificateContext Create(System.Security.Cryptography.X509Certificates.X509Certificate2 target, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? additionalCertificates, bool offline) { throw null; }
151+
public static SslStreamCertificateContext Create(System.Security.Cryptography.X509Certificates.X509Certificate2 target, System.Security.Cryptography.X509Certificates.X509Certificate2Collection? additionalCertificates, bool offline = false, SslCertificateTrust? trust = null) { throw null; }
137152
}
138153
public partial class SslClientAuthenticationOptions
139154
{

src/libraries/System.Net.Security/src/Resources/Strings.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,10 @@
458458
<data name="net_android_ssl_api_level_unsupported" xml:space="preserve">
459459
<value>Setting an SNI hostname is not supported on this API level.</value>
460460
</data>
461+
<data name="net_ssl_trust_store" xml:space="preserve">
462+
<value>Only LocalMachine stores are supported on Windows.</value>
463+
</data>
464+
<data name="net_ssl_trust_collection" xml:space="preserve">
465+
<value>Sending trust from collection is not supported on Windows.</value>
466+
</data>
461467
</root>

src/libraries/System.Net.Security/src/System.Net.Security.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<TargetFrameworks>$(NetCoreAppCurrent)-windows;$(NetCoreAppCurrent)-Unix;$(NetCoreAppCurrent)-Android;$(NetCoreAppCurrent)-OSX;$(NetCoreAppCurrent)-iOS;$(NetCoreAppCurrent)-tvOS;$(NetCoreAppCurrent)</TargetFrameworks>
55
<!-- This is needed so that code for TlsCipherSuite will have no namespace (causes compile errors) when used within T4 template -->
66
<DefineConstants>$(DefineConstants);PRODUCT</DefineConstants>
7+
<DefineConstants Condition="'$(TargetsWindows)' == 'true'">$(DefineConstants);TARGET_WINDOWS</DefineConstants>
78
<Nullable>enable</Nullable>
89
</PropertyGroup>
910
<PropertyGroup>
@@ -29,6 +30,7 @@
2930
<Compile Include="System\Net\Security\ProtectionLevel.cs" />
3031
<Compile Include="System\Net\Security\SslApplicationProtocol.cs" />
3132
<Compile Include="System\Net\Security\SslAuthenticationOptions.cs" />
33+
<Compile Include="System\Net\Security\SslCertificateTrust.cs" />
3234
<Compile Include="System\Net\Security\SslClientAuthenticationOptions.cs" />
3335
<Compile Include="System\Net\Security\SslClientHelloInfo.cs" />
3436
<Compile Include="System\Net\Security\SslServerAuthenticationOptions.cs" />
@@ -156,6 +158,8 @@
156158
<Compile Include="$(CommonPath)System\Net\Security\SecurityContextTokenHandle.cs"
157159
Link="Common\System\Net\Security\SecurityContextTokenHandle.cs" />
158160
<!-- Interop -->
161+
<Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
162+
Link="Common\Interop\Windows\Interop.BOOL.cs" />
159163
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
160164
Link="Common\Interop\Windows\Interop.Libraries.cs" />
161165
<Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"

src/libraries/System.Net.Security/src/System/Net/Security/SecureChannel.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -922,7 +922,7 @@ internal SecurityStatusPal Decrypt(Span<byte> buffer, out int outputOffset, out
922922
--*/
923923

924924
//This method validates a remote certificate.
925-
internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remoteCertValidationCallback, ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus)
925+
internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remoteCertValidationCallback, SslCertificateTrust? trust, ref ProtocolToken? alertToken, out SslPolicyErrors sslPolicyErrors, out X509ChainStatusFlags chainStatus)
926926
{
927927
sslPolicyErrors = SslPolicyErrors.None;
928928
chainStatus = X509ChainStatusFlags.NoError;
@@ -965,6 +965,19 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot
965965
chain.ChainPolicy.ExtraStore.AddRange(remoteCertificateStore);
966966
}
967967

968+
if (trust != null)
969+
{
970+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
971+
if (trust._store != null)
972+
{
973+
chain.ChainPolicy.CustomTrustStore.AddRange(trust._store.Certificates);
974+
}
975+
if (trust._trustList != null)
976+
{
977+
chain.ChainPolicy.CustomTrustStore.AddRange(trust._trustList);
978+
}
979+
}
980+
968981
sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties(
969982
_securityContext!,
970983
chain,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Runtime.Versioning;
5+
using System.Security.Cryptography.X509Certificates;
6+
7+
namespace System.Net.Security
8+
{
9+
public sealed class SslCertificateTrust
10+
{
11+
internal X509Store? _store;
12+
internal X509Certificate2Collection? _trustList;
13+
internal bool _sendTrustInHandshake;
14+
15+
public static SslCertificateTrust CreateForX509Store(X509Store store, bool sendTrustInHandshake = false)
16+
{
17+
18+
#if TARGET_WINDOWS
19+
if (sendTrustInHandshake && store.Location != StoreLocation.LocalMachine)
20+
{
21+
throw new PlatformNotSupportedException(SR.net_ssl_trust_store);
22+
}
23+
#else
24+
if (sendTrustInHandshake)
25+
{
26+
// to be removed when implemented.
27+
throw new PlatformNotSupportedException("Not supported yet.");
28+
}
29+
#endif
30+
if (!store.IsOpen)
31+
{
32+
store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
33+
}
34+
35+
var trust = new SslCertificateTrust();
36+
trust._store = store;
37+
trust._sendTrustInHandshake = sendTrustInHandshake;
38+
return trust;
39+
}
40+
41+
[UnsupportedOSPlatform("windows")]
42+
public static SslCertificateTrust CreateForX509Collection(X509Certificate2Collection trustList, bool sendTrustInHandshake = false)
43+
{
44+
if (sendTrustInHandshake)
45+
{
46+
// to be removed when implemented.
47+
throw new PlatformNotSupportedException("Not supported yet.");
48+
}
49+
50+
#if TARGET_WINDOWS
51+
if (sendTrustInHandshake)
52+
{
53+
throw new PlatformNotSupportedException(SR.net_ssl_trust_collection);
54+
}
55+
#endif
56+
var trust = new SslCertificateTrust();
57+
trust._trustList = trustList;
58+
trust._sendTrustInHandshake = sendTrustInHandshake;
59+
return trust;
60+
}
61+
62+
private SslCertificateTrust() { }
63+
}
64+
}

src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Implementation.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ private bool CompleteHandshake(ref ProtocolToken? alertToken, out SslPolicyError
664664
return true;
665665
}
666666

667-
if (!_context.VerifyRemoteCertificate(_sslAuthenticationOptions!.CertValidationDelegate, ref alertToken, out sslPolicyErrors, out chainStatus))
667+
if (!_context.VerifyRemoteCertificate(_sslAuthenticationOptions!.CertValidationDelegate, _sslAuthenticationOptions!.CertificateContext?.Trust, ref alertToken, out sslPolicyErrors, out chainStatus))
668668
{
669669
_handshakeCompleted = false;
670670
return false;

0 commit comments

Comments
 (0)