diff options
author | Sebastien Pouliot <sebastien@ximian.com> | 2004-12-14 04:58:24 +0300 |
---|---|---|
committer | Sebastien Pouliot <sebastien@ximian.com> | 2004-12-14 04:58:24 +0300 |
commit | 52dcb75610d713953ef0d79bcd143943159ccc70 (patch) | |
tree | 0e6488b716293284dc16c8e829ba6c175ed54397 /mcs/tools/security/certmgr.cs | |
parent | 346bef5d92cf86e6a97469b65dab85c763fc8c28 (diff) |
2004-12-13 Sebastien Pouliot <sebastien@ximian.com>
* certmgr.cs: Add a new -ssl action to download and add the
certificates from an SSL connection into thr right stores.
svn path=/trunk/mcs/; revision=37709
Diffstat (limited to 'mcs/tools/security/certmgr.cs')
-rw-r--r-- | mcs/tools/security/certmgr.cs | 213 |
1 files changed, 188 insertions, 25 deletions
diff --git a/mcs/tools/security/certmgr.cs b/mcs/tools/security/certmgr.cs index 5f5862bf11e..432dd7fb4ef 100644 --- a/mcs/tools/security/certmgr.cs +++ b/mcs/tools/security/certmgr.cs @@ -10,12 +10,16 @@ using System; using System.Collections; using System.IO; +using System.Net; +using System.Net.Sockets; using System.Reflection; using System.Security.Cryptography; +using SSCX = System.Security.Cryptography.X509Certificates; using System.Text; using Mono.Security.Authenticode; using Mono.Security.X509; +using Mono.Security.Protocol.Tls; [assembly: AssemblyTitle ("Mono Certificate Manager")] [assembly: AssemblyDescription ("Add/Remove certificates and CRL from stores")] @@ -31,11 +35,13 @@ namespace Mono.Tools { static private void Help () { - Console.WriteLine ("Usage: certmgr [action] [object type] [options] store [filename]{0}", Environment.NewLine); + Console.WriteLine ("Usage: certmgr [action] [object type] [options] store [filename]", Environment.NewLine); + Console.WriteLine (" or: certmgr -ssl [options] url{0}", Environment.NewLine); Console.WriteLine ("actions"); Console.WriteLine ("\t-add\tAdd a certificate, CRL or CTL to specified store"); Console.WriteLine ("\t-del\tRemove a certificate, CRL or CTL to specified store"); Console.WriteLine ("\t-put\tCopy a certificate, CRL or CTL from a store to a file"); + Console.WriteLine ("\t-ssl\tDownload and add certificates from an SSL session"); Console.WriteLine ("object types"); Console.WriteLine ("\t-c\tadd/del/put certificates"); Console.WriteLine ("\t-crl\tadd/del/put certificate revocation lists"); @@ -44,6 +50,7 @@ namespace Mono.Tools { Console.WriteLine ("\t-m\tuse the machine certificate store (default to user)"); Console.WriteLine ("\t-v\tverbose mode (display status for every steps)"); Console.WriteLine ("\t-?\th[elp]\tDisplay this help message"); + Console.WriteLine (); } static string GetCommand (string arg) @@ -68,7 +75,8 @@ namespace Mono.Tools { None, Add, Delete, - Put + Put, + Ssl } static Action GetAction (string arg) @@ -84,6 +92,10 @@ namespace Mono.Tools { case "PUT": action = Action.Put; break; + case "SSL": + case "TLS": + action = Action.Ssl; + break; } return action; } @@ -263,28 +275,163 @@ namespace Mono.Tools { }*/ } + static X509CertificateCollection GetCertificatesFromSslSession (string url) + { + Uri uri = new Uri (url); + IPHostEntry host = Dns.Resolve (uri.Host); + IPAddress ip = host.AddressList [0]; + Socket socket = new Socket (ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + socket.Connect (new IPEndPoint (ip, uri.Port)); + NetworkStream ns = new NetworkStream (socket, false); + SslClientStream ssl = new SslClientStream (ns, uri.Host, false, Mono.Security.Protocol.Tls.SecurityProtocolType.Default, null); + ssl.ServerCertValidationDelegate += new CertificateValidationCallback (CertificateValidation); + + try { + // we don't really want to write to the server (as we don't know + // the protocol it using) but we must send something to be sure the + // SSL handshake is done (so we receive the X.509 certificates). + StreamWriter sw = new StreamWriter (ssl); + sw.WriteLine (Environment.NewLine); + sw.Flush (); + socket.Poll (30000, SelectMode.SelectRead); + } + finally { + socket.Close (); + } + + // we need a little reflection magic to get this information + PropertyInfo pi = typeof (SslClientStream).GetProperty ("ServerCertificates", BindingFlags.Instance | BindingFlags.NonPublic); + if (pi == null) { + Console.WriteLine ("Sorry but you need a newer version of Mono.Security.dll to use this feature."); + return null; + } + return (X509CertificateCollection) pi.GetValue (ssl, null); + } + + static bool CertificateValidation (SSCX.X509Certificate certificate, int[] certificateErrors) + { + // the main reason to download it is that it's not trusted + return true; + // OTOH we ask user confirmation before adding certificates into the stores + } + + static void Ssl (string host, bool machine, bool verbose) + { + if (verbose) { + Console.WriteLine ("Importing certificates from '{0}' into the {1} stores.", + host, machine ? "machine" : "user"); + } + int n=0; + + X509CertificateCollection coll = GetCertificatesFromSslSession (host); + if (coll != null) { + X509Store store = null; + // start by the end (root) so we can stop adding them anytime afterward + for (int i = coll.Count - 1; i >= 0; i--) { + X509Certificate x509 = coll [i]; + bool selfsign = false; + bool failed = false; + try { + selfsign = x509.IsSelfSigned; + } + catch { + // sadly it's hard to interpret old certificates with MD2 + // without manually changing the machine.config file + failed = true; + } + + if (selfsign) { + // this is a root + store = GetStoreFromName (X509Stores.Names.TrustedRoot, machine); + } else if (i == 0) { + // server certificate isn't (generally) an intermediate CA + store = GetStoreFromName (X509Stores.Names.OtherPeople, machine); + } else { + // all other certificates should be intermediate CA + store = GetStoreFromName (X509Stores.Names.IntermediateCA, machine); + } + + Console.WriteLine ("{0}{1} X.509 Certificate v{2}", + Environment.NewLine, + selfsign ? "Self-signed " : String.Empty, + x509.Version); + Console.WriteLine (" Issued from: {0}", x509.IssuerName); + Console.WriteLine (" Issued to: {0}", x509.SubjectName); + Console.WriteLine (" Valid from: {0}", x509.ValidFrom); + Console.WriteLine (" Valid until: {0}", x509.ValidUntil); + + if (!x509.IsCurrent) + Console.WriteLine (" *** WARNING: Certificate isn't current ***"); + if ((i > 0) && !selfsign) { + X509Certificate signer = coll [i-1]; + bool signed = false; + try { + if (signer.RSA != null) { + signed = x509.VerifySignature (signer.RSA); + } else if (signer.DSA != null) { + signed = x509.VerifySignature (signer.RSA); + } else { + Console.WriteLine (" *** WARNING: Couldn't not find who signed this certificate ***"); + signed = true; // skip next warning + } + + if (!signed) + Console.WriteLine (" *** WARNING: Certificate signature is INVALID ***"); + } + catch { + failed = true; + } + } + if (failed) { + Console.WriteLine (" *** ERROR: Couldn't decode certificate properly ***"); + Console.WriteLine (" *** try 'man certmgr' for additional help or report to bugzilla.ximian.com ***"); + break; + } + + if (store.Certificates.Contains (x509)) { + Console.WriteLine ("This certificate is already in the {0} store.", store.Name); + } else { + Console.Write ("Import this certificate into the {0} store ?", store.Name); + string answer = Console.ReadLine ().ToUpper (); + if ((answer == "YES") || (answer == "Y")) { + store.Import (x509); + n++; + } else { + if (verbose) { + Console.WriteLine ("Certificate not imported into store {0}.", + store.Name); + } + break; + } + } + } + } + + Console.WriteLine (); + if (n == 0) { + Console.WriteLine ("No certificate were added to the stores."); + } else { + Console.WriteLine ("{0} certificate{1} added to the stores.", + n, (n == 1) ? String.Empty : "s"); + } + } + [STAThread] static void Main (string[] args) { Header (); - if (args.Length < 4) { + if (args.Length < 2) { Help (); return; } Action action = GetAction (args [0]); - ObjectType type = GetObjectType (args [1]); - - if ((action == Action.None) || (type == ObjectType.None)) { - Help (); - return; - } - if (type == ObjectType.CTL) { - Console.WriteLine ("CTL are not supported"); - return; - } + ObjectType type = ObjectType.None; - int n = 2; + int n = 1; + if (action != Action.Ssl) + type = GetObjectType (args [n++]); + bool verbose = (GetCommand (args [n]) == "V"); if (verbose) n++; @@ -292,17 +439,30 @@ namespace Mono.Tools { if (machine) n++; - string storeName = args [n++]; - X509Store store = GetStoreFromName (storeName, machine); - if (store == null) { - Console.WriteLine ("Invalid Store: {0}", storeName); - Console.WriteLine ("Valid stores are: {0}, {1}, {2}, {3} and {4}", - X509Stores.Names.Personal, - X509Stores.Names.OtherPeople, - X509Stores.Names.IntermediateCA, - X509Stores.Names.TrustedRoot, - X509Stores.Names.Untrusted); - return; + X509Store store = null; + string storeName = null; + if (action != Action.Ssl) { + if ((action == Action.None) || (type == ObjectType.None)) { + Help (); + return; + } + if (type == ObjectType.CTL) { + Console.WriteLine ("CTL are not supported"); + return; + } + + storeName = args [n++]; + store = GetStoreFromName (storeName, machine); + if (store == null) { + Console.WriteLine ("Invalid Store: {0}", storeName); + Console.WriteLine ("Valid stores are: {0}, {1}, {2}, {3} and {4}", + X509Stores.Names.Personal, + X509Stores.Names.OtherPeople, + X509Stores.Names.IntermediateCA, + X509Stores.Names.TrustedRoot, + X509Stores.Names.Untrusted); + return; + } } string file = args [n]; @@ -319,6 +479,9 @@ namespace Mono.Tools { case Action.Put: Put (type, store, file, verbose); break; + case Action.Ssl: + Ssl (file, machine, verbose); + break; default: throw new NotSupportedException (action.ToString ()); } |