Add support for CRL and Revoked objects.
diff --git a/doc/pyOpenSSL.tex b/doc/pyOpenSSL.tex
index bd83d6d..d43ad96 100644
--- a/doc/pyOpenSSL.tex
+++ b/doc/pyOpenSSL.tex
@@ -207,6 +207,14 @@
 method.
 \end{classdesc}
 
+\begin{classdesc}{CRL}{}
+A class representing Certifcate Revocation List objects.
+\end{classdesc}
+
+\begin{classdesc}{Revoked}{}
+A class representing Revocation objects of CRL.
+\end{classdesc}
+
 \begin{datadesc}{FILETYPE_PEM}
 \dataline{FILETYPE_ASN1}
 File type constants.
@@ -259,6 +267,12 @@
 pass phrase.
 \end{funcdesc}
 
+\begin{funcdesc}{load_crl}{type, buffer}
+Load Certificate Revocation List (CRL) data from a string \var{buffer}.
+\var{buffer} encoded with the type \var{type}.  The type \var{type} 
+must either \constant{FILETYPE_PEM} or \constant{FILETYPE_ASN1}).
+\end{funcdesc}
+
 \begin{funcdesc}{load_pkcs7_data}{type, buffer}
 Load pkcs7 data from the string \var{buffer} encoded with the type \var{type}.
 \end{funcdesc}
@@ -579,6 +593,45 @@
 Verify the NetscapeSPKI object using the given \var{key}.
 \end{methoddesc}
 
+\subsubsection{CRL objects \label{crl}}
+
+CRL objects have the following methods:
+
+\begin{methoddesc}[CRL]{add_revoked}{revoked}
+Add a Revoked object to the CRL, by value not reference.
+\end{methoddesc}
+
+\begin{methoddesc}[CRL]{export}{cert, key\optional{, type=FILETYPE_PEM}\optional{, days=100}}
+Use \var{cert} and \var{key} to sign the CRL and return the CRL as a string.
+\var{days} is the number of days before the next CRL is due.
+\end{methoddesc}
+
+\begin{methoddesc}[CRL]{get_revoked}{}
+Return a tuple of Revoked objects, by value not reference.
+\end{methoddesc}
+
+\subsubsection{Revoked objects \label{revoked}}
+
+Revoked objects have the following methods:
+
+\begin{methoddesc}[Revoked]{get_rev_date}{}
+Return the revocation date as a str.
+The string is formatted as an ASN1 GENERALIZEDTIME.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{get_serial}{}
+Return a str containing a hex number of the serial of the revoked certificate.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{set_rev_date}{date}
+Set the revocation date.
+The string is formatted as an ASN1 GENERALIZEDTIME.
+\end{methoddesc}
+
+\begin{methoddesc}[Revoked]{set_serial}{serial}
+\var{serial} is a string containing a hex number of the serial of the revoked certificate.
+\end{methoddesc}
+
 
 % % % rand module
 
diff --git a/setup.py b/setup.py
index a6a446c..7011f9d 100755
--- a/setup.py
+++ b/setup.py
@@ -23,12 +23,14 @@
               'src/crypto/x509store.c', 'src/crypto/x509req.c',
               'src/crypto/x509ext.c', 'src/crypto/pkcs7.c',
               'src/crypto/pkcs12.c', 'src/crypto/netscape_spki.c',
+              'src/crypto/revoked.c', 'src/crypto/crl.c',
               'src/util.c']
 crypto_dep = ['src/crypto/crypto.h', 'src/crypto/x509.h',
               'src/crypto/x509name.h', 'src/crypto/pkey.h',
               'src/crypto/x509store.h', 'src/crypto/x509req.h',
               'src/crypto/x509ext.h', 'src/crypto/pkcs7.h',
               'src/crypto/pkcs12.h', 'src/crypto/netscape_spki.h',
+              'src/crypto/revoked.h', 'src/crypto/crl.h',
               'src/util.h']
 rand_src = ['src/rand/rand.c', 'src/util.c']
 rand_dep = ['src/util.h']
diff --git a/src/crypto/crl.c b/src/crypto/crl.c
new file mode 100644
index 0000000..c90cbe8
--- /dev/null
+++ b/src/crypto/crl.c
@@ -0,0 +1,294 @@
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+
+static X509_REVOKED * X509_REVOKED_dup(X509_REVOKED *orig)
+{
+    X509_REVOKED *dupe = NULL;
+
+    dupe = X509_REVOKED_new();
+    if(dupe == NULL)
+        return NULL;
+    if(orig->serialNumber)
+    {
+        dupe->serialNumber = M_ASN1_INTEGER_dup(orig->serialNumber); 
+    }
+    if(orig->revocationDate)
+    {
+        dupe->revocationDate = M_ASN1_INTEGER_dup(orig->revocationDate); 
+    }
+    if(orig->extensions)
+    {
+        STACK_OF(X509_EXTENSION) *sk = NULL;
+        X509_EXTENSION * ext;
+        int j;
+
+        sk = sk_X509_EXTENSION_new_null();
+        for(j = 0; j < sk_X509_EXTENSION_num(orig->extensions); j++) {
+             ext = sk_X509_EXTENSION_value(orig->extensions, j);
+             ext = X509_EXTENSION_dup(ext);
+             sk_X509_EXTENSION_push(sk, ext);
+        }
+        dupe->extensions = sk;
+    }
+    dupe->sequence = orig->sequence;
+    return dupe;
+}
+
+static char crypto_CRL_get_revoked_doc[] = "\n\
+Return revoked portion of the CRL structure (by value\n\
+not reference).\n\
+\n\
+@return: A tuple of Revoked objects.\n\
+";
+static PyObject *
+crypto_CRL_get_revoked(crypto_CRLObj *self, PyObject *args)
+{
+    int j, num_rev;
+    X509_REVOKED *r = NULL;
+    PyObject *obj = NULL, *rev_obj;
+
+    if (!PyArg_ParseTuple(args, ":get_revoked"))
+        return NULL;
+
+    num_rev = sk_X509_REVOKED_num(self->crl->crl->revoked);
+    if(num_rev < 0) 
+    {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    if ((obj = PyTuple_New(num_rev)) == NULL)
+        return NULL;
+
+    for(j = 0; j < num_rev; j++) 
+    {
+         r = sk_X509_REVOKED_value(self->crl->crl->revoked, j);
+         r = X509_REVOKED_dup(r);
+         if( r == NULL ) 
+             goto error;
+         rev_obj = (PyObject *) crypto_Revoked_New(r);
+         if( rev_obj == NULL )
+             goto error;
+         r = NULL; /* it's now owned by rev_obj */
+         PyTuple_SET_ITEM(obj, j, rev_obj);
+    }
+    return obj;
+
+ error:
+    if(r)
+       X509_REVOKED_free(r);
+    Py_XDECREF(obj);
+    return NULL;
+}
+
+static char crypto_CRL_add_revoked_doc[] = "\n\
+Add a revoked (by value not reference) to the CRL structure\n\
+\n\
+@param cert: The new revoked.\n\
+@type cert: L{X509}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_CRL_add_revoked(crypto_CRLObj *self, PyObject *args, PyObject *keywds)
+{
+    crypto_RevokedObj * rev_obj = NULL;
+    static char *kwlist[] = {"revoked", NULL};
+    X509_REVOKED * dup;
+
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!:add_revoked", 
+        kwlist, &crypto_Revoked_Type, &rev_obj))
+        return NULL;
+
+    dup = X509_REVOKED_dup( rev_obj->revoked );
+    if(dup == NULL)
+        return NULL;
+    X509_CRL_add0_revoked(self->crl, dup);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+static char crypto_CRL_export_doc[] = "\n\
+Export a CRL as a string\n\
+\n\
+@param cert: Used to sign CRL.\n\
+@type cert: L{X509}\n\
+@param key: Used to sign CRL.\n\
+@type key: L{PKey}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_CRL_export(crypto_CRLObj *self, PyObject *args, PyObject *keywds)
+{
+	int ret, buf_len, type = X509_FILETYPE_PEM, days = 100;
+	char *temp;
+	BIO *bio;
+	PyObject *buffer;
+	crypto_PKeyObj *key;
+	ASN1_TIME *tmptm;
+	crypto_X509Obj *x509;	
+        static char *kwlist[] = {"cert", "key", "type", "days", NULL};
+    
+	if (!PyArg_ParseTupleAndKeywords(args, keywds, "O!O!|ii:dump_crl", kwlist,
+			      &crypto_X509_Type, &x509, 
+                              &crypto_PKey_Type, &key, &type, &days))
+		return NULL;
+    
+	bio=BIO_new(BIO_s_mem());
+	tmptm = ASN1_TIME_new();
+	if (!tmptm)
+		return 0;
+	X509_gmtime_adj(tmptm,0);
+	X509_CRL_set_lastUpdate(self->crl, tmptm);
+	X509_gmtime_adj(tmptm,days*24*60*60);
+	X509_CRL_set_nextUpdate(self->crl, tmptm);
+	ASN1_TIME_free(tmptm);
+	X509_CRL_set_issuer_name(self->crl, X509_get_subject_name(x509->x509));
+	X509_CRL_sign(self->crl, key->pkey, EVP_md5());
+        switch (type)
+        {
+            case X509_FILETYPE_PEM:
+	        ret = PEM_write_bio_X509_CRL(bio, self->crl);
+                break;
+
+            case X509_FILETYPE_ASN1:
+                ret = (int) i2d_X509_CRL_bio(bio, self->crl);
+                break;
+
+            case X509_FILETYPE_TEXT:
+                ret = X509_CRL_print(bio, self->crl);
+                break;
+
+            default:
+                PyErr_SetString(PyExc_ValueError,
+                        "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+                return NULL;
+        };
+        if( ! ret )
+        {
+            exception_from_error_queue(crypto_Error);
+            BIO_free(bio);
+            return NULL;
+        }
+	buf_len = BIO_get_mem_data(bio, &temp);
+	buffer = PyString_FromStringAndSize(temp, buf_len);
+	BIO_free(bio);
+	return buffer;
+}
+
+crypto_CRLObj *
+crypto_CRL_New(X509_CRL *crl)
+{
+    crypto_CRLObj *self;
+
+    self = PyObject_New(crypto_CRLObj, &crypto_CRL_Type);
+    if (self==NULL)
+	    return NULL;
+    self->crl = crl;
+    return self;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_CRL_name, METH_VARARGS, crypto_CRL_name_doc }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS, crypto_CRL_##name##_doc }
+#define ADD_KW_METHOD(name)        \
+    { #name, (PyCFunction)crypto_CRL_##name, METH_VARARGS | METH_KEYWORDS, crypto_CRL_##name##_doc }
+static PyMethodDef crypto_CRL_methods[] =
+{
+    ADD_KW_METHOD(add_revoked),
+    ADD_METHOD(get_revoked),
+    ADD_KW_METHOD(export),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+static PyObject *
+crypto_CRL_getattr(crypto_CRLObj *self, char *name)
+{
+    return Py_FindMethod(crypto_CRL_methods, (PyObject *)self, name);
+}
+
+static void
+crypto_CRL_dealloc(crypto_CRLObj *self)
+{
+    X509_CRL_free(self->crl);
+    self->crl = NULL;
+
+    PyObject_Del(self);
+}
+
+static char crypto_CRL_doc[] = "\n\
+CRL() -> CRL instance\n\
+\n\
+Create a new empty CRL object.\n\
+\n\
+@returns: The CRL object\n\
+";
+
+static PyObject* crypto_CRL_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
+    	if (!PyArg_ParseTuple(args, ":CRL")) {
+		return NULL;
+    	}
+	
+    	return (PyObject *)crypto_CRL_New(X509_CRL_new());
+}
+
+PyTypeObject crypto_CRL_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "CRL",
+    sizeof(crypto_CRLObj),
+    0,
+    (destructor)crypto_CRL_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_CRL_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL, /* str */
+    NULL, /* getattro */
+    NULL, /* setattro */
+    NULL, /* as_buffer */
+    Py_TPFLAGS_DEFAULT,
+    crypto_CRL_doc, /* doc */
+    NULL, /* traverse */
+    NULL, /* clear */
+    NULL, /* tp_richcompare */
+    0, /* tp_weaklistoffset */
+    NULL, /* tp_iter */
+    NULL, /* tp_iternext */
+    crypto_CRL_methods, /* tp_methods */
+    NULL, /* tp_members */
+    NULL, /* tp_getset */
+    NULL, /* tp_base */
+    NULL, /* tp_dict */
+    NULL, /* tp_descr_get */
+    NULL, /* tp_descr_set */
+    0, /* tp_dictoffset */
+    NULL, /* tp_init */
+    NULL, /* tp_alloc */
+    crypto_CRL_new, /* tp_new */
+};
+
+int init_crypto_crl(PyObject *module) {
+       if(PyType_Ready(&crypto_CRL_Type) < 0) {
+       	       return 0;
+       }
+
+       if (PyModule_AddObject(module, "CRL", (PyObject *)&crypto_CRL_Type) != 0) {
+       	       return 0;
+       }
+       return 1;
+}
+
diff --git a/src/crypto/crl.h b/src/crypto/crl.h
new file mode 100644
index 0000000..87f5048
--- /dev/null
+++ b/src/crypto/crl.h
@@ -0,0 +1,19 @@
+#ifndef PyOpenSSL_crypto_CRL_H_
+#define PyOpenSSL_crypto_CRL_H_
+
+#include <Python.h>
+
+extern  int       init_crypto_crl   (PyObject *);
+
+extern  PyTypeObject      crypto_CRL_Type;
+
+#define crypto_CRL_Check(v) ((v)->ob_type == &crypto_CRL_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509_CRL *crl;
+} crypto_CRLObj;
+
+crypto_CRLObj * crypto_CRL_New(X509_CRL *crl);
+
+#endif
diff --git a/src/crypto/crypto.c b/src/crypto/crypto.c
index 21501c1..a3ce9a9 100644
--- a/src/crypto/crypto.c
+++ b/src/crypto/crypto.c
@@ -430,6 +430,53 @@
     return buffer;
 }
 
+static char crypto_load_crl_doc[] = "\n\
+Load a certificate revocation list from a buffer\n\
+\n\
+@param type: The file type (one of FILETYPE_PEM, FILETYPE_ASN1)\n\
+@param buffer: The buffer the CRL is stored in\n\
+\n\
+@return: The PKey object\n\
+";
+
+static PyObject *
+crypto_load_crl(PyObject *spam, PyObject *args)
+{
+    int type, len;
+    char *buffer;
+    BIO *bio;
+    X509_CRL *crl;
+
+    if (!PyArg_ParseTuple(args, "is#:load_crl", &type, &buffer, &len))
+        return NULL;
+
+    bio = BIO_new_mem_buf(buffer, len);
+    switch (type)
+    {
+        case X509_FILETYPE_PEM:
+            crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL);
+            break;
+
+        case X509_FILETYPE_ASN1:
+            crl = d2i_X509_CRL_bio(bio, NULL);
+            break;
+
+        default:
+            PyErr_SetString(PyExc_ValueError, "type argument must be FILETYPE_PEM or FILETYPE_ASN1");
+            BIO_free(bio);
+            return NULL;
+    }
+    BIO_free(bio);
+
+    if (crl == NULL)
+    {
+        exception_from_error_queue(crypto_Error);
+        return NULL;
+    }
+
+    return (PyObject *)crypto_CRL_New(crl);
+}
+
 static char crypto_load_pkcs7_data_doc[] = "\n\
 Load pkcs7 data from a buffer\n\
 \n\
@@ -555,6 +602,7 @@
     { "dump_certificate", (PyCFunction)crypto_dump_certificate, METH_VARARGS, crypto_dump_certificate_doc },
     { "load_certificate_request", (PyCFunction)crypto_load_certificate_request, METH_VARARGS, crypto_load_certificate_request_doc },
     { "dump_certificate_request", (PyCFunction)crypto_dump_certificate_request, METH_VARARGS, crypto_dump_certificate_request_doc },
+    { "load_crl",         (PyCFunction)crypto_load_crl,         METH_VARARGS, crypto_load_crl_doc },
     { "load_pkcs7_data", (PyCFunction)crypto_load_pkcs7_data, METH_VARARGS, crypto_load_pkcs7_data_doc },
     { "load_pkcs12", (PyCFunction)crypto_load_pkcs12, METH_VARARGS, crypto_load_pkcs12_doc },
     { "X509_verify_cert_error_string", (PyCFunction)crypto_X509_verify_cert_error_string, METH_VARARGS, crypto_X509_verify_cert_error_string_doc },
@@ -696,7 +744,10 @@
         goto error;
     if (!init_crypto_netscape_spki(module))
         goto error;
-
+    if (!init_crypto_crl(module))
+        goto error;
+    if (!init_crypto_revoked(module))
+        goto error;
 error:
     ;
 }
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index b5e6b65..057b5dd 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -23,6 +23,8 @@
 #include "x509ext.h"
 #include "pkcs7.h"
 #include "pkcs12.h"
+#include "crl.h"
+#include "revoked.h"
 #include "../util.h"
 
 extern PyObject *crypto_Error;
diff --git a/src/crypto/revoked.c b/src/crypto/revoked.c
new file mode 100644
index 0000000..8289408
--- /dev/null
+++ b/src/crypto/revoked.c
@@ -0,0 +1,258 @@
+#include <Python.h>
+#define crypto_MODULE
+#include "crypto.h"
+
+
+static char crypto_Revoked_get_rev_date_doc[] = "\n\
+Retrieve the revocation date\n\
+\n\
+@return: A string giving the timestamp, in the format:\n\
+\n\
+                 YYYYMMDDhhmmssZ\n\
+                 YYYYMMDDhhmmss+hhmm\n\
+                 YYYYMMDDhhmmss-hhmm\n\
+";
+
+static PyObject*
+crypto_Revoked_get_rev_date(crypto_RevokedObj *self, PyObject *args)
+{
+	/* returns a borrowed reference.  */
+	return _get_asn1_time(
+		":get_rev_date", self->revoked->revocationDate, args);
+}
+
+static char crypto_Revoked_set_rev_date_doc[] = "\n\
+Set the revocation timestamp\n\
+\n\
+@param when: A string giving the timestamp, in the format:\n\
+\n\
+                 YYYYMMDDhhmmssZ\n\
+                 YYYYMMDDhhmmss+hhmm\n\
+                 YYYYMMDDhhmmss-hhmm\n\
+\n\
+@return: None\n\
+";
+
+static PyObject*
+crypto_Revoked_set_rev_date(crypto_RevokedObj *self, PyObject *args)
+{
+	return _set_asn1_time(
+		"s:set_rev_date", self->revoked->revocationDate, args);
+}
+
+
+static PyObject *
+ASN1_INTEGER_to_PyString(ASN1_INTEGER *asn1_int)
+{
+    BIO *bio = NULL;
+    PyObject *buf = NULL;
+    int ret, pending;
+
+    /* Create a openssl BIO buffer */
+    bio = BIO_new(BIO_s_mem());
+    if (bio == NULL)
+        goto err;
+
+    /* Write the integer to the BIO as a hex string. */
+    i2a_ASN1_INTEGER(bio, asn1_int);
+
+    /* Allocate a Python string. */
+    pending = BIO_pending(bio);
+    buf = PyString_FromStringAndSize(NULL, pending);
+    if (buf == NULL) {
+        goto err;
+    }
+
+    /* Copy the BIO contents to a Python string. */
+    ret = BIO_read(bio, PyString_AsString(buf), pending);
+    if (ret <= 0) { /* problem with BIO_read */
+        goto err;
+    }
+
+    /* Cleanup */
+    BIO_free(bio);
+    bio = NULL;
+    return buf;
+
+ err:
+    if(bio) {
+        BIO_free(bio);
+    }
+    if(buf) {
+        Py_DECREF(buf);
+    }
+    return NULL;
+}
+
+
+static char crypto_Revoked_get_serial_doc[] = "\n\
+Return the serial number of a Revoked structure\n\
+\n\
+@return: The serial number as a string\n\
+";
+static PyObject *
+crypto_Revoked_get_serial(crypto_RevokedObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":get_serial"))
+        return NULL;
+
+    if(self->revoked->serialNumber == NULL) {
+        /* never happens */
+        Py_INCREF(Py_None);
+        return Py_None;
+    } else {
+        return ASN1_INTEGER_to_PyString(self->revoked->serialNumber);
+    }
+}
+
+static char crypto_Revoked_set_serial_doc[] = "\n\
+Set the serial number of a revoked Revoked structure\n\
+\n\
+@param hex_str: The new serial number.\n\
+@type hex_str: L{str}\n\
+@return: None\n\
+";
+static PyObject *
+crypto_Revoked_set_serial(crypto_RevokedObj *self, PyObject *args, PyObject *keywds)
+{
+    static char *kwlist[] = {"hex_str", NULL};
+    const char *hex_str = NULL;
+    BIGNUM *serial = NULL;
+    ASN1_INTEGER *tmpser = NULL;
+
+    if (!PyArg_ParseTupleAndKeywords(args, keywds, "s:set_serial", 
+        kwlist, &hex_str))
+        return NULL;
+
+    if( ! BN_hex2bn(&serial, hex_str) ) {
+        PyErr_SetString(PyExc_TypeError, "bad hex string");
+        return NULL;
+    }
+
+    tmpser = BN_to_ASN1_INTEGER(serial, NULL);
+    BN_free(serial);
+    serial = NULL;
+    X509_REVOKED_set_serialNumber(self->revoked, tmpser);
+    ASN1_INTEGER_free(tmpser);
+
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+crypto_RevokedObj *
+crypto_Revoked_New(X509_REVOKED *revoked)
+{
+    crypto_RevokedObj *self;
+
+    self = PyObject_New(crypto_RevokedObj, &crypto_Revoked_Type);
+    if (self==NULL)
+	    return NULL;
+    self->revoked = revoked;
+    return self;
+}
+
+/*
+ * ADD_METHOD(name) expands to a correct PyMethodDef declaration
+ *   {  'name', (PyCFunction)crypto_Revoked_name, METH_VARARGS, crypto_Revoked_name_doc }
+ * for convenience
+ */
+#define ADD_METHOD(name)        \
+    { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS, crypto_Revoked_##name##_doc }
+#define ADD_KW_METHOD(name)        \
+    { #name, (PyCFunction)crypto_Revoked_##name, METH_VARARGS | METH_KEYWORDS, crypto_Revoked_##name##_doc }
+static PyMethodDef crypto_Revoked_methods[] =
+{
+    ADD_METHOD(get_rev_date),
+    ADD_METHOD(set_rev_date),
+    ADD_METHOD(get_serial),
+    ADD_KW_METHOD(set_serial),
+    { NULL, NULL }
+};
+#undef ADD_METHOD
+
+
+static PyObject *
+crypto_Revoked_getattr(crypto_RevokedObj *self, char *name)
+{
+    return Py_FindMethod(crypto_Revoked_methods, (PyObject *)self, name);
+}
+
+static void
+crypto_Revoked_dealloc(crypto_RevokedObj *self)
+{
+    X509_REVOKED_free(self->revoked);
+    self->revoked = NULL;
+
+    PyObject_Del(self);
+}
+
+static char crypto_Revoked_doc[] = "\n\
+Revoked() -> Revoked instance\n\
+\n\
+Create a new empty Revoked object.\n\
+\n\
+@returns: The Revoked object\n\
+";
+
+static PyObject* crypto_Revoked_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) {
+    	if (!PyArg_ParseTuple(args, ":Revoked")) {
+		return NULL;
+    	}
+	
+    	return (PyObject *)crypto_Revoked_New(X509_REVOKED_new());
+}
+
+PyTypeObject crypto_Revoked_Type = {
+    PyObject_HEAD_INIT(NULL)
+    0,
+    "Revoked",
+    sizeof(crypto_RevokedObj),
+    0,
+    (destructor)crypto_Revoked_dealloc,
+    NULL, /* print */
+    (getattrfunc)crypto_Revoked_getattr,
+    NULL, /* setattr */
+    NULL, /* compare */
+    NULL, /* repr */
+    NULL, /* as_number */
+    NULL, /* as_sequence */
+    NULL, /* as_mapping */
+    NULL, /* hash */
+    NULL, /* call */
+    NULL, /* str */
+    NULL, /* getattro */
+    NULL, /* setattro */
+    NULL, /* as_buffer */
+    Py_TPFLAGS_DEFAULT,
+    crypto_Revoked_doc, /* doc */
+    NULL, /* traverse */
+    NULL, /* clear */
+    NULL, /* tp_richcompare */
+    0, /* tp_weaklistoffset */
+    NULL, /* tp_iter */
+    NULL, /* tp_iternext */
+    crypto_Revoked_methods, /* tp_methods */
+    NULL, /* tp_members */
+    NULL, /* tp_getset */
+    NULL, /* tp_base */
+    NULL, /* tp_dict */
+    NULL, /* tp_descr_get */
+    NULL, /* tp_descr_set */
+    0, /* tp_dictoffset */
+    NULL, /* tp_init */
+    NULL, /* tp_alloc */
+    crypto_Revoked_new, /* tp_new */
+};
+
+int init_crypto_revoked(PyObject *module) {
+       if(PyType_Ready(&crypto_Revoked_Type) < 0) {
+       	       return 0;
+       }
+    
+       if (PyModule_AddObject(module, "Revoked", (PyObject *)&crypto_Revoked_Type) != 0) {
+       	       return 0;
+       }
+       return 1;
+}
+
diff --git a/src/crypto/revoked.h b/src/crypto/revoked.h
new file mode 100644
index 0000000..fb85ac6
--- /dev/null
+++ b/src/crypto/revoked.h
@@ -0,0 +1,18 @@
+#ifndef PyOpenSSL_crypto_REVOKED_H_
+#define PyOpenSSL_crypto_REVOKED_H_
+
+#include <Python.h>
+
+extern  PyTypeObject      crypto_Revoked_Type;
+
+#define crypto_Revoked_Check(v) ((v)->ob_type == &crypto_Revoked_Type)
+
+typedef struct {
+    PyObject_HEAD
+    X509_REVOKED *revoked;
+} crypto_RevokedObj;
+
+extern  int       init_crypto_revoked   (PyObject *);
+extern crypto_RevokedObj * crypto_Revoked_New(X509_REVOKED *revoked);
+
+#endif
diff --git a/src/crypto/x509.c b/src/crypto/x509.c
index e089d40..daae351 100644
--- a/src/crypto/x509.c
+++ b/src/crypto/x509.c
@@ -335,8 +335,8 @@
     return Py_None;
 }
 
-static PyObject*
-_set_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)
+PyObject*
+_set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
 {
 	char *when;
 
@@ -375,7 +375,7 @@
 crypto_X509_set_notBefore(crypto_X509Obj *self, PyObject *args)
 {
 	return _set_asn1_time(
-		"s:set_notBefore", X509_get_notBefore(self->x509), self, args);
+		"s:set_notBefore", X509_get_notBefore(self->x509), args);
 }
 
 static char crypto_X509_set_notAfter_doc[] = "\n\
@@ -394,11 +394,11 @@
 crypto_X509_set_notAfter(crypto_X509Obj *self, PyObject *args)
 {
 	return _set_asn1_time(
-		"s:set_notAfter", X509_get_notAfter(self->x509), self, args);
+		"s:set_notAfter", X509_get_notAfter(self->x509), args);
 }
 
-static PyObject*
-_get_asn1_time(char *format, ASN1_TIME* timestamp, crypto_X509Obj *self, PyObject *args)
+PyObject*
+_get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args)
 {
 	ASN1_GENERALIZEDTIME *gt_timestamp = NULL;
 	PyObject *py_timestamp = NULL;
@@ -450,7 +450,7 @@
 	 * X509_get_notBefore returns a borrowed reference.
 	 */
 	return _get_asn1_time(
-		":get_notBefore", X509_get_notBefore(self->x509), self, args);
+		":get_notBefore", X509_get_notBefore(self->x509), args);
 }
 
 
@@ -472,7 +472,7 @@
 	 * X509_get_notAfter returns a borrowed reference.
 	 */
 	return _get_asn1_time(
-		":get_notAfter", X509_get_notAfter(self->x509), self, args);
+		":get_notAfter", X509_get_notAfter(self->x509), args);
 }
 
 
diff --git a/src/crypto/x509.h b/src/crypto/x509.h
index 40768cf..43e41eb 100644
--- a/src/crypto/x509.h
+++ b/src/crypto/x509.h
@@ -16,8 +16,6 @@
 #include <Python.h>
 #include <openssl/ssl.h>
 
-extern  int       init_crypto_x509   (PyObject *);
-
 extern  PyTypeObject      crypto_X509_Type;
 
 #define crypto_X509_Check(v) ((v)->ob_type == &crypto_X509_Type)
@@ -28,5 +26,9 @@
     int                  dealloc;
 } crypto_X509Obj;
 
+PyObject* _set_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
+PyObject* _get_asn1_time(char *format, ASN1_TIME* timestamp, PyObject *args);
+extern  int       init_crypto_x509   (PyObject *);
+
 
 #endif
diff --git a/test/test_crypto.py b/test/test_crypto.py
index fce2441..630ce01 100644
--- a/test/test_crypto.py
+++ b/test/test_crypto.py
@@ -19,6 +19,7 @@
 from OpenSSL.crypto import dump_certificate_request, dump_privatekey
 from OpenSSL.crypto import PKCS7Type, load_pkcs7_data
 from OpenSSL.crypto import PKCS12Type, load_pkcs12
+from OpenSSL.crypto import CRL, Revoked, load_crl
 from OpenSSL.crypto import NetscapeSPKI, NetscapeSPKIType
 from OpenSSL.test.util import TestCase
 
@@ -1142,6 +1143,177 @@
         self.assertTrue(isinstance(nspki, NetscapeSPKIType))
 
 
+def _runopenssl(pem, *args):
+    """
+    Run the command line openssl tool with the given arguments and write
+    the given PEM to its stdin.
+    """
+    write, read = popen2(" ".join(("openssl",) + args), "b")
+    write.write(pem)
+    write.close()
+    return read.read()
+
+
+class RevokedTests(TestCase):
+    """
+    Tests for L{OpenSSL.crypto.Revoked}
+    """
+    def test_construction(self):
+        """
+        Confirm we can create L{OpenSSL.crypto.Revoked}.  Check
+        that it is empty.
+        """
+        revoked = Revoked()
+        self.assertTrue( isinstance(revoked, Revoked) )
+        self.assertEqual( type(revoked), Revoked )
+        self.assertEqual( revoked.get_serial(), '00' )
+        self.assertEqual( revoked.get_rev_date(), None )
+
+
+    def test_serial(self):
+        """
+        Confirm we can set and get serial numbers from 
+        L{OpenSSL.crypto.Revoked}.  Confirm errors are handled
+        with grace.
+        """
+        revoked = Revoked()
+        ret = revoked.set_serial('10b')
+        self.assertEqual( ret, None )
+        ser = revoked.get_serial()
+        self.assertEqual( ser, '010B' )
+
+        revoked.set_serial('31ppp')  # a type error would be nice
+        ser = revoked.get_serial()
+        self.assertEqual( ser, '31' )
+
+        self.assertRaises(TypeError, revoked.set_serial, 'pqrst')
+        self.assertRaises(TypeError, revoked.set_serial, 100)
+
+
+    def test_date(self):
+        """
+        Confirm we can set and get revocation dates from 
+        L{OpenSSL.crypto.Revoked}.  Confirm errors are handled
+        with grace.
+        """
+        revoked = Revoked()
+        date = revoked.get_rev_date()
+        self.assertEqual( date, None )
+
+        now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+        ret = revoked.set_rev_date(now)
+        self.assertEqual( ret, None )
+        date = revoked.get_rev_date()
+        self.assertEqual( date, now )
+
+
+
+class CRLTests(TestCase):
+    """
+    Tests for L{OpenSSL.crypto.CRL}
+    """
+    cert = load_certificate(FILETYPE_PEM, cleartextCertificatePEM)
+    pkey = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
+
+    def test_construction(self):
+        """
+        Confirm we can create L{OpenSSL.crypto.CRL}.  Check
+        that it is empty
+        """
+        crl = CRL()
+        self.assertTrue( isinstance(crl, CRL) )
+        self.assertEqual(crl.get_revoked(), None)
+
+
+    def test_export(self):
+        """
+        Use python to create a simple CRL with a revocation, and export
+        the CRL in formats of PEM, DER and text.  Those outputs are verified
+        with the openssl program.
+        """
+        crl = CRL()
+        revoked = Revoked()
+        now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+        revoked.set_rev_date(now)
+        revoked.set_serial('3ab')
+        crl.add_revoked(revoked)
+
+        # PEM format
+        dumped_crl = crl.export(self.cert, self.pkey, days=20)
+        text = _runopenssl(dumped_crl, "crl", "-noout", "-text")
+        text.index('Serial Number: 03AB')
+        text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
+
+        # DER format
+        dumped_crl = crl.export(self.cert, self.pkey, FILETYPE_ASN1)
+        text = _runopenssl(dumped_crl, "crl", "-noout", "-text", "-inform", "DER")
+        text.index('Serial Number: 03AB')
+        text.index('Issuer: /C=US/ST=IL/L=Chicago/O=Testing/CN=Testing Root CA')
+
+        # text format
+        dumped_text = crl.export(self.cert, self.pkey, type=FILETYPE_TEXT)
+        self.assertEqual(text, dumped_text)
+
+
+    def test_get_revoked(self):
+        """
+        Use python to create a simple CRL with two revocations.  
+        Get back the L{Revoked} using L{OpenSSL.CRL.get_revoked} and 
+        verify them.
+        """
+        crl = CRL()
+
+        revoked = Revoked()
+        now = datetime.now().strftime("%Y%m%d%H%M%SZ")
+        revoked.set_rev_date(now)
+        revoked.set_serial('3ab')
+        crl.add_revoked(revoked)
+        revoked.set_serial('100')
+        crl.add_revoked(revoked)
+
+        revs = crl.get_revoked()
+        self.assertEqual(len(revs), 2)
+        self.assertEqual(type(revs[0]), Revoked)
+        self.assertEqual(type(revs[1]), Revoked)
+        self.assertEqual(revs[0].get_serial(), '03AB')
+        self.assertEqual(revs[1].get_serial(), '0100')
+        self.assertEqual(revs[0].get_rev_date(), now)
+        self.assertEqual(revs[1].get_rev_date(), now)
+
+
+    def test_load_crl(self):
+        """
+        Load a known CRL and inspect its revocations.  Both
+        PEM and DER formats are loaded.
+        """
+
+        crl_txt = """
+-----BEGIN X509 CRL-----
+MIIBTTCBtzANBgkqhkiG9w0BAQQFADBYMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
+SUwxEDAOBgNVBAcTB0NoaWNhZ28xEDAOBgNVBAoTB1Rlc3RpbmcxGDAWBgNVBAMT
+D1Rlc3RpbmcgUm9vdCBDQRcNMDkwNzI1MDIxMjE0WhcNMDkxMTAyMDIxMjE0WjAu
+MBUCAgOrGA8yMDA5MDcyNDIxMTIxNFowFQICAQAYDzIwMDkwNzI0MjExMjE0WjAN
+BgkqhkiG9w0BAQQFAAOBgQApflU91pdbbSXNMLxRHAwz+2M2vzhmpFDYsX8gPe76
+GgrEY475v1CGJTdmKQnwosUx1tJ6HgoueAfTvzLGgVhqfeeR6BTjhnJH69rW+L6A
+w47xSB7rmUglsn3HlAdZl4tIex+SlH7AB1mEsWNJ0VA0mDEF01eOaBwBfEmK3zGd
+ng==
+-----END X509 CRL-----
+"""
+        crl = load_crl(FILETYPE_PEM, crl_txt) 
+        revs = crl.get_revoked()
+        self.assertEqual(len(revs), 2)
+        self.assertEqual(revs[0].get_serial(), '03AB')
+        self.assertEqual(revs[1].get_serial(), '0100')
+
+        der = _runopenssl(crl_txt, "crl", "-outform", "DER")
+        crl = load_crl(FILETYPE_ASN1, der) 
+        revs = crl.get_revoked()
+        self.assertEqual(len(revs), 2)
+        self.assertEqual(revs[0].get_serial(), '03AB')
+        self.assertEqual(revs[1].get_serial(), '0100')
+
+
+
 
 if __name__ == '__main__':
     main()