2 // SN.cs: sn clone tool
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://www.novell.com)
13 using System.Reflection;
14 using System.Security.Cryptography;
18 using Mono.Security.Cryptography;
20 [assembly: AssemblyTitle("Mono StrongName")]
21 [assembly: AssemblyDescription("StrongName utility for signing assemblies")]
23 namespace Mono.Tools {
27 static private void Header ()
29 Console.WriteLine (new AssemblyInfo ().ToString ());
32 static string defaultCSP;
34 static bool LoadConfig (bool quiet)
36 MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
37 BindingFlags.Static|BindingFlags.NonPublic);
40 string path = (string) config.Invoke (null, null);
42 bool exist = File.Exists (path);
44 Console.WriteLine ("Couldn't find machine.config");
46 StrongNameManager.LoadConfig (path);
50 Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
57 static int SaveConfig ()
63 static byte[] ReadFromFile (string fileName)
66 FileStream fs = File.Open (fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
68 data = new byte [fs.Length];
69 fs.Read (data, 0, data.Length);
77 static void WriteToFile (string fileName, byte[] data)
79 FileStream fs = File.Open (fileName, FileMode.Create, FileAccess.Write);
81 fs.Write (data, 0, data.Length);
88 static void WriteCSVToFile (string fileName, byte[] data, string mask)
90 StreamWriter sw = File.CreateText (fileName);
92 for (int i=0; i < data.Length; i++) {
95 sw.Write (data [i].ToString (mask));
104 static string ToString (byte[] data)
106 StringBuilder sb = new StringBuilder ();
107 for (int i=0; i < data.Length; i++) {
108 if ((i % 39 == 0) && (data.Length > 39))
109 sb.Append (Environment.NewLine);
110 sb.Append (data [i].ToString ("x2"));
112 sb.Append (" !!! TOO LONG !!!");
116 return sb.ToString ();
119 // is assembly signed (or delayed signed) ?
120 static bool IsStrongNamed (Assembly assembly)
122 if (assembly == null)
125 object[] attrs = assembly.GetCustomAttributes (true);
126 foreach (object o in attrs) {
127 if (o is AssemblyKeyFileAttribute)
129 else if (o is AssemblyKeyNameAttribute)
135 static bool ReSign (string assemblyName, RSA key)
137 // this doesn't load the assembly (well it unloads it ;)
138 // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
139 AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
141 Console.WriteLine ("Unable to load assembly: {0}", assemblyName);
145 StrongName sign = new StrongName (key);
146 byte[] token = an.GetPublicKeyToken ();
148 // first, try to compare using a mapped public key (e.g. ECMA)
149 bool same = Compare (sign.PublicKey, StrongNameManager.GetMappedPublicKey (token));
151 // second, try to compare using the assembly public key
152 same = Compare (sign.PublicKey, an.GetPublicKey ());
154 // third (and last) chance, try to compare public key token
155 same = Compare (sign.PublicKeyToken, token);
160 bool signed = sign.Sign (assemblyName);
161 Console.WriteLine (signed ? "Assembly {0} signed." : "Couldn't sign the assembly {0}.",
166 Console.WriteLine ("Couldn't sign the assembly {0} with this key pair.", assemblyName);
170 static int Verify (string assemblyName, bool forceVerification)
172 // this doesn't load the assembly (well it unloads it ;)
173 // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
174 AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
176 Console.WriteLine ("Unable to load assembly: {0}", assemblyName);
180 byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
181 if ((publicKey == null) || (publicKey.Length < 12)) {
183 publicKey = an.GetPublicKey ();
184 if ((publicKey == null) || (publicKey.Length < 12)) {
185 Console.WriteLine ("{0} is not a strongly named assembly.", assemblyName);
190 // Note: MustVerify is based on the original token (by design). Public key
191 // remapping won't affect if the assebmly is verified or not.
192 if (forceVerification || StrongNameManager.MustVerify (an)) {
193 RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
194 StrongName sn = new StrongName (rsa);
195 if (sn.Verify (assemblyName)) {
196 Console.WriteLine ("Assembly {0} is strongnamed.", assemblyName);
200 Console.WriteLine ("Assembly {0} isn't strongnamed", assemblyName);
205 Console.WriteLine ("Assembly {0} is strongnamed (verification skipped).", assemblyName);
210 static bool Compare (byte[] value1, byte[] value2)
212 if ((value1 == null) || (value2 == null))
214 bool result = (value1.Length == value2.Length);
216 for (int i=0; i < value1.Length; i++) {
217 if (value1 [i] != value2 [i])
224 static void Help (string details)
226 Console.WriteLine ("Usage: sn [-q | -quiet] options [parameters]{0}", Environment.NewLine);
227 Console.WriteLine (" -q | -quiet \tQuiet mode (minimal display){0}", Environment.NewLine);
230 Console.WriteLine ("Configuration options <1>");
231 Console.WriteLine (" -c provider{0}\tChange the default CSP provider", Environment.NewLine);
232 Console.WriteLine (" -m [y|n]{0}\tUse a machine [y] key container or user key container [n]", Environment.NewLine);
233 Console.WriteLine (" -Vl{0}\tList the verification options", Environment.NewLine);
234 Console.WriteLine (" -Vr assembly [userlist]{0}\tExempt the specified assembly from verification for the user list", Environment.NewLine);
235 Console.WriteLine (" -Vu assembly{0}\tRemove exemption entry for the specified assembly", Environment.NewLine);
236 Console.WriteLine (" -Vx{0}\tRemove all exemptions entries", Environment.NewLine);
239 Console.WriteLine ("CSP related options");
240 Console.WriteLine (" -d container{0}\tDelete the specified key container", Environment.NewLine);
241 Console.WriteLine (" -i keypair.snk container{0}\tImport the keypair from a SNK file into a CSP container", Environment.NewLine);
242 Console.WriteLine (" -pc container public.key{0}\tExport the public key from a CSP container to the specified file", Environment.NewLine);
245 Console.WriteLine ("Convertion options");
246 Console.WriteLine (" -e assembly output.pub{0}\tExport the assembly public key to the specified file", Environment.NewLine);
247 Console.WriteLine (" -p keypair.snk output.pub{0}\tExport the public key from a SNK file to the specified file", Environment.NewLine);
248 Console.WriteLine (" -o input output.txt{0}\tConvert the input file to a CSV file (using decimal).", Environment.NewLine);
249 Console.WriteLine (" -oh input output.txt{0}\tConvert the input file to a CSV file (using hexadecimal).", Environment.NewLine);
252 Console.WriteLine ("StrongName signing options");
253 Console.WriteLine (" -D assembly1 assembly2{0}\tCompare assembly1 and assembly2 (without signatures)", Environment.NewLine);
254 Console.WriteLine (" -k keypair.snk{0}\tCreate a new keypair in the specified file", Environment.NewLine);
255 Console.WriteLine (" -R assembly keypair.snk{0}\tResign the assembly with the specified StrongName key file", Environment.NewLine);
256 Console.WriteLine (" -Rc assembly container{0}\tResign the assembly with the specified CSP container", Environment.NewLine);
257 Console.WriteLine (" -t file{0}\tShow the public key from the specified file", Environment.NewLine);
258 Console.WriteLine (" -tp file{0}\tShow the public key and pk token from the specified file", Environment.NewLine);
259 Console.WriteLine (" -T assembly{0}\tShow the public key from the specified assembly", Environment.NewLine);
260 Console.WriteLine (" -Tp assembly{0}\tShow the public key and pk token from the specified assembly", Environment.NewLine);
261 Console.WriteLine (" -v assembly{0}\tVerify the specified assembly signature", Environment.NewLine);
262 Console.WriteLine (" -vf assembly{0}\tVerify the specified assembly signature (even if disabled).", Environment.NewLine);
265 Console.WriteLine ("Help options");
266 Console.WriteLine (" -? | -h \tShow this help screen about the tool");
267 Console.WriteLine (" -? | -h config \tConfiguration options");
268 Console.WriteLine (" -? | -h csp \tCrypto Service Provider (CSP) related options");
269 Console.WriteLine (" -? | -h convert\tFormat convertion options");
270 Console.WriteLine (" -? | -h sn \tStrongName signing options");
273 Console.WriteLine ("{0}<1> Currently not implemented in the tool", Environment.NewLine);
277 static int Main (string[] args)
279 if (args.Length < 1) {
286 string param = args [i];
287 bool quiet = ((param == "-quiet") || (param == "-q"));
295 StrongName sn = null;
296 AssemblyName an = null;
297 RSACryptoServiceProvider rsa = null;
298 CspParameters csp = new CspParameters ();
299 csp.ProviderName = defaultCSP;
301 switch (args [i++]) {
303 // Change global CSP provider options
304 defaultCSP = args [i];
305 return SaveConfig ();
307 // Delete specified key container
308 csp.KeyContainerName = args [i];
309 rsa = new RSACryptoServiceProvider (csp);
310 rsa.PersistKeyInCsp = false;
312 Console.WriteLine ("Keypair in container {0} has been deleted", args [i]);
315 StrongName a1 = new StrongName ();
316 byte[] h1 = a1.Hash (args [i++]);
317 StrongName a2 = new StrongName ();
318 byte[] h2 = a2.Hash (args [i++]);
319 if (Compare (h1, h2)) {
320 Console.WriteLine ("Both assembly are identical (same digest for metadata)");
321 // TODO: if equals then compare signatures
324 Console.WriteLine ("Assemblies are not identical (different digest for metadata)");
327 // Export public key from assembly
328 an = AssemblyName.GetAssemblyName (args [i++]);
329 WriteToFile (args[i], an.GetPublicKey ());
331 Console.WriteLine ("Public Key extracted to file {0}", args [i]);
334 // import keypair from SNK to container
335 sn = new StrongName (ReadFromFile (args [i++]));
336 csp.KeyContainerName = args [i];
337 rsa = new RSACryptoServiceProvider (csp);
338 rsa.ImportParameters (sn.RSA.ExportParameters (true));
341 // Create a new strong name key pair
342 // (a new RSA keypair automagically if none is present)
343 sn = new StrongName ();
344 WriteToFile (args[i], CryptoConvert.ToCapiKeyBlob (sn.RSA, true));
346 Console.WriteLine ("A new strong name keypair has been generated in {0}", args [i]);
349 Console.WriteLine ("Unimplemented option");
352 byte[] infileD = ReadFromFile (args [i++]);
353 WriteCSVToFile (args [i], infileD, "D");
355 Console.WriteLine ("Output CVS file is {0} (decimal format)", args [i]);
358 byte[] infileX2 = ReadFromFile (args [i++]);
359 WriteCSVToFile (args [i], infileX2, "X2");
361 Console.WriteLine ("Output CVS file is {0} (hexadecimal format)", args [i]);
364 // Extract public key from SNK file
365 sn = new StrongName (ReadFromFile (args [i++]));
366 WriteToFile (args[i], sn.PublicKey);
368 Console.WriteLine ("Public Key extracted to file {0}", args [i]);
371 // Extract public key from container
372 csp.KeyContainerName = args [i++];
373 rsa = new RSACryptoServiceProvider (csp);
374 sn = new StrongName (rsa);
375 WriteToFile (args[i], sn.PublicKey);
377 Console.WriteLine ("Public Key extracted to file {0}", args [i]);
380 string filename = args [i++];
381 sn = new StrongName (ReadFromFile (args [i]));
382 if (! ReSign (filename, sn.RSA))
386 filename = args [i++];
387 csp.KeyContainerName = args [i];
388 rsa = new RSACryptoServiceProvider (csp);
389 if (! ReSign (filename, rsa))
393 // Show public key token from file
394 sn = new StrongName (ReadFromFile (args [i]));
395 // note: ignore quiet
396 Console.WriteLine ("Public Key Token: " + ToString (sn.PublicKeyToken), Environment.NewLine);
399 // Show public key and public key token from assembly
400 sn = new StrongName (ReadFromFile (args [i]));
401 // note: ignore quiet
402 Console.WriteLine ("Public Key:" + ToString (sn.PublicKey));
403 Console.WriteLine ("{0}Public Key Token: " + ToString (sn.PublicKeyToken), Environment.NewLine);
406 // Show public key token from assembly
407 an = AssemblyName.GetAssemblyName (args [i++]);
408 // note: ignore quiet
409 byte [] pkt = an.GetPublicKeyToken ();
411 Console.WriteLine ("{0} does not represent a strongly named assembly.", args [i - 1]);
413 Console.WriteLine ("Public Key Token: " + ToString (pkt));
417 // Show public key and public key token from assembly
418 an = AssemblyName.GetAssemblyName (args [i++]);
419 byte [] token = an.GetPublicKeyToken ();
421 Console.WriteLine ("{0} does not represent a strongly named assembly.", args [i - 1]);
423 Console.WriteLine ("Public Key:" + ToString (an.GetPublicKey ()));
424 Console.WriteLine ("{0}Public Key Token: " + ToString (token), Environment.NewLine);
428 filename = args [i++];
429 return Verify (filename, false);
431 filename = args [i++];
432 return Verify (filename, true); // force verification
434 Console.WriteLine (new StrongNameManager ().ToString ());
437 Console.WriteLine ("Unimplemented option");
440 Console.WriteLine ("Unimplemented option");
443 // we must remove <verificationSettings> from each config files
444 Console.WriteLine ("Unimplemented option");
448 Help ((i < args.Length) ? args [i] : null);
452 Console.WriteLine ("Unknown option {0}", args [i-1]);