2 // crlupdate.cs: CRL downloader / updater
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // Copyright (C) 2011 Novell, Inc (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System.Reflection;
33 using Mono.Security.X509;
34 using Mono.Security.X509.Extensions;
36 [assembly: AssemblyTitle ("Mono CRL Updater")]
37 [assembly: AssemblyDescription ("Download and update X.509 certificate revocation lists from your stores.")]
39 namespace Mono.Tools {
43 static private void Header ()
45 Console.WriteLine (new AssemblyInfo ().ToString ());
48 static private void Help ()
50 Console.WriteLine ("Usage: crlupdate [-m] [-v] [-f] [-?]");
52 Console.WriteLine ("\t-m\tuse the machine certificate store (default to user)");
53 Console.WriteLine ("\t-v\tverbose mode (display status for every steps)");
54 Console.WriteLine ("\t-f\tforce download (and replace existing CRL)");
55 Console.WriteLine ("\t-?\tDisplay this help message");
59 static X509Certificate FindCrlIssuer (string name, byte[] aki, X509CertificateCollection col)
61 foreach (X509Certificate cert in col) {
62 if (name != cert.SubjectName)
64 if ((aki == null) || Compare (aki, GetSubjectKeyIdentifier (cert.Extensions ["2.5.29.14"])))
70 static X509Certificate FindCrlIssuer (X509Crl crl)
72 string name = crl.IssuerName;
73 byte [] aki = GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"]);
74 X509Certificate cert = FindCrlIssuer (name, aki, X509StoreManager.IntermediateCACertificates);
77 return FindCrlIssuer (name, aki, X509StoreManager.TrustedRootCertificates);
80 static X509Chain chain = new X509Chain ();
82 static bool VerifyCrl (X509Crl crl)
84 X509Certificate issuer = FindCrlIssuer (crl);
88 if (!crl.VerifySignature (issuer))
92 return chain.Build (issuer);
95 static void Download (string url, X509Store store)
98 Console.WriteLine ("Downloading: {0}", url);
100 WebClient wc = new WebClient ();
101 string error = "download";
103 byte [] data = wc.DownloadData (url);
105 X509Crl crl = new X509Crl (data);
107 // warn if CRL is not current - but still allow it to be imported
108 if (!crl.IsCurrent && verbose)
109 Console.WriteLine ("WARNING: CRL is not current: {0}", url);
111 // only import the CRL if its signature is valid and coming from a trusted root
115 Console.WriteLine ("ERROR: could not validate CRL: {0}", url);
117 catch (Exception e) {
118 Console.WriteLine ("ERROR: could not {0}: {1}", error, url);
120 Console.WriteLine (e);
121 Console.WriteLine ();
126 static byte [] GetAuthorityKeyIdentifier (X509Extension ext)
131 AuthorityKeyIdentifierExtension aki = new AuthorityKeyIdentifierExtension (ext);
132 return aki.Identifier;
135 static byte [] GetSubjectKeyIdentifier (X509Extension ext)
140 SubjectKeyIdentifierExtension ski = new SubjectKeyIdentifierExtension (ext);
141 return ski.Identifier;
144 static bool Compare (byte [] a, byte [] b)
151 if (a.Length != b.Length)
154 for (int i = 0; i < a.Length; i++) {
161 static X509Crl FindCrl (X509Certificate cert, X509Store store)
163 string name = cert.SubjectName;
164 byte [] ski = GetSubjectKeyIdentifier (cert.Extensions ["2.5.29.14"]);
165 foreach (X509Crl crl in store.Crls) {
166 if (crl.IssuerName != name)
168 if ((ski == null) || Compare (ski, GetAuthorityKeyIdentifier (crl.Extensions ["2.5.29.35"])))
174 static void UpdateStore (X509Store store)
176 // for each certificate
177 foreach (X509Certificate cert in store.Certificates) {
179 // do we already have a matching CRL ? (or are we forced to download?)
180 X509Crl crl = force ? null : FindCrl (cert, store);
181 // without a CRL (or with a CRL in need of updating)
182 if ((crl == null) || !crl.IsCurrent) {
183 X509Extension ext = cert.Extensions ["2.5.29.31"];
186 Console.WriteLine ("WARNING: No cRL distribution point found for '{0}'", cert.SubjectName);
190 CRLDistributionPointsExtension crlDP = new CRLDistributionPointsExtension (ext);
191 foreach (var dp in crlDP.DistributionPoints) {
192 string name = dp.Name.Trim ();
193 if (name.StartsWith ("URL="))
194 Download (name.Substring (4), store);
196 Console.WriteLine ("WARNING: Unsupported distribution point: '{0}'", name);
202 static bool verbose = false;
203 static bool force = false;
205 static int Main (string [] args)
207 bool machine = false;
209 for (int i = 0; i < args.Length; i++) {
233 X509Stores stores = ((machine) ? X509StoreManager.LocalMachine : X509StoreManager.CurrentUser);
234 // for all store (expect Untrusted)
235 UpdateStore (stores.TrustedRoot);
236 UpdateStore (stores.IntermediateCA);
237 UpdateStore (stores.Personal);
238 UpdateStore (stores.OtherPeople);
241 catch (Exception e) {
242 Console.WriteLine ("ERROR: Unexpected exception: {0}", e);