# Buggy code
def create_crl(
    path=None,
    text=False,
    signing_private_key=None,
    signing_cert=None,
    revoked=None,
    include_expired=False,
    days_valid=100,
):
    """
    Create a CRL

    :depends:   - PyOpenSSL Python module

    path:
        Path to write the crl to.

    text:
        If ``True``, return the PEM text without writing to a file. Default ``False``.

    signing_private_key:
        A path or string of the private key in PEM format that will be used to sign this crl.
        This is required.

    signing_cert:
        A certificate matching the private key that will be used to sign this crl. This is
        required.

    revoked:
        A list of dicts containing all the certificates to revoke. Each dict represents one
        certificate. A dict must contain either the key ``serial_number`` with the value of
        the serial number to revoke, or ``certificate`` with either the PEM encoded text of
        the certificate, or a path ot the certificate to revoke.

        The dict can optionally contain the ``revocation_date`` key. If this key is omitted
        the revocation date will be set to now. If should be a string in the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``not_after`` key. This is redundant if the
        ``certificate`` key is included. If the ``Certificate`` key is not included, this
        can be used for the logic behind the ``include_expired`` parameter.
        If should be a string in the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``reason`` key. This is the reason code for the
        revocation. Available choices are ``unspecified``, ``keyCompromise``, ``CACompromise``,
        ``affiliationChanged``, ``superseded``, ``cessationOfOperation`` and ``certificateHold``.

    include_expired:
        Include expired certificates in the CRL. Default is ``False``.

    days_valid:
        The number of days that the CRL should be valid. This sets the Next Update field in the CRL.

    .. note

        At this time the pyOpenSSL library does not allow choosing a signing algorithm for CRLs
        See https://github.com/pyca/pyopenssl/issues/159

    CLI Example:

    .. code-block:: bash

        salt '*' x509.create_crl path=/etc/pki/mykey.key signing_private_key=/etc/pki/ca.key \\
                signing_cert=/etc/pki/ca.crt \\
                revoked="{'compromized-web-key': {'certificate': '/etc/pki/certs/www1.crt', \\
                'revocation_date': '2015-03-01 00:00:00'}}"
    """
    # pyOpenSSL is required for dealing with CSLs. Importing inside these functions because
    # Client operations like creating CRLs shouldn't require pyOpenSSL
    # Note due to current limitations in pyOpenSSL it is impossible to specify a digest
    # For signing the CRL. This will hopefully be fixed soon: https://github.com/pyca/pyopenssl/pull/161
    import OpenSSL

    crl = OpenSSL.crypto.CRL()

    if revoked is None:
        revoked = []

    for rev_item in revoked:
        if "certificate" in rev_item:
            rev_cert = read_certificate(rev_item["certificate"])
            rev_item["serial_number"] = rev_cert["Serial Number"]
            rev_item["not_after"] = rev_cert["Not After"]

        serial_number = rev_item["serial_number"].replace(":", "")
        serial_number = str(int(serial_number, 16))

        if "not_after" in rev_item and not include_expired:
            not_after = datetime.datetime.strptime(
                rev_item["not_after"], "%Y-%m-%d %H:%M:%S"
            )
            if datetime.datetime.now() > not_after:
                continue

        if "revocation_date" not in rev_item:
            rev_item["revocation_date"] = datetime.datetime.now().strftime(
                "%Y-%m-%d %H:%M:%S"
            )

        rev_date = datetime.datetime.strptime(
            rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S"
        )
        rev_date = rev_date.strftime("%Y%m%d%H%M%SZ")

        rev = OpenSSL.crypto.Revoked()
        rev.set_serial(serial_number)
        rev.set_rev_date(rev_date)

        if "reason" in rev_item:
            rev.set_reason(rev_item["reason"])

        crl.add_revoked(rev)

    signing_cert = _text_or_file(signing_cert)
    cert = OpenSSL.crypto.load_certificate(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type="CERTIFICATE")
    )
    signing_private_key = _text_or_file(signing_private_key)
    key = OpenSSL.crypto.load_privatekey(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)
    )

    crltext = crl.export(cert, key, OpenSSL.crypto.FILETYPE_PEM, days=days_valid)

    if text:
        return crltext

    return write_pem(text=crltext, path=path, pem_type="X509 CRL")


# Traceback (X509Error)
[WARNING ] /usr/lib/python2.7/dist-packages/salt/grains/core.py:1493: DeprecationWarning: The "osmajorrelease" will be a type of an integer.

[ERROR   ] An un-handled exception was caught by salt's global exception handler:
X509Error: 140503767979776:error:0D0C40D8:asn1 encoding routines:c2i_ASN1_OBJECT:invalid object encoding:a_object.c:283:
140503767979776:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=algorithm, Type=X509_ALGOR
140503767979776:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=sig_alg, Type=X509_REQ
140503767979776:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:pem_oth.c:83:

Traceback (most recent call last):
File "/usr/bin/salt-call", line 11, in <module>
salt_call()
File "/usr/lib/python2.7/dist-packages/salt/scripts.py", line 352, in salt_call
client.run()
File "/usr/lib/python2.7/dist-packages/salt/cli/call.py", line 58, in run
caller.run()
File "/usr/lib/python2.7/dist-packages/salt/cli/caller.py", line 134, in run
ret = self.call()
File "/usr/lib/python2.7/dist-packages/salt/cli/caller.py", line 197, in call
ret['return'] = func(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/salt/modules/x509.py", line 500, in read_csr
csr = _get_request_obj(csr)
File "/usr/lib/python2.7/dist-packages/salt/modules/x509.py", line 321, in _get_request_obj
return M2Crypto.X509.load_request_string(text)
File "/usr/lib/python2.7/dist-packages/M2Crypto/X509.py", line 1040, in load_request_string
return load_request_bio(bio, format)
File "/usr/lib/python2.7/dist-packages/M2Crypto/X509.py", line 1024, in load_request_bio
raise X509Error(Err.get_error())
X509Error: 140503767979776:error:0D0C40D8:asn1 encoding routines:c2i_ASN1_OBJECT:invalid object encoding:a_object.c:283:
140503767979776:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=algorithm, Type=X509_ALGOR
140503767979776:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=sig_alg, Type=X509_REQ
140503767979776:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:pem_oth.c:83:
Traceback (most recent call last):
File "/usr/bin/salt-call", line 11, in <module>
salt_call()
File "/usr/lib/python2.7/dist-packages/salt/scripts.py", line 352, in salt_call
client.run()
File "/usr/lib/python2.7/dist-packages/salt/cli/call.py", line 58, in run
caller.run()
File "/usr/lib/python2.7/dist-packages/salt/cli/caller.py", line 134, in run
ret = self.call()
File "/usr/lib/python2.7/dist-packages/salt/cli/caller.py", line 197, in call
ret['return'] = func(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/salt/modules/x509.py", line 500, in read_csr
csr = _get_request_obj(csr)
File "/usr/lib/python2.7/dist-packages/salt/modules/x509.py", line 321, in _get_request_obj
return M2Crypto.X509.load_request_string(text)
File "/usr/lib/python2.7/dist-packages/M2Crypto/X509.py", line 1040, in load_request_string
return load_request_bio(bio, format)
File "/usr/lib/python2.7/dist-packages/M2Crypto/X509.py", line 1024, in load_request_bio
raise X509Error(Err.get_error())
M2Crypto.X509.X509Error: 140503767979776:error:0D0C40D8:asn1 encoding routines:c2i_ASN1_OBJECT:invalid object encoding:a_object.c:283:
140503767979776:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=algorithm, Type=X509_ALGOR
140503767979776:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=sig_alg, Type=X509_REQ
140503767979776:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:pem_oth.c:83:

# Fixed code
def create_crl(
    path=None,
    text=False,
    signing_private_key=None,
    signing_cert=None,
    revoked=None,
    include_expired=False,
    days_valid=100,
    digest="",
):
    """
    Create a CRL

    :depends:   - PyOpenSSL Python module

    path:
        Path to write the crl to.

    text:
        If ``True``, return the PEM text without writing to a file. Default ``False``.

    signing_private_key:
        A path or string of the private key in PEM format that will be used to sign this crl.
        This is required.

    signing_cert:
        A certificate matching the private key that will be used to sign this crl. This is
        required.

    revoked:
        A list of dicts containing all the certificates to revoke. Each dict represents one
        certificate. A dict must contain either the key ``serial_number`` with the value of
        the serial number to revoke, or ``certificate`` with either the PEM encoded text of
        the certificate, or a path ot the certificate to revoke.

        The dict can optionally contain the ``revocation_date`` key. If this key is omitted
        the revocation date will be set to now. If should be a string in the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``not_after`` key. This is redundant if the
        ``certificate`` key is included. If the ``Certificate`` key is not included, this
        can be used for the logic behind the ``include_expired`` parameter.
        If should be a string in the format "%Y-%m-%d %H:%M:%S".

        The dict can also optionally contain the ``reason`` key. This is the reason code for the
        revocation. Available choices are ``unspecified``, ``keyCompromise``, ``CACompromise``,
        ``affiliationChanged``, ``superseded``, ``cessationOfOperation`` and ``certificateHold``.

    include_expired:
        Include expired certificates in the CRL. Default is ``False``.

    days_valid:
        The number of days that the CRL should be valid. This sets the Next Update field in the CRL.
    digest:
        The digest to use for signing the CRL.
        This has no effect on versions of pyOpenSSL less than 0.14

    .. note

        At this time the pyOpenSSL library does not allow choosing a signing algorithm for CRLs
        See https://github.com/pyca/pyopenssl/issues/159

    CLI Example:

    .. code-block:: bash

        salt '*' x509.create_crl path=/etc/pki/mykey.key signing_private_key=/etc/pki/ca.key \\
                signing_cert=/etc/pki/ca.crt \\
                revoked="{'compromized-web-key': {'certificate': '/etc/pki/certs/www1.crt', \\
                'revocation_date': '2015-03-01 00:00:00'}}"
    """
    # pyOpenSSL is required for dealing with CSLs. Importing inside these functions because
    # Client operations like creating CRLs shouldn't require pyOpenSSL
    # For signing the CRL. This will hopefully be fixed soon: https://github.com/pyca/pyopenssl/pull/161
    import OpenSSL

    crl = OpenSSL.crypto.CRL()

    if revoked is None:
        revoked = []

    for rev_item in revoked:
        if "certificate" in rev_item:
            rev_cert = read_certificate(rev_item["certificate"])
            rev_item["serial_number"] = rev_cert["Serial Number"]
            rev_item["not_after"] = rev_cert["Not After"]

        serial_number = rev_item["serial_number"].replace(":", "")
        serial_number = str(int(serial_number, 16))

        if "not_after" in rev_item and not include_expired:
            not_after = datetime.datetime.strptime(
                rev_item["not_after"], "%Y-%m-%d %H:%M:%S"
            )
            if datetime.datetime.now() > not_after:
                continue

        if "revocation_date" not in rev_item:
            rev_item["revocation_date"] = datetime.datetime.now().strftime(
                "%Y-%m-%d %H:%M:%S"
            )

        rev_date = datetime.datetime.strptime(
            rev_item["revocation_date"], "%Y-%m-%d %H:%M:%S"
        )
        rev_date = rev_date.strftime("%Y%m%d%H%M%SZ")

        rev = OpenSSL.crypto.Revoked()
        rev.set_serial(serial_number)
        rev.set_rev_date(rev_date)

        if "reason" in rev_item:
            rev.set_reason(rev_item["reason"])

        crl.add_revoked(rev)

    signing_cert = _text_or_file(signing_cert)
    cert = OpenSSL.crypto.load_certificate(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_cert, pem_type="CERTIFICATE")
    )
    signing_private_key = _text_or_file(signing_private_key)
    key = OpenSSL.crypto.load_privatekey(
        OpenSSL.crypto.FILETYPE_PEM, get_pem_entry(signing_private_key)
    )

    try:
        crltext = crl.export(
            cert,
            key,
            OpenSSL.crypto.FILETYPE_PEM,
            days=days_valid,
            digest=bytes(digest),
        )
    except TypeError:
        log.warning(
            "Error signing crl with specified digest. Are you using pyopenssl 0.15 or newer? The default md5 digest will be used."
        )
        crltext = crl.export(cert, key, OpenSSL.crypto.FILETYPE_PEM, days=days_valid)

    if text:
        return crltext

    return write_pem(text=crltext, path=path, pem_type="X509 CRL")