diff --git a/.gitignore b/.gitignore
index 9f62575e5..4387e1c9d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,3 +120,5 @@ nbproject
 __pycache__/
 *.pyc
 *.log
+.gitignore
+/dist/
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index bb65304d4..e95ff1b05 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -56,6 +56,8 @@ using namespace epee;
 #include "rpc/core_rpc_server_commands_defs.h"
 #include "daemonizer/daemonizer.h"
 
+#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\003"
+
 #undef MONERO_DEFAULT_LOG_CATEGORY
 #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
 
@@ -3104,6 +3106,152 @@ namespace tools
 
     return true;
   }
+  //------------------------------------------------------------------------------------------------------------------------------
+  bool wallet_rpc_server::on_export_encrypted_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+{
+  if (!m_wallet) return not_open(er);
+  try
+  {
+    std::pair<uint64_t, std::vector<std::pair<crypto::key_image, crypto::signature>>> ski = m_wallet->export_key_images(req.all);
+    res.offset = ski.first;
+
+    std::string data;
+    const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address;
+
+    data.reserve(4 + sizeof(crypto::public_key) * 2 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)));
+
+    data.resize(4);
+    data[0] = res.offset & 0xff;
+    data[1] = (res.offset >> 8) & 0xff;
+    data[2] = (res.offset >> 16) & 0xff;
+    data[3] = (res.offset >> 24) & 0xff;
+
+    data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key));
+    data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key));
+
+    for (const auto &item : ski.second)
+    {
+      data += std::string((const char *)&item.first, sizeof(crypto::key_image));
+      data += std::string((const char *)&item.second, sizeof(crypto::signature));
+    }
+
+    std::string ciphertext = m_wallet->encrypt_with_view_secret_key(data);
+
+    std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC));
+    res.encrypted_key_images_blob = epee::string_tools::buff_to_hex_nodelimer(magic + ciphertext);
+  }
+  catch (const std::exception& e)
+  {
+    handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+    return false;
+  }
+
+  return true;
+}
+//------------------------------------------------------------------------------------------------------------------------------
+bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx)
+{
+  if (!m_wallet) return not_open(er);
+  if (m_restricted)
+  {
+    er.code = WALLET_RPC_ERROR_CODE_DENIED;
+    er.message = "Command unavailable in restricted mode.";
+    return false;
+  }
+  if (!m_wallet->is_trusted_daemon())
+  {
+    er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
+    er.message = "This command requires a trusted daemon.";
+    return false;
+  }
+  try
+  {
+    std::string data;
+    if (!epee::string_tools::parse_hexstr_to_binbuff(req.encrypted_key_images_blob, data))
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Failed to parse encrypted key images blob";
+      return false;
+    }
+
+    const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC);
+    if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen))
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Bad key image export file magic in blob";
+      return false;
+    }
+
+    try
+    {
+      data = m_wallet->decrypt_with_view_secret_key(std::string(data, magiclen));
+    }
+    catch (const std::exception& e)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Failed to decrypt blob";
+      return false;
+    }
+
+    const size_t headerlen = 4 + 2 * sizeof(crypto::public_key);
+    if (data.size() < headerlen)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Bad data size from file";
+      return false;
+    }
+
+    uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24);
+    if (offset > m_wallet->get_num_transfer_details())
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Offset larger than known outputs";
+      return false;
+    }
+
+    const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4];
+    const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)];
+    const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address;
+    if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Key images from different account";
+      return false;
+    }
+
+    const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature);
+    if ((data.size() - headerlen) % record_size)
+    {
+      er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE;
+      er.message = "Bad data size from file";
+      return false;
+    }
+    size_t nki = (data.size() - headerlen) / record_size;
+
+    std::vector<std::pair<crypto::key_image, crypto::signature>> ski;
+    ski.reserve(nki);
+    for (size_t n = 0; n < nki; ++n)
+    {
+      crypto::key_image key_image = *reinterpret_cast<const crypto::key_image*>(&data[headerlen + n * record_size]);
+      crypto::signature signature = *reinterpret_cast<const crypto::signature*>(&data[headerlen + n * record_size + sizeof(crypto::key_image)]);
+
+      ski.push_back(std::make_pair(key_image, signature));
+    }
+
+    uint64_t spent = 0, unspent = 0;
+    uint64_t height = m_wallet->import_key_images(ski, offset, spent, unspent);
+    res.height = height;
+    res.spent = spent;
+    res.unspent = unspent;
+  }
+  catch (const std::exception& e)
+  {
+    handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR);
+    return false;
+  }
+
+  return true;
+}
   //------------------------------------------------------------------------------------------------------------------------------
   bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx)
   {
diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h
index f118f581a..af8a5f234 100644
--- a/src/wallet/wallet_rpc_server.h
+++ b/src/wallet/wallet_rpc_server.h
@@ -124,7 +124,9 @@ namespace tools
         MAP_JON_RPC_WE("export_outputs",     on_export_outputs,     wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS)
         MAP_JON_RPC_WE("import_outputs",     on_import_outputs,     wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS)
         MAP_JON_RPC_WE("export_key_images",  on_export_key_images,  wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES)
-        MAP_JON_RPC_WE("import_key_images",  on_import_key_images,  wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES)
+	MAP_JON_RPC_WE("import_key_images",  on_import_key_images,  wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES)
+	MAP_JON_RPC_WE("export_encrypted_key_images",  on_export_encrypted_key_images,  wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES)
+	MAP_JON_RPC_WE("import_encrypted_key_images",  on_import_encrypted_key_images,  wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES)
         MAP_JON_RPC_WE("make_uri",           on_make_uri,           wallet_rpc::COMMAND_RPC_MAKE_URI)
         MAP_JON_RPC_WE("parse_uri",          on_parse_uri,          wallet_rpc::COMMAND_RPC_PARSE_URI)
         MAP_JON_RPC_WE("get_address_book",   on_get_address_book,   wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY)
@@ -220,6 +222,8 @@ namespace tools
       bool on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+      bool on_export_encrypted_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
+      bool on_import_encrypted_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
       bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index ab7898299..9ae3881ed 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -1025,7 +1025,7 @@ namespace wallet_rpc
     };
     typedef epee::misc_utils::struct_init<response_t> response;
   };
-  
+
   struct transfer_details
   {
     uint64_t amount;
@@ -1825,6 +1825,60 @@ namespace wallet_rpc
     typedef epee::misc_utils::struct_init<response_t> response;
   };
 
+  struct COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES
+  {
+    struct request_t
+    {
+      bool all;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(all, false)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint32_t offset;
+      std::string encrypted_key_images_blob;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(offset)
+        KV_SERIALIZE(encrypted_key_images_blob)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
+  struct COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES
+  {
+    struct request_t
+    {
+      uint32_t offset;
+      std::string encrypted_key_images_blob;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE_OPT(offset, (uint32_t)0)
+        KV_SERIALIZE(encrypted_key_images_blob)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<request_t> request;
+
+    struct response_t
+    {
+      uint64_t height;
+      uint64_t spent;
+      uint64_t unspent;
+
+      BEGIN_KV_SERIALIZE_MAP()
+        KV_SERIALIZE(height)
+        KV_SERIALIZE(spent)
+        KV_SERIALIZE(unspent)
+      END_KV_SERIALIZE_MAP()
+    };
+    typedef epee::misc_utils::struct_init<response_t> response;
+  };
+
   struct uri_spec
   {
     std::string address;
@@ -2310,7 +2364,7 @@ namespace wallet_rpc
     };
     typedef epee::misc_utils::struct_init<response_t> response;
   };
-  
+
   struct COMMAND_RPC_IS_MULTISIG
   {
     struct request_t