def new_opensslconf(): """ Create new openssl.conf configuration file. """ # print "new_opensslconf" _log.debug("__init__::new_opensslconf") for section in self.__class__.DEFAULT.keys(): self.config.add_section(section) # print "[{}]".format(section) hostname = socket.gethostname() for option in self.__class__.DEFAULT[section]: if option == "0.organizationName": value = self.domain #TODO: use dynamic number of DNS entries instead of hardcoding the number elif option == "DNS.1": value = self.node_name elif (option == "DNS.2") and len(self.hostnames) > 0: value = self.hostnames[0] elif (option == "DNS.3") and len(self.hostnames) > 1: value = self.hostnames[1] elif (option == "DNS.4") and len(self.hostnames) > 2: value = self.hostnames[2] elif option == "IP.1": value = self.ip elif option == "dir": value = self.runtime_dir elif section == 'req_distinguished_name' and option == 'commonName': value = self.node_name elif option == 'dnQualifier': value = self.node_id #The python cryptography and the pyOpensSSL packages does not support #parsing the Attributes extension in a CSR, so instead it is stored #outside of the CSR # elif option == 'challengePassword': # value = self.enrollment_password else: value = self.__class__.DEFAULT[section][option] # print "\t{}={}".format(option, value) self.config.set(section, option, value) with open(self.configfile, 'wb') as configfd: self.config.write(configfd) configfd.close() confsort.reorder(self.configfile)
def _new_opensslconf(self): """ Create new openssl.conf configuration file. """ # print "new_opensslconf" _log.debug("__init__::new_opensslconf") for section in self.__class__.DEFAULT.keys(): self.config.add_section(section) # print "[{}]".format(section) hostname = socket.gethostname() for option in self.__class__.DEFAULT[section]: if option == "0.organizationName": value = self.domain #TODO: use dynamic number of DNS entries instead of hardcoding the number elif option == "DNS.1": value = self.node_name elif (option == "DNS.2") and len(self.hostnames)>0: value = self.hostnames[0] elif (option == "DNS.3") and len(self.hostnames)>1: value = self.hostnames[1] elif (option == "DNS.4") and len(self.hostnames)>2: value = self.hostnames[2] elif option == "IP.1": value = self.ip elif option == "dir": value = self.runtime_dir elif section == 'req_distinguished_name' and option == 'commonName': value = self.node_name elif option == 'dnQualifier': value = self.node_id #The python cryptography and the pyOpensSSL packages does not support #parsing the Attributes extension in a CSR, so instead it is stored #outside of the CSR # elif option == 'challengePassword': # value = self.enrollment_password else: value = self.__class__.DEFAULT[section][option] # print "\t{}={}".format(option, value) self.config.set(section, option, value) with open(self.configfile, 'wb') as configfd: self.config.write(configfd) configfd.close() confsort.reorder(self.configfile)
class CA(): """ A openssl.conf configuration parser class. Create this object by pointing at the configuration file to be parsed. To access a previously known openssl configuration file. myconf = CA(configfile="/tmp/openssl.conf") or to create a new domain: myconf = CA(domain="mydomain") to access an existing known domain configuration use: myconf = CA(domain="myolddomain") """ DEFAULT = { 'v3_req': { 'subjectAltName': 'email:move' }, 'req': { 'distinguished_name': 'req_distinguished_name', 'attributes': 'req_attributes', 'prompt': 'no', 'default_keyfile': 'privkey.pem', 'default_bits': '2048' }, 'req_attributes': {}, 'req_distinguished_name': { '0.organizationName': 'domain', 'commonName': 'runtime' }, 'ca': { 'default_ca': 'CA_default' }, 'CA_default': { 'dir': '~/.calvin/security/', 'preserve': 'no', 'crl_dir': '$dir/crl', 'RANDFILE': '$dir/private/.rand', 'certificate': '$dir/cacert.pem', 'database': '$dir/index.txt', 'private_dir': '$dir/private/', 'new_certs_dir': '$dir/newcerts', 'private_key': '$dir/private/ca.key', 'runtimes_dir': '$dir/runtimes', 'email_in_dn': 'no', 'x509_extensions': 'usr_cert', 'copy_extensions': 'copy', 'certs': '$dir/certs', 'default_days': '365', 'policy': 'policy_any', 'cert_opt': 'ca_default', 'serial': '$dir/serial', 'default_crl_days': '30', 'name_opt': 'ca_default', 'crl': '$dir/crl.pem', 'default_md': 'sha256' }, 'v3_ca': { 'basicConstraints': 'CA:true', 'subjectKeyIdentifier': 'hash', 'authorityKeyIdentifier': 'keyid:always,issuer:always' }, 'usr_cert': { 'subjectKeyIdentifier': 'hash', 'authorityKeyIdentifier': 'keyid,issuer', 'basicConstraints': 'CA:false' }, 'policy_any': { 'countryName': 'optional', 'organizationalUnitName': 'optional', 'organizationName': 'supplied', # match 'emailAddress': 'optional', 'commonName': 'supplied', 'dnQualifier': 'optional', 'stateOrProvinceName': 'optional' } } # TODO Find out why the policy does not match equal org names. def __init__(self, domain, commonName=None, security_dir=None, force=False, readonly=False): _log.debug("__init__") self.configfile = None self.commonName = commonName or 'runtime' self.config = ConfigParser.SafeConfigParser() self.config.optionxform = str self.enrollment_challenge_db = {} os.umask(0077) self.domain = domain _log.debug("CA init") security_path = _conf.get("security", "security_dir") if security_dir: self.configfile = os.path.join(security_dir, domain, "openssl.conf") elif security_path: self.configfile = os.path.join(security_path, domain, "openssl.conf") else: homefolder = get_home() self.configfile = os.path.join(homefolder, ".calvin", "security", domain, "openssl.conf") exist = os.path.isfile(self.configfile) if not exist and readonly: raise Exception( "Configuration file does not exist, create CA first") if exist and not force: self.configuration = self.parse_opensslconf() _log.debug("Configuration already exists") # print "Configuration already exists " \ # "using {}".format(self.configfile) else: _log.debug("Configuration does not exist, let's create CA") self.new_opensslconf() self.configuration = self.parse_opensslconf() #Generate keys and CA certiticate try: self.new_ca_credentials(security_dir=security_dir, force=False, readonly=False) except: _log.error("Creation of new CA credentials failed") raise # print "Made new configuration at " \ # "{}".format(self.configfile) self.cert_enrollment_update_db_file() def new_ca_credentials(self, security_dir=None, force=False, readonly=False): """ Generate keys, files and certificate for the new CA """ from os import urandom _log.debug("new_ca_credentials") outpath = self.configuration["CA_default"]["new_certs_dir"] private = self.configuration["CA_default"]["private_dir"] crlpath = self.configuration["CA_default"]["crl_dir"] private_key = self.configuration["CA_default"]["private_key"] out = self.configuration["CA_default"]["certificate"] password_file = os.path.join(private, "ca_password") os.umask(0077) try: os.mkdir(crlpath, 0700) except OSError: pass try: os.mkdir(outpath, 0700) except OSError: pass try: os.mkdir(private, 0700) except OSError: pass certificate.touch(self.configuration["CA_default"]["database"]) serialfd = open(self.configuration["CA_default"]["serial"], 'w') serialfd.write("1000") serialfd.close() organization = self.domain commonname = self.commonName subject = "/O={}/CN={}".format(organization, commonname) # Generate a password for protection of the private key, # store it in the password file password = self.generate_password(20) try: with open(password_file, 'w') as fd: fd.write(password) except Exception as err: _log.err("Failed to write CA password to file") raise # Generate keys and CA certificate log = subprocess.Popen([ "openssl", "req", "-new", "-config", self.configfile, "-x509", "-days", "1825", "-utf8", "-subj", subject, "-passout", "file:{}".format(password_file), "-out", out, "-keyout", private_key ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = log.communicate() _log.debug("new_ca_credentials") if log.returncode != 0: _log.error("CA req failed") raise IOError(stderr) return def new_opensslconf(self): """ Create new openssl.conf configuration file. """ directory = os.path.dirname(self.configfile) for section in self.__class__.DEFAULT.keys(): self.config.add_section(section) for option in self.__class__.DEFAULT[section]: if option == "0.organizationName": value = self.domain elif option == "dir": value = directory elif section == 'req_distinguished_name' and option == 'commonName': value = self.commonName else: value = self.__class__.DEFAULT[section][option] self.config.set(section, option, value) try: os.makedirs(directory, 0700) except OSError, e: _log.error("Failed to create directory, err={}".format(e)) with open(self.configfile, 'wb') as configfd: self.config.write(configfd) configfd.close() confsort.reorder(self.configfile)