pythonopensslcryptographypyopenssl

How to add a challenge password attribute during csr generation using python openssl?


I have a python code for Csr and key generation using pyopenssl. I am trying to add an additional attribute, "challenge password" while generating the CSR. Could someone please help on implementing challenge password to the below code?

Additional attribute using openssl look something like this: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []:asdfasdf

import OpenSSL
from OpenSSL.crypto import load_certificate_request, FILETYPE_PEM
def create_csr(common_name, country, state=None, city=None,
               organization=None, organizational_unit=None,
               email_address=None, san_list=None):
        key = OpenSSL.crypto.PKey()
        key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)

        req = OpenSSL.crypto.X509Req()
        req.get_subject().CN = common_name
        if country:
            req.get_subject().C = country
        if state:
            req.get_subject().ST = state
        if city:
            req.get_subject().L = city
        if organization:
            req.get_subject().O = organization
        if organizational_unit:
            req.get_subject().OU = organizational_unit
        if email_address:
            req.get_subject().emailAddress = email_address
        if san_list:
            req.add_extensions([
                OpenSSL.crypto.X509Extension(
                    "subjectAltName".encode("utf-8"), False, (", ").join(san_list).encode("utf-8"))])
        req.set_pubkey(key)
        req.sign(key, 'sha256')
        private_key = OpenSSL.crypto.dump_privatekey(
            OpenSSL.crypto.FILETYPE_PEM, key)
        csr = OpenSSL.crypto.dump_certificate_request(
                   OpenSSL.crypto.FILETYPE_PEM, req)
        private_key = private_key.decode()
        csr = csr.decode()
        return private_key, csr
    ```

   


Solution

  • pyopenssl does not have appear to have support for this.

    You could do it using cryptography instead, though.

    # safe stuff
    from cryptography import x509
    from cryptography.x509.oid import NameOID, AttributeOID
    
    # land mines, dragons, and dinosaurs with laser guns
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives.hashes import SHA256
    from cryptography.hazmat.primitives.serialization import Encoding
    from cryptography.hazmat.primitives.serialization import NoEncryption
    from cryptography.hazmat.primitives.serialization import PrivateFormat
    
    # Standard RSA public exponent, should be a "big enough" low hamming weight prime
    # https://en.wikipedia.org/wiki/Coppersmith%27s_attack#Low_public_exponent_attack
    RSA_E = 65537
    RSA_BITS = 2048
    PEM = Encoding.PEM
    PKCS8 = PrivateFormat.PKCS8
    
    def create_csr(common_name, country, state=None, city=None,
                   organization=None, organizational_unit=None,
                   email_address=None, san_list=None,
                   password=None, backend=default_backend):
        key = rsa.generate_private_key(RSA_E, RSA_BITS, backend())
        csrb = x509.CertificateSigningRequestBuilder()
        subject = []
        def add_subject(oid, value):
            if value is not None:
                subject.append(x509.NameAttribute(oid, value))
    
        add_subject(NameOID.COMMON_NAME, common_name)
        add_subject(NameOID.COUNTRY_NAME, country)
        add_subject(NameOID.STATE_OR_PROVINCE_NAME, state)
        add_subject(NameOID.LOCALITY_NAME, city)
        add_subject(NameOID.ORGANIZATION_NAME, organization)
        add_subject(NameOID.ORGANIZATIONAL_UNIT_NAME, organizational_unit)
        add_subject(NameOID.EMAIL_ADDRESS, email_address)
        csrb = csrb.subject_name(x509.Name(subject))
    
        if san_list is None: san_list = [common_name]
        sans = list(map(x509.DNSName, san_list))
        csrb = csrb.add_extension(x509.SubjectAlternativeName(sans), critical=False)
    
        # challenge password is included as plaintext in the request
        if password is not None:
            csrb = csrb.add_attribute(
                AttributeOID.CHALLENGE_PASSWORD,
                password.encode('utf-8'),
            )
    
        req = csrb.sign(key, SHA256())
    
        pem_key = key.private_bytes(PEM, PKCS8, NoEncryption()).decode()
        pem_req = req.public_bytes(encoding=PEM).decode()
        return pem_key, pem_req