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 }