2 // makecert.cs: makecert clone tool
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
12 using System.Collections;
13 using System.Globalization;
15 using System.Reflection;
16 using System.Security.Cryptography;
18 using Mono.Security.Authenticode;
19 using Mono.Security.X509;
20 using Mono.Security.X509.Extensions;
22 [assembly: AssemblyTitle("Mono MakeCert")]
23 [assembly: AssemblyDescription("X.509 Certificate Builder")]
25 namespace Mono.Tools {
29 static private void Header ()
31 Console.WriteLine (new AssemblyInfo ().ToString ());
34 static private void Help ()
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);
48 static private void ExtendedHelp ()
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)");
63 static X509Certificate LoadCertificate (string filename)
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);
69 return new X509Certificate (rawcert);
72 static void WriteCertificate (string filename, byte[] rawcert)
74 FileStream fs = File.Open (filename, FileMode.Create, FileAccess.Write);
75 fs.Write (rawcert, 0, rawcert.Length);
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>";
81 static string defaultIssuer = "CN=Mono Test Root Agency";
82 static string defaultSubject = "CN=Poupou's-Software-Factory";
85 static int Main (string[] args)
87 if (args.Length < 1) {
89 Console.WriteLine ("ERROR: Missing output filename {0}", Environment.NewLine);
94 string fileName = args [args.Length - 1];
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
103 RSA issuerKey = (RSA)RSA.Create ();
104 issuerKey.FromXmlString (MonoTestRootAgency);
105 RSA subjectKey = (RSA)RSA.Create ();
107 bool selfSigned = false;
108 string hashName = "SHA512";
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;
122 while (i < args.Length) {
123 switch (args [i++]) {
127 sn = BitConverter.GetBytes (Convert.ToInt32 (args [i++]));
130 // Subject Distinguish Name
131 subject = args [i++];
134 // (authenticode) commercial or individual
135 // CRITICAL KeyUsageRestriction extension
137 string usageRestriction = args [i++].ToLower ();
138 switch (usageRestriction) {
141 Console.WriteLine ("WARNING: Unsupported deprecated certification extension KeyUsageRestriction not included");
142 // Console.WriteLine ("WARNING: ExtendedKeyUsage for codesigning has been included.");
145 Console.WriteLine ("Unsupported restriction " + usageRestriction);
152 switch (args [i++].ToLower ()) {
160 Console.WriteLine ("WARNING: SHA1 is not safe for this usage.");
164 Console.WriteLine ("WARNING: MD5 is not safe for this usage.");
168 Console.WriteLine ("Unsupported hash algorithm");
173 // Validity / notBefore
174 notBefore = DateTime.Parse (args [i++] + " 23:59:59", CultureInfo.InvariantCulture);
177 // basic constraints - autority or end-entity
178 switch (args [i++].ToLower ()) {
181 bce = new BasicConstraintsExtension ();
182 bce.CertificateAuthority = true;
185 // do not include extension
189 Console.WriteLine ("ERROR: No more supported in X.509");
192 Console.WriteLine ("Unsupported certificate type");
197 // CN private extension ?
198 Console.WriteLine ("Unsupported option");
201 // Validity / notAfter
202 notAfter = DateTime.Parse (args [i++] + " 23:59:59", CultureInfo.InvariantCulture);
205 // extendedKeyUsage extension
206 char[] sep = { ',' };
207 string[] purposes = args [i++].Split (sep);
209 eku = new ExtendedKeyUsageExtension ();
210 foreach (string purpose in purposes) {
211 eku.KeyPurpose.Add (purpose);
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.
220 bce = new BasicConstraintsExtension ();
221 bce.CertificateAuthority = true;
223 bce.PathLenConstraint = Convert.ToInt32 (args [i++]);
227 string [] dnsNames = File.ReadAllLines (args [i++]);
228 alt = new SubjectAltNameExtension (null, dnsNames, null, null);
232 issuerCertificate = LoadCertificate (args [i++]);
233 issuer = issuerCertificate.SubjectName;
240 PrivateKey pvk = PrivateKey.CreateFromFile (args [i++]);
245 // spcSpAgencyInfo private extension
246 Console.WriteLine ("Unsupported option");
249 // validity period (in months)
250 notAfter = notBefore.AddMonths (Convert.ToInt32 (args [i++]));
253 // Netscape's private extensions - NetscapeCertType
254 // BasicContraints - End Entity
255 Console.WriteLine ("Unsupported option");
261 // subject certificate ? renew ?
262 Console.WriteLine ("Unsupported option");
264 // Issuer CspParameters options
266 issuerParams.KeyContainerName = args [i++];
269 // select a key in the provider
270 string ikn = args [i++].ToLower ();
273 issuerParams.KeyNumber = 0;
276 issuerParams.KeyNumber = 1;
279 issuerParams.KeyNumber = Convert.ToInt32 (ikn);
284 issuerParams.ProviderName = args [i++];
287 switch (args [i++].ToLower ()) {
289 issuerParams.Flags = CspProviderFlags.UseMachineKeyStore;
292 issuerParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
295 Console.WriteLine ("Unknown key store for issuer");
300 Console.WriteLine ("Unsupported option");
303 issuerParams.ProviderType = Convert.ToInt32 (args [i++]);
305 // Subject CspParameters Options
307 subjectParams.KeyContainerName = args [i++];
310 // select a key in the provider
311 string skn = args [i++].ToLower ();
314 subjectParams.KeyNumber = 0;
317 subjectParams.KeyNumber = 1;
320 subjectParams.KeyNumber = Convert.ToInt32 (skn);
325 subjectParams.ProviderName = args [i++];
328 switch (args [i++].ToLower ()) {
330 subjectParams.Flags = CspProviderFlags.UseMachineKeyStore;
333 subjectParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
336 Console.WriteLine ("Unknown key store for subject");
341 Console.WriteLine ("Unsupported option");
344 string pvkFile = args [i++];
345 if (File.Exists (pvkFile)) {
346 PrivateKey key = PrivateKey.CreateFromFile (pvkFile);
347 subjectKey = key.RSA;
350 PrivateKey key = new PrivateKey ();
351 key.RSA = subjectKey;
356 subjectParams.ProviderType = Convert.ToInt32 (args [i++]);
358 // Mono Specific Options
360 p12file = args [i++];
371 if (i != args.Length) {
372 Console.WriteLine ("ERROR: Unknown parameter");
380 // serial number MUST be positive
381 if ((sn [0] & 0x80) == 0x80)
385 if (subject != defaultSubject) {
387 issuerKey = subjectKey;
391 subjectKey = issuerKey;
396 throw new Exception ("Missing Subject Name");
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;
407 cb.Extensions.Add (bce);
409 cb.Extensions.Add (eku);
411 cb.Extensions.Add (alt);
414 byte[] rawcert = cb.Sign (issuerKey);
416 if (p12file == null) {
417 WriteCertificate (fileName, rawcert);
419 PKCS12 p12 = new PKCS12 ();
420 p12.Password = p12pwd;
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);
429 p12.AddCertificate (new X509Certificate (rawcert), attributes);
430 if (issuerCertificate != null)
431 p12.AddCertificate (issuerCertificate);
432 p12.AddPkcs8ShroudedKeyBag (subjectKey, attributes);
433 p12.SaveToFile (p12file);
435 Console.WriteLine ("Success");
438 catch (Exception e) {
439 Console.WriteLine ("ERROR: " + e.ToString ());