# Buggy code
def create_certificate(path=None, text=False, ca_server=None, **kwargs):
    """
    Create an X509 certificate.

    path:
        Path to write the certificate to.

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

    kwargs:
        Any of the properties below can be included as additional keyword arguments.

    ca_server:
        Request a remotely signed certificate from ca_server. For this to work, a ``signing_policy`` must
        be specified, and that same policy must be configured on the ca_server. See ``signing_policy`` for
        details. Also the salt master must permit peers to call the ``sign_remote_certificate`` function.

        Example:

        /etc/salt/master.d/peer.conf

        .. code-block:: yaml

            peer:
              .*:
                - x509.sign_remote_certificate

    subject properties:
        Any of the values below can be incldued to set subject properties
        Any other subject properties supported by OpenSSL should also work.

        C:
            2 letter Country code
        CN:
            Certificate common name, typically the FQDN.

        Email:
            Email address

        GN:
            Given Name

        L:
            Locality

        O:
            Organization

        OU:
            Organization Unit

        SN:
            SurName

        ST:
            State or Province

    signing_private_key:
        A path or string of the private key in PEM format that will be used to sign this certificate.
        If neither ``signing_cert``, ``public_key``, or ``csr`` are included, it will be assumed that
        this is a self-signed certificate, and the public key matching ``signing_private_key`` will
        be used to create the certificate.

    signing_cert:
        A certificate matching the private key that will be used to sign this certificate. This is used
        to populate the issuer values in the resulting certificate. Do not include this value for
        self-signed certificates.

    public_key:
        The public key to be included in this certificate. This can be sourced from a public key,
        certificate, csr or private key. If a private key is used, the matching public key from
        the private key will be generated before any processing is done. This means you can request a
        certificate from a remote CA using a private key file as your public_key and only the
        public key will be sent across the network to the CA.
        If neither ``public_key`` or ``csr`` are
        specified, it will be assumed that this is a self-signed certificate, and the public key
        derived from ``signing_private_key`` will be used. Specify either ``public_key`` or ``csr``,
        not both. Because you can input a CSR as a public key or as a CSR, it is important to understand
        the difference. If you import a CSR as a public key, only the public key will be added
        to the certificate, subject or extension information in the CSR will be lost.

    csr:
        A file or PEM string containing a certificate signing request. This will be used to supply the
        subject, extensions and public key of a certificate. Any subject or extensions specified
        explicitly will overwrite any in the CSR.

    basicConstraints:
        X509v3 Basic Constraints extension.

    extensions:
        The following arguments set X509v3 Extension values. If the value starts with ``critical ``,
        the extension will be marked as critical.

        Some special extensions are ``subjectKeyIdentifier`` and ``authorityKeyIdentifier``.

        ``subjectKeyIdentifier`` can be an explicit value or it can be the special string ``hash``.
        ``hash`` will set the subjectKeyIdentifier equal to the SHA1 hash of the modulus of the
        public key in this certificate. Note that this is not the exact same hashing method used by
        OpenSSL when using the hash value.

        ``authorityKeyIdentifier`` Use values acceptable to the openssl CLI tools. This will
        automatically populate ``authorityKeyIdentifier`` with the ``subjectKeyIdentifier`` of
        ``signing_cert``. If this is a self-signed cert these values will be the same.

        basicConstraints:
            X509v3 Basic Constraints

        keyUsage:
            X509v3 Key Usage

        extendedKeyUsage:
            X509v3 Extended Key Usage

        subjectKeyIdentifier:
            X509v3 Subject Key Identifier

        issuerAltName:
            X509v3 Issuer Alternative Name

        subjectAltName:
            X509v3 Subject Alternative Name

        crlDistributionPoints:
            X509v3 CRL distribution points

        issuingDistributionPoint:
            X509v3 Issuing Distribution Point

        certificatePolicies:
            X509v3 Certificate Policies

        policyConstraints:
            X509v3 Policy Constraints

        inhibitAnyPolicy:
            X509v3 Inhibit Any Policy

        nameConstraints:
            X509v3 Name Constraints

        noCheck:
            X509v3 OCSP No Check

        nsComment:
            Netscape Comment

        nsCertType:
            Netscape Certificate Type

    days_valid:
        The number of days this certificate should be valid. This sets the ``notAfter`` property
        of the certificate. Defaults to 365.

    version:
        The version of the X509 certificate. Defaults to 3. This is automatically converted to the
        version value, so ``version=3`` sets the certificate version field to 0x2.

    serial_number:
        The serial number to assign to this certificate. If omitted a random serial number of size
        ``serial_bits`` is generated.

    serial_bits:
        The number of bits to use when randomly generating a serial number. Defaults to 64.

    algorithm:
        The hashing algorithm to be used for signing this certificate. Defaults to sha256.

    copypath:
        An additional path to copy the resulting certificate to. Can be used to maintain a copy
        of all certificates issued for revocation purposes.

    signing_policy:
        A signing policy that should be used to create this certificate. Signing policies should be defined
        in the minion configuration, or in a minion pillar. It should be a yaml formatted list of arguments
        which will override any arguments passed to this function. If the ``minions`` key is included in
        the signing policy, only minions matching that pattern will be permitted to remotely request certificates
        from that policy.

        Example:

        .. code-block:: yaml

            x509_signing_policies:
              www:
                - minions: 'www*'
                - signing_private_key: /etc/pki/ca.key
                - signing_cert: /etc/pki/ca.crt
                - C: US
                - ST: Utah
                - L: Salt Lake City
                - basicConstraints: "critical CA:false"
                - keyUsage: "critical cRLSign, keyCertSign"
                - subjectKeyIdentifier: hash
                - authorityKeyIdentifier: keyid,issuer:always
                - days_valid: 90
                - copypath: /etc/pki/issued_certs/

        The above signing policy can be invoked with ``signing_policy=www``

    CLI Example:

    .. code-block:: bash

        salt '*' x509.create_certificate path=/etc/pki/myca.crt \\
        signing_private_key='/etc/pki/myca.key' csr='/etc/pki/myca.csr'}
    """

    if (
        not path
        and not text
        and ("testrun" not in kwargs or kwargs["testrun"] is False)
    ):
        raise salt.exceptions.SaltInvocationError(
            "Either path or text must be specified."
        )
    if path and text:
        raise salt.exceptions.SaltInvocationError(
            "Either path or text must be specified, not both."
        )

    if ca_server:
        if "signing_policy" not in kwargs:
            raise salt.exceptions.SaltInvocationError(
                "signing_policy must be specified"
                "if requesting remote certificate from ca_server {0}.".format(ca_server)
            )
        if "csr" in kwargs:
            kwargs["csr"] = get_pem_entry(
                kwargs["csr"], pem_type="CERTIFICATE REQUEST"
            ).replace("\n", "")
        if "public_key" in kwargs:
            # Strip newlines to make passing through as cli functions easier
            kwargs["public_key"] = get_public_key(kwargs["public_key"]).replace(
                "\n", ""
            )

        # Remove system entries in kwargs
        # Including listen_in and preqreuired because they are not included in STATE_INTERNAL_KEYWORDS
        # for salt 2014.7.2
        for ignore in list(_STATE_INTERNAL_KEYWORDS) + ["listen_in", "preqrequired"]:
            kwargs.pop(ignore, None)

        cert_txt = __salt__["publish.publish"](
            tgt=ca_server, fun="x509.sign_remote_certificate", arg=str(kwargs)
        )[ca_server]
        if path:
            return write_pem(text=cert_txt, path=path, pem_type="CERTIFICATE")
        else:
            return cert_txt

    signing_policy = {}
    if "signing_policy" in kwargs:
        signing_policy = _get_signing_policy(kwargs["signing_policy"])
        if isinstance(signing_policy, list):
            dict_ = {}
            for item in signing_policy:
                dict_.update(item)
            signing_policy = dict_

    # Overwrite any arguments in kwargs with signing_policy
    kwargs.update(signing_policy)

    for prop, default in six.iteritems(CERT_DEFAULTS):
        if prop not in kwargs:
            kwargs[prop] = default

    cert = M2Crypto.X509.X509()

    # X509 Version 3 has a value of 2 in the field.
    # Version 2 has a value of 1.
    # https://tools.ietf.org/html/rfc5280#section-4.1.2.1
    cert.set_version(kwargs["version"] - 1)

    # Random serial number if not specified
    if "serial_number" not in kwargs:
        kwargs["serial_number"] = _dec2hex(random.getrandbits(kwargs["serial_bits"]))
    cert.set_serial_number(int(kwargs["serial_number"].replace(":", ""), 16))

    # Set validity dates
    # pylint: disable=no-member
    not_before = M2Crypto.m2.x509_get_not_before(cert.x509)
    not_after = M2Crypto.m2.x509_get_not_after(cert.x509)
    M2Crypto.m2.x509_gmtime_adj(not_before, 0)
    M2Crypto.m2.x509_gmtime_adj(not_after, 60 * 60 * 24 * kwargs["days_valid"])
    # pylint: enable=no-member

    # If neither public_key or csr are included, this cert is self-signed
    if "public_key" not in kwargs and "csr" not in kwargs:
        kwargs["public_key"] = kwargs["signing_private_key"]

    csrexts = {}
    if "csr" in kwargs:
        kwargs["public_key"] = kwargs["csr"]
        csr = _get_request_obj(kwargs["csr"])
        cert.set_subject(csr.get_subject())
        csrexts = read_csr(kwargs["csr"])["X509v3 Extensions"]

    cert.set_pubkey(get_public_key(kwargs["public_key"], asObj=True))

    subject = cert.get_subject()

    for entry, num in six.iteritems(subject.nid):  # pylint: disable=unused-variable
        if entry in kwargs:
            setattr(subject, entry, kwargs[entry])

    if "signing_cert" in kwargs:
        signing_cert = _get_certificate_obj(kwargs["signing_cert"])
    else:
        signing_cert = cert
    cert.set_issuer(signing_cert.get_subject())

    for extname, extlongname in six.iteritems(EXT_NAME_MAPPINGS):
        if (
            extname in kwargs
            or extlongname in kwargs
            or extname in csrexts
            or extlongname in csrexts
        ) is False:
            continue

        # Use explicitly set values first, fall back to CSR values.
        extval = (
            kwargs[extname]
            or kwargs[extlongname]
            or csrexts[extname]
            or csrexts[extlongname]
        )

        critical = False
        if extval.startswith("critical "):
            critical = True
            extval = extval[9:]

        if extname == "subjectKeyIdentifier" and "hash" in extval:
            extval = extval.replace("hash", _get_pubkey_hash(cert))

        issuer = None
        if extname == "authorityKeyIdentifier":
            issuer = signing_cert

        ext = _new_extension(
            name=extname, value=extval, critical=critical, issuer=issuer
        )
        if not ext.x509_ext:
            log.info("Invalid X509v3 Extension. {0}: {1}".format(extname, extval))
            continue

        cert.add_ext(ext)

    if "testrun" in kwargs and kwargs["testrun"] is True:
        cert_props = read_certificate(cert)
        cert_props["Issuer Public Key"] = get_public_key(kwargs["signing_private_key"])
        return cert_props

    if not verify_private_key(kwargs["signing_private_key"], signing_cert):
        raise salt.exceptions.SaltInvocationError(
            "signing_private_key: {0}does no match signing_cert: {1}".format(
                kwargs["signing_private_key"], kwargs["signing_cert"]
            )
        )

    cert.sign(_get_private_key_obj(kwargs["signing_private_key"]), kwargs["algorithm"])

    if not verify_signature(cert, signing_pub_key=signing_cert):
        raise salt.exceptions.SaltInvocationError(
            "failed to verify certificate signature"
        )

    if "copypath" in kwargs:
        write_pem(
            text=cert.as_pem(),
            path=os.path.join(kwargs["copypath"], kwargs["serial_number"] + ".crt"),
            pem_type="CERTIFICATE",
        )

    if path:
        return write_pem(text=cert.as_pem(), path=path, pem_type="CERTIFICATE")
    else:
        return cert.as_pem()


# 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_certificate(path=None, text=False, ca_server=None, **kwargs):
    """
    Create an X509 certificate.

    path:
        Path to write the certificate to.

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

    kwargs:
        Any of the properties below can be included as additional keyword arguments.

    ca_server:
        Request a remotely signed certificate from ca_server. For this to work, a ``signing_policy`` must
        be specified, and that same policy must be configured on the ca_server. See ``signing_policy`` for
        details. Also the salt master must permit peers to call the ``sign_remote_certificate`` function.

        Example:

        /etc/salt/master.d/peer.conf

        .. code-block:: yaml

            peer:
              .*:
                - x509.sign_remote_certificate

    subject properties:
        Any of the values below can be incldued to set subject properties
        Any other subject properties supported by OpenSSL should also work.

        C:
            2 letter Country code
        CN:
            Certificate common name, typically the FQDN.

        Email:
            Email address

        GN:
            Given Name

        L:
            Locality

        O:
            Organization

        OU:
            Organization Unit

        SN:
            SurName

        ST:
            State or Province

    signing_private_key:
        A path or string of the private key in PEM format that will be used to sign this certificate.
        If neither ``signing_cert``, ``public_key``, or ``csr`` are included, it will be assumed that
        this is a self-signed certificate, and the public key matching ``signing_private_key`` will
        be used to create the certificate.

    signing_cert:
        A certificate matching the private key that will be used to sign this certificate. This is used
        to populate the issuer values in the resulting certificate. Do not include this value for
        self-signed certificates.

    public_key:
        The public key to be included in this certificate. This can be sourced from a public key,
        certificate, csr or private key. If a private key is used, the matching public key from
        the private key will be generated before any processing is done. This means you can request a
        certificate from a remote CA using a private key file as your public_key and only the
        public key will be sent across the network to the CA.
        If neither ``public_key`` or ``csr`` are
        specified, it will be assumed that this is a self-signed certificate, and the public key
        derived from ``signing_private_key`` will be used. Specify either ``public_key`` or ``csr``,
        not both. Because you can input a CSR as a public key or as a CSR, it is important to understand
        the difference. If you import a CSR as a public key, only the public key will be added
        to the certificate, subject or extension information in the CSR will be lost.

    csr:
        A file or PEM string containing a certificate signing request. This will be used to supply the
        subject, extensions and public key of a certificate. Any subject or extensions specified
        explicitly will overwrite any in the CSR.

    basicConstraints:
        X509v3 Basic Constraints extension.

    extensions:
        The following arguments set X509v3 Extension values. If the value starts with ``critical ``,
        the extension will be marked as critical.

        Some special extensions are ``subjectKeyIdentifier`` and ``authorityKeyIdentifier``.

        ``subjectKeyIdentifier`` can be an explicit value or it can be the special string ``hash``.
        ``hash`` will set the subjectKeyIdentifier equal to the SHA1 hash of the modulus of the
        public key in this certificate. Note that this is not the exact same hashing method used by
        OpenSSL when using the hash value.

        ``authorityKeyIdentifier`` Use values acceptable to the openssl CLI tools. This will
        automatically populate ``authorityKeyIdentifier`` with the ``subjectKeyIdentifier`` of
        ``signing_cert``. If this is a self-signed cert these values will be the same.

        basicConstraints:
            X509v3 Basic Constraints

        keyUsage:
            X509v3 Key Usage

        extendedKeyUsage:
            X509v3 Extended Key Usage

        subjectKeyIdentifier:
            X509v3 Subject Key Identifier

        issuerAltName:
            X509v3 Issuer Alternative Name

        subjectAltName:
            X509v3 Subject Alternative Name

        crlDistributionPoints:
            X509v3 CRL distribution points

        issuingDistributionPoint:
            X509v3 Issuing Distribution Point

        certificatePolicies:
            X509v3 Certificate Policies

        policyConstraints:
            X509v3 Policy Constraints

        inhibitAnyPolicy:
            X509v3 Inhibit Any Policy

        nameConstraints:
            X509v3 Name Constraints

        noCheck:
            X509v3 OCSP No Check

        nsComment:
            Netscape Comment

        nsCertType:
            Netscape Certificate Type

    days_valid:
        The number of days this certificate should be valid. This sets the ``notAfter`` property
        of the certificate. Defaults to 365.

    version:
        The version of the X509 certificate. Defaults to 3. This is automatically converted to the
        version value, so ``version=3`` sets the certificate version field to 0x2.

    serial_number:
        The serial number to assign to this certificate. If omitted a random serial number of size
        ``serial_bits`` is generated.

    serial_bits:
        The number of bits to use when randomly generating a serial number. Defaults to 64.

    algorithm:
        The hashing algorithm to be used for signing this certificate. Defaults to sha256.

    copypath:
        An additional path to copy the resulting certificate to. Can be used to maintain a copy
        of all certificates issued for revocation purposes.

    signing_policy:
        A signing policy that should be used to create this certificate. Signing policies should be defined
        in the minion configuration, or in a minion pillar. It should be a yaml formatted list of arguments
        which will override any arguments passed to this function. If the ``minions`` key is included in
        the signing policy, only minions matching that pattern will be permitted to remotely request certificates
        from that policy.

        Example:

        .. code-block:: yaml

            x509_signing_policies:
              www:
                - minions: 'www*'
                - signing_private_key: /etc/pki/ca.key
                - signing_cert: /etc/pki/ca.crt
                - C: US
                - ST: Utah
                - L: Salt Lake City
                - basicConstraints: "critical CA:false"
                - keyUsage: "critical cRLSign, keyCertSign"
                - subjectKeyIdentifier: hash
                - authorityKeyIdentifier: keyid,issuer:always
                - days_valid: 90
                - copypath: /etc/pki/issued_certs/

        The above signing policy can be invoked with ``signing_policy=www``

    CLI Example:

    .. code-block:: bash

        salt '*' x509.create_certificate path=/etc/pki/myca.crt \\
        signing_private_key='/etc/pki/myca.key' csr='/etc/pki/myca.csr'}
    """

    if (
        not path
        and not text
        and ("testrun" not in kwargs or kwargs["testrun"] is False)
    ):
        raise salt.exceptions.SaltInvocationError(
            "Either path or text must be specified."
        )
    if path and text:
        raise salt.exceptions.SaltInvocationError(
            "Either path or text must be specified, not both."
        )

    if ca_server:
        if "signing_policy" not in kwargs:
            raise salt.exceptions.SaltInvocationError(
                "signing_policy must be specified"
                "if requesting remote certificate from ca_server {0}.".format(ca_server)
            )
        if "csr" in kwargs:
            kwargs["csr"] = get_pem_entry(
                kwargs["csr"], pem_type="CERTIFICATE REQUEST"
            ).replace("\n", "")
        if "public_key" in kwargs:
            # Strip newlines to make passing through as cli functions easier
            kwargs["public_key"] = get_public_key(kwargs["public_key"]).replace(
                "\n", ""
            )

        # Remove system entries in kwargs
        # Including listen_in and preqreuired because they are not included in STATE_INTERNAL_KEYWORDS
        # for salt 2014.7.2
        for ignore in list(_STATE_INTERNAL_KEYWORDS) + [
            "listen_in",
            "preqrequired",
            "__prerequired__",
        ]:
            kwargs.pop(ignore, None)

        cert_txt = __salt__["publish.publish"](
            tgt=ca_server, fun="x509.sign_remote_certificate", arg=str(kwargs)
        )[ca_server]
        if path:
            return write_pem(text=cert_txt, path=path, pem_type="CERTIFICATE")
        else:
            return cert_txt

    signing_policy = {}
    if "signing_policy" in kwargs:
        signing_policy = _get_signing_policy(kwargs["signing_policy"])
        if isinstance(signing_policy, list):
            dict_ = {}
            for item in signing_policy:
                dict_.update(item)
            signing_policy = dict_

    # Overwrite any arguments in kwargs with signing_policy
    kwargs.update(signing_policy)

    for prop, default in six.iteritems(CERT_DEFAULTS):
        if prop not in kwargs:
            kwargs[prop] = default

    cert = M2Crypto.X509.X509()

    # X509 Version 3 has a value of 2 in the field.
    # Version 2 has a value of 1.
    # https://tools.ietf.org/html/rfc5280#section-4.1.2.1
    cert.set_version(kwargs["version"] - 1)

    # Random serial number if not specified
    if "serial_number" not in kwargs:
        kwargs["serial_number"] = _dec2hex(random.getrandbits(kwargs["serial_bits"]))
    cert.set_serial_number(int(kwargs["serial_number"].replace(":", ""), 16))

    # Set validity dates
    # pylint: disable=no-member
    not_before = M2Crypto.m2.x509_get_not_before(cert.x509)
    not_after = M2Crypto.m2.x509_get_not_after(cert.x509)
    M2Crypto.m2.x509_gmtime_adj(not_before, 0)
    M2Crypto.m2.x509_gmtime_adj(not_after, 60 * 60 * 24 * kwargs["days_valid"])
    # pylint: enable=no-member

    # If neither public_key or csr are included, this cert is self-signed
    if "public_key" not in kwargs and "csr" not in kwargs:
        kwargs["public_key"] = kwargs["signing_private_key"]

    csrexts = {}
    if "csr" in kwargs:
        kwargs["public_key"] = kwargs["csr"]
        csr = _get_request_obj(kwargs["csr"])
        cert.set_subject(csr.get_subject())
        csrexts = read_csr(kwargs["csr"])["X509v3 Extensions"]

    cert.set_pubkey(get_public_key(kwargs["public_key"], asObj=True))

    subject = cert.get_subject()

    for entry, num in six.iteritems(subject.nid):  # pylint: disable=unused-variable
        if entry in kwargs:
            setattr(subject, entry, kwargs[entry])

    if "signing_cert" in kwargs:
        signing_cert = _get_certificate_obj(kwargs["signing_cert"])
    else:
        signing_cert = cert
    cert.set_issuer(signing_cert.get_subject())

    for extname, extlongname in six.iteritems(EXT_NAME_MAPPINGS):
        if (
            extname in kwargs
            or extlongname in kwargs
            or extname in csrexts
            or extlongname in csrexts
        ) is False:
            continue

        # Use explicitly set values first, fall back to CSR values.
        extval = (
            kwargs[extname]
            or kwargs[extlongname]
            or csrexts[extname]
            or csrexts[extlongname]
        )

        critical = False
        if extval.startswith("critical "):
            critical = True
            extval = extval[9:]

        if extname == "subjectKeyIdentifier" and "hash" in extval:
            extval = extval.replace("hash", _get_pubkey_hash(cert))

        issuer = None
        if extname == "authorityKeyIdentifier":
            issuer = signing_cert

        ext = _new_extension(
            name=extname, value=extval, critical=critical, issuer=issuer
        )
        if not ext.x509_ext:
            log.info("Invalid X509v3 Extension. {0}: {1}".format(extname, extval))
            continue

        cert.add_ext(ext)

    if "testrun" in kwargs and kwargs["testrun"] is True:
        cert_props = read_certificate(cert)
        cert_props["Issuer Public Key"] = get_public_key(kwargs["signing_private_key"])
        return cert_props

    if not verify_private_key(kwargs["signing_private_key"], signing_cert):
        raise salt.exceptions.SaltInvocationError(
            "signing_private_key: {0}does no match signing_cert: {1}".format(
                kwargs["signing_private_key"], kwargs["signing_cert"]
            )
        )

    cert.sign(_get_private_key_obj(kwargs["signing_private_key"]), kwargs["algorithm"])

    if not verify_signature(cert, signing_pub_key=signing_cert):
        raise salt.exceptions.SaltInvocationError(
            "failed to verify certificate signature"
        )

    if "copypath" in kwargs:
        write_pem(
            text=cert.as_pem(),
            path=os.path.join(kwargs["copypath"], kwargs["serial_number"] + ".crt"),
            pem_type="CERTIFICATE",
        )

    if path:
        return write_pem(text=cert.as_pem(), path=path, pem_type="CERTIFICATE")
    else:
        return cert.as_pem()