Repository: pyca/cryptography

Issue:
Support Serializing CRLs
We need a `public_bytes` method...



Discussion:

Resolving patch:
diff --git a/src/cryptography/hazmat/backends/openssl/x509.py b/src/cryptography/hazmat/backends/openssl/x509.py
--- a/src/cryptography/hazmat/backends/openssl/x509.py
+++ b/src/cryptography/hazmat/backends/openssl/x509.py
@@ -833,6 +833,20 @@ def tbs_certlist_bytes(self):
         )
         return self._backend._ffi.buffer(pp[0], res)[:]
 
+    def public_bytes(self, encoding):
+        bio = self._backend._create_mem_bio()
+        if encoding is serialization.Encoding.PEM:
+            res = self._backend._lib.PEM_write_bio_X509_CRL(
+                bio, self._x509_crl
+            )
+        elif encoding is serialization.Encoding.DER:
+            res = self._backend._lib.i2d_X509_CRL_bio(bio, self._x509_crl)
+        else:
+            raise TypeError("encoding must be an item from the Encoding enum")
+
+        self._backend.openssl_assert(res == 1)
+        return self._backend._read_mem_bio(bio)
+
     def _revoked_certificates(self):
         revoked = self._backend._lib.X509_CRL_get_REVOKED(self._x509_crl)
         revoked_list = []
diff --git a/src/cryptography/x509/base.py b/src/cryptography/x509/base.py
--- a/src/cryptography/x509/base.py
+++ b/src/cryptography/x509/base.py
@@ -156,6 +156,11 @@ def public_bytes(self, encoding):
 
 @six.add_metaclass(abc.ABCMeta)
 class CertificateRevocationList(object):
+    @abc.abstractmethod
+    def public_bytes(self, encoding):
+        """
+        Serializes the CRL to PEM or DER format.
+        """
 
     @abc.abstractmethod
     def fingerprint(self, algorithm):


Test patch:
diff --git a/tests/test_x509.py b/tests/test_x509.py
--- a/tests/test_x509.py
+++ b/tests/test_x509.py
@@ -222,6 +222,72 @@ def test_tbs_certlist_bytes(self, backend):
         verifier.update(crl.tbs_certlist_bytes)
         verifier.verify()
 
+    def test_public_bytes_pem(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "crl_empty.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+
+        # Encode it to PEM and load it back.
+        crl = x509.load_pem_x509_crl(crl.public_bytes(
+            encoding=serialization.Encoding.PEM,
+        ), backend)
+
+        assert len(crl) == 0
+        assert crl.last_update == datetime.datetime(2015, 12, 20, 23, 44, 47)
+        assert crl.next_update == datetime.datetime(2015, 12, 28, 0, 44, 47)
+
+    def test_public_bytes_der(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "crl_all_reasons.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+
+        # Encode it to DER and load it back.
+        crl = x509.load_der_x509_crl(crl.public_bytes(
+            encoding=serialization.Encoding.DER,
+        ), backend)
+
+        assert len(crl) == 12
+        assert crl.last_update == datetime.datetime(2015, 1, 1, 0, 0, 0)
+        assert crl.next_update == datetime.datetime(2016, 1, 1, 0, 0, 0)
+
+    @pytest.mark.parametrize(
+        ("cert_path", "loader_func", "encoding"),
+        [
+            (
+                os.path.join("x509", "custom", "crl_all_reasons.pem"),
+                x509.load_pem_x509_crl,
+                serialization.Encoding.PEM,
+            ),
+            (
+                os.path.join("x509", "PKITS_data", "crls", "GoodCACRL.crl"),
+                x509.load_der_x509_crl,
+                serialization.Encoding.DER,
+            ),
+        ]
+    )
+    def test_public_bytes_match(self, cert_path, loader_func, encoding,
+                                backend):
+        crl_bytes = load_vectors_from_file(
+            cert_path, lambda pemfile: pemfile.read(), mode="rb"
+        )
+        crl = loader_func(crl_bytes, backend)
+        serialized = crl.public_bytes(encoding)
+        assert serialized == crl_bytes
+
+    def test_public_bytes_invalid_encoding(self, backend):
+        crl = _load_cert(
+            os.path.join("x509", "custom", "crl_empty.pem"),
+            x509.load_pem_x509_crl,
+            backend
+        )
+
+        with pytest.raises(TypeError):
+            crl.public_bytes('NotAnEncoding')
+
 
 @pytest.mark.requires_backend_interface(interface=X509Backend)
 class TestRevokedCertificate(object):