diff --git a/include/opendht/crypto.h b/include/opendht/crypto.h
index d4cf548e810b08645fcbe62ae4d44f8c7b7f4e3b..82163aefa76e13422040732f6264a044fb112673 100644
--- a/include/opendht/crypto.h
+++ b/include/opendht/crypto.h
@@ -53,6 +53,7 @@ class OPENDHT_PUBLIC DecryptError : public CryptoException {
 
 struct PrivateKey;
 struct Certificate;
+class RevocationList;
 
 using Identity = std::pair<std::shared_ptr<PrivateKey>, std::shared_ptr<Certificate>>;
 
@@ -310,6 +311,11 @@ struct OPENDHT_PUBLIC Certificate {
 
     std::string print() const;
 
+    void revoke(const PrivateKey&, const Certificate&);
+    std::vector<std::shared_ptr<RevocationList>> getRevocationLists() const { return revocation_lists; }
+    void addRevocationList(RevocationList&&);
+    void addRevocationList(std::shared_ptr<RevocationList>);
+
     static Certificate generate(const PrivateKey& key, const std::string& name = "dhtnode", Identity ca = {}, bool is_ca = false);
 
     gnutls_x509_crt_t cert {};
@@ -317,6 +323,7 @@ struct OPENDHT_PUBLIC Certificate {
 private:
     Certificate(const Certificate&) = delete;
     Certificate& operator=(const Certificate&) = delete;
+    std::vector<std::shared_ptr<RevocationList>> revocation_lists;
 };
 
 
@@ -340,10 +347,20 @@ public:
         return b;
     }
 
-    bool isRevoked(const Certificate& crt) const;
+    template <typename Packer>
+    void msgpack_pack(Packer& p) const
+    {
+        Blob b = getPacked();
+        p.pack_bin(b.size());
+        p.pack_bin_body((const char*)b.data(), b.size());
+    }
+
+    void msgpack_unpack(msgpack::object o);
 
     void revoke(const Certificate& crt, time_point t = time_point::min());
 
+    bool isRevoked(const Certificate& crt) const;
+
     /**
      * Sign this revocation list using provided key and certificate.
      */
@@ -354,6 +371,11 @@ public:
 
     std::string toString() const;
 
+    /**
+     * Read the CRL number extension field.
+     */
+    Blob getNumber() const;
+
     gnutls_x509_crl_t get() { return crl; }
 
 private:
diff --git a/src/crypto.cpp b/src/crypto.cpp
index fc1fedbad0a0c8ed83bed02778464b43922b4b7d..f40337c33fc1c0a96523dfeb224b203b78bdbe1f 100644
--- a/src/crypto.cpp
+++ b/src/crypto.cpp
@@ -735,6 +735,30 @@ Certificate::print() const
     return ret;
 }
 
+void
+Certificate::revoke(const PrivateKey& key, const Certificate& to_revoke)
+{
+    if (revocation_lists.empty())
+        revocation_lists.emplace_back(std::make_shared<RevocationList>());
+    auto& list = *revocation_lists.back();
+    list.revoke(to_revoke);
+    list.sign(key, *this);
+}
+
+void
+Certificate::addRevocationList(RevocationList&& list)
+{
+    addRevocationList(std::make_shared<RevocationList>(std::forward<RevocationList>(list)));
+}
+
+void
+Certificate::addRevocationList(std::shared_ptr<RevocationList> list)
+{
+    if (not list->isSignedBy(*this))
+        throw CryptoException("CRL is not signed by this certificate");
+    revocation_lists.emplace_back(std::move(list));
+}
+
 PrivateKey
 PrivateKey::generate(unsigned key_length)
 {
@@ -884,6 +908,21 @@ RevocationList::unpack(const uint8_t* dat, size_t dat_size)
         }
 }
 
+void
+RevocationList::msgpack_unpack(msgpack::object o)
+{
+    try {
+        if (o.type == msgpack::type::BIN)
+            unpack((const uint8_t*)o.via.bin.ptr, o.via.bin.size);
+        else {
+            Blob dat = unpackBlob(o);
+            unpack(dat.data(), dat.size());
+        }
+    } catch (...) {
+        throw msgpack::type_error();
+    }
+}
+
 bool
 RevocationList::isRevoked(const Certificate& crt) const
 {
@@ -965,6 +1004,19 @@ RevocationList::isSignedBy(const Certificate& issuer) const
     return result == 0;
 }
 
+
+Blob
+RevocationList::getNumber() const
+{
+    Blob number(20);
+    size_t number_sz {number.size()};
+    unsigned critical {0};
+    gnutls_x509_crl_get_number(crl, number.data(), &number_sz, &critical);
+    if (number_sz != number.size())
+        number.resize(number_sz);
+    return number;
+}
+
 std::string
 RevocationList::toString() const
 {