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 Novell, Inc (http://www.novell.com)
12 using System
.Globalization
;
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
{
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
);
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
);
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";
96 static int Main (string[] args
)
98 if (args
.Length
< 1) {
100 Console
.WriteLine ("ERROR: Missing output filename {0}", Environment
.NewLine
);
105 string fileName
= args
[args
.Length
- 1];
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;
129 while (i
< args
.Length
) {
130 switch (args
[i
++]) {
134 sn
= BitConverter
.GetBytes (Convert
.ToInt32 (args
[i
++]));
137 // Subject Distinguish Name
138 subject
= args
[i
++];
141 // (authenticode) commercial or individual
142 // CRITICAL KeyUsageRestriction extension
144 string usageRestriction
= args
[i
++].ToLower ();
145 switch (usageRestriction
) {
148 Console
.WriteLine ("WARNING: Unsupported deprecated certification extension KeyUsageRestriction not included");
149 // Console.WriteLine ("WARNING: ExtendedKeyUsage for codesigning has been included.");
152 Console
.WriteLine ("Unsupported restriction " + usageRestriction
);
159 switch (args
[i
++].ToLower ()) {
167 Console
.WriteLine ("Unsupported hash algorithm");
172 // Validity / notBefore
173 notBefore
= DateTime
.Parse (args
[i
++] + " 23:59:59", CultureInfo
.InvariantCulture
);
176 // basic constraints - autority or end-entity
177 switch (args
[i
++].ToLower ()) {
180 bce
= new BasicConstraintsExtension ();
181 bce
.CertificateAuthority
= true;
184 // do not include extension
188 Console
.WriteLine ("ERROR: No more supported in X.509");
191 Console
.WriteLine ("Unsupported certificate type");
196 // CN private extension ?
197 Console
.WriteLine ("Unsupported option");
200 // Validity / notAfter
201 notAfter
= DateTime
.Parse (args
[i
++] + " 23:59:59", CultureInfo
.InvariantCulture
);
204 // extendedKeyUsage extension
205 char[] sep
= { ',' }
;
206 string[] purposes
= args
[i
++].Split (sep
);
208 eku
= new ExtendedKeyUsageExtension ();
209 foreach (string purpose
in purposes
) {
210 eku
.KeyPurpose
.Add (purpose
);
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.
219 bce
= new BasicConstraintsExtension ();
220 bce
.CertificateAuthority
= true;
222 bce
.PathLenConstraint
= Convert
.ToInt32 (args
[i
++]);
225 X509Certificate x509
= LoadCertificate (args
[i
++]);
226 issuer
= x509
.SubjectName
;
233 PrivateKey pvk
= PrivateKey
.CreateFromFile (args
[i
++]);
238 // spcSpAgencyInfo private extension
239 Console
.WriteLine ("Unsupported option");
242 // validity period (in months)
243 notAfter
= notBefore
.AddMonths (Convert
.ToInt32 (args
[i
++]));
246 // Netscape's private extensions - NetscapeCertType
247 // BasicContraints - End Entity
248 Console
.WriteLine ("Unsupported option");
254 // subject certificate ? renew ?
255 Console
.WriteLine ("Unsupported option");
257 // Issuer CspParameters options
259 issuerParams
.KeyContainerName
= args
[i
++];
262 // select a key in the provider
263 string ikn
= args
[i
++].ToLower ();
266 issuerParams
.KeyNumber
= 0;
269 issuerParams
.KeyNumber
= 1;
272 issuerParams
.KeyNumber
= Convert
.ToInt32 (ikn
);
277 issuerParams
.ProviderName
= args
[i
++];
280 switch (args
[i
++].ToLower ()) {
282 issuerParams
.Flags
= CspProviderFlags
.UseMachineKeyStore
;
285 issuerParams
.Flags
= CspProviderFlags
.UseDefaultKeyContainer
;
288 Console
.WriteLine ("Unknown key store for issuer");
293 Console
.WriteLine ("Unsupported option");
296 issuerParams
.ProviderType
= Convert
.ToInt32 (args
[i
++]);
298 // Subject CspParameters Options
300 subjectParams
.KeyContainerName
= args
[i
++];
303 // select a key in the provider
304 string skn
= args
[i
++].ToLower ();
307 subjectParams
.KeyNumber
= 0;
310 subjectParams
.KeyNumber
= 1;
313 subjectParams
.KeyNumber
= Convert
.ToInt32 (skn
);
318 subjectParams
.ProviderName
= args
[i
++];
321 switch (args
[i
++].ToLower ()) {
323 subjectParams
.Flags
= CspProviderFlags
.UseMachineKeyStore
;
326 subjectParams
.Flags
= CspProviderFlags
.UseDefaultKeyContainer
;
329 Console
.WriteLine ("Unknown key store for subject");
334 Console
.WriteLine ("Unsupported option");
337 string pvkFile
= args
[i
++];
338 if (File
.Exists (pvkFile
)) {
339 PrivateKey key
= PrivateKey
.CreateFromFile (pvkFile
);
340 subjectKey
= key
.RSA
;
343 PrivateKey key
= new PrivateKey ();
344 key
.RSA
= subjectKey
;
349 subjectParams
.ProviderType
= Convert
.ToInt32 (args
[i
++]);
359 if (i
!= args
.Length
) {
360 Console
.WriteLine ("ERROR: Unknown parameter");
368 // serial number MUST be positive
369 if ((sn
[0] & 0x80) == 0x80)
373 if (subject
!= defaultSubject
) {
375 issuerKey
= subjectKey
;
379 subjectKey
= issuerKey
;
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
;
395 cb
.Extensions
.Add (bce
);
397 cb
.Extensions
.Add (eku
);
400 byte[] rawcert
= cb
.Sign (issuerKey
);
401 WriteCertificate (fileName
, rawcert
);
402 Console
.WriteLine ("Success");
405 catch (Exception e
) {
406 Console
.WriteLine ("ERROR: " + e
.ToString ());