diff --git a/c/opendht.cpp b/c/opendht.cpp
index b497ecc0a0865d1048bfa7835c4d2c84b0dd96b2..58a7db343955303264d7d0681d44bff340da6349 100644
--- a/c/opendht.cpp
+++ b/c/opendht.cpp
@@ -3,6 +3,7 @@
 
 using ValueSp = std::shared_ptr<dht::Value>;
 using PrivkeySp = std::shared_ptr<dht::crypto::PrivateKey>;
+using PubkeySp = std::shared_ptr<const dht::crypto::PublicKey>;
 using CertSp = std::shared_ptr<dht::crypto::Certificate>;
 
 #ifdef __cplusplus
@@ -10,6 +11,19 @@ extern "C" {
 #endif
 
 // dht::InfoHash
+
+inline dht_infohash dht_infohash_to_c(const dht::InfoHash& h) {
+    dht_infohash ret;
+    *reinterpret_cast<dht::InfoHash*>(&ret) = h;
+    return ret;
+}
+
+inline dht_pkid dht_pkid_to_c(const dht::PkId& h) {
+    dht_pkid ret;
+    *reinterpret_cast<dht::PkId*>(&ret) = h;
+    return ret;
+}
+
 const char* dht_infohash_print(const dht_infohash* h) {
     return reinterpret_cast<const dht::InfoHash*>(h)->to_c_str();
 }
@@ -26,24 +40,20 @@ void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size) {
     *reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::get(dat, dat_size);
 }
 
-bool dht_infohash_is_zero(const dht_infohash* h) {
-    return !static_cast<bool>(*reinterpret_cast<const dht::InfoHash*>(h));
+void dht_infohash_get_from_string(dht_infohash* h, const char* dat) {
+    *reinterpret_cast<dht::InfoHash*>(h) = dht::InfoHash::get((const uint8_t*)dat, (size_t)strlen(dat));
 }
 
-const char* dht_pkid_print(const dht_pkid* h) {
-    return reinterpret_cast<const dht::PkId*>(h)->to_c_str();
+bool dht_infohash_is_zero(const dht_infohash* h) {
+    return !static_cast<bool>(*reinterpret_cast<const dht::InfoHash*>(h));
 }
 
-inline dht_infohash dht_infohash_to_c(const dht::InfoHash& h) {
-    dht_infohash ret;
-    *reinterpret_cast<dht::InfoHash*>(&ret) = h;
-    return ret;
+void dht_infohash_from_hex(dht_infohash* h, const char* dat) {
+    *h = dht_infohash_to_c(dht::InfoHash(std::string(dat, HASH_LEN*2)));
 }
 
-inline dht_pkid dht_pkid_to_c(const dht::PkId& h) {
-    dht_pkid ret;
-    *reinterpret_cast<dht::PkId*>(&ret) = h;
-    return ret;
+const char* dht_pkid_print(const dht_pkid* h) {
+    return reinterpret_cast<const dht::PkId*>(h)->to_c_str();
 }
 
 // dht::Blob
@@ -72,6 +82,21 @@ dht_value_id dht_value_get_id(const dht_value* data) {
     return vsp->id;
 }
 
+dht_publickey* dht_value_get_owner(const dht_value* data) {
+    const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
+    return reinterpret_cast<dht_publickey*>(new PubkeySp(vsp->owner));
+}
+
+dht_infohash dht_value_get_recipient(const dht_value* data) {
+    const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
+    return dht_infohash_to_c(vsp->recipient);
+}
+
+const char* dht_value_get_user_type(const dht_value* data) {
+    const ValueSp& vsp(*reinterpret_cast<const ValueSp*>(data));
+    return vsp->user_type.c_str();
+}
+
 dht_value* dht_value_new(const uint8_t* data, size_t size) {
     return reinterpret_cast<dht_value*>(new ValueSp(std::make_shared<dht::Value>(data, size)));
 }
@@ -85,44 +110,42 @@ void dht_value_unref(dht_value* v) {
 }
 
 // dht::crypto::PublicKey
-dht_publickey* dht_publickey_new() {
-    return reinterpret_cast<dht_publickey*>(new dht::crypto::PublicKey);
-}
-
-void dht_publickey_delete(dht_publickey* pk) {
-    delete reinterpret_cast<dht::crypto::PublicKey*>(pk);
-}
-
-int dht_publickey_unpack(dht_publickey* pk, const uint8_t* dat, size_t dat_size) {
+dht_publickey* dht_publickey_import(const uint8_t* dat, size_t dat_size) {
     try {
-        reinterpret_cast<dht::crypto::PublicKey*>(pk)->unpack(dat, dat_size);
+        return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<const dht::crypto::PublicKey>(dat, dat_size)));
     } catch (const dht::crypto::CryptoException& e) {
-        return -1;
+        return nullptr;
     }
-    return 0;
+}
+
+void dht_publickey_delete(dht_publickey* pk) {
+    delete reinterpret_cast<PubkeySp*>(pk);
 }
 
 int dht_publickey_pack(dht_publickey* pk, char* out, size_t* outlen) {
-    return gnutls_pubkey_export(reinterpret_cast<dht::crypto::PublicKey*>(pk)->pk, GNUTLS_X509_FMT_DER, out, outlen);
+    const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
+    return gnutls_pubkey_export(pkey->pk, GNUTLS_X509_FMT_DER, out, outlen);
 }
 
 dht_infohash dht_publickey_get_id(const dht_publickey* pk) {
-    auto pkey = reinterpret_cast<const dht::crypto::PublicKey*>(pk);
+    const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
     return dht_infohash_to_c(pkey->getId());
 }
 
 dht_pkid dht_publickey_get_long_id(const dht_publickey* pk) {
-    auto pkey = reinterpret_cast<const dht::crypto::PublicKey*>(pk);
+    const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
     return dht_pkid_to_c(pkey->getLongId());
 }
 
 bool dht_publickey_check_signature(const dht_publickey* pk, const char* data, size_t data_size, const char* signature, size_t signature_size) {
-    return reinterpret_cast<const dht::crypto::PublicKey*>(pk)->checkSignature((const uint8_t*)data, data_size, (const uint8_t*)signature, signature_size);
+    const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
+    return pkey->checkSignature((const uint8_t*)data, data_size, (const uint8_t*)signature, signature_size);
 }
 
 dht_blob* dht_publickey_encrypt(const dht_publickey* pk, const char* data, size_t data_size) {
+    const auto& pkey = *reinterpret_cast<const PubkeySp*>(pk);
     auto rdata = new dht::Blob;
-    *rdata = reinterpret_cast<const dht::crypto::PublicKey*>(pk)->encrypt((const uint8_t*)data, data_size);
+    *rdata = pkey->encrypt((const uint8_t*)data, data_size);
     return (dht_blob*)rdata;
 }
 
@@ -143,7 +166,7 @@ dht_privatekey* dht_privatekey_import(const uint8_t* dat, size_t dat_size, const
 
 dht_publickey* dht_privatekey_get_publickey(const dht_privatekey* k) {
     const auto& key = *reinterpret_cast<const PrivkeySp*>(k);
-    return reinterpret_cast<dht_publickey*>(new dht::crypto::PublicKey(key->getPublicKey()));
+    return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<dht::crypto::PublicKey>(key->getPublicKey())));
 }
 
 void dht_privatekey_delete(dht_privatekey* pk) {
@@ -175,7 +198,7 @@ dht_pkid dht_certificate_get_long_id(const dht_certificate* c) {
 
 dht_publickey* dht_certificate_get_publickey(const dht_certificate* c) {
     const auto& cert = *reinterpret_cast<const CertSp*>(c);
-    return reinterpret_cast<dht_publickey*>(new dht::crypto::PublicKey(cert->getPublicKey()));
+    return reinterpret_cast<dht_publickey*>(new PubkeySp(std::make_shared<dht::crypto::PublicKey>(cert->getPublicKey())));
 }
 
 // dht::crypto::Identity
diff --git a/c/opendht_c.h b/c/opendht_c.h
index a78f7d33e44264ff455394055a80600cf0e3f162..c1fce291fbf171fa9d79eae7c64f4250015fa7d6 100644
--- a/c/opendht_c.h
+++ b/c/opendht_c.h
@@ -18,16 +18,6 @@ struct OPENDHT_C_PUBLIC dht_data_view {
 };
 typedef struct dht_data_view dht_data_view;
 
-// dht::Value
-struct OPENDHT_C_PUBLIC dht_value;
-typedef struct dht_value dht_value;
-typedef uint64_t dht_value_id;
-OPENDHT_C_PUBLIC dht_data_view dht_value_get_data(const dht_value* data);
-OPENDHT_C_PUBLIC dht_value_id dht_value_get_id(const dht_value* data);
-OPENDHT_C_PUBLIC dht_value* dht_value_new(const uint8_t* data, size_t size);
-OPENDHT_C_PUBLIC dht_value* dht_value_ref(const dht_value*);
-OPENDHT_C_PUBLIC void dht_value_unref(dht_value*);
-
 // dht::Blob
 struct OPENDHT_C_PUBLIC dht_blob;
 typedef struct dht_blob dht_blob;
@@ -39,7 +29,9 @@ struct OPENDHT_C_PUBLIC dht_infohash { uint8_t d[HASH_LEN]; };
 typedef struct dht_infohash dht_infohash;
 OPENDHT_C_PUBLIC void dht_infohash_zero(dht_infohash* h);
 OPENDHT_C_PUBLIC void dht_infohash_random(dht_infohash* h);
+OPENDHT_C_PUBLIC void dht_infohash_from_hex(dht_infohash* h, const char* dat);
 OPENDHT_C_PUBLIC void dht_infohash_get(dht_infohash* h, const uint8_t* dat, size_t dat_size);
+OPENDHT_C_PUBLIC void dht_infohash_get_from_string(dht_infohash* h, const char* str);
 OPENDHT_C_PUBLIC const char* dht_infohash_print(const dht_infohash* h);
 OPENDHT_C_PUBLIC bool dht_infohash_is_zero(const dht_infohash* h);
 
@@ -51,9 +43,8 @@ OPENDHT_C_PUBLIC const char* dht_pkid_print(const dht_pkid* h);
 // dht::crypto::PublicKey
 struct OPENDHT_C_PUBLIC dht_publickey;
 typedef struct dht_publickey dht_publickey;
-OPENDHT_C_PUBLIC dht_publickey* dht_publickey_new();
+OPENDHT_C_PUBLIC dht_publickey* dht_publickey_import(const uint8_t* dat, size_t dat_size);
 OPENDHT_C_PUBLIC void dht_publickey_delete(dht_publickey* pk);
-OPENDHT_C_PUBLIC int dht_publickey_unpack(dht_publickey* pk, const uint8_t* dat, size_t dat_size);
 OPENDHT_C_PUBLIC int dht_publickey_pack(dht_publickey* pk, char* out, size_t* out_size);
 OPENDHT_C_PUBLIC dht_infohash dht_publickey_get_id(const dht_publickey* pk);
 OPENDHT_C_PUBLIC dht_pkid dht_publickey_get_long_id(const dht_publickey* pk);
@@ -85,6 +76,19 @@ typedef struct dht_identity dht_identity;
 OPENDHT_C_PUBLIC dht_identity dht_identity_generate(const char* common_name, const dht_identity* ca);
 OPENDHT_C_PUBLIC void dht_identity_delete(dht_identity*);
 
+// dht::Value
+struct OPENDHT_C_PUBLIC dht_value;
+typedef struct dht_value dht_value;
+typedef uint64_t dht_value_id;
+OPENDHT_C_PUBLIC dht_value* dht_value_new(const uint8_t* data, size_t size);
+OPENDHT_C_PUBLIC dht_value* dht_value_ref(const dht_value*);
+OPENDHT_C_PUBLIC void dht_value_unref(dht_value*);
+OPENDHT_C_PUBLIC dht_data_view dht_value_get_data(const dht_value* data);
+OPENDHT_C_PUBLIC dht_value_id dht_value_get_id(const dht_value* data);
+OPENDHT_C_PUBLIC dht_publickey* dht_value_get_owner(const dht_value* data);
+OPENDHT_C_PUBLIC dht_infohash dht_value_get_recipient(const dht_value* data);
+OPENDHT_C_PUBLIC const char* dht_value_get_user_type(const dht_value* data);
+
 // callbacks
 typedef bool (*dht_get_cb)(const dht_value* value, void* user_data);
 typedef bool (*dht_value_cb)(const dht_value* value, bool expired, void* user_data);
@@ -105,8 +109,7 @@ struct OPENDHT_PUBLIC dht_node_config {
 };
 typedef struct dht_node_config dht_node_config;
 
-struct OPENDHT_PUBLIC dht_secure_config
-{
+struct OPENDHT_PUBLIC dht_secure_config {
     dht_node_config node_config;
     dht_identity id;
 };
diff --git a/include/opendht/crypto.h b/include/opendht/crypto.h
index 1bdcb200b9268d06cdf4babeccbe52ace304fb68..72eda3d57819bc106d46ed552c8584fc64aac837 100644
--- a/include/opendht/crypto.h
+++ b/include/opendht/crypto.h
@@ -72,7 +72,8 @@ struct OPENDHT_PUBLIC PublicKey
      * Takes ownership of an existing gnutls_pubkey.
      */
     PublicKey(gnutls_pubkey_t k) : pk(k) {}
-    PublicKey(const Blob& pk);
+    PublicKey(const uint8_t* dat, size_t dat_size);
+    PublicKey(const Blob& pk) : PublicKey(pk.data(), pk.size()) {}
     PublicKey(PublicKey&& o) noexcept : pk(o.pk) { o.pk = nullptr; };
 
     ~PublicKey();
diff --git a/src/crypto.cpp b/src/crypto.cpp
index 6546ed36fc55816ccf8d6d85d79748040fec22ed..0e3ed7ab59104a86a19486005a873ac46f735c86 100644
--- a/src/crypto.cpp
+++ b/src/crypto.cpp
@@ -374,9 +374,9 @@ PublicKey::PublicKey()
         throw CryptoException(std::string("Can't initialize public key: ") + gnutls_strerror(err));
 }
 
-PublicKey::PublicKey(const Blob& dat) : PublicKey()
+PublicKey::PublicKey(const uint8_t* dat, size_t dat_size) : PublicKey()
 {
-    unpack(dat.data(), dat.size());
+    unpack(dat, dat_size);
 }
 
 PublicKey::~PublicKey()
diff --git a/tools/dhtcnode.c b/tools/dhtcnode.c
index ad72a72ddb33367dea6dea639f67c83b3ad743e6..c57d809e76bba52c89875d952ed7fc8a6c6989b1 100644
--- a/tools/dhtcnode.c
+++ b/tools/dhtcnode.c
@@ -62,6 +62,22 @@ char* print_addr(const struct sockaddr* addr) {
 
 int main()
 {
+    dht_identity id = dht_identity_generate("testNode", NULL);
+    dht_infohash cert_id = dht_certificate_get_id(id.certificate);
+    printf("Cert ID: %s\n", dht_infohash_print(&cert_id));
+
+    dht_publickey* pk = dht_certificate_get_publickey(id.certificate);
+    dht_infohash pk_id = dht_publickey_get_id(pk);
+    printf("PK ID: %s\n", dht_infohash_print(&pk_id));
+    dht_publickey_delete(pk);
+
+    pk = dht_privatekey_get_publickey(id.privatekey);
+    pk_id = dht_publickey_get_id(pk);
+    printf("Key ID: %s\n", dht_infohash_print(&pk_id));
+    dht_publickey_delete(pk);
+
+    dht_identity_delete(&id);
+
     dht_runner* runner = dht_runner_new();
     dht_runner_run(runner, 4040);