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