1 module app;
2 
3 import botan.constants;
4 version = X509;
5 import botan.cert.x509.certstor;
6 import botan.cert.x509.x509_crl;
7 import botan.cert.x509.x509self;
8 import botan.cert.x509.pkcs10;
9 import botan.rng.rng;
10 import botan.rng.auto_rng;
11 import botan.pubkey.pubkey;
12 import botan.pubkey.x509_key;
13 import botan.pubkey.pkcs8;
14 import botan.pubkey.algo.rsa;
15 import botan.pubkey.algo.ecdsa;
16 import memutils.unique;
17 import std.stdio;
18 import std.file;
19 import std.datetime;
20 
21 const string hash_fn = "SHA-256";
22 
23 // If CA Key is present, it will be loaded rather than created
24 const string ca_key_file = "ca.key";
25 const string ca_cert_file = "ca.crt";
26 
27 // The CA Certificate can be installed in a machine to trust other certificates signed by it
28 X509CertOptions caOpts()
29 {
30 	// Common Name/Country/Organization/OrgUnit
31 	X509CertOptions opts = X509CertOptions("***REMOVED***.com/CA/***REMOVED*** Team/Security");
32     
33     opts.uri = "http://***REMOVED***.com";
34     opts.dns = "app.***REMOVED***.com";
35     opts.email = "info@***REMOVED***.com";
36     opts.end = 15.yearsLater();
37     opts.CAKey(1);
38     
39     return opts;
40 }
41 
42 // User's signed certificate
43 const string key_file = "private.pem";
44 const string pub_file = "public.pem";
45 const string cert_file = "cert.crt";
46 const size_t signed_cert_expiration_years = 5; // years
47 
48 // The certificate request must be signed by a CA Certificate to inherit trust/authority and become a valid certificate
49 X509CertOptions reqOpts()
50 {
51     X509CertOptions opts = X509CertOptions("***REMOVED***.com/CA/***REMOVED*** Team/Application Admin Mgmnt");
52     
53     opts.uri = "https://app.***REMOVED***.com";
54     opts.dns = "app.***REMOVED***.com";
55     opts.email = "info@***REMOVED***.com";
56     
57     //opts.addExConstraint("PKIX.EmailProtection");
58     opts.addExConstraint("PKIX.ServerAuth");
59     //opts.addExConstraint("PKIX.ClientAuth");
60     //opts.addExConstraint("PKIX.CodeSigning");
61     return opts;
62 }
63 
64 X509Time yearsLater(size_t in_years)
65 {
66 	return X509Time(Clock.currTime.to!Date().add!"years"(in_years, AllowDayOverflow.no).toISOExtString());
67 }
68 
69 void main() {
70 	Unique!AutoSeededRNG rng = new AutoSeededRNG;
71 	
72 	string ca_key_pass_verif = "";
73 	string ca_key_pass = "";
74 	do {
75 		if (std.file.exists(ca_key_file)) {
76 			assert(std.file.exists(ca_cert_file), "Found CA Private Key but could not find CA Cert file.");
77 			writeln("Using saved CA Key/CA Cert");
78 		}
79 		stdout.writeln("Enter a password for the CA Private Key (default: '')");
80 		stdout.write("Password: ");
81 		ca_key_pass = stdin.readln();
82 		if (!std.file.exists(ca_key_file))
83 		{
84 			stdout.write("Verify: ");
85 			ca_key_pass_verif = stdin.readln();
86 		} else ca_key_pass_verif = ca_key_pass;
87 	} while (ca_key_pass_verif != ca_key_pass);
88 	ca_key_pass = ca_key_pass[0 .. $-1]; // remove \n
89 	
90 	string key_pass_verif = "";
91 	string key_pass = "";
92 	do {
93 		stdout.writeln("Enter a password for the Private Key (default: '')");
94 		stdout.write("Password: ");
95 		key_pass = stdin.readln();
96 		stdout.write("Verify: ");
97 		key_pass_verif = stdin.readln();
98 	} while (key_pass_verif != key_pass);
99 	key_pass = key_pass[0 .. $-1]; // remove \n
100     // Create the CA's key and self-signed cert
101     RSAPrivateKey ca_key;
102 	X509Certificate ca_cert;
103 	if (!std.file.exists(ca_key_file))
104 	{
105 		ca_key = RSAPrivateKey(*rng, 2048);	
106 		auto ca_key_enc = pkcs8.PEM_encode(cast(PrivateKey)*ca_key, *rng, ca_key_pass);		
107 		std.file.write(ca_key_file, ca_key_enc);		
108 		ca_cert = x509self.createSelfSignedCert(caOpts(), *ca_key, hash_fn, *rng);		
109 		auto ca_cert_enc = ca_cert.PEM_encode();		
110 		std.file.write(ca_cert_file, ca_cert_enc);
111 	}
112 	else {
113 		ca_key = RSAPrivateKey(loadKey(ca_key_file, *rng, ca_key_pass));
114 		ca_cert = X509Certificate(ca_cert_file);
115 	}
116     // Create user's key and cert request
117 	auto user_key = RSAPrivateKey(*rng, 1024).release();
118 	
119 	auto user_key_enc = pkcs8.PEM_encode(cast(PrivateKey)user_key, *rng, key_pass);
120 	auto user_pub_enc = x509_key.PEM_encode(cast(PublicKey)user_key);
121 	
122 	std.file.write(key_file, user_key_enc);
123 	std.file.write(pub_file, user_pub_enc);
124 	
125     PKCS10Request sign_req = x509self.createCertReq(reqOpts(), user_key, hash_fn, *rng);
126     
127     // Create the CA object
128     X509CA ca = X509CA(ca_cert, *ca_key, hash_fn);
129     
130     // Sign the requests with the CA object to create the cert
131     X509Certificate user_cert = ca.signRequest(sign_req, *rng, X509Time(Clock.currTime().to!Date().toISOExtString()), signed_cert_expiration_years.yearsLater());
132    
133 	std.file.write(cert_file, user_cert.PEM_encode());
134 	
135 	writeln(user_cert.toString());
136 }