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