diff --git a/include/opendht/crypto.h b/include/opendht/crypto.h
index 83d2cb90d2171328b241de827a59eadb94b33ea1..8960f37a26108ed14681d2cd5965515ea8c53131 100644
--- a/include/opendht/crypto.h
+++ b/include/opendht/crypto.h
@@ -164,7 +164,9 @@ struct OPENDHT_PUBLIC PrivateKey
     ~PrivateKey();
     explicit operator bool() const { return key; }
 
-    PublicKey getPublicKey() const;
+    const PublicKey& getPublicKey() const;
+    const std::shared_ptr<PublicKey>& getSharedPublicKey() const;
+
     int serialize(uint8_t* out, size_t* out_len, const std::string& password = {}) const;
     Blob serialize(const std::string& password = {}) const;
 
@@ -199,7 +201,7 @@ private:
     PrivateKey& operator=(const PrivateKey&) = delete;
     Blob decryptBloc(const uint8_t* src, size_t src_size) const;
 
-    //friend dht::crypto::Identity dht::crypto::generateIdentity(const std::string&, dht::crypto::Identity, unsigned key_length);
+    mutable std::shared_ptr<PublicKey> publicKey_ {};
 };
 
 class OPENDHT_PUBLIC RevocationList
diff --git a/include/opendht/securedht.h b/include/opendht/securedht.h
index 237dfb50058bf363f5d1188cd354319d7a6d7703..e1d046606404c3b4701867960cdc47d7715f8c71 100644
--- a/include/opendht/securedht.h
+++ b/include/opendht/securedht.h
@@ -61,6 +61,9 @@ public:
     PkId getLongId() const {
         return key_ ? key_->getPublicKey().getLongId() : PkId();
     }
+    Sp<crypto::PublicKey> getPublicKey() const {
+        return key_ ? key_->getSharedPublicKey() : Sp<crypto::PublicKey>{};
+    }
 
     ValueType secureType(ValueType&& type);
 
diff --git a/include/opendht/value.h b/include/opendht/value.h
index a99363705b91a11192e0904ad2c3ce6b58e25ac9..f949c9b7aed5ca4bda701278ba0be2b52dfb2a17 100644
--- a/include/opendht/value.h
+++ b/include/opendht/value.h
@@ -356,7 +356,7 @@ struct OPENDHT_PUBLIC Value
     void sign(const crypto::PrivateKey& key) {
         if (isEncrypted())
             throw DhtException("Can't sign encrypted data.");
-        owner = std::make_shared<crypto::PublicKey>(key.getPublicKey());
+        owner = key.getSharedPublicKey();
         signature = key.sign(getToSign());
     }
 
diff --git a/python/opendht.pyx b/python/opendht.pyx
index 31ac76094b396eade4fb80427051fd04937d7f7c..8ffe6202d62053e7bbbfd40ed9f7b94dcd4faf89 100644
--- a/python/opendht.pyx
+++ b/python/opendht.pyx
@@ -329,11 +329,11 @@ cdef class PrivateKey(_WithID):
     cdef shared_ptr[cpp.PrivateKey] _key
     def getId(self):
         h = InfoHash()
-        h._infohash = self._key.get().getPublicKey().getId()
+        h._infohash = self._key.get().getSharedPublicKey().get().getId()
         return h
     def getPublicKey(self):
         pk = PublicKey()
-        pk._key = self._key.get().getPublicKey()
+        pk._key = self._key.get().getSharedPublicKey()
         return pk
     def decrypt(self, bytes dat):
         cdef size_t d_len = len(dat)
@@ -358,17 +358,17 @@ cdef class PrivateKey(_WithID):
         return k
 
 cdef class PublicKey(_WithID):
-    cdef cpp.PublicKey _key
+    cdef cpp.shared_ptr[cpp.PublicKey] _key
     def getId(self):
         h = InfoHash()
-        h._infohash = self._key.getId()
+        h._infohash = self._key.get().getId()
         return h
     def encrypt(self, bytes dat):
         cdef size_t d_len = len(dat)
         cdef cpp.uint8_t* d_ptr = <cpp.uint8_t*>dat
         cdef cpp.Blob indat
         indat.assign(d_ptr, <cpp.uint8_t*>(d_ptr + d_len))
-        cdef cpp.Blob encrypted = self._key.encrypt(indat)
+        cdef cpp.Blob encrypted = self._key.get().encrypt(indat)
         cdef char* encrypted_c_str = <char *>encrypted.data()
         cdef Py_ssize_t length = encrypted.size()
         return encrypted_c_str[:length]
@@ -440,7 +440,7 @@ cdef class Identity(object):
     property publickey:
         def __get__(self):
             k = PublicKey()
-            k._key = self._id.first.get().getPublicKey()
+            k._key = self._id.first.get().getSharedPublicKey()
             return k
     property certificate:
         def __get__(self):
diff --git a/python/opendht_cpp.pxd b/python/opendht_cpp.pxd
index 2aeff604ccf93054a6d811c9845fa4cb46370a30..0033caaa0a67f4e1af34853a8ec059589b282311 100644
--- a/python/opendht_cpp.pxd
+++ b/python/opendht_cpp.pxd
@@ -97,7 +97,7 @@ cdef extern from "opendht/crypto.h" namespace "dht::crypto":
 
     cdef cppclass PrivateKey:
         PrivateKey()
-        PublicKey getPublicKey() const
+        shared_ptr[PublicKey] getSharedPublicKey() const
         Blob decrypt(Blob data) const
         @staticmethod
         PrivateKey generate()
diff --git a/src/crypto.cpp b/src/crypto.cpp
index 78ccb0825b005b11456e90aacc8e117852f557ac..80dbbbdce0f921f86524998bc4afdc200da1d824 100644
--- a/src/crypto.cpp
+++ b/src/crypto.cpp
@@ -368,13 +368,22 @@ PrivateKey::serialize(uint8_t* out, size_t* out_len, const std::string& password
         : gnutls_x509_privkey_export_pkcs8(x509_key, GNUTLS_X509_FMT_PEM, password.c_str(), GNUTLS_PKCS_PBES2_AES_256, out, out_len);
 }
 
-PublicKey
+const PublicKey&
 PrivateKey::getPublicKey() const
 {
-    PublicKey pk;
-    if (auto err = gnutls_pubkey_import_privkey(pk.pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0))
+    return *getSharedPublicKey();
+}
+
+const std::shared_ptr<PublicKey>&
+PrivateKey::getSharedPublicKey() const
+{
+    if (publicKey_)
+        return publicKey_;
+    auto pk = std::make_shared<PublicKey>();
+    if (auto err = gnutls_pubkey_import_privkey(pk->pk, key, GNUTLS_KEY_KEY_CERT_SIGN | GNUTLS_KEY_CRL_SIGN, 0))
         throw CryptoException(std::string("Can't retreive public key: ") + gnutls_strerror(err));
-    return pk;
+    publicKey_ = pk;
+    return publicKey_;
 }
 
 PublicKey::PublicKey()
@@ -540,12 +549,7 @@ PublicKey::getId() const
         return {};
     InfoHash id;
     size_t sz = id.size();
-#if GNUTLS_VERSION_NUMBER < 0x030401
-    const int flags = 0;
-#else
-    const int flags = (id.size() == 32) ? GNUTLS_KEYID_USE_SHA256 : 0;
-#endif
-    if (auto err = gnutls_pubkey_get_key_id(pk, flags, id.data(), &sz))
+    if (auto err = gnutls_pubkey_get_key_id(pk, 0, id.data(), &sz))
         throw CryptoException(std::string("Can't get public key ID: ") + gnutls_strerror(err));
     if (sz != id.size())
         throw CryptoException("Can't get public key ID: wrong output length.");
@@ -1169,7 +1173,7 @@ Certificate::generate(const PrivateKey& key, const std::string& name, const Iden
     }
 
     // TODO: compute the subject key using the recommended RFC method
-    auto pk = key.getPublicKey();
+    const auto& pk = key.getPublicKey();
     auto pk_id = pk.getId();
     const std::string uid_str = pk_id.toString();
 
diff --git a/tests/cryptotester.cpp b/tests/cryptotester.cpp
index 4ca56528375d88f12313ed1db29b39af78b892c2..8c4c4e996213f74bd3d53a46548d7d6132624b5e 100644
--- a/tests/cryptotester.cpp
+++ b/tests/cryptotester.cpp
@@ -33,7 +33,7 @@ CryptoTester::setUp() {
 void
 CryptoTester::testSignatureEncryption() {
     auto key = dht::crypto::PrivateKey::generate();
-    auto public_key = key.getPublicKey();
+    const auto& public_key = key.getPublicKey();
 
     std::vector<uint8_t> data1 {5, 10};
     std::vector<uint8_t> data2(64 * 1024, 10);