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_private_key_passphrase:
        Passphrase to decrypt the private key.

    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 to 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'}}"

def create_crl(  # pylint: disable=too-many-arguments,too-many-locals
        path=None, text=False, signing_private_key=None,
        signing_private_key_passphrase=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_private_key_passphrase:
        Passphrase to decrypt the private key.

    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 to 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 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
    if OpenSSL is None:
        raise salt.exceptions.SaltInvocationError(
            'Could not load OpenSSL module, OpenSSL unavailable'
        )
    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(':', '')
        # OpenSSL bindings requires this to be a non-unicode string
        serial_number = salt.utils.stringutils.to_bytes(serial_number)

        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_date = salt.utils.stringutils.to_bytes(rev_date)

        rev = OpenSSL.crypto.Revoked()
        rev.set_serial(salt.utils.stringutils.to_bytes(serial_number))
        rev.set_rev_date(salt.utils.stringutils.to_bytes(rev_date))

        if 'reason' in rev_item:
            # Same here for OpenSSL bindings and non-unicode strings
            reason = salt.utils.stringutils.to_str(rev_item['reason'])
            rev.set_reason(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 = _get_private_key_obj(signing_private_key,
                                               passphrase=signing_private_key_passphrase).as_pem(cipher=None)
    key = OpenSSL.crypto.load_privatekey(
        OpenSSL.crypto.FILETYPE_PEM,
        get_pem_entry(signing_private_key))

    export_kwargs = {
        'cert': cert,
        'key': key,
        'type': OpenSSL.crypto.FILETYPE_PEM,
        'days': days_valid
    }
    if digest:
        export_kwargs['digest'] = salt.utils.stringutils.to_bytes(digest)
    else:
        log.warning('No digest specified. The default md5 digest will be used.')

    try:
        crltext = crl.export(**export_kwargs)
    except (TypeError, ValueError):
        log.warning('Error signing crl with specified digest. '
                    'Are you using pyopenssl 0.15 or newer? '
                    'The default md5 digest will be used.')
        export_kwargs.pop('digest', None)
        crltext = crl.export(**export_kwargs)

    if text:
        return crltext

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