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 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
);
50 string path
= (string) config
.Invoke (null, null);
52 bool exist
= File
.Exists (path
);
54 Console
.WriteLine ("Couldn't find machine.config");
56 StrongNameManager
.LoadConfig (path
);
60 Console
.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
67 static int SaveConfig ()
73 static byte[] ReadFromFile (string fileName
)
76 FileStream fs
= File
.Open (fileName
, FileMode
.Open
, FileAccess
.Read
, FileShare
.Read
);
78 data
= new byte [fs
.Length
];
79 fs
.Read (data
, 0, data
.Length
);
87 static void WriteToFile (string fileName
, byte[] data
)
89 FileStream fs
= File
.Open (fileName
, FileMode
.Create
, FileAccess
.Write
);
91 fs
.Write (data
, 0, data
.Length
);
98 static void WriteCSVToFile (string fileName
, byte[] data
, string mask
)
100 StreamWriter sw
= File
.CreateText (fileName
);
102 for (int i
=0; i
< data
.Length
; i
++) {
105 sw
.Write (data
[i
].ToString (mask
));
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"));
122 sb
.Append (" !!! TOO LONG !!!");
126 return sb
.ToString ();
129 // is assembly signed (or delayed signed) ?
130 static bool IsStrongNamed (Assembly assembly
)
132 if (assembly
== null)
135 object[] attrs
= assembly
.GetCustomAttributes (true);
136 foreach (object o
in attrs
) {
137 if (o
is AssemblyKeyFileAttribute
)
139 else if (o
is AssemblyKeyNameAttribute
)
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
);
151 Console
.WriteLine ("Unable to load assembly: {0}", assemblyName
);
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
));
161 // second, try to compare using the assembly public key
162 same
= Compare (sign
.PublicKey
, an
.GetPublicKey ());
164 // third (and last) chance, try to compare public key token
165 same
= Compare (sign
.PublicKeyToken
, token
);
170 bool signed
= sign
.Sign (assemblyName
);
171 Console
.WriteLine (signed
? "Assembly {0} signed." : "Couldn't sign the assembly {0}.",
176 Console
.WriteLine ("Couldn't sign the assembly {0} with this key pair.", assemblyName
);
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
);
186 Console
.WriteLine ("Unable to load assembly: {0}", assemblyName
);
190 byte[] publicKey
= StrongNameManager
.GetMappedPublicKey (an
.GetPublicKeyToken ());
191 if ((publicKey
== null) || (publicKey
.Length
< 12)) {
193 publicKey
= an
.GetPublicKey ();
194 if ((publicKey
== null) || (publicKey
.Length
< 12)) {
195 Console
.WriteLine ("{0} is not a strongly named assembly.", assemblyName
);
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
);
210 Console
.WriteLine ("Assembly {0} isn't strongnamed", assemblyName
);
215 Console
.WriteLine ("Assembly {0} is strongnamed (verification skipped).", assemblyName
);
220 static bool Compare (byte[] value1
, byte[] value2
)
222 if ((value1
== null) || (value2
== null))
224 bool result
= (value1
.Length
== value2
.Length
);
226 for (int i
=0; i
< value1
.Length
; i
++) {
227 if (value1
[i
] != value2
[i
])
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
);
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
);
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
);
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
);
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
);
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");
283 Console
.WriteLine ("{0}<1> Currently not implemented in the tool", Environment
.NewLine
);
287 static int Main (string[] args
)
289 if (args
.Length
< 1) {
296 string param
= args
[i
];
297 bool quiet
= ((param
== "-quiet") || (param
== "-q"));
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
++]) {
313 // Change global CSP provider options
314 defaultCSP
= args
[i
];
315 return SaveConfig ();
317 // Delete specified key container
318 csp
.KeyContainerName
= args
[i
];
319 rsa
= new RSACryptoServiceProvider (csp
);
320 rsa
.PersistKeyInCsp
= false;
322 Console
.WriteLine ("Keypair in container {0} has been deleted", args
[i
]);
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
334 Console
.WriteLine ("Assemblies are not identical (different digest for metadata)");
337 // Export public key from assembly
338 an
= AssemblyName
.GetAssemblyName (args
[i
++]);
339 WriteToFile (args
[i
], an
.GetPublicKey ());
341 Console
.WriteLine ("Public Key extracted to file {0}", args
[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));
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));
356 Console
.WriteLine ("A new strong name keypair has been generated in {0}", args
[i
]);
359 Console
.WriteLine ("Unimplemented option");
362 byte[] infileD
= ReadFromFile (args
[i
++]);
363 WriteCSVToFile (args
[i
], infileD
, "D");
365 Console
.WriteLine ("Output CVS file is {0} (decimal format)", args
[i
]);
368 byte[] infileX2
= ReadFromFile (args
[i
++]);
369 WriteCSVToFile (args
[i
], infileX2
, "X2");
371 Console
.WriteLine ("Output CVS file is {0} (hexadecimal format)", args
[i
]);
374 // Extract public key from SNK file
375 sn
= new StrongName (ReadFromFile (args
[i
++]));
376 WriteToFile (args
[i
], sn
.PublicKey
);
378 Console
.WriteLine ("Public Key extracted to file {0}", args
[i
]);
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
);
387 Console
.WriteLine ("Public Key extracted to file {0}", args
[i
]);
390 string filename
= args
[i
++];
391 sn
= new StrongName (ReadFromFile (args
[i
]));
392 if (! ReSign (filename
, sn
.RSA
))
396 filename
= args
[i
++];
397 csp
.KeyContainerName
= args
[i
];
398 rsa
= new RSACryptoServiceProvider (csp
);
399 if (! ReSign (filename
, rsa
))
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
);
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
);
416 // Show public key token from assembly
417 an
= AssemblyName
.GetAssemblyName (args
[i
++]);
418 // note: ignore quiet
419 byte [] pkt
= an
.GetPublicKeyToken ();
421 Console
.WriteLine ("{0} does not represent a strongly named assembly.", args
[i
- 1]);
423 Console
.WriteLine ("Public Key Token: " + ToString (pkt
));
427 // Show public key and public key token from assembly
428 an
= AssemblyName
.GetAssemblyName (args
[i
++]);
429 byte [] token
= an
.GetPublicKeyToken ();
431 Console
.WriteLine ("{0} does not represent a strongly named assembly.", args
[i
- 1]);
433 Console
.WriteLine ("Public Key:" + ToString (an
.GetPublicKey ()));
434 Console
.WriteLine ("{0}Public Key Token: " + ToString (token
), Environment
.NewLine
);
438 filename
= args
[i
++];
439 return Verify (filename
, false);
441 filename
= args
[i
++];
442 return Verify (filename
, true); // force verification
444 Console
.WriteLine (new StrongNameManager ().ToString ());
447 Console
.WriteLine ("Unimplemented option");
450 Console
.WriteLine ("Unimplemented option");
453 // we must remove <verificationSettings> from each config files
454 Console
.WriteLine ("Unimplemented option");
458 Help ((i
< args
.Length
) ? args
[i
] : null);
462 Console
.WriteLine ("Unknown option {0}", args
[i
-1]);