2 // MakeCert.cs: makecert clone tool
5 // Sebastien Pouliot (spouliot@motus.com)
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
12 using System.Reflection;
13 using System.Security.Cryptography;
15 using Mono.Security.Authenticode;
16 using Mono.Security.X509;
17 using Mono.Security.X509.Extensions;
19 [assembly: AssemblyTitle("Mono MakeCert")]
20 [assembly: AssemblyDescription("X.509 Certificate Builder")]
22 namespace Mono.Tools {
26 static private void Header ()
28 Assembly a = Assembly.GetExecutingAssembly ();
29 AssemblyName an = a.GetName ();
31 object [] att = a.GetCustomAttributes (typeof (AssemblyTitleAttribute), false);
32 string title = ((att.Length > 0) ? ((AssemblyTitleAttribute) att [0]).Title : "Mono MakeCert");
34 att = a.GetCustomAttributes (typeof (AssemblyCopyrightAttribute), false);
35 string copyright = ((att.Length > 0) ? ((AssemblyCopyrightAttribute) att [0]).Copyright : "");
37 Console.WriteLine ("{0} {1}", title, an.Version.ToString ());
38 Console.WriteLine ("{0}{1}", copyright, Environment.NewLine);
41 static private void Help ()
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);
55 static private void ExtendedHelp ()
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)");
72 static X509Certificate LoadCertificate (string filename)
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);
78 return new X509Certificate (rawcert);
81 static void WriteCertificate (string filename, byte[] rawcert)
83 FileStream fs = File.Open (filename, FileMode.Create, FileAccess.Write);
84 fs.Write (rawcert, 0, rawcert.Length);
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>";
90 static string defaultIssuer = "CN=Mono Test Root Agency";
91 static string defaultSubject = "CN=Poupou's-Software-Factory";
94 static int Main (string[] args)
96 if (args.Length < 1) {
98 Console.WriteLine ("ERROR: Missing output filename {0}", Environment.NewLine);
103 System.IFormatProvider format = new System.Globalization.CultureInfo("en-US", true);
104 string fileName = args [args.Length - 1];
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);
113 RSA issuerKey = (RSA)RSA.Create ();
114 issuerKey.FromXmlString (MonoTestRootAgency);
115 RSA subjectKey = (RSA)RSA.Create ();
117 bool selfSigned = false;
118 string hashName = "MD5";
120 CspParameters subjectParams = new CspParameters ();
121 CspParameters issuerParams = new CspParameters ();
122 BasicConstraintsExtension bce = null;
123 ExtendedKeyUsageExtension eku = null;
128 while (i < args.Length) {
129 switch (args [i++]) {
133 sn = BitConverter.GetBytes (Convert.ToInt32 (args [i++]));
136 // Subject Distinguish Name
137 subject = args [i++];
140 // (authenticode) commercial or individual
141 // CRITICAL KeyUsageRestriction extension
143 string usageRestriction = args [i++].ToLower ();
144 switch (usageRestriction) {
147 Console.WriteLine ("WARNING: Unsupported deprecated certification extension KeyUsageRestriction not included");
148 // Console.WriteLine ("WARNING: ExtendedKeyUsage for codesigning has been included.");
151 Console.WriteLine ("Unsupported restriction " + usageRestriction);
158 switch (args [i++].ToLower ()) {
166 Console.WriteLine ("Unsupported hash algorithm");
171 // Validity / notBefore
172 notBefore = DateTime.Parse (args [i++] + " 23:59:59", format);
175 // basic constraints - autority or end-entity
176 switch (args [i++].ToLower ()) {
179 bce = new BasicConstraintsExtension ();
180 bce.CertificateAuthority = true;
183 // do not include extension
187 Console.WriteLine ("ERROR: No more supported in X.509");
190 Console.WriteLine ("Unsupported certificate type");
195 // CN private extension ?
196 Console.WriteLine ("Unsupported option");
199 // Validity / notAfter
200 notAfter = DateTime.Parse (args [i++] + " 23:59:59", format);
203 // extendedKeyUsage extension
204 char[] sep = { ',' };
205 string[] purposes = args [i++].Split (sep);
207 eku = new ExtendedKeyUsageExtension ();
208 foreach (string purpose in purposes) {
209 eku.KeyPurpose.Add (purpose);
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.
218 bce = new BasicConstraintsExtension ();
219 bce.CertificateAuthority = true;
221 bce.PathLenConstraint = Convert.ToInt32 (args [i++]);
224 X509Certificate x509 = LoadCertificate (args [i++]);
225 issuer = x509.SubjectName;
232 PrivateKey pvk = PrivateKey.CreateFromFile (args [i++]);
237 // spcSpAgencyInfo private extension
238 Console.WriteLine ("Unsupported option");
241 // validity period (in months)
242 notAfter = notBefore.AddMonths (Convert.ToInt32 (args [i++]));
245 // Netscape's private extensions - NetscapeCertType
246 // BasicContraints - End Entity
247 Console.WriteLine ("Unsupported option");
253 // subject certificate ? renew ?
254 Console.WriteLine ("Unsupported option");
256 // Issuer CspParameters options
258 issuerParams.KeyContainerName = args [i++];
261 // select a key in the provider
262 string ikn = args [i++].ToLower ();
265 issuerParams.KeyNumber = 0;
268 issuerParams.KeyNumber = 1;
271 issuerParams.KeyNumber = Convert.ToInt32 (ikn);
276 issuerParams.ProviderName = args [i++];
279 switch (args [i++].ToLower ()) {
281 issuerParams.Flags = CspProviderFlags.UseMachineKeyStore;
284 issuerParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
287 Console.WriteLine ("Unknown key store for issuer");
292 Console.WriteLine ("Unsupported option");
295 issuerParams.ProviderType = Convert.ToInt32 (args [i++]);
297 // Subject CspParameters Options
299 subjectParams.KeyContainerName = args [i++];
302 // select a key in the provider
303 string skn = args [i++].ToLower ();
306 subjectParams.KeyNumber = 0;
309 subjectParams.KeyNumber = 1;
312 subjectParams.KeyNumber = Convert.ToInt32 (skn);
317 subjectParams.ProviderName = args [i++];
320 switch (args [i++].ToLower ()) {
322 subjectParams.Flags = CspProviderFlags.UseMachineKeyStore;
325 subjectParams.Flags = CspProviderFlags.UseDefaultKeyContainer;
328 Console.WriteLine ("Unknown key store for subject");
333 Console.WriteLine ("Unsupported option");
336 string pvkFile = args [i++];
337 if (File.Exists (pvkFile)) {
338 PrivateKey key = PrivateKey.CreateFromFile (pvkFile);
339 subjectKey = key.RSA;
342 PrivateKey key = new PrivateKey ();
343 key.RSA = subjectKey;
348 subjectParams.ProviderType = Convert.ToInt32 (args [i++]);
358 if (i != args.Length) {
359 Console.WriteLine ("ERROR: Unknown parameter");
367 // serial number MUST be positive
368 if ((sn [0] & 0x80) == 0x80)
372 if (subject != defaultSubject) {
374 issuerKey = subjectKey;
378 subjectKey = issuerKey;
383 throw new Exception ("Missing Subject Name");
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;
394 cb.Extensions.Add (bce);
396 cb.Extensions.Add (eku);
399 byte[] rawcert = cb.Sign (issuerKey);
400 WriteCertificate (fileName, rawcert);
401 Console.WriteLine ("Success");
404 catch (Exception e) {
405 Console.WriteLine ("ERROR: " + e.ToString ());