1
1
using System . Diagnostics ;
2
2
using System . Security . Cryptography . X509Certificates ;
3
3
using System . Text . RegularExpressions ;
4
+ using HttpSys . NetSh ;
4
5
5
6
namespace HttpSys
6
7
{
@@ -28,12 +29,49 @@ public static void DeleteBinding(string ipPort)
28
29
Console . WriteLine ( "Disabled http.sys settings for mTLS" ) ;
29
30
}
30
31
31
- public static bool BindingExists ( string ipPort , out string certThumbprint , out string appId )
32
+
33
+
34
+ public static bool TryGetSslCertBinding ( string ipPort , out SslCertBinding result )
32
35
{
33
- certThumbprint = string . Empty ;
34
- appId = string . Empty ;
36
+ result = new SslCertBinding ( ) ;
35
37
36
- var bindings = ExecuteNetShCommand ( "http show sslcert" ) ;
38
+ /*
39
+ Example of output:
40
+ -----------------
41
+ IP:port : <ip:port>
42
+ Certificate Hash : <hash>
43
+ Application ID : {<guid>}
44
+ Certificate Store Name : <store-name>
45
+ Verify Client Certificate Revocation : Enabled
46
+ Verify Revocation Using Cached Client Certificate Only : Disabled
47
+ Usage Check : Enabled
48
+ Revocation Freshness Time : 0
49
+ URL Retrieval Timeout : 0
50
+ Ctl Identifier : (null)
51
+ Ctl Store Name : (null)
52
+ DS Mapper Usage : Disabled
53
+ Negotiate Client Certificate : Disabled
54
+ Reject Connections : Disabled
55
+ Disable HTTP2 : Not Set
56
+ Disable QUIC : Not Set
57
+ Disable TLS1.2 : Not Set
58
+ Disable TLS1.3 : Not Set
59
+ Disable OCSP Stapling : Not Set
60
+ Enable Token Binding : Not Set
61
+ Log Extended Events : Not Set
62
+ Disable Legacy TLS Versions : Not Set
63
+ Enable Session Ticket : Not Set
64
+ Disable Session ID : Not Set
65
+ Enable Caching Client Hello : Not Set
66
+ Extended Properties:
67
+ PropertyId : 0
68
+ Receive Window : 1048576
69
+ Extended Properties:
70
+ PropertyId : 1
71
+ Max Settings Per Frame : 2796202
72
+ Max Settings Per Minute : 4294967295
73
+ */
74
+ var bindings = ExecuteNetShCommand ( $ "http show sslcert ipport={ ipPort } ") ;
37
75
if ( string . IsNullOrEmpty ( bindings ) || ! bindings . Contains ( ipPort ) )
38
76
{
39
77
return false ;
@@ -43,22 +81,51 @@ public static bool BindingExists(string ipPort, out string certThumbprint, out s
43
81
var thumbprintMatch = Regex . Match ( bindings , @"Certificate Hash\s+:\s+([a-fA-F0-9]+)" ) ;
44
82
if ( thumbprintMatch . Success )
45
83
{
46
- certThumbprint = thumbprintMatch . Groups [ 1 ] . Value ;
84
+ result . CertificateThumbprint = thumbprintMatch . Groups [ 1 ] . Value ;
47
85
}
48
86
49
87
// Extract the application ID
50
88
var appIdMatch = Regex . Match ( bindings , @"Application ID\s+:\s+{([a-fA-F0-9-]+)}" ) ;
51
89
if ( appIdMatch . Success )
52
90
{
53
- appId = appIdMatch . Groups [ 1 ] . Value ;
91
+ result . ApplicationId = appIdMatch . Groups [ 1 ] . Value ;
92
+ }
93
+
94
+ var negotiateClientCertEnabledRegex = Regex . Match ( bindings , @"Negotiate Client Certificate\s+:\s+([a-zA-Z0-9]+)" ) ;
95
+ if ( negotiateClientCertEnabledRegex . Success )
96
+ {
97
+ var negotiateClientCertValue = negotiateClientCertEnabledRegex . Groups [ 1 ] . Value ;
98
+ result . NegotiateClientCertificate = ParseNetShFlag ( negotiateClientCertValue ) ;
99
+ }
100
+
101
+ var disableSessionId = Regex . Match ( bindings , @"Disable Session ID\s+:\s+([a-zA-Z0-9 ]+)" ) ;
102
+ if ( disableSessionId . Success )
103
+ {
104
+ var disableSessionIdValue = disableSessionId . Groups [ 1 ] . Value ;
105
+ result . DisableSessionIdTlsResumption = ParseNetShFlag ( disableSessionIdValue ) ;
106
+ }
107
+
108
+ var enableSessionTicket = Regex . Match ( bindings , @"Enable Session Ticket\s+:\s+([a-zA-Z0-9 ]+)" ) ;
109
+ if ( enableSessionTicket . Success )
110
+ {
111
+ var enableSessionTicketValue = enableSessionTicket . Groups [ 1 ] . Value ;
112
+ result . EnableSessionTicketTlsResumption = ParseNetShFlag ( enableSessionTicketValue ) ;
54
113
}
55
114
56
115
return true ;
116
+
117
+ NetShFlag ParseNetShFlag ( string prop ) => prop switch
118
+ {
119
+ "Not Set" => NetShFlag . NotSet ,
120
+ "Disable" or "Disabled" => NetShFlag . Disabled ,
121
+ "Enable" or "Enabled" or "Set" => NetShFlag . Enable ,
122
+ _ => throw new ArgumentOutOfRangeException ( nameof ( prop ) , $ "unexpected netsh flag '{ prop } ' for ssl cert binding") ,
123
+ } ;
57
124
}
58
125
59
- public static void Show ( )
126
+ public static void LogSslCertBinding ( string ipPort )
60
127
{
61
- ExecuteNetShCommand ( "http show sslcert" , alwaysLogOutput : true ) ;
128
+ ExecuteNetShCommand ( $ "http show sslcert ipport= { ipPort } ", alwaysLogOutput : true ) ;
62
129
}
63
130
64
131
public static void SetTestCertBinding ( string ipPort , bool enableClientCertNegotiation )
@@ -76,7 +143,7 @@ public static void SetTestCertBinding(string ipPort, bool enableClientCertNegoti
76
143
}
77
144
78
145
string certThumbprint = certificate . Thumbprint ;
79
- SetCertBinding ( ipPort , certThumbprint , enableClientCertNegotiation : enableClientCertNegotiation ) ;
146
+ AddCertBinding ( ipPort , certThumbprint , clientCertNegotiation : enableClientCertNegotiation ? NetShFlag . Enable : NetShFlag . Disabled ) ;
80
147
81
148
Console . WriteLine ( "Configured binding for testCert for http.sys" ) ;
82
149
}
@@ -108,16 +175,77 @@ public static bool TrySelfSignCertificate(string ipPort, out string certThumbpri
108
175
}
109
176
}
110
177
111
- public static void SetCertBinding ( string ipPort , string certThumbprint , string appId = null , bool enableClientCertNegotiation = false )
178
+ public static void AddCertBinding (
179
+ string ipPort , string certThumbprint ,
180
+ string ? appId = null ,
181
+ NetShFlag clientCertNegotiation = NetShFlag . Disabled ,
182
+ NetShFlag disablesessionid = NetShFlag . Enable ,
183
+ NetShFlag enablesessionticket = NetShFlag . Disabled )
184
+ => CertBindingCore ( "add" , ipPort , certThumbprint , appId , clientCertNegotiation , disablesessionid , enablesessionticket ) ;
185
+
186
+ public static void UpdateCertBinding ( string ipPort , SslCertBinding binding ) => UpdateCertBinding (
187
+ ipPort ,
188
+ binding . CertificateThumbprint ,
189
+ binding . ApplicationId ,
190
+ binding . NegotiateClientCertificate ,
191
+ binding . DisableSessionIdTlsResumption ,
192
+ binding . EnableSessionTicketTlsResumption ) ;
193
+
194
+ public static void UpdateCertBinding (
195
+ string ipPort , string certThumbprint ,
196
+ string ? appId = null ,
197
+ NetShFlag clientCertNegotiation = NetShFlag . Disabled ,
198
+ NetShFlag disablesessionid = NetShFlag . Enable ,
199
+ NetShFlag enablesessionticket = NetShFlag . Disabled )
200
+ => CertBindingCore ( "update" , ipPort , certThumbprint , appId , clientCertNegotiation , disablesessionid , enablesessionticket ) ;
201
+
202
+ private static void CertBindingCore (
203
+ string httpOperation ,
204
+ string ipPort , string certThumbprint ,
205
+ string ? appId = null ,
206
+ NetShFlag clientcertnegotiation = NetShFlag . Disabled ,
207
+ NetShFlag disablesessionid = NetShFlag . Enable ,
208
+ NetShFlag enablesessionticket = NetShFlag . Disabled )
112
209
{
113
- var negotiateClientCert = enableClientCertNegotiation ? "enable" : "disable" ;
114
210
if ( string . IsNullOrEmpty ( appId ) )
115
211
{
116
212
appId = "00000000-0000-0000-0000-000000000000" ;
117
213
}
118
- string command = $ "http add sslcert ipport={ ipPort } certstorename=MY certhash={ certThumbprint } appid={{{appId}}} clientcertnegotiation={ negotiateClientCert } ";
119
- ExecuteNetShCommand ( command ) ;
120
- Console . WriteLine ( $ "Performed cert bindign for { ipPort } ") ;
214
+
215
+ var clientcertnegotiationFlag = GetFlagValue ( clientcertnegotiation ) ;
216
+ var disablesessionidFlag = GetFlagValue ( disablesessionid ) ;
217
+ var enablesessionticketFlag = GetFlagValue ( enablesessionticket ) ;
218
+ string command = $ "http { httpOperation } sslcert ipport={ ipPort } certstorename=MY certhash={ certThumbprint } appid={{{appId}}}";
219
+
220
+ if ( clientcertnegotiationFlag != null )
221
+ {
222
+ command += $ " clientcertnegotiation={ clientcertnegotiationFlag } ";
223
+ }
224
+
225
+ // below options are supported only in later versions of HTTP.SYS
226
+ // you can identify if it is available by running `netsh http add sslcert help`
227
+ // ---
228
+ // workaround is to control SChannel settings via registry
229
+
230
+ //if (disablesessionidFlag != null)
231
+ //{
232
+ // command += $" disablesessionid={disablesessionidFlag}";
233
+ //}
234
+ //if (enablesessionticketFlag != null)
235
+ //{
236
+ // command += $" enablesessionticket={enablesessionticketFlag}";
237
+ //}
238
+
239
+ ExecuteNetShCommand ( command , alwaysLogOutput : true ) ;
240
+ Console . WriteLine ( $ "Performed cert binding for { ipPort } ") ;
241
+
242
+ string ? GetFlagValue ( NetShFlag flag ) => flag switch
243
+ {
244
+ NetShFlag . NotSet => null ,
245
+ NetShFlag . Disabled => "disable" ,
246
+ NetShFlag . Enable => "enable" ,
247
+ _ => throw new ArgumentOutOfRangeException ( nameof ( flag ) ) ,
248
+ } ;
121
249
}
122
250
123
251
private static string ExecutePowershellCommand ( string command , bool alwaysLogOutput = false )
@@ -126,7 +254,7 @@ private static string ExecutePowershellCommand(string command, bool alwaysLogOut
126
254
private static string ExecuteNetShCommand ( string command , bool alwaysLogOutput = false )
127
255
=> ExecuteCommand ( "netsh" , command , alwaysLogOutput ) ;
128
256
129
- private static string ExecuteCommand ( string fileName , string command , bool alwaysLogOutput = false )
257
+ private static string ExecuteCommand ( string fileName , string command , bool logOutput = false )
130
258
{
131
259
ProcessStartInfo processInfo = new ProcessStartInfo ( fileName , command )
132
260
{
@@ -141,7 +269,7 @@ private static string ExecuteCommand(string fileName, string command, bool alway
141
269
string output = process . StandardOutput . ReadToEnd ( ) ;
142
270
process . WaitForExit ( ) ;
143
271
144
- if ( alwaysLogOutput )
272
+ if ( logOutput )
145
273
{
146
274
Console . WriteLine ( output ) ;
147
275
}
0 commit comments