New test.
[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 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                                 PrivateKey pvk = PrivateKey.CreateFromFile (keyfile);
66                                 if (pvk.Encrypted) {
67                                         Console.WriteLine ("Enter password for {0}: ", keyfile);
68                                         string password = Console.ReadLine ();
69                                         pvk = PrivateKey.CreateFromFile (keyfile, password);
70                                         if (pvk.RSA == null)
71                                                 Console.WriteLine ("Invalid password!");
72                                 }
73                                 rsa = pvk.RSA;
74                         }
75                         else {
76                                 rsa = new RSACryptoServiceProvider (csp);
77                         }
78
79                         return rsa;
80                 }
81
82                 static private X509CertificateCollection GetCertificates (string spcfile)
83                 {
84                         if (spcfile == null) {
85                                 Console.WriteLine ("Missing SPC (certificate) file.");
86                                 return null;
87                         }
88                         if (!File.Exists (spcfile)) {
89                                 Console.WriteLine ("Couldn't find '{0}' file.", spcfile);
90                                 return null;
91                         }
92
93                         SoftwarePublisherCertificate spc = SoftwarePublisherCertificate.CreateFromFile (spcfile);
94                         return spc.Certificates;
95                 }
96
97                 [STAThread]
98                 static int Main(string[] args)
99                 {
100                         Header ();
101                         if (args.Length < 1) {
102                                 Help ();
103                                 return 1;
104                         }
105
106                         CspParameters csp = new CspParameters ();
107                         string pvkFilename = null;
108                         string spcFilename = null;
109                         int timestampRetry = 1;
110                         int timestampDelay = 0;
111                         bool sign = true;
112
113                         // to be signed
114                         string tbsFilename = args [args.Length - 1];
115
116                         AuthenticodeFormatter af = new AuthenticodeFormatter ();
117
118                         int i = 0;
119                         while (i < args.Length - 1) {
120                                 switch (args[i++]) {
121                                         case "-spc":
122                                                 spcFilename = args [i++];
123                                                 break;
124                                         case "-v":
125                                                 pvkFilename = args [i++];
126                                                 break;
127                                         case "-a":
128                                                 af.Hash = args [i++];
129                                                 break;
130                                         case "-$":
131                                                 string auth = args [i++].ToLower ();
132                                                 switch (auth) {
133                                                         case "individual":
134                                                                 af.Authority = Authority.Commercial;
135                                                                 break;
136                                                         case "commercial":
137                                                                 af.Authority = Authority.Individual;
138                                                                 break;
139                                                         default:
140                                                                 Console.WriteLine ("Unknown authority {0}", auth);
141                                                                 return 1;
142                                                 }
143                                                 break;
144                                         case "-n":
145                                                 af.Description = args [i++];
146                                                 break;
147                                         case "-i":
148                                                 af.Url = new Uri (args [i++]);
149                                                 break;
150                                         // timestamp options
151                                         case "-t":
152                                                 af.TimestampUrl = new Uri (args [i++]);
153                                                 break;
154                                         case "-tr":
155                                                 timestampRetry = Convert.ToInt32 (args [i++]);
156                                                 break;
157                                         case "-tw":
158                                                 timestampDelay = Convert.ToInt32 (args [i++]) * 1000;
159                                                 break;
160                                         case "-x":
161                                                 // only timestamp
162                                                 sign = false;  
163                                                 break;
164                                         // CSP provider options
165                                         case "-k":
166                                                 csp.KeyContainerName = args [i++];
167                                                 break;
168                                         case "-p":
169                                                 csp.ProviderName = args [i++];
170                                                 break;
171                                         case "-y":
172                                                 csp.ProviderType = Convert.ToInt32 (args [i++]);
173                                                 break;
174                                         case "-ky":
175                                                 string key = args [i++];
176                                                 switch (key) {
177                                                         case "signature":
178                                                                 csp.KeyNumber = 0;
179                                                                 break;
180                                                         case "exchange":
181                                                                 csp.KeyNumber = 0;
182                                                                 break;
183                                                         default:
184                                                                 csp.KeyNumber = Convert.ToInt32 (key);
185                                                                 break;
186                                                 }
187                                                 break;
188                                         case "-r":
189                                                 string location = args [i++];
190                                                 switch (location) {
191                                                         case "localMachine":
192                                                                 csp.Flags = CspProviderFlags.UseMachineKeyStore;
193                                                                 break;
194                                                         case "currentUser":
195                                                                 csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
196                                                                 break;
197                                                         default:
198                                                                 Console.WriteLine ("Unknown location {0}", location);
199                                                                 return 1;
200                                                 }
201                                                 break;
202                                         // unsupported options
203                                         case "-j":
204                                         case "-jp":
205                                                 Console.WriteLine ("Unsupported option {0}", args[i-1]);
206                                                 return 1;
207                                         // other options
208                                         case "-?":
209                                                 Help ();
210                                                 return 0;
211                                 }
212                         }
213
214                         // no need to continue if we can't find the assembly
215                         // to be signed (and/or timestamped)
216                         if (!File.Exists (tbsFilename)) {
217                                 Console.WriteLine ("Couldn't find {0}.", tbsFilename);
218                                 return 1;
219                         }
220
221                         if (sign) {
222                                 RSA rsa = GetPrivateKey (pvkFilename, csp);
223                                 if (rsa == null) {
224                                         Console.WriteLine ("No private key available to sign the assembly.");
225                                         return 1;
226                                 }
227                                 af.RSA = rsa;
228
229                                 X509CertificateCollection certs = GetCertificates (spcFilename);
230                                 if ((certs == null) || (certs.Count == 0)) {
231                                         Console.WriteLine ("No certificates available to sign the assembly.");
232                                         return 1;
233                                 }
234                                 af.Certificates.AddRange (certs);\r
235 \r
236                                 if (!af.Sign (tbsFilename)) {\r
237                                         Console.WriteLine ("Couldn't sign file '{0}'.", tbsFilename);\r
238                                         return 1;\r
239                                 }\r
240                         } else if (af.TimestampUrl != null) {\r
241                                 bool ts = false;\r
242                                 // only timestamp an already signed file\r
243                                 for (int j = 0; j < timestampRetry && !ts; j++) {\r
244                                         ts = af.Timestamp (tbsFilename);\r
245                                         // wait (unless it's the last try) and retry\r
246                                         if (!ts && (j < timestampRetry - 1)) {\r
247                                                 Console.WriteLine ("Couldn't timestamp file '{0}', will retry in {1} ms", tbsFilename, timestampDelay);\r
248                                                 Thread.Sleep (timestampDelay);\r
249                                         }\r
250                                 }\r
251                                 if (!ts) {\r
252                                         Console.WriteLine ("Couldn't timestamp file '{0}' after {1} retries.", tbsFilename, timestampRetry);\r
253                                         return 1;\r
254                                 }\r
255                         } else {\r
256                                 Help ();\r
257                                 return 1;\r
258                         }\r
259 \r
260                         Console.WriteLine ("Success");
261                         return 0;
262                 }
263         }
264 }