merge -r 58784:58785
[mono.git] / mcs / class / Mono.Security / Test / tools / tlstest / tlstest.cs
1 //
2 // TlsTest.cs: TLS/SSL Test Program
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2004 Novell (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.Globalization;
13 using System.IO;
14 using System.Net;
15 using System.Net.Sockets;
16 using System.Reflection;
17 using System.Security.Cryptography.X509Certificates;
18 using System.Text;
19
20 using Mono.Security.Protocol.Tls;
21
22 public class TlsTest {
23
24         public static void Usage (string message) 
25         {
26                 Console.WriteLine ("{0}tlstest - Copyright (c) 2004 Novell", Environment.NewLine);
27                 if (message != null) {
28                         Console.WriteLine ("{0}{1}{0}", Environment.NewLine, message);
29                 }
30                 Console.WriteLine ("Usage:");
31                 Console.WriteLine ("tlstest [protocol] [class] [credentials] [--x:x509 [--x:x509]] [--time] [--show] url [...]");
32                 Console.WriteLine ("{0}protocol (only applicable when using stream)", Environment.NewLine);
33                 Console.WriteLine ("\t--any   \tNegotiate protocol [default]");
34                 Console.WriteLine ("\t--ssl   \tUse SSLv3");
35                 Console.WriteLine ("\t--ssl2  \tUse SSLv2 - unsupported on Mono");
36                 Console.WriteLine ("\t--ssl3  \tUse SSLv3");
37                 Console.WriteLine ("\t--tls   \tUse TLSv1");
38                 Console.WriteLine ("\t--tls1  \tUse TLSv1");
39                 Console.WriteLine ("{0}class", Environment.NewLine);
40                 Console.WriteLine ("\t--stream\tDirectly use the SslClientStream [default]");
41                 Console.WriteLine ("\t--web   \tUse the WebRequest/WebResponse classes");
42                 Console.WriteLine ("{0}credentials", Environment.NewLine);
43                 Console.WriteLine ("\t--basic:username:password:domain\tBasic Authentication");
44                 Console.WriteLine ("\t--digest:username:password:domain\tDigest Authentication");
45                 Console.WriteLine ("{0}options", Environment.NewLine);
46                 Console.WriteLine ("\t--x:x509\tX.509 client certificate (multiple entries allowed");
47                 Console.WriteLine ("\t--time  \tShow the time required for each page load");
48                 Console.WriteLine ("\t--show  \tShow the web page content on screen");
49                 Console.WriteLine ("{0}\turl [...]\tOne, or more, URL to download{0}", Environment.NewLine);
50         }
51
52         private static bool show;
53         private static bool time;
54         private static bool web;
55         private static Mono.Security.Protocol.Tls.SecurityProtocolType protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Default;
56         private static X509CertificateCollection certificates = new X509CertificateCollection ();
57         private static NetworkCredential basicCred;
58         private static NetworkCredential digestCred;
59
60         public static void Main (string[] args) 
61         {
62                 if (args.Length == 0) {
63                         Usage ("Missing arguments");
64                         return;
65                 }
66
67                 ArrayList urls = new ArrayList ();
68                 foreach (string arg in args) {
69                         switch (arg) {
70                                 // protocol
71                                 case "--any":
72                                         protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Default;
73                                         break;
74                                 case "--ssl":
75                                 case "--ssl3":
76                                         protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl3;
77                                         break;
78                                 case "--ssl2":
79                                         protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl2;
80                                         // note: will only works with Fx 1.2
81                                         // but the tool doesn't link with it
82                                         Usage ("Not supported");
83                                         return;
84                                 case "--tls":
85                                 case "--tls1":
86                                         protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Tls;
87                                         break;
88                                 // class
89                                 case "--stream":
90                                         web = false;
91                                         break;
92                                 case "--web":
93                                         web = true;
94                                         break;
95                                 // options
96                                 case "--time":
97                                         time = true;
98                                         break;
99                                 case "--show":
100                                         show = true;
101                                         break;
102                                 case "--help":
103                                         Usage (null);
104                                         return;
105                                 // credentials, certificates, urls or bad options
106                                 default:
107                                         if (arg.StartsWith ("--digest:")) {
108                                                 digestCred = GetCredentials (arg.Substring (9));
109                                                 continue;
110                                         }
111                                         else if (arg.StartsWith ("--basic:")) {
112                                                 basicCred = GetCredentials (arg.Substring (8));
113                                                 continue;
114                                         }
115                                         else if (arg.StartsWith ("--x:")) {
116                                                 string filename = arg.Substring (4);
117                                                 X509Certificate x509 = X509Certificate.CreateFromCertFile (filename);
118                                                 certificates.Add (x509);
119                                                 continue;
120                                         }
121                                         else if (arg.StartsWith ("--")) {
122                                                 Usage ("Invalid option " + arg);
123                                                 return;
124                                         }
125                                         urls.Add (arg);
126                                         break;
127                         }
128                 }
129
130                 if ((web) && (protocol != Mono.Security.Protocol.Tls.SecurityProtocolType.Default)) {
131                         Usage ("You can't set the protocol when using the WebRequest/WebResponse class");
132                         return;
133                 }
134
135                 if (urls.Count == 0) {
136                         Usage ("no URL were specified");
137                         return;
138                 }
139
140                 foreach (string url in urls) {
141                         Console.WriteLine ("{0}{1}", Environment.NewLine, url);
142                         string content = null;
143                         DateTime start = DateTime.Now;
144                         
145                         try {
146                                 if (web) {
147                                         content = GetWebPage (url);
148                                 }
149                                 else {
150                                         content = GetStreamPage (url);
151                                 }
152                         }
153                         catch (Exception e) {
154                                 // HResult is protected - but very useful in debugging
155                                 PropertyInfo pi = e.GetType ().GetProperty ("HResult", BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.Instance);
156                                 Console.WriteLine ("FAILED: #{0}", (int)pi.GetValue (e, null));
157                                 Console.WriteLine (e.ToString ());
158                         }
159
160                         TimeSpan ts = (DateTime.Now - start);
161                         if ((show) && (content != null)) {
162                                 Console.WriteLine ("{0}{1}{0}", Environment.NewLine, content);
163                         }
164                         if (time) {
165                                 Console.WriteLine ("Time: " + ts.ToString ());
166                         }
167                 }
168         }
169
170         public static string GetWebPage (string url) 
171         {
172                 ServicePointManager.CertificatePolicy = new TestCertificatePolicy ();
173
174                 Uri uri = new Uri (url);
175                 HttpWebRequest req = (HttpWebRequest) WebRequest.Create (uri);
176
177                 if ((digestCred != null) || (basicCred != null)) {
178                         CredentialCache cache = new CredentialCache ();
179                         if (digestCred != null)
180                                 cache.Add (uri, "Digest", digestCred);
181                         if (basicCred != null)
182                                 cache.Add (uri, "Basic", basicCred);
183                         req.Credentials = cache;
184                 }
185
186                 if (certificates.Count > 0)
187                         req.ClientCertificates.AddRange (certificates);
188                 
189                 WebResponse resp = req.GetResponse ();
190                 Stream stream = resp.GetResponseStream ();
191                 StreamReader sr = new StreamReader (stream, Encoding.UTF8);
192                 return sr.ReadToEnd ();
193         }
194
195         public static string GetStreamPage (string url) 
196         {
197                 Uri uri = new Uri (url);
198                 if (uri.Scheme != Uri.UriSchemeHttps)
199                         throw new NotSupportedException ("Stream only works with HTTPS protocol");
200
201                 IPHostEntry host = Dns.Resolve (uri.Host);
202                 IPAddress ip = host.AddressList [0];
203                 Socket socket = new Socket (ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
204                 socket.Connect (new IPEndPoint (ip, uri.Port));
205                 NetworkStream ns = new NetworkStream (socket, false);
206                 SslClientStream ssl = new SslClientStream (ns, uri.Host, false, protocol, certificates);
207                 ssl.ServerCertValidationDelegate += new CertificateValidationCallback (CertificateValidation);
208
209                 StreamWriter sw = new StreamWriter (ssl);
210                 sw.WriteLine ("GET {0}{1}", uri.AbsolutePath, Environment.NewLine);
211                 sw.Flush ();
212
213                 StreamReader sr = new StreamReader (ssl, Encoding.UTF8);
214                 return sr.ReadToEnd ();
215         }
216
217         private static NetworkCredential GetCredentials (string credentials) 
218         {
219                 string[] creds = credentials.Split (':');
220                 NetworkCredential nc = new NetworkCredential ();
221                 nc.UserName = ((creds.Length > 0) ? creds [0] : String.Empty);
222                 nc.Password = ((creds.Length > 1) ? creds [1] : String.Empty);
223                 nc.Domain = ((creds.Length > 2) ? creds [2] : String.Empty);
224                 return nc;
225         }
226
227         private static void ShowCertificateError (int error) 
228         {
229                 string message = null;
230                 switch (error) {
231                         case -2146762490:
232                                 message = "CERT_E_PURPOSE 0x800B0106";
233                                 break;
234                         case -2146762481:
235                                 message = "CERT_E_CN_NO_MATCH 0x800B010F";
236                                 break;
237                         case -2146869223:
238                                 message = "TRUST_E_BASIC_CONSTRAINTS 0x80096019";
239                                 break;
240                         case -2146869232:
241                                 message = "TRUST_E_BAD_DIGEST 0x80096010";
242                                 break;
243                         case -2146762494:
244                                 message = "CERT_E_VALIDITYPERIODNESTING 0x800B0102";
245                                 break;
246                         case -2146762495:
247                                 message = "CERT_E_EXPIRED 0x800B0101";
248                                 break;
249                         case -2146762486:
250                                 message = "CERT_E_CHAINING 0x800B010A";
251                                 break;
252                         case -2146762487:
253                                 message = "CERT_E_UNTRUSTEDROOT 0x800B0109";
254                                 break;
255                         default:
256                                 message = "unknown (try WinError.h)";
257                                 break;
258                 }
259                 Console.WriteLine ("Error #{0}: {1}", error, message);
260         }
261
262         private static bool CertificateValidation (X509Certificate certificate, int[] certificateErrors)
263         {
264                 if (certificateErrors.Length > 0) {
265                         Console.WriteLine (certificate.ToString (true));
266                         // X509Certificate.ToString(true) doesn't show dates :-(
267                         Console.WriteLine ("\tValid From:  {0}", certificate.GetEffectiveDateString ());
268                         Console.WriteLine ("\tValid Until: {0}{1}", certificate.GetExpirationDateString (), Environment.NewLine);
269                         // multiple errors are possible using SslClientStream
270                         foreach (int error in certificateErrors) {
271                                 ShowCertificateError (error);
272                         }
273                 }
274                 // whatever the reason we do not stop the SSL connection
275                 return true;
276         }
277
278         public class TestCertificatePolicy : ICertificatePolicy {
279
280                 public bool CheckValidationResult (ServicePoint sp, X509Certificate certificate, WebRequest request, int error)
281                 {
282                         if (error != 0) {
283                                 Console.WriteLine (certificate.ToString (true));
284                                 // X509Certificate.ToString(true) doesn't show dates :-(
285                                 Console.WriteLine ("\tValid From:  {0}", certificate.GetEffectiveDateString ());
286                                 Console.WriteLine ("\tValid Until: {0}{1}", certificate.GetExpirationDateString (), Environment.NewLine);
287
288                                 ShowCertificateError (error);
289                         }
290                         // whatever the reason we do not stop the SSL connection
291                         return true;
292                 }
293         }
294 }
295