Merge branch 'master' of github.com:tgiphil/mono
[mono.git] / mcs / tools / security / signcode.cs
1 //
2 // SignCode.cs: secutil clone tool
3 //
4 // Author:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004,2006-2007 Novell, Inc (http://www.novell.com)
9 //
10
11 using System;
12 using System.IO;
13 using System.Reflection;
14 using System.Security.Cryptography;
15 using System.Text;
16 using System.Threading;
17
18 using Mono.Security.Authenticode;
19 using Mono.Security.X509;
20
21 [assembly: AssemblyTitle("Mono SignCode")]
22 [assembly: AssemblyDescription("Sign assemblies and PE files using Authenticode(tm).")]
23
24 namespace Mono.Tools {
25
26         class SignCode {
27
28                 static private void Header () 
29                 {
30                         Console.WriteLine (new AssemblyInfo ().ToString ());
31                 }
32
33                 static private void Help () 
34                 {
35                         Console.WriteLine ("Usage: signcode [options] filename{0}", Environment.NewLine);
36                         Console.WriteLine ("\t-spc spc\tSoftware Publisher Certificate file");
37                         Console.WriteLine ("\t-v pvk\t\tPrivate Key file");
38                         Console.WriteLine ("\t-a md5 | sha1\tHash Algorithm (default: MD5)");
39                         Console.WriteLine ("\t-$ indivisual | commercial\tSignature type");
40                         Console.WriteLine ("\t-n description\tDescription for the signed file");
41                         Console.WriteLine ("\t-i url\tURL for the signed file");
42                         Console.WriteLine ("Timestamp options");
43                         Console.WriteLine ("\t-t url\tTimestamp service http URL");
44                         Console.WriteLine ("\t-tr #\tNumber of retries for timestamp");
45                         Console.WriteLine ("\t-tw #\tDelay between retries");
46                         Console.WriteLine ("\t-x\tOnly timestamp (no signature)");
47                         Console.WriteLine ("CSP options");
48                         Console.WriteLine ("\t-k name\tKey Container Name");
49                         Console.WriteLine ("\t-p name\tProvider Name");
50                         Console.WriteLine ("\t-y #\tProvider Type");
51                         Console.WriteLine ("\t-ky [signature|exchange|#]\tKey Type");
52                         Console.WriteLine ("\t-r [localMachine|currentUser]\tKey Location");
53                 }
54
55                 static private RSA GetPrivateKey (string keyfile, CspParameters csp)
56                 {
57                         RSA rsa = null;
58
59                         if (keyfile != null) {
60                                 if (!File.Exists (keyfile)) {
61                                         Console.WriteLine ("Couldn't find '{0}' file.", keyfile);
62                                         return null;
63                                 }
64
65                                 try {
66                                         PrivateKey pvk = PrivateKey.CreateFromFile (keyfile);
67                                         rsa = pvk.RSA;
68                                 }
69                                 catch (CryptographicException) {
70                                         Console.WriteLine ("Enter password for {0}: ", keyfile);
71                                         string password = Console.ReadLine ();
72                                         try {
73                                                 PrivateKey pvk = PrivateKey.CreateFromFile (keyfile, password);
74                                                 rsa = pvk.RSA;
75                                         }
76                                         catch (CryptographicException) {
77                                                 Console.WriteLine ("Invalid password!");
78                                         }
79                                 }
80                         }
81                         else {
82                                 rsa = new RSACryptoServiceProvider (csp);
83                         }
84
85                         return rsa;
86                 }
87
88                 static private X509CertificateCollection GetCertificates (string spcfile)
89                 {
90                         if (spcfile == null) {
91                                 Console.WriteLine ("Missing SPC (certificate) file.");
92                                 return null;
93                         }
94                         if (!File.Exists (spcfile)) {
95                                 Console.WriteLine ("Couldn't find '{0}' file.", spcfile);
96                                 return null;
97                         }
98
99                         SoftwarePublisherCertificate spc = SoftwarePublisherCertificate.CreateFromFile (spcfile);
100                         return spc.Certificates;
101                 }
102
103                 [STAThread]
104                 static int Main(string[] args)
105                 {
106                         Header ();
107                         if (args.Length < 1) {
108                                 Help ();
109                                 return 1;
110                         }
111
112                         CspParameters csp = new CspParameters ();
113                         string pvkFilename = null;
114                         string spcFilename = null;
115                         int timestampRetry = 1;
116                         int timestampDelay = 0;
117                         bool sign = true;
118
119                         // to be signed
120                         string tbsFilename = args [args.Length - 1];
121
122                         AuthenticodeFormatter af = new AuthenticodeFormatter ();
123
124                         int i = 0;
125                         while (i < args.Length - 1) {
126                                 switch (args[i++]) {
127                                         case "-spc":
128                                                 spcFilename = args [i++];
129                                                 break;
130                                         case "-v":
131                                                 pvkFilename = args [i++];
132                                                 break;
133                                         case "-a":
134                                                 af.Hash = args [i++];
135                                                 break;
136                                         case "-$":
137                                                 string auth = args [i++].ToLower ();
138                                                 switch (auth) {
139                                                         case "individual":
140                                                                 af.Authority = Authority.Individual;
141                                                                 break;
142                                                         case "commercial":
143                                                                 af.Authority = Authority.Commercial;
144                                                                 break;
145                                                         default:
146                                                                 Console.WriteLine ("Unknown authority {0}", auth);
147                                                                 return 1;
148                                                 }
149                                                 break;
150                                         case "-n":
151                                                 af.Description = args [i++];
152                                                 break;
153                                         case "-i":
154                                                 af.Url = new Uri (args [i++]);
155                                                 break;
156                                         // timestamp options
157                                         case "-t":
158                                                 af.TimestampUrl = new Uri (args [i++]);
159                                                 break;
160                                         case "-tr":
161                                                 timestampRetry = Convert.ToInt32 (args [i++]);
162                                                 break;
163                                         case "-tw":
164                                                 timestampDelay = Convert.ToInt32 (args [i++]) * 1000;
165                                                 break;
166                                         case "-x":
167                                                 // only timestamp
168                                                 sign = false;  
169                                                 break;
170                                         // CSP provider options
171                                         case "-k":
172                                                 csp.KeyContainerName = args [i++];
173                                                 break;
174                                         case "-p":
175                                                 csp.ProviderName = args [i++];
176                                                 break;
177                                         case "-y":
178                                                 csp.ProviderType = Convert.ToInt32 (args [i++]);
179                                                 break;
180                                         case "-ky":
181                                                 string key = args [i++];
182                                                 switch (key) {
183                                                         case "signature":
184                                                                 csp.KeyNumber = 0;
185                                                                 break;
186                                                         case "exchange":
187                                                                 csp.KeyNumber = 0;
188                                                                 break;
189                                                         default:
190                                                                 csp.KeyNumber = Convert.ToInt32 (key);
191                                                                 break;
192                                                 }
193                                                 break;
194                                         case "-r":
195                                                 string location = args [i++];
196                                                 switch (location) {
197                                                         case "localMachine":
198                                                                 csp.Flags = CspProviderFlags.UseMachineKeyStore;
199                                                                 break;
200                                                         case "currentUser":
201                                                                 csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
202                                                                 break;
203                                                         default:
204                                                                 Console.WriteLine ("Unknown location {0}", location);
205                                                                 return 1;
206                                                 }
207                                                 break;
208                                         // unsupported options
209                                         case "-j":
210                                         case "-jp":
211                                                 Console.WriteLine ("Unsupported option {0}", args[i-1]);
212                                                 return 1;
213                                         // other options
214                                         case "-?":
215                                                 Help ();
216                                                 return 0;
217                                 }
218                         }
219
220                         // no need to continue if we can't find the assembly
221                         // to be signed (and/or timestamped)
222                         if (!File.Exists (tbsFilename)) {
223                                 Console.WriteLine ("Couldn't find {0}.", tbsFilename);
224                                 return 1;
225                         }
226
227                         if (sign) {
228                                 RSA rsa = GetPrivateKey (pvkFilename, csp);
229                                 if (rsa == null) {
230                                         Console.WriteLine ("No private key available to sign the assembly.");
231                                         return 1;
232                                 }
233                                 af.RSA = rsa;
234
235                                 X509CertificateCollection certs = GetCertificates (spcFilename);
236                                 if ((certs == null) || (certs.Count == 0)) {
237                                         Console.WriteLine ("No certificates available to sign the assembly.");
238                                         return 1;
239                                 }
240                                 af.Certificates.AddRange (certs);\r
241 \r
242                                 if (!af.Sign (tbsFilename)) {\r
243                                         Console.WriteLine ("Couldn't sign file '{0}'.", tbsFilename);\r
244                                         return 1;\r
245                                 }\r
246                         } else if (af.TimestampUrl != null) {\r
247                                 bool ts = false;\r
248                                 // only timestamp an already signed file\r
249                                 for (int j = 0; j < timestampRetry && !ts; j++) {\r
250                                         ts = af.Timestamp (tbsFilename);\r
251                                         // wait (unless it's the last try) and retry\r
252                                         if (!ts && (j < timestampRetry - 1)) {\r
253                                                 Console.WriteLine ("Couldn't timestamp file '{0}', will retry in {1} ms", tbsFilename, timestampDelay);\r
254                                                 Thread.Sleep (timestampDelay);\r
255                                         }\r
256                                 }\r
257                                 if (!ts) {\r
258                                         Console.WriteLine ("Couldn't timestamp file '{0}' after {1} retries.", tbsFilename, timestampRetry);\r
259                                         return 1;\r
260                                 }\r
261                         } else {\r
262                                 Help ();\r
263                                 return 1;\r
264                         }\r
265 \r
266                         Console.WriteLine ("Success");
267                         return 0;
268                 }
269         }
270 }