Added uninstall target
[mono.git] / mcs / tools / security / MakeCert.cs
1 //
2 // MakeCert.cs: makecert clone tool
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System;
11 using System.IO;
12 using System.Reflection;
13 using System.Security.Cryptography;
14
15 using Mono.Security.Authenticode;
16 using Mono.Security.X509;
17 using Mono.Security.X509.Extensions;
18
19 [assembly: AssemblyTitle("Mono MakeCert")]
20 [assembly: AssemblyDescription("X.509 Certificate Builder")]
21
22 namespace Mono.Tools {
23
24         class MakeCert {
25
26                 static private void Header () 
27                 {
28                         Assembly a = Assembly.GetExecutingAssembly ();
29                         AssemblyName an = a.GetName ();
30
31                         object [] att = a.GetCustomAttributes (typeof (AssemblyTitleAttribute), false);
32                         string title = ((att.Length > 0) ? ((AssemblyTitleAttribute) att [0]).Title : "Mono MakeCert");
33
34                         att = a.GetCustomAttributes (typeof (AssemblyCopyrightAttribute), false);
35                         string copyright = ((att.Length > 0) ? ((AssemblyCopyrightAttribute) att [0]).Copyright : "");
36
37                         Console.WriteLine ("{0} {1}", title, an.Version.ToString ());
38                         Console.WriteLine ("{0}{1}", copyright, Environment.NewLine);
39                 }
40
41                 static private void Help () 
42                 {
43                         Console.WriteLine ("Usage: makecert [options] certificate{0}", Environment.NewLine);
44                         Console.WriteLine (" -# num{0}\tCertificate serial number", Environment.NewLine);
45                         Console.WriteLine (" -n dn{0}\tSubject Distinguished Name", Environment.NewLine);
46                         Console.WriteLine (" -in dn{0}\tIssuert Distinguished Name", Environment.NewLine);
47                         Console.WriteLine (" -r{0}\tCreate a self-signed (root) certificate", Environment.NewLine);
48                         Console.WriteLine (" -sv pkvfile{0}\tPrivate key file (.PVK) for the subject (created if missing)", Environment.NewLine);
49                         Console.WriteLine (" -iv pvkfile{0}\tPrivate key file (.PVK) for the issuer", Environment.NewLine);
50                         Console.WriteLine (" -ic certfile{0}\tExtract the issuer's name from the specified certificate", Environment.NewLine);
51                         Console.WriteLine (" -?{0}\thelp (display this help message)", Environment.NewLine);
52                         Console.WriteLine (" -!{0}\textended help (for advanced options)", Environment.NewLine);
53                 }
54
55                 static private void ExtendedHelp () 
56                 {
57                         Console.WriteLine ("Usage: makecert [options] certificate{0}", Environment.NewLine);
58                         Console.WriteLine (" -a hash\tSelect hash algorithm. Only MD5 and SHA1 are supported.");
59                         Console.WriteLine (" -b date\tThe date since when the certificate is valid (notBefore).");
60                         Console.WriteLine (" -cy [authority|end]\tBasic constraints. Select Authority or End-Entity certificate.");
61                         Console.WriteLine (" -e date\tThe date until when the certificate is valid (notAfter).");
62                         Console.WriteLine (" -eku oid[,oid]\tAdd some extended key usage OID to the certificate.");
63                         Console.WriteLine (" -h number\tAdd a path length restriction to the certificate chain.");
64                         Console.WriteLine (" -ic cert\tTake the issuer's name from the specified certificate.");
65                         Console.WriteLine (" -in name\tTake the issuer's name from the specified parameter.");
66                         Console.WriteLine (" -iv pvkfile\tSign the certificate using the private key inside the PVK file.");
67                         Console.WriteLine (" -m number\tCertificate validity period (in months).");
68                         Console.WriteLine (" -sv pvkfile\tCreate a new PVK file if non-existant, otherwise use the PVK file as the subject public key.");
69                         Console.WriteLine (" -?\thelp (display basic message)");
70                 }
71
72                 static X509Certificate LoadCertificate (string filename) 
73                 {
74                         FileStream fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
75                         byte[] rawcert = new byte [fs.Length];
76                         fs.Read (rawcert, 0, rawcert.Length);
77                         fs.Close ();
78                         return new X509Certificate (rawcert);
79                 }
80
81                 static void WriteCertificate (string filename, byte[] rawcert) 
82                 {
83                         FileStream fs = File.Open (filename, FileMode.Create, FileAccess.Write);
84                         fs.Write (rawcert, 0, rawcert.Length);
85                         fs.Close ();
86                 }
87
88                 static string MonoTestRootAgency = "<RSAKeyValue><Modulus>v/4nALBxCE+9JgEC0LnDUvKh6e96PwTpN4Rj+vWnqKT7IAp1iK/JjuqvAg6DQ2vTfv0dTlqffmHH51OyioprcT5nzxcSTsZb/9jcHScG0s3/FRIWnXeLk/fgm7mSYhjUaHNI0m1/NTTktipicjKxo71hGIg9qucCWnDum+Krh/k=</Modulus><Exponent>AQAB</Exponent><P>9jbKxMXEruW2CfZrzhxtull4O8P47+mNsEL+9gf9QsRO1jJ77C+jmzfU6zbzjf8+ViK+q62tCMdC1ZzulwdpXQ==</P><Q>x5+p198l1PkK0Ga2mRh0SIYSykENpY2aLXoyZD/iUpKYAvATm0/wvKNrE4dKJyPCA+y3hfTdgVag+SP9avvDTQ==</Q><DP>ISSjCvXsUfbOGG05eddN1gXxL2pj+jegQRfjpk7RAsnWKvNExzhqd5x+ZuNQyc6QH5wxun54inP4RTUI0P/IaQ==</DP><DQ>R815VQmR3RIbPqzDXzv5j6CSH6fYlcTiQRtkBsUnzhWmkd/y3XmamO+a8zJFjOCCx9CcjpVuGziivBqi65lVPQ==</DQ><InverseQ>iYiu0KwMWI/dyqN3RJYUzuuLj02/oTD1pYpwo2rvNCXU1Q5VscOeu2DpNg1gWqI+1RrRCsEoaTNzXB1xtKNlSw==</InverseQ><D>nIfh1LYF8fjRBgMdAH/zt9UKHWiaCnc+jXzq5tkR8HVSKTVdzitD8bl1JgAfFQD8VjSXiCJqluexy/B5SGrCXQ49c78NIQj0hD+J13Y8/E0fUbW1QYbhj6Ff7oHyhaYe1WOQfkp2t/h+llHOdt1HRf7bt7dUknYp7m8bQKGxoYE=</D></RSAKeyValue>";
89
90                 static string defaultIssuer = "CN=Mono Test Root Agency";
91                 static string defaultSubject = "CN=Poupou's-Software-Factory";
92
93                 [STAThread]
94                 static int Main (string[] args)
95                 {
96                         if (args.Length < 1) {
97                                 Header ();
98                                 Console.WriteLine ("ERROR: Missing output filename {0}", Environment.NewLine);
99                                 Help ();
100                                 return -1;
101                         }
102
103                         System.IFormatProvider format = new System.Globalization.CultureInfo("en-US", true);
104                         string fileName = args [args.Length - 1];
105
106                         // default values
107                         byte[] sn = Guid.NewGuid ().ToByteArray ();
108                         string subject = defaultSubject;
109                         string issuer = defaultIssuer;
110                         DateTime notBefore = DateTime.Now;
111                         DateTime notAfter =  DateTime.Parse ("12/31/2039 23:59:59Z", format);
112
113                         RSA issuerKey = (RSA)RSA.Create ();
114                         issuerKey.FromXmlString (MonoTestRootAgency);
115                         RSA subjectKey = (RSA)RSA.Create ();
116
117                         bool selfSigned = false;
118                         string hashName = "MD5";
119
120                         CspParameters subjectParams = new CspParameters ();
121                         CspParameters issuerParams = new CspParameters ();
122                         BasicConstraintsExtension bce = null;
123                         ExtendedKeyUsageExtension eku = null;
124
125                         Header();
126                         try {
127                                 int i=0;
128                                 while (i < args.Length) {
129                                         switch (args [i++]) {
130                                                 // Basic options
131                                                 case "-#":
132                                                         // Serial Number
133                                                         sn = BitConverter.GetBytes (Convert.ToInt32 (args [i++]));
134                                                         break;
135                                                 case "-n":
136                                                         // Subject Distinguish Name
137                                                         subject = args [i++];
138                                                         break;
139                                                 case "-$":
140                                                         // (authenticode) commercial or individual
141                                                         // CRITICAL KeyUsageRestriction extension
142                                                         // hash algorithm
143                                                         string usageRestriction = args [i++].ToLower ();
144                                                         switch (usageRestriction) {
145                                                                 case "commercial":
146                                                                 case "individual":
147                                                                         Console.WriteLine ("WARNING: Unsupported deprecated certification extension KeyUsageRestriction not included");
148 //                                                                      Console.WriteLine ("WARNING: ExtendedKeyUsage for codesigning has been included.");
149                                                                         break;
150                                                                 default:
151                                                                         Console.WriteLine ("Unsupported restriction " + usageRestriction);
152                                                                         return -1;
153                                                         }
154                                                         break;
155                                                 // Extended Options
156                                                 case "-a":
157                                                         // hash algorithm
158                                                         switch (args [i++].ToLower ()) {
159                                                                 case "sha1":
160                                                                         hashName = "SHA1";
161                                                                         break;
162                                                                 case "md5":
163                                                                         hashName = "MD5";
164                                                                         break;
165                                                                 default:
166                                                                         Console.WriteLine ("Unsupported hash algorithm");
167                                                                         break;
168                                                         }
169                                                         break;
170                                                 case "-b":
171                                                         // Validity / notBefore
172                                                         notBefore = DateTime.Parse (args [i++] + " 23:59:59", format);
173                                                         break;
174                                                 case "-cy":
175                                                         // basic constraints - autority or end-entity
176                                                         switch (args [i++].ToLower ()) {
177                                                                 case "authority":
178                                                                         if (bce == null)
179                                                                                 bce = new BasicConstraintsExtension ();
180                                                                         bce.CertificateAuthority = true;
181                                                                         break;
182                                                                 case "end":
183                                                                         // do not include extension
184                                                                         bce = null;
185                                                                         break;
186                                                                 case "both":
187                                                                         Console.WriteLine ("ERROR: No more supported in X.509");
188                                                                         return -1;
189                                                                 default:
190                                                                         Console.WriteLine ("Unsupported certificate type");
191                                                                         return -1;
192                                                         }
193                                                         break;
194                                                 case "-d":
195                                                         // CN private extension ?
196                                                         Console.WriteLine ("Unsupported option");
197                                                         break;
198                                                 case "-e":
199                                                         // Validity / notAfter
200                                                         notAfter = DateTime.Parse (args [i++] + " 23:59:59", format);
201                                                         break;
202                                                 case "-eku":
203                                                         // extendedKeyUsage extension
204                                                         char[] sep = { ',' };
205                                                         string[] purposes = args [i++].Split (sep);
206                                                         if (eku == null)
207                                                                 eku = new ExtendedKeyUsageExtension ();
208                                                         foreach (string purpose in purposes) {
209                                                                 eku.KeyPurpose.Add (purpose);
210                                                         }
211                                                         break;
212                                                 case "-h":
213                                                         // pathLength (basicConstraints)
214                                                         // MS use an old basicConstrains (2.5.29.10) which 
215                                                         // allows both CA and End-Entity. This is no
216                                                         // more supported with 2.5.29.19.
217                                                         if (bce == null) {
218                                                                 bce = new BasicConstraintsExtension ();
219                                                                 bce.CertificateAuthority = true;
220                                                         }
221                                                         bce.PathLenConstraint = Convert.ToInt32 (args [i++]);
222                                                         break;
223                                                 case "-ic":
224                                                         X509Certificate x509 = LoadCertificate (args [i++]);
225                                                         issuer = x509.SubjectName;
226                                                         break;
227                                                 case "-in":
228                                                         issuer = args [i++];
229                                                         break;
230                                                 case "-iv":
231                                                         // TODO password
232                                                         PrivateKey pvk = PrivateKey.CreateFromFile (args [i++]);
233                                                         issuerKey = pvk.RSA;
234                                                         break;
235                                                 case "-l":
236                                                         // link (URL)
237                                                         // spcSpAgencyInfo private extension
238                                                         Console.WriteLine ("Unsupported option");
239                                                         break;
240                                                 case "-m":
241                                                         // validity period (in months)
242                                                         notAfter = notBefore.AddMonths (Convert.ToInt32 (args [i++]));
243                                                         break;
244                                                 case "-nscp":
245                                                         // Netscape's private extensions - NetscapeCertType
246                                                         // BasicContraints - End Entity
247                                                         Console.WriteLine ("Unsupported option");
248                                                         break;
249                                                 case "-r":
250                                                         selfSigned = true;
251                                                         break;
252                                                 case "-sc":
253                                                         // subject certificate ? renew ?
254                                                         Console.WriteLine ("Unsupported option");
255                                                         break;
256                                                 // Issuer CspParameters options
257                                                 case "-ik":
258                                                         issuerParams.KeyContainerName = args [i++];
259                                                         break;
260                                                 case "-iky":
261                                                         // select a key in the provider
262                                                         string ikn = args [i++].ToLower ();
263                                                         switch (ikn) {
264                                                                 case "signature":
265                                                                         issuerParams.KeyNumber = 0;
266                                                                         break;
267                                                                 case "exchange":
268                                                                         issuerParams.KeyNumber = 1;
269                                                                         break;
270                                                                 default:
271                                                                         issuerParams.KeyNumber = Convert.ToInt32 (ikn);
272                                                                         break;
273                                                         }
274                                                         break;
275                                                 case "-ip":
276                                                         issuerParams.ProviderName = args [i++];
277                                                         break;
278                                                 case "-ir":
279                                                         switch (args [i++].ToLower ()) {
280                                                                 case "localmachine":
281                                                                         issuerParams.Flags = CspProviderFlags.UseMachineKeyStore;
282                                                                         break;
283                                                                 case "currentuser":
284                                                                         issuerParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
285                                                                         break;
286                                                                 default:
287                                                                         Console.WriteLine ("Unknown key store for issuer");
288                                                                         return -1;
289                                                         }
290                                                         break;
291                                                 case "-is":
292                                                         Console.WriteLine ("Unsupported option");
293                                                         return -1;
294                                                 case "-iy":
295                                                         issuerParams.ProviderType = Convert.ToInt32 (args [i++]);
296                                                         break;
297                                                 // Subject CspParameters Options
298                                                 case "-sk":
299                                                         subjectParams.KeyContainerName = args [i++];
300                                                         break;
301                                                 case "-sky":
302                                                         // select a key in the provider
303                                                         string skn = args [i++].ToLower ();
304                                                         switch (skn) {
305                                                                 case "signature":
306                                                                         subjectParams.KeyNumber = 0;
307                                                                         break;
308                                                                 case "exchange":
309                                                                         subjectParams.KeyNumber = 1;
310                                                                         break;
311                                                                 default:
312                                                                         subjectParams.KeyNumber = Convert.ToInt32 (skn);
313                                                                         break;
314                                                         }
315                                                         break;
316                                                 case "-sp":
317                                                         subjectParams.ProviderName = args [i++];
318                                                         break;
319                                                 case "-sr":
320                                                         switch (args [i++].ToLower ()) {
321                                                                 case "localmachine":
322                                                                         subjectParams.Flags = CspProviderFlags.UseMachineKeyStore;
323                                                                         break;
324                                                                 case "currentuser":
325                                                                         subjectParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
326                                                                         break;
327                                                                 default:
328                                                                         Console.WriteLine ("Unknown key store for subject");
329                                                                         return -1;
330                                                         }
331                                                         break;
332                                                 case "-ss":
333                                                         Console.WriteLine ("Unsupported option");
334                                                         return -1;
335                                                 case "-sv":
336                                                         string pvkFile = args [i++];
337                                                         if (File.Exists (pvkFile)) {
338                                                                 PrivateKey key = PrivateKey.CreateFromFile (pvkFile);
339                                                                 subjectKey = key.RSA;
340                                                         }
341                                                         else {
342                                                                 PrivateKey key = new PrivateKey ();
343                                                                 key.RSA = subjectKey;
344                                                                 key.Save (pvkFile);
345                                                         }
346                                                         break;
347                                                 case "-sy":
348                                                         subjectParams.ProviderType = Convert.ToInt32 (args [i++]);
349                                                         break;
350                                                 // Other options
351                                                 case "-?":
352                                                         Help ();
353                                                         return 0;
354                                                 case "-!":
355                                                         ExtendedHelp ();
356                                                         return 0;
357                                                 default:
358                                                         if (i != args.Length) {
359                                                                 Console.WriteLine ("ERROR: Unknown parameter");
360                                                                 Help ();
361                                                                 return -1;
362                                                         }
363                                                         break;
364                                         }
365                                 }
366
367                                 // serial number MUST be positive
368                                 if ((sn [0] & 0x80) == 0x80)
369                                         sn [0] -= 0x80;
370
371                                 if (selfSigned) {
372                                         if (subject != defaultSubject) {
373                                                 issuer = subject;
374                                                 issuerKey = subjectKey;
375                                         }
376                                         else {
377                                                 subject = issuer;
378                                                 subjectKey = issuerKey;
379                                         }
380                                 }
381
382                                 if (subject == null)
383                                         throw new Exception ("Missing Subject Name");
384
385                                 X509CertificateBuilder cb = new X509CertificateBuilder (3);
386                                 cb.SerialNumber = sn;
387                                 cb.IssuerName = issuer;
388                                 cb.NotBefore = notBefore;
389                                 cb.NotAfter = notAfter;
390                                 cb.SubjectName = subject;
391                                 cb.SubjectPublicKey = subjectKey;
392                                 // extensions
393                                 if (bce != null)
394                                         cb.Extensions.Add (bce);
395                                 if (eku != null)
396                                         cb.Extensions.Add (eku);
397                                 // signature
398                                 cb.Hash = hashName;
399                                 byte[] rawcert = cb.Sign (issuerKey);
400                                 WriteCertificate (fileName, rawcert);
401                                 Console.WriteLine ("Success");
402                                 return 0;
403                         }
404                         catch (Exception e) {
405                                 Console.WriteLine ("ERROR: " + e.ToString ());
406                                 Help ();
407                         }
408                         return 1;
409                 }
410         }
411 }