def _init_ca(config): """ Generate the CA certificate :param config: :return: """ # Write the database f = open("{0!s}/index.txt".format(config.directory), "w") f.write("") f.close() # Write the serial file f = open("{0!s}/serial".format(config.directory), "w") f.write("1000") f.close() # create the privacy key and set accesss rights f = open("{0!s}/cakey.pem".format(config.directory), "w") f.write("") f.close() import stat os.chmod("{0!s}/cakey.pem".format(config.directory), stat.S_IRUSR | stat.S_IWUSR) command = "openssl genrsa -out {0!s}/cakey.pem {1!s}".format( config.directory, config.keysize) print("Running command...") print(command) args = shlex.split(command) p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=config.directory, universal_newlines=True) result, error = p.communicate() if p.returncode != 0: # pragma: no cover # Some error occurred raise CAError(error) # create the CA certificate command = """openssl req -config openssl.cnf -key cakey.pem \ -new -x509 -days {ca_days!s} -sha256 -extensions v3_ca \ -out cacert.pem -subj {ca_dn!s}""".format(ca_days=config.validity_ca, ca_dn=config.dn) print("Running command...") print(command) args = shlex.split(command) p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=config.directory, universal_newlines=True) result, error = p.communicate() if p.returncode != 0: # pragma: no cover # Some error occurred raise CAError(error) print("!" * 60) print("Please check the ownership of the private key") print("{0!s}/cakey.pem".format(config.directory)) print("!" * 60)
def revoke_cert(self, certificate, reason=CRL_REASONS[0]): """ Revoke the specified certificate. At this point only the database index.txt is updated. :param certificate: The certificate to revoke :type certificate: Either takes X509 object or a PEM encoded certificate (string) :param reason: One of the available reasons the certificate gets revoked :type reason: basestring :return: Returns the serial number of the revoked certificate. Otherwise an error is raised. """ if isinstance(certificate, string_types): cert_obj = crypto.load_certificate(crypto.FILETYPE_PEM, certificate) elif type(certificate) == crypto.X509: cert_obj = certificate else: raise CAError("Certificate in unsupported format") serial = cert_obj.get_serial_number() serial_hex = int_to_hex(serial) filename = serial_hex + ".pem" cmd = CA_REVOKE.format( cakey=self.cakey, cacert=self.cacert, config=self.config.get(ATTR.OPENSSL_CNF), certificate="/".join( p for p in [self.config.get(ATTR.CERT_DIR), filename] if p), reason=reason) workingdir = self.config.get(ATTR.WORKING_DIR) args = shlex.split(cmd) p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=workingdir, universal_newlines=True) result, error = p.communicate() if p.returncode != 0: # pragma: no cover # Some error occurred raise CAError(error) return serial_hex
def create_crl(self, publish=True, check_validity=False): """ Create and Publish the CRL. :param publish: Whether the CRL should be published at its CDPs :param check_validity: Onle create a new CRL, if the old one is about to expire. Therfore the overlap period and the remaining runtime of the CRL is checked. If the remaining runtime is smaller than the overlap period, we recreate the CRL. :return: the CRL location or None, if no CRL was created """ crl = self.config.get(ATTR.CRL, "crl.pem") workingdir = self.config.get(ATTR.WORKING_DIR) create_new_crl = True ret = None # Check if we need to create a new CRL if check_validity: if crl.startswith("/"): full_path_crl = crl else: full_path_crl = workingdir + "/" + crl next_update = _get_crl_next_update(full_path_crl) if datetime.datetime.now() + \ datetime.timedelta(days=self.overlap) > next_update: log.info("We checked the overlap period and we need to create " "the new CRL.") else: log.info("No need to create a new CRL, yet. Next Update: " "{0!s}, overlap: {1!s}".format( next_update, self.overlap)) create_new_crl = False if create_new_crl: cmd = CA_GENERATE_CRL.format(cakey=self.cakey, cacert=self.cacert, config=self.config.get( ATTR.OPENSSL_CNF), CRL=crl) args = shlex.split(cmd) p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=workingdir, universal_newlines=True) result, error = p.communicate() if p.returncode != 0: # pragma: no cover # Some error occurred raise CAError(error) ret = crl return ret
def sign_request(self, csr, options=None): """ Signs a certificate request with the key of the CA. options can be WorkingDir: The directory where the configuration like openssl.cnf can be found. CSRDir: The directory, where to save the certificate signing requests. This is relative to the WorkingDir. CertificateDir: The directory where to save the certificates. This is relative to the WorkingDir. :param csr: Certificate signing request :type csr: PEM string or SPKAC :param options: Additional options like the validity time or the template or spkac=1 :type options: dict :return: Returns the certificate object :rtype: X509 """ # Sign the certificate for one year options = options or {} days = options.get("days", 365) spkac = options.get("spkac") config = options.get( ATTR.OPENSSL_CNF, self.config.get(ATTR.OPENSSL_CNF, "/etc/ssl/openssl.cnf")) extension = options.get("extension", "server") template_name = options.get("template") workingdir = options.get(ATTR.WORKING_DIR, self.config.get(ATTR.WORKING_DIR)) csrdir = options.get(ATTR.CSR_DIR, self.config.get(ATTR.CSR_DIR, "")) certificatedir = options.get(ATTR.CERT_DIR, self.config.get(ATTR.CERT_DIR, "")) if workingdir: if not csrdir.startswith("/"): # No absolut path csrdir = workingdir + "/" + csrdir if not certificatedir.startswith("/"): certificatedir = workingdir + "/" + certificatedir if template_name: t_data = self.templates.get(template_name) extension = t_data.get("extensions", extension) days = t_data.get("days", days) # Determine filename from the CN of the request if spkac: common_name = re.search("CN=(.*)", csr).group(0).split('=')[1] csr_filename = common_name + ".txt" certificate_filename = common_name + ".der" else: csr_obj = crypto.load_certificate_request(crypto.FILETYPE_PEM, csr) csr_filename = self._filename_from_x509(csr_obj.get_subject(), file_extension="req") certificate_filename = self._filename_from_x509( csr_obj.get_subject(), file_extension="pem") #csr_extensions = csr_obj.get_extensions() csr_filename = csr_filename.replace(" ", "_") certificate_filename = certificate_filename.replace(" ", "_") # dump the file csr_filename = to_unicode(csr_filename.encode('ascii', 'ignore')) with open(os.path.join(csrdir, csr_filename), "w") as f: f.write(csr) # TODO: use the template name to set the days and the extension! if spkac: cmd = CA_SIGN_SPKAC.format( cakey=self.cakey, cacert=self.cacert, days=days, config=config, extension=extension, spkacfile=os.path.join(csrdir, csr_filename), certificate=os.path.join(certificatedir, certificate_filename)) else: cmd = CA_SIGN.format(cakey=self.cakey, cacert=self.cacert, days=days, config=config, extension=extension, csrfile=os.path.join(csrdir, csr_filename), certificate=os.path.join( certificatedir, certificate_filename)) # run the command args = shlex.split(cmd) p = Popen(args, stdout=PIPE, stderr=PIPE, cwd=workingdir, universal_newlines=True) result, error = p.communicate() if p.returncode != 0: # pragma: no cover # Some error occurred raise CAError(error) with open(os.path.join(certificatedir, certificate_filename), "rb") as f: certificate = f.read() # We return the cert_obj. if spkac: filetype = crypto.FILETYPE_ASN1 else: filetype = crypto.FILETYPE_PEM cert_obj = crypto.load_certificate(filetype, certificate) return cert_obj
def _check_attributes(self): for req_key in [ATTR.CAKEY, ATTR.CACERT]: if req_key not in self.config: raise CAError( "required argument '{0!s}' is missing.".format(req_key))
def _check_attributes(self): if "cakey" not in self.config: raise CAError("required argument 'cakey' is missing.") if "cacert" not in self.config: raise CAError("required argument 'cacert' is missing.")