(DISTFILES): Comment out a few missing files.
[mono-project.git] / mcs / tools / security / sn.cs
blob5127b713790e0276ddb8d7a87837df1019273871
1 //
2 // SN.cs: sn clone tool
3 //
4 // Author:
5 // Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
9 //
11 using System;
12 using System.IO;
13 using System.Reflection;
14 using System.Security.Cryptography;
15 using System.Text;
17 using Mono.Security;
18 using Mono.Security.Cryptography;
20 [assembly: AssemblyTitle("Mono StrongName")]
21 [assembly: AssemblyDescription("StrongName utility for signing assemblies")]
23 namespace Mono.Tools {
25 class SN {
27 static private void Header ()
29 Assembly a = Assembly.GetExecutingAssembly ();
30 AssemblyName an = a.GetName ();
32 object [] att = a.GetCustomAttributes (typeof (AssemblyTitleAttribute), false);
33 string title = ((att.Length > 0) ? ((AssemblyTitleAttribute) att [0]).Title : "Mono StrongName");
35 att = a.GetCustomAttributes (typeof (AssemblyCopyrightAttribute), false);
36 string copyright = ((att.Length > 0) ? ((AssemblyCopyrightAttribute) att [0]).Copyright : "");
38 Console.WriteLine ("{0} {1}", title, an.Version.ToString ());
39 Console.WriteLine ("{0}{1}", copyright, Environment.NewLine);
42 static string defaultCSP;
44 static bool LoadConfig (bool quiet)
46 MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
47 BindingFlags.Static|BindingFlags.NonPublic);
49 if (config != null) {
50 string path = (string) config.Invoke (null, null);
52 bool exist = File.Exists (path);
53 if (!quiet && !exist)
54 Console.WriteLine ("Couldn't find machine.config");
56 StrongNameManager.LoadConfig (path);
57 return exist;
59 else if (!quiet)
60 Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
62 // default CSP
63 return false;
66 // TODO
67 static int SaveConfig ()
69 // default CSP
70 return 1;
73 static byte[] ReadFromFile (string fileName)
75 byte[] data = null;
76 FileStream fs = File.Open (fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
77 try {
78 data = new byte [fs.Length];
79 fs.Read (data, 0, data.Length);
81 finally {
82 fs.Close ();
84 return data;
87 static void WriteToFile (string fileName, byte[] data)
89 FileStream fs = File.Open (fileName, FileMode.Create, FileAccess.Write);
90 try {
91 fs.Write (data, 0, data.Length);
93 finally {
94 fs.Close ();
98 static void WriteCSVToFile (string fileName, byte[] data, string mask)
100 StreamWriter sw = File.CreateText (fileName);
101 try {
102 for (int i=0; i < data.Length; i++) {
103 if (mask [0] == 'X')
104 sw.Write ("0x");
105 sw.Write (data [i].ToString (mask));
106 sw.Write (", ");
109 finally {
110 sw.Close ();
114 static string ToString (byte[] data)
116 StringBuilder sb = new StringBuilder ();
117 for (int i=0; i < data.Length; i++) {
118 if ((i % 39 == 0) && (data.Length > 39))
119 sb.Append (Environment.NewLine);
120 sb.Append (data [i].ToString ("x2"));
121 if (i > 1000) {
122 sb.Append (" !!! TOO LONG !!!");
123 break;
126 return sb.ToString ();
129 // is assembly signed (or delayed signed) ?
130 static bool IsStrongNamed (Assembly assembly)
132 if (assembly == null)
133 return false;
135 object[] attrs = assembly.GetCustomAttributes (true);
136 foreach (object o in attrs) {
137 if (o is AssemblyKeyFileAttribute)
138 return true;
139 else if (o is AssemblyKeyNameAttribute)
140 return true;
142 return false;
145 static bool ReSign (string assemblyName, RSA key)
147 // this doesn't load the assembly (well it unloads it ;)
148 // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
149 AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
150 if (an == null) {
151 Console.WriteLine ("Unable to load assembly: {0}", assemblyName);
152 return false;
155 StrongName sign = new StrongName (key);
156 byte[] token = an.GetPublicKeyToken ();
158 // first, try to compare using a mapped public key (e.g. ECMA)
159 bool same = Compare (sign.PublicKey, StrongNameManager.GetMappedPublicKey (token));
160 if (!same) {
161 // second, try to compare using the assembly public key
162 same = Compare (sign.PublicKey, an.GetPublicKey ());
163 if (!same) {
164 // third (and last) chance, try to compare public key token
165 same = Compare (sign.PublicKeyToken, token);
169 if (same) {
170 bool signed = sign.Sign (assemblyName);
171 Console.WriteLine (signed ? "Assembly {0} signed." : "Couldn't sign the assembly {0}.",
172 assemblyName);
173 return signed;
176 Console.WriteLine ("Couldn't sign the assembly {0} with this key pair.", assemblyName);
177 return false;
180 static int Verify (string assemblyName, bool forceVerification)
182 // this doesn't load the assembly (well it unloads it ;)
183 // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
184 AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
185 if (an == null) {
186 Console.WriteLine ("Unable to load assembly: {0}", assemblyName);
187 return 2;
190 byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
191 if ((publicKey == null) || (publicKey.Length < 12)) {
192 // no mapping
193 publicKey = an.GetPublicKey ();
194 if ((publicKey == null) || (publicKey.Length < 12)) {
195 Console.WriteLine ("{0} is not a strongly named assembly.", assemblyName);
196 return 2;
200 // Note: MustVerify is based on the original token (by design). Public key
201 // remapping won't affect if the assebmly is verified or not.
202 if (forceVerification || StrongNameManager.MustVerify (an)) {
203 RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
204 StrongName sn = new StrongName (rsa);
205 if (sn.Verify (assemblyName)) {
206 Console.WriteLine ("Assembly {0} is strongnamed.", assemblyName);
207 return 0;
209 else {
210 Console.WriteLine ("Assembly {0} isn't strongnamed", assemblyName);
211 return 1;
214 else {
215 Console.WriteLine ("Assembly {0} is strongnamed (verification skipped).", assemblyName);
216 return 0;
220 static bool Compare (byte[] value1, byte[] value2)
222 if ((value1 == null) || (value2 == null))
223 return false;
224 bool result = (value1.Length == value2.Length);
225 if (result) {
226 for (int i=0; i < value1.Length; i++) {
227 if (value1 [i] != value2 [i])
228 return false;
231 return result;
234 static void Help (string details)
236 Console.WriteLine ("Usage: sn [-q | -quiet] options [parameters]{0}", Environment.NewLine);
237 Console.WriteLine (" -q | -quiet \tQuiet mode (minimal display){0}", Environment.NewLine);
238 switch (details) {
239 case "config":
240 Console.WriteLine ("Configuration options <1>");
241 Console.WriteLine (" -c provider{0}\tChange the default CSP provider", Environment.NewLine);
242 Console.WriteLine (" -m [y|n]{0}\tUse a machine [y] key container or user key container [n]", Environment.NewLine);
243 Console.WriteLine (" -Vl{0}\tList the verification options", Environment.NewLine);
244 Console.WriteLine (" -Vr assembly [userlist]{0}\tExempt the specified assembly from verification for the user list", Environment.NewLine);
245 Console.WriteLine (" -Vu assembly{0}\tRemove exemption entry for the specified assembly", Environment.NewLine);
246 Console.WriteLine (" -Vx{0}\tRemove all exemptions entries", Environment.NewLine);
247 break;
248 case "csp":
249 Console.WriteLine ("CSP related options");
250 Console.WriteLine (" -d container{0}\tDelete the specified key container", Environment.NewLine);
251 Console.WriteLine (" -i keypair.snk container{0}\tImport the keypair from a SNK file into a CSP container", Environment.NewLine);
252 Console.WriteLine (" -pc container public.key{0}\tExport the public key from a CSP container to the specified file", Environment.NewLine);
253 break;
254 case "convert":
255 Console.WriteLine ("Convertion options");
256 Console.WriteLine (" -e assembly output.pub{0}\tExport the assembly public key to the specified file", Environment.NewLine);
257 Console.WriteLine (" -p keypair.snk output.pub{0}\tExport the public key from a SNK file to the specified file", Environment.NewLine);
258 Console.WriteLine (" -o input output.txt{0}\tConvert the input file to a CSV file (using decimal).", Environment.NewLine);
259 Console.WriteLine (" -oh input output.txt{0}\tConvert the input file to a CSV file (using hexadecimal).", Environment.NewLine);
260 break;
261 case "sn":
262 Console.WriteLine ("StrongName signing options");
263 Console.WriteLine (" -D assembly1 assembly2{0}\tCompare assembly1 and assembly2 (without signatures)", Environment.NewLine);
264 Console.WriteLine (" -k keypair.snk{0}\tCreate a new keypair in the specified file", Environment.NewLine);
265 Console.WriteLine (" -R assembly keypair.snk{0}\tResign the assembly with the specified StrongName key file", Environment.NewLine);
266 Console.WriteLine (" -Rc assembly container{0}\tResign the assembly with the specified CSP container", Environment.NewLine);
267 Console.WriteLine (" -t file{0}\tShow the public key from the specified file", Environment.NewLine);
268 Console.WriteLine (" -tp file{0}\tShow the public key and pk token from the specified file", Environment.NewLine);
269 Console.WriteLine (" -T assembly{0}\tShow the public key from the specified assembly", Environment.NewLine);
270 Console.WriteLine (" -Tp assembly{0}\tShow the public key and pk token from the specified assembly", Environment.NewLine);
271 Console.WriteLine (" -v assembly{0}\tVerify the specified assembly signature", Environment.NewLine);
272 Console.WriteLine (" -vf assembly{0}\tVerify the specified assembly signature (even if disabled).", Environment.NewLine);
273 break;
274 default:
275 Console.WriteLine ("Help options");
276 Console.WriteLine (" -? | -h \tShow this help screen about the tool");
277 Console.WriteLine (" -? | -h config \tConfiguration options");
278 Console.WriteLine (" -? | -h csp \tCrypto Service Provider (CSP) related options");
279 Console.WriteLine (" -? | -h convert\tFormat convertion options");
280 Console.WriteLine (" -? | -h sn \tStrongName signing options");
281 break;
283 Console.WriteLine ("{0}<1> Currently not implemented in the tool", Environment.NewLine);
286 [STAThread]
287 static int Main (string[] args)
289 if (args.Length < 1) {
290 Header ();
291 Help (null);
292 return 1;
295 int i = 0;
296 string param = args [i];
297 bool quiet = ((param == "-quiet") || (param == "-q"));
298 if (quiet)
299 i++;
300 else
301 Header();
303 bool config = LoadConfig (quiet);
305 StrongName sn = null;
306 AssemblyName an = null;
307 RSACryptoServiceProvider rsa = null;
308 CspParameters csp = new CspParameters ();
309 csp.ProviderName = defaultCSP;
311 switch (args [i++]) {
312 case "-c":
313 // Change global CSP provider options
314 defaultCSP = args [i];
315 return SaveConfig ();
316 case "-d":
317 // Delete specified key container
318 csp.KeyContainerName = args [i];
319 rsa = new RSACryptoServiceProvider (csp);
320 rsa.PersistKeyInCsp = false;
321 if (!quiet)
322 Console.WriteLine ("Keypair in container {0} has been deleted", args [i]);
323 break;
324 case "-D":
325 StrongName a1 = new StrongName ();
326 byte[] h1 = a1.Hash (args [i++]);
327 StrongName a2 = new StrongName ();
328 byte[] h2 = a2.Hash (args [i++]);
329 if (Compare (h1, h2)) {
330 Console.WriteLine ("Both assembly are identical (same digest for metadata)");
331 // TODO: if equals then compare signatures
333 else
334 Console.WriteLine ("Assemblies are not identical (different digest for metadata)");
335 break;
336 case "-e":
337 // Export public key from assembly
338 an = AssemblyName.GetAssemblyName (args [i++]);
339 WriteToFile (args[i], an.GetPublicKey ());
340 if (!quiet)
341 Console.WriteLine ("Public Key extracted to file {0}", args [i]);
342 break;
343 case "-i":
344 // import keypair from SNK to container
345 sn = new StrongName (ReadFromFile (args [i++]));
346 csp.KeyContainerName = args [i];
347 rsa = new RSACryptoServiceProvider (csp);
348 rsa.ImportParameters (sn.RSA.ExportParameters (true));
349 break;
350 case "-k":
351 // Create a new strong name key pair
352 // (a new RSA keypair automagically if none is present)
353 sn = new StrongName ();
354 WriteToFile (args[i], CryptoConvert.ToCapiKeyBlob (sn.RSA, true));
355 if (!quiet)
356 Console.WriteLine ("A new strong name keypair has been generated in {0}", args [i]);
357 break;
358 case "-m":
359 Console.WriteLine ("Unimplemented option");
360 break;
361 case "-o":
362 byte[] infileD = ReadFromFile (args [i++]);
363 WriteCSVToFile (args [i], infileD, "D");
364 if (!quiet)
365 Console.WriteLine ("Output CVS file is {0} (decimal format)", args [i]);
366 break;
367 case "-oh":
368 byte[] infileX2 = ReadFromFile (args [i++]);
369 WriteCSVToFile (args [i], infileX2, "X2");
370 if (!quiet)
371 Console.WriteLine ("Output CVS file is {0} (hexadecimal format)", args [i]);
372 break;
373 case "-p":
374 // Extract public key from SNK file
375 sn = new StrongName (ReadFromFile (args [i++]));
376 WriteToFile (args[i], sn.PublicKey);
377 if (!quiet)
378 Console.WriteLine ("Public Key extracted to file {0}", args [i]);
379 break;
380 case "-pc":
381 // Extract public key from container
382 csp.KeyContainerName = args [i++];
383 rsa = new RSACryptoServiceProvider (csp);
384 sn = new StrongName (rsa);
385 WriteToFile (args[i], sn.PublicKey);
386 if (!quiet)
387 Console.WriteLine ("Public Key extracted to file {0}", args [i]);
388 break;
389 case "-R":
390 string filename = args [i++];
391 sn = new StrongName (ReadFromFile (args [i]));
392 if (! ReSign (filename, sn.RSA))
393 return 1;
394 break;
395 case "-Rc":
396 filename = args [i++];
397 csp.KeyContainerName = args [i];
398 rsa = new RSACryptoServiceProvider (csp);
399 if (! ReSign (filename, rsa))
400 return 1;
401 break;
402 case "-t":
403 // Show public key token from file
404 sn = new StrongName (ReadFromFile (args [i]));
405 // note: ignore quiet
406 Console.WriteLine ("Public Key Token: " + ToString (sn.PublicKeyToken), Environment.NewLine);
407 break;
408 case "-tp":
409 // Show public key and public key token from assembly
410 sn = new StrongName (ReadFromFile (args [i]));
411 // note: ignore quiet
412 Console.WriteLine ("Public Key:" + ToString (sn.PublicKey));
413 Console.WriteLine ("{0}Public Key Token: " + ToString (sn.PublicKeyToken), Environment.NewLine);
414 break;
415 case "-T":
416 // Show public key token from assembly
417 an = AssemblyName.GetAssemblyName (args [i++]);
418 // note: ignore quiet
419 byte [] pkt = an.GetPublicKeyToken ();
420 if (pkt == null) {
421 Console.WriteLine ("{0} does not represent a strongly named assembly.", args [i - 1]);
422 } else {
423 Console.WriteLine ("Public Key Token: " + ToString (pkt));
425 break;
426 case "-Tp":
427 // Show public key and public key token from assembly
428 an = AssemblyName.GetAssemblyName (args [i++]);
429 byte [] token = an.GetPublicKeyToken ();
430 if (token == null) {
431 Console.WriteLine ("{0} does not represent a strongly named assembly.", args [i - 1]);
432 } else {
433 Console.WriteLine ("Public Key:" + ToString (an.GetPublicKey ()));
434 Console.WriteLine ("{0}Public Key Token: " + ToString (token), Environment.NewLine);
436 break;
437 case "-v":
438 filename = args [i++];
439 return Verify (filename, false);
440 case "-vf":
441 filename = args [i++];
442 return Verify (filename, true); // force verification
443 case "-Vl":
444 Console.WriteLine (new StrongNameManager ().ToString ());
445 break;
446 case "-Vr":
447 Console.WriteLine ("Unimplemented option");
448 break;
449 case "-Vu":
450 Console.WriteLine ("Unimplemented option");
451 break;
452 case "-Vx":
453 // we must remove <verificationSettings> from each config files
454 Console.WriteLine ("Unimplemented option");
455 break;
456 case "-?":
457 case "-h":
458 Help ((i < args.Length) ? args [i] : null);
459 break;
460 default:
461 if (!quiet)
462 Console.WriteLine ("Unknown option {0}", args [i-1]);
463 return 1;
465 return 0;