# Buggy code
def create_ca_signed_cert(
    ca_name,
    CN,
    days=365,
    cacert_path=None,
    ca_filename=None,
    cert_path=None,
    cert_filename=None,
    digest="sha256",
):
    """
    Create a Certificate (CERT) signed by a named Certificate Authority (CA)

    If the certificate file already exists, the function just returns assuming
    the CERT already exists.

    The CN *must* match an existing CSR generated by create_csr. If it
    does not, this method does nothing.


    ca_name
        name of the CA

    CN
        common name matching the certificate signing request

    days
        number of days certificate is valid, default is 365 (1 year)

    cacert_path
        absolute path to ca certificates root directory

    ca_filename
        alternative filename for the CA

    cert_path
        full path to the certificates directory

    cert_filename
        alternative filename for the certificate, useful when using special characters in the CN

    digest
        The message digest algorithm. Must be a string describing a digest
        algorithm supported by OpenSSL (by EVP_get_digestbyname, specifically).
        For example, "md5" or "sha1". Default: 'sha256'

    If the following values were set:

    .. code-block:: text

        ca.cert_base_path='/etc/pki'
        ca_name='koji'
        CN='test.egavas.org'

    the resulting signed certificate would be written in the following
    location:

    .. code-block:: text

        /etc/pki/koji/certs/test.egavas.org.crt

    CLI Example:

    .. code-block:: bash

        salt '*' tls.create_ca_signed_cert test localhost
    """
    ret = {}

    set_ca_path(cacert_path)

    if not ca_filename:
        ca_filename = "{0}_ca_cert".format(ca_name)

    if not cert_path:
        cert_path = "{0}/{1}/certs".format(cert_base_path(), ca_name)

    if not cert_filename:
        cert_filename = CN

    if os.path.exists("{0}/{1}.crt".format(cert_path, cert_filename)):
        return 'Certificate "{0}" already exists'.format(cert_filename)

    try:
        maybe_fix_ssl_version(ca_name, ca_filename=ca_filename)
        with salt.utils.fopen(
            "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename)
        ) as fhr:
            ca_cert = OpenSSL.crypto.load_certificate(
                OpenSSL.crypto.FILETYPE_PEM, fhr.read()
            )
        with salt.utils.fopen(
            "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename)
        ) as fhr:
            ca_key = OpenSSL.crypto.load_privatekey(
                OpenSSL.crypto.FILETYPE_PEM, fhr.read()
            )
    except IOError:
        ret["retcode"] = 1
        ret["comment"] = 'There is no CA named "{0}"'.format(ca_name)
        return ret

    try:
        with salt.utils.fopen("{0}/{1}.csr".format(cert_path, cert_filename)) as fhr:
            req = OpenSSL.crypto.load_certificate_request(
                OpenSSL.crypto.FILETYPE_PEM, fhr.read()
            )
    except IOError:
        ret["retcode"] = 1
        ret["comment"] = 'There is no CSR that matches the CN "{0}"'.format(
            cert_filename
        )
        return ret

    exts = []
    try:
        # see: http://bazaar.launchpad.net/~exarkun/pyopenssl/master/revision/189
        # support is there from quite a long time, but without API
        # so we mimic the newly get_extensions method present in ultra
        # recent pyopenssl distros
        native_exts_obj = OpenSSL._util.lib.X509_REQ_get_extensions(req._req)
        for i in range(OpenSSL._util.lib.sk_X509_EXTENSION_num(native_exts_obj)):
            ext = OpenSSL.crypto.X509Extension.__new__(OpenSSL.crypto.X509Extension)
            ext._extension = OpenSSL._util.lib.sk_X509_EXTENSION_value(
                native_exts_obj, i
            )
            exts.append(ext)
    except Exception:
        log.error("Support for extensions is not available, upgrade PyOpenSSL")

    cert = OpenSSL.crypto.X509()
    cert.set_version(2)
    cert.set_subject(req.get_subject())
    cert.gmtime_adj_notBefore(0)
    cert.gmtime_adj_notAfter(int(days) * 24 * 60 * 60)
    if exts:
        cert.add_extensions(exts)
    cert.set_serial_number(_new_serial(ca_name, CN))
    cert.set_issuer(ca_cert.get_subject())
    cert.set_pubkey(req.get_pubkey())
    cert.sign(ca_key, digest)

    with salt.utils.fopen("{0}/{1}.crt".format(cert_path, cert_filename), "w+") as crt:
        crt.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))

    _write_cert_to_database(ca_name, cert)

    return ('Created Certificate for "{0}": "{1}/{2}.crt"').format(
        CN, cert_path, cert_filename
    )


# Traceback (ImportError)
# python -c 'import OpenSSL; print OpenSSL.__version__'
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named OpenSSL

# Fixed code
def create_ca_signed_cert(
    ca_name,
    CN,
    days=365,
    cacert_path=None,
    ca_filename=None,
    cert_path=None,
    cert_filename=None,
    digest="sha256",
):
    """
    Create a Certificate (CERT) signed by a named Certificate Authority (CA)

    If the certificate file already exists, the function just returns assuming
    the CERT already exists.

    The CN *must* match an existing CSR generated by create_csr. If it
    does not, this method does nothing.


    ca_name
        name of the CA

    CN
        common name matching the certificate signing request

    days
        number of days certificate is valid, default is 365 (1 year)

    cacert_path
        absolute path to ca certificates root directory

    ca_filename
        alternative filename for the CA

    cert_path
        full path to the certificates directory

    cert_filename
        alternative filename for the certificate, useful when using special characters in the CN

    digest
        The message digest algorithm. Must be a string describing a digest
        algorithm supported by OpenSSL (by EVP_get_digestbyname, specifically).
        For example, "md5" or "sha1". Default: 'sha256'

    If the following values were set:

    .. code-block:: text

        ca.cert_base_path='/etc/pki'
        ca_name='koji'
        CN='test.egavas.org'

    the resulting signed certificate would be written in the following
    location:

    .. code-block:: text

        /etc/pki/koji/certs/test.egavas.org.crt

    CLI Example:

    .. code-block:: bash

        salt '*' tls.create_ca_signed_cert test localhost
    """
    ret = {}

    set_ca_path(cacert_path)

    if not ca_filename:
        ca_filename = "{0}_ca_cert".format(ca_name)

    if not cert_path:
        cert_path = "{0}/{1}/certs".format(cert_base_path(), ca_name)

    if not cert_filename:
        cert_filename = CN

    if os.path.exists(
        os.path.join(
            os.path.sep.join(
                "{0}/{1}/certs/{2}.crt".format(
                    cert_base_path(), ca_name, cert_filename
                ).split("/")
            )
        )
    ):
        return 'Certificate "{0}" already exists'.format(cert_filename)

    try:
        maybe_fix_ssl_version(ca_name, ca_filename=ca_filename)
        with salt.utils.fopen(
            "{0}/{1}/{2}.crt".format(cert_base_path(), ca_name, ca_filename)
        ) as fhr:
            ca_cert = OpenSSL.crypto.load_certificate(
                OpenSSL.crypto.FILETYPE_PEM, fhr.read()
            )
        with salt.utils.fopen(
            "{0}/{1}/{2}.key".format(cert_base_path(), ca_name, ca_filename)
        ) as fhr:
            ca_key = OpenSSL.crypto.load_privatekey(
                OpenSSL.crypto.FILETYPE_PEM, fhr.read()
            )
    except IOError:
        ret["retcode"] = 1
        ret["comment"] = 'There is no CA named "{0}"'.format(ca_name)
        return ret

    try:
        with salt.utils.fopen("{0}/{1}.csr".format(cert_path, cert_filename)) as fhr:
            req = OpenSSL.crypto.load_certificate_request(
                OpenSSL.crypto.FILETYPE_PEM, fhr.read()
            )
    except IOError:
        ret["retcode"] = 1
        ret["comment"] = 'There is no CSR that matches the CN "{0}"'.format(
            cert_filename
        )
        return ret

    exts = []
    try:
        exts.extend(req.get_extensions())
        log.debug(
            "req.get_extensions() supported in pyOpenSSL {0}".format(
                OpenSSL.__dict__.get("__version__", "")
            )
        )
    except AttributeError:
        try:
            # see: http://bazaar.launchpad.net/~exarkun/pyopenssl/master/revision/189
            # support is there from quite a long time, but without API
            # so we mimic the newly get_extensions method present in ultra
            # recent pyopenssl distros
            log.info(
                "req.get_extensions() not supported in pyOpenSSL versions "
                "prior to 0.15. Switching to Dark Magic(tm) "
                " Your version: {0}".format(
                    OpenSSL.__dict__.get("__version__", "pre-2014")
                )
            )

            native_exts_obj = OpenSSL._util.lib.X509_REQ_get_extensions(req._req)
            for i in range(OpenSSL._util.lib.sk_X509_EXTENSION_num(native_exts_obj)):
                ext = OpenSSL.crypto.X509Extension.__new__(OpenSSL.crypto.X509Extension)
                ext._extension = OpenSSL._util.lib.sk_X509_EXTENSION_value(
                    native_exts_obj, i
                )
                exts.append(ext)
        except Exception:
            log.error(
                "X509 extensions are unsupported in pyOpenSSL "
                "versions prior to 0.14. Upgrade required. Current "
                "version: {0}".format(OpenSSL.__dict__.get("__version__", "pre-2014"))
            )

    cert = OpenSSL.crypto.X509()
    cert.set_version(2)
    cert.set_subject(req.get_subject())
    cert.gmtime_adj_notBefore(0)
    cert.gmtime_adj_notAfter(int(days) * 24 * 60 * 60)
    cert.set_serial_number(_new_serial(ca_name, CN))
    cert.set_issuer(ca_cert.get_subject())
    cert.set_pubkey(req.get_pubkey())

    cert.add_extensions(exts)

    cert.sign(ca_key, digest)

    with salt.utils.fopen("{0}/{1}.crt".format(cert_path, cert_filename), "w+") as crt:
        crt.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert))

    _write_cert_to_database(ca_name, cert)

    return ('Created Certificate for "{0}": "{1}/{2}.crt"').format(
        CN, cert_path, cert_filename
    )