-1

I have a Python script that looks for all x509 self-signed certificates in a directory, verifies them and writes information about them into the table (valid date, expiration date, subject, issuer). Is there any way to create a list of revoked .crl certificates using the pyOpenSSL library and revoke one of them? Or is it done only with the help of terminal commands and the os library?

I did not find such questions with the answer on the forum.

Mark
  • 43
  • 8

1 Answers1

2

Reviewing your question, I noticed you say self-signed certificates. Revoking a self-signed certificate usually doesn't work. To revoke a self-signed certificates you have to include it in a CRL signed by the self-signed certificate itself. As revoking a certificate implies the certificate's key may be compromised, an attacker in possession of the key can easily generate a newer CRL that doesn't include the self-signed certificate. To solve that, you need a trusted third party to issue the CRLs. However, it's very hard to achieve.

I'll keep the example bellow, but it doesn't solve your problem.

--

Yes, it's possible to create a CRL using pyopenssl, you can see the example bellow (based on this example).

However, pyopenssl's CRL.set_nextUpdate function has a bug and doesn't set the specified date. So you have to wait the fix to really use it.

# -*- coding: latin-1 -*-
#
# Copyright (C) AB Strakt
# Copyright (C) Jean-Paul Calderone
# See LICENSE for details.

"""
Certificate generation module.
"""

from OpenSSL import crypto

TYPE_RSA = crypto.TYPE_RSA
TYPE_DSA = crypto.TYPE_DSA

def createKeyPair(type, bits):
    """
    Create a public/private key pair.
    Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA
               bits - Number of bits to use in the key
    Returns:   The public/private key pair in a PKey object
    """
    pkey = crypto.PKey()
    pkey.generate_key(type, bits)
    return pkey

def createCertRequest(pkey, digest="sha256", **name):
    """
    Create a certificate request.
    Arguments: pkey   - The key to associate with the request
               digest - Digestion method to use for signing, default is md5
               **name - The name of the subject of the request, possible
                        arguments are:
                          C  - Country name
                          ST - State or province name
                          L  - Locality name
                          O  - Organization name
                          OU - Organizational unit name
                          CN - Common name
                          emailAddress - E-mail address
    Returns:   The certificate request in an X509Req object
    """
    req = crypto.X509Req()
    subj = req.get_subject()

    for (key,value) in name.items():
        setattr(subj, key, value)

    req.set_pubkey(pkey)
    req.sign(pkey, digest)
    return req

def createCertificate(req, issuerCert, issuerKey, serial, notBefore, notAfter, digest="sha256"):
    """
    Generate a certificate given a certificate request.
    Arguments: req        - Certificate reqeust to use
               issuerCert - The certificate of the issuer
               issuerKey  - The private key of the issuer
               serial     - Serial number for the certificate
               notBefore  - Timestamp (relative to now) when the certificate
                            starts being valid
               notAfter   - Timestamp (relative to now) when the certificate
                            stops being valid
               digest     - Digest method to use for signing, default is md5
    Returns:   The signed certificate in an X509 object
    """
    cert = crypto.X509()
    cert.set_serial_number(serial)
    cert.gmtime_adj_notBefore(notBefore)
    cert.gmtime_adj_notAfter(notAfter)
    cert.set_issuer(issuerCert.get_subject())
    cert.set_subject(req.get_subject())
    cert.set_pubkey(req.get_pubkey())
    cert.sign(issuerKey, digest)
    return cert

def createCrl(issuerCert, issuerKey, serial, lastUpdate, nextUpdate, revokedList, digest="sha256"):
    """
    Generate a certificate revocation list (CRL).
    Arguments: issuerCert  - The certificate of the issuer
               issuerKey   - The private key of the issuer
               serial      - Serial number for the crl
               lastUpdate  - ASN1 timestamp ("YYYMMDDhhmmssZ") of the last crl update
               nextUpdate  - ASN1 timestamp ("YYYMMDDhhmmssZ") of the next crl update
               revokedList - A list of Revoked objects.
               digest      - Digest method to use for signing, default is sha256
    Returns:   The signed crl in a CRL object
    """
    crl = crypto.CRL()
    crl.set_lastUpdate(lastUpdate)
    crl.set_nextUpdate(nextUpdate)  # BUG: this line doesn't set the next update
    for revoked in revokedList:
        crl.add_revoked(revoked)
    crl.sign(issuerCert, issuerKey, digest)
    return crl

# Creates a self signed certificate
pkey = createKeyPair(TYPE_RSA, 2048)
req = createCertRequest(pkey, "sha256", C="BR", CN="Teste")
cert = createCertificate(req, req, pkey, 1, 0, 60*60*24*365*5, "sha256")

# Creates the revoked objects
revoked1 = crypto.Revoked()
revoked1.set_serial(b"1")                 # certificate's serial number
revoked1.set_rev_date(b"20190601010101Z") # certificate's revocation date
revoked1.set_reason(b'keyCompromise')     # certificate's revocation reason

revoked2 = crypto.Revoked()
revoked2.set_serial(b"2")
revoked2.set_rev_date(b"20190601010101Z")
revoked2.set_reason(None)

# Creates the CRL using the revoked objects
crl = createCrl(cert, pkey, 1, b"20190101010101Z", b"20190101010101Z", [revoked1, revoked2, ], b"sha256")

# Prints the CRL as PEM and TEXT
crl_pem = crypto.dump_crl(crypto.FILETYPE_PEM, crl)
print(crl_pem)
print()

crl_str = crypto.dump_crl(crypto.FILETYPE_TEXT, crl)
print(crl_str)
Lucas Martins
  • 553
  • 3
  • 8
  • Your link to the PyOpenSSL bug no longer works. I have a workaround at the following URL, in short you can set the nextUpdate if you use `crl.export()` (which takes a "days" argument), rather than using `crypto.dump_crl()`. https://github.com/pyca/pyopenssl/issues/794#issuecomment-1159491295 – Sean Reifschneider Jun 18 '22 at 16:21