2 // TlsTest.cs: TLS/SSL Test Program
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2004 Novell (http://www.novell.com)
11 using System.Collections;
12 using System.Globalization;
15 using System.Net.Sockets;
16 using System.Reflection;
17 using System.Security.Cryptography.X509Certificates;
20 using Mono.Security.Protocol.Tls;
22 public class TlsTest {
24 public static void Usage (string message)
26 Console.WriteLine ("{0}tlstest - Copyright (c) 2004 Novell", Environment.NewLine);
27 if (message != null) {
28 Console.WriteLine ("{0}{1}{0}", Environment.NewLine, message);
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);
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;
60 public static void Main (string[] args)
62 if (args.Length == 0) {
63 Usage ("Missing arguments");
67 ArrayList urls = new ArrayList ();
68 foreach (string arg in args) {
72 protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Default;
76 protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Ssl3;
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");
86 protocol = Mono.Security.Protocol.Tls.SecurityProtocolType.Tls;
105 // credentials, certificates, urls or bad options
107 if (arg.StartsWith ("--digest:")) {
108 digestCred = GetCredentials (arg.Substring (9));
111 else if (arg.StartsWith ("--basic:")) {
112 basicCred = GetCredentials (arg.Substring (8));
115 else if (arg.StartsWith ("--x:")) {
116 string filename = arg.Substring (4);
117 X509Certificate x509 = X509Certificate.CreateFromCertFile (filename);
118 certificates.Add (x509);
121 else if (arg.StartsWith ("--")) {
122 Usage ("Invalid option " + arg);
130 if ((web) && (protocol != Mono.Security.Protocol.Tls.SecurityProtocolType.Default)) {
131 Usage ("You can't set the protocol when using the WebRequest/WebResponse class");
135 if (urls.Count == 0) {
136 Usage ("no URL were specified");
140 foreach (string url in urls) {
141 Console.WriteLine ("{0}{1}", Environment.NewLine, url);
142 string content = null;
143 DateTime start = DateTime.Now;
147 content = GetWebPage (url);
150 content = GetStreamPage (url);
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 ());
160 TimeSpan ts = (DateTime.Now - start);
161 if ((show) && (content != null)) {
162 Console.WriteLine ("{0}{1}{0}", Environment.NewLine, content);
165 Console.WriteLine ("Time: " + ts.ToString ());
170 public static string GetWebPage (string url)
172 ServicePointManager.CertificatePolicy = new TestCertificatePolicy ();
174 Uri uri = new Uri (url);
175 HttpWebRequest req = (HttpWebRequest) WebRequest.Create (uri);
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;
186 if (certificates.Count > 0)
187 req.ClientCertificates.AddRange (certificates);
189 WebResponse resp = req.GetResponse ();
190 Stream stream = resp.GetResponseStream ();
191 StreamReader sr = new StreamReader (stream, Encoding.UTF8);
192 return sr.ReadToEnd ();
195 public static string GetStreamPage (string url)
197 Uri uri = new Uri (url);
198 if (uri.Scheme != Uri.UriSchemeHttps)
199 throw new NotSupportedException ("Stream only works with HTTPS protocol");
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);
209 StreamWriter sw = new StreamWriter (ssl);
210 sw.WriteLine ("GET {0} HTTP/1.0{1}", uri.AbsolutePath, Environment.NewLine);
213 StreamReader sr = new StreamReader (ssl, Encoding.UTF8);
214 return sr.ReadToEnd ();
217 private static NetworkCredential GetCredentials (string credentials)
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);
227 private static void ShowCertificateError (int error)
229 string message = null;
232 message = "CERT_E_PURPOSE 0x800B0106";
235 message = "CERT_E_CN_NO_MATCH 0x800B010F";
238 message = "TRUST_E_BASIC_CONSTRAINTS 0x80096019";
241 message = "TRUST_E_BAD_DIGEST 0x80096010";
244 message = "CERT_E_VALIDITYPERIODNESTING 0x800B0102";
247 message = "CERT_E_EXPIRED 0x800B0101";
250 message = "CERT_E_CHAINING 0x800B010A";
253 message = "CERT_E_UNTRUSTEDROOT 0x800B0109";
256 message = "unknown (try WinError.h)";
259 Console.WriteLine ("Error #{0}: {1}", error, message);
262 private static bool CertificateValidation (X509Certificate certificate, int[] certificateErrors)
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);
274 // whatever the reason we do not stop the SSL connection
278 public class TestCertificatePolicy : ICertificatePolicy {
280 public bool CheckValidationResult (ServicePoint sp, X509Certificate certificate, WebRequest request, int error)
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);
288 ShowCertificateError (error);
290 // whatever the reason we do not stop the SSL connection