#!/usr/bin/env python # -*- coding: utf-8 -*- # # Generate OpenVPN Certs # Stuart Morgan <stuart.morgan@mwrinfosecurity.com> @ukstufus # # This will generate a CA and certificate (signed by the CA) for mutual SSL/TLS # authentication. It is intended to be used with OpenVPN. It also generates # configuration files, keys and other related files needed to execute OpenVPN # with a minimum of hassle. # import os import sys import tarfile import subprocess from OpenSSL import crypto # Change this if you want to use DH parameters of a different size DH_PARAM_SIZE = 4096 def certerator_config(): server_ca = {} server_cert = {} client_ca = {} client_cert = {} # Server Certification Authority server_ca['commonName'] = "Server CA" #server_ca['stateOrProvinceName'] = "Hampshire" #server_ca['localityName'] = "Basingstoke" #server_ca['organizationName'] = "MWR InfoSecurity" #server_ca['organizationalUnitName'] = "Certification Authority" #server_ca['emailAddress'] = "labs@mwrinfosecurity.com" #server_ca['countryName'] = "GB" server_ca['cert_filename'] = "server_ca.pem" server_ca['cert_key'] = "server_ca.key" server_ca['serial'] = 12345999 server_ca['validfrom'] = "20160101000000Z" server_ca['validto'] = "20170101000000Z" server_ca['keyfilesize'] = 4096 server_ca['hashalgorithm'] = "sha512" # Server Certificate (signed by the CA above) server_cert['commonName'] = "Server Cert" #server_cert['stateOrProvinceName'] = "Hampshire" #server_cert['localityName'] = "Basingstoke" #server_cert['organizationName'] = "MWR InfoSecurity" #server_cert['organizationalUnitName'] = "Certification Authority" #server_cert['emailAddress'] = "labs@mwrinfosecurity.com" #server_cert['countryName'] = "GB" server_cert['cert_filename'] = "server_cert.pem" server_cert['cert_key'] = "server_cert.key" server_cert['serial'] = 12345888 server_cert['validfrom'] = "20160101000000Z" server_cert['validto'] = "20170101000000Z" server_cert['keyfilesize'] = 4096 server_cert['hashalgorithm'] = "sha512" # Client Certification Authority client_ca['commonName'] = "Client CA" #client_ca['stateOrProvinceName'] = "Hampshire" #client_ca['localityName'] = "Basingstoke" #client_ca['organizationName'] = "MWR InfoSecurity" #client_ca['organizationalUnitName'] = "Certification Authority" #client_ca['emailAddress'] = "labs@mwrinfosecurity.com" #client_ca['countryName'] = "GB" client_ca['cert_filename'] = "client_ca.pem" client_ca['cert_key'] = "client_ca.key" client_ca['serial'] = 12345777 client_ca['validfrom'] = "20160101000000Z" client_ca['validto'] = "20170101000000Z" client_ca['keyfilesize'] = 4096 client_ca['hashalgorithm'] = "sha512" # Client Certificate (signed by the CA above) client_cert['commonName'] = "Client Cert" #client_cert['stateOrProvinceName'] = "Hampshire" #client_cert['localityName'] = "Basingstoke" #client_cert['organizationName'] = "MWR InfoSecurity" #client_cert['organizationalUnitName'] = "Certification Authority" #client_cert['emailAddress'] = "labs@mwrinfosecurity.com" #client_cert['countryName'] = "GB" client_cert['cert_filename'] = "client_cert.pem" client_cert['cert_key'] = "client_cert.key" client_cert['serial'] = 12345666 client_cert['validfrom'] = "20160101000000Z" client_cert['validto'] = "20170101000000Z" client_cert['keyfilesize'] = 4096 client_cert['hashalgorithm'] = "sha512" return server_ca, server_cert, client_ca, client_cert def banner(): sys.stdout.write("\n") sys.stdout.write(" .mMMMMMm. MMm M WW W WW RRRRR\n") sys.stdout.write(" mMMMMMMMMMMM. MM MM W W W R R\n") sys.stdout.write(" /MMMM- -MM. MM MM W W W R R\n") sys.stdout.write(" /MMM. _ \/ ^ M M M M W W W W RRRR\n") sys.stdout.write(" |M. aRRr /W| M M M M W W W W R R\n") sys.stdout.write(" \/ .. ^^^ wWWW| M M M W W R R\n") sys.stdout.write(" /WW\. .wWWWW/ M M M W W R R\n") sys.stdout.write(" |WWWWWWWWWWW/\n") sys.stdout.write(" .WWWWWW. Server/Client Cert Generator (for OpenVPN)\n") sys.stdout.write(" stuart.morgan@mwrinfosecurity.com | @ukstufus\n") sys.stdout.write("\n") sys.stdout.flush() def openssl_generate_privatekey(size): key = crypto.PKey() key.generate_key(crypto.TYPE_RSA, size) return key def generate_ca(config_ca): ca = crypto.X509() ca.set_version(2) ca.set_serial_number(config_ca['serial']) ca_subj = ca.get_subject() if 'commonName' in config_ca: ca_subj.commonName = config_ca['commonName'] if 'stateOrProvinceName' in config_ca: ca_subj.stateOrProvinceName = config_ca['stateOrProvinceName'] if 'localityName' in config_ca: ca_subj.localityName = config_ca['localityName'] if 'organizationName' in config_ca: ca_subj.organizationName = config_ca['organizationName'] if 'organizationalUnitName' in config_ca: ca_subj.organizationalUnitName = config_ca['organizationalUnitName'] if 'emailAddress' in config_ca: ca_subj.emailAddress = config_ca['emailAddress'] if 'countryName' in config_ca: ca_subj.countryName = config_ca['countryName'] if 'validfrom' in config_ca: ca.set_notBefore(config_ca['validfrom']) if 'validto' in config_ca: ca.set_notAfter(config_ca['validto']) key = openssl_generate_privatekey(config_ca['keyfilesize']) ca.add_extensions([ crypto.X509Extension("basicConstraints", True, "CA:TRUE, pathlen:0"), crypto.X509Extension("keyUsage", False, "keyCertSign, cRLSign"), crypto.X509Extension("subjectKeyIdentifier", False, "hash", subject=ca), ]) ca.add_extensions([ crypto.X509Extension("authorityKeyIdentifier", False, "keyid:always",issuer=ca) ]) ca.set_issuer(ca.get_subject()) ca.set_pubkey(key) ca.sign(key, config_ca['hashalgorithm']) return ca, key def colourise(string,colour): return "\033["+colour+"m"+string+"\033[0m" def generate_certificate(config_cert, ca, cakey, name): # Generate the private key key = openssl_generate_privatekey(config_cert['keyfilesize']) # Generate the certificate request req = crypto.X509Req() req_subj = req.get_subject() if 'commonName' in config_cert: req_subj.commonName = config_cert['commonName'] if 'stateOrProvinceName' in config_cert: req_subj.stateOrProvinceName = config_cert['stateOrProvinceName'] if 'localityName' in config_cert: req_subj.localityName = config_cert['localityName'] if 'organizationName' in config_cert: req_subj.organizationName = config_cert['organizationName'] if 'organizationalUnitName' in config_cert: req_subj.organizationalUnitName = config_cert['organizationalUnitName'] if 'emailAddress' in config_cert: req_subj.emailAddress = config_cert['emailAddress'] if 'countryName' in config_cert: req_subj.countryName = config_cert['countryName'] req.set_pubkey(key) req.sign(key, config_cert['hashalgorithm']) # Now generate the certificate itself cert = crypto.X509() cert.set_version(2) cert.set_serial_number(config_cert['serial']) cert.set_subject(req.get_subject()) cert.set_pubkey(req.get_pubkey()) cert.set_issuer(ca.get_subject()) if 'validfrom' in config_cert: cert.set_notBefore(config_cert['validfrom']) if 'validto' in config_cert: cert.set_notAfter(config_cert['validto']) if name == 'client': usage = 'clientAuth' nscerttype = 'client' elif name == 'server': usage = 'serverAuth' nscerttype = 'server' else: sys.stdout.write("ERROR: Bad certificate type\n") sys.exit(1) cert.add_extensions([ crypto.X509Extension("basicConstraints", True, "CA:FALSE"), crypto.X509Extension("keyUsage", False, "digitalSignature,keyAgreement"), crypto.X509Extension("extendedKeyUsage", False, usage), crypto.X509Extension("nsCertType", False, nscerttype), crypto.X509Extension("subjectKeyIdentifier", False, "hash", subject=cert), crypto.X509Extension("authorityKeyIdentifier", False, "keyid:always", issuer=ca) ]) cert.sign(cakey, config_cert['hashalgorithm']) return req, cert, key def build_tar(filename,includedfiles): tar = tarfile.open(filename, "w:gz") for name in includedfiles: tar.add(name) tar.close() def build_ca(server_ca,name): if os.path.isfile(server_ca['cert_filename']) and os.path.isfile(server_ca['cert_key']): sys.stdout.write(colourise("Reusing "+server_ca['cert_filename']+" as the "+name+"\n",'0;36')) ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, file(server_ca['cert_filename']).read()) ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, file(server_ca['cert_key']).read()) sys.stdout.write(colourise(" "+name+" Fingerprint: "+ca_cert.digest('sha1')+"\n", '0;32')) else: sys.stdout.write(colourise("Generating new "+name+" CA...",'0;32')) sys.stdout.flush() ca_cert, ca_key = generate_ca(server_ca) sys.stdout.write(colourise("..done\n",'0;32')) open(server_ca['cert_filename'], "w").write(crypto.dump_certificate(crypto.FILETYPE_PEM, ca_cert)) open(server_ca['cert_key'], "w").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, ca_key)) sys.stdout.write(colourise(" Written PEM CA certificate to "+server_ca['cert_filename']+"\n", '0;32')) sys.stdout.write(colourise(" Written PEM CA key to "+server_ca['cert_key']+"\n", '0;32')) sys.stdout.write(colourise(" "+name+" Fingerprint: "+ca_cert.digest('sha1')+"\n", '0;32')) os.chmod(server_ca['cert_key'], 0600) return ca_cert, ca_key def build_cert(config_cert,ca_cert,ca_key,name): if os.path.isfile(config_cert['cert_filename']) and os.path.isfile(config_cert['cert_key']): sys.stdout.write(colourise("Reusing "+config_cert['cert_filename']+" as the "+name+" certificate\n",'0;36')) cert_cert = crypto.load_certificate(crypto.FILETYPE_PEM, file(config_cert['cert_filename']).read()) cert_key = crypto.load_privatekey(crypto.FILETYPE_PEM, file(config_cert['cert_key']).read()) sys.stdout.write(colourise(" SHA1 "+name+" Cert Fingerprint: "+cert_cert.digest('sha1')+"\n", '0;32')) else: sys.stdout.write(colourise("Generating new "+name+" certificate...",'0;32')) sys.stdout.flush() cert_req, cert_cert, cert_key = generate_certificate(config_cert,ca_cert,ca_key,name) sys.stdout.write(colourise("..done\n",'0;32')) open(config_cert['cert_filename'], "w").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert_cert)) open(config_cert['cert_key'], "w").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, cert_key)) sys.stdout.write(colourise(" Written PEM certificate to "+config_cert['cert_filename']+"\n", '0;32')) sys.stdout.write(colourise(" Written private key to "+config_cert['cert_key']+"\n", '0;32')) sys.stdout.write(colourise(" SHA1 "+name+" Cert Fingerprint: "+cert_cert.digest('sha1')+"\n", '0;32')) os.chmod(config_cert['cert_key'], 0600) return cert_cert, cert_key def run_cmd(cmd): popen = subprocess.Popen(cmd) popen.wait() def build_openssl_extra(): if os.path.isfile('ta.key'): sys.stdout.write(colourise("Reusing ta.key\n",'0;36')) else: sys.stdout.write(colourise("Generating new HMAC key...\n",'0;32')) run_cmd(['openvpn','--genkey','--secret','ta.key']) os.chmod('ta.key', 0600) sys.stdout.write("\n") if os.path.isfile('dh'+str(DH_PARAM_SIZE)+'.pem'): sys.stdout.write(colourise("Reusing dh"+str(DH_PARAM_SIZE)+".pem\n",'0;36')) else: sys.stdout.write(colourise("Generating DH params...\n",'0;32')) sys.stdout.write("\033[0;90m") sys.stdout.flush() run_cmd(['openssl','dhparam','-out','dh'+str(DH_PARAM_SIZE)+'.pem',str(DH_PARAM_SIZE)]) sys.stdout.write("\033[0;37m") sys.stdout.write("\n") sys.stdout.flush() if __name__ == "__main__": banner() try: # Gather all the configuration files config_server_ca, config_server_cert, config_client_ca, config_client_cert = certerator_config() # Build the Server and Client CA (if they do not already exist) server_ca_cert, server_ca_key = build_ca(config_server_ca,'Server') sys.stdout.write("\n") client_ca_cert, client_ca_key = build_ca(config_client_ca,'Client') sys.stdout.write("\n") # Build the server and client certificate (signed by the above CAs) build_cert(config_server_cert, server_ca_cert, server_ca_key, 'server') sys.stdout.write("\n") build_cert(config_client_cert, client_ca_cert, client_ca_key, 'client') sys.stdout.write("\n") # Now build ta.key and dh params if they do not already exist build_openssl_extra() sys.stdout.write("\n") # Now give instructions sys.stdout.write(colourise("On the OpenVPN Server:\n",'0;40')) sys.stdout.write(colourise("ca client_ca.pem\n",'0;32')) sys.stdout.write(colourise("cert server_cert.pem\n",'0;32')) sys.stdout.write(colourise("key server_cert.key\n",'0;32')) sys.stdout.write("\n") sys.stdout.write(colourise("On the OpenVPN Client:\n",'0;40')) sys.stdout.write(colourise("ca server_ca.pem\n",'0;32')) sys.stdout.write(colourise("cert client_cert.pem\n",'0;32')) sys.stdout.write(colourise("key client_cert.key\n",'0;32')) sys.stdout.write("\n") # Build the server config file server_config = open('example.server.conf', 'w') server_config.write("port 1194\n") server_config.write("proto udp\n") server_config.write("dev tun\n") server_config.write("ca client_ca.pem\n") server_config.write("cert server_cert.pem\n") server_config.write("key server_cert.key\n") server_config.write("dh dh"+str(DH_PARAM_SIZE)+".pem\n") server_config.write("server 10.255.255.0 255.255.255.0\n") server_config.write("topology net30\n") server_config.write("ifconfig-pool-persist ipp.txt\n") server_config.write("push \"redirect-gateway def1 bypass-dhcp\"\n") server_config.write("push \"dhcp-option DNS 8.8.8.8\"\n") server_config.write("push \"dhcp-option DNS 8.8.4.4\"\n") server_config.write("keepalive 10 120\n") server_config.write("tls-auth ta.key 0\n") server_config.write("cipher AES-128-CBC\n") server_config.write("comp-lzo\n") server_config.write("persist-key\n") server_config.write("persist-tun\n") server_config.write("#user openvpn\n") server_config.write("#group openvpn\n") server_config.write("status openvpn-status.log\n") server_config.close() # Build the client config file client_config = open('example.client.conf', 'w') client_config.write("client\n") client_config.write("dev tun\n") client_config.write("proto udp\n") client_config.write("remote SERVER 1194\n") client_config.write("resolv-retry infinite\n") client_config.write("nobind\n") client_config.write("#user openvpn\n") client_config.write("#group openvpn\n") client_config.write("persist-key\n") client_config.write("persist-tun\n") client_config.write("ca server_ca.pem\n") client_config.write("cert client_cert.pem\n") client_config.write("key client_cert.key\n") client_config.write("remote-cert-tls server\n") client_config.write("tls-auth ta.key 1\n") client_config.write("cipher AES-128-CBC\n") client_config.write("comp-lzo\n") client_config.write("#For HTTP proxy uncomment the below\n") client_config.write("#http-proxy-retry\n") client_config.write("#http-proxy HTTPPROXYSERVER HTTPPROXYPORT\n") client_config.write("#http-proxy-option AGENT Mozilla/5.0+(Windows;+U;+Windows+NT+5.0;+en-GB;+rv:1.7.6)+Gecko/20050226+Firefox/1.0.1\n") client_config.write("#or create a 2 line text file, username on first line, pass on second, call it userpass.txt\n") client_config.write("#http-proxy HTTPPROXYSERVER HTTPPROXYPORT userpass.txt_file basic\n") client_config.write("#http-proxy HTTPPROXYSERVER HTTPPROXYPORT userpass.txt_file ntlm\n") client_config.write("#socks-proxy SERVER PORT\n") client_config.close() # Inform the user sys.stdout.write(colourise("Example configs written to example.server.conf and example.client.conf\n", '0;32')) # Tar up the required files for the server and client build_tar('example.server.tar.gz', ['client_ca.pem','ta.key','dh'+str(DH_PARAM_SIZE)+'.pem','server_cert.pem','server_cert.key','example.server.conf']) build_tar('example.client.tar.gz', ['server_ca.pem','ta.key','client_cert.pem','client_cert.key','example.client.conf']) # Inform the user sys.stdout.write(colourise("\nServer configuration and related files are in example.server.tar.gz\n", '0;32')) sys.stdout.write(colourise("Client configuration and related files are in example.client.tar.gz\n", '0;32')) sys.stdout.write("\033[0;37m") sys.stdout.flush() sys.exit(0) except Exception as e: sys.stderr.write("Error: %s\n" % e) sys.exit(1)