encryption public-key
Updated Fri, 23 Sep 2022 17:58:40 GMT

How to encrypt data which never leave the client but can't be decrypted without the server?


Client: Browser, mobile app or desktop application written using using JavaScript, HTML, CSS and packaged with Capacitor / Tauri.

Server: NodeJS based API.

Data: Username / password

Vendor: Third party integration


I am writing an integration with various vendors that requires users to enter their vendors data for our automated processes to log in and perform their task. For the benefit of the customers mindset on security, we're not storing the data on the server (encrypted or otherwise). The processing is done client side and in turn, the server doesn't need to have access to the data.


I want to encrypt this data in such a way that it can only be decrypted when the user is logged in and a connection has been made to the server.

My solutions:

Idea 1: Key stored on server

  1. User adds a vendor integration
  2. The server generates a "key" specific to that vendor, saves it in the DB for that integration and returns it to the client
  3. Client encrypts the data and stores it.
  4. When a sync needs to take place (which would only happen when the user is logged into my platform) we request the vendor key and decrypt the data and use it in memory only.

Idea 2: Asymmetric Encryption

  1. User adds a vendor integration
  2. Server generates a public / private key, stores the private key against the users vendor integration in the DB and returns the public key to the client. The public key would be available to the user whenever they're logged in.
  3. Client then encrypts the data using the public key
  4. When the sync needs to take place, we send the encrypted data to the server and the server returns a decrypted version for us to use in memory only.


From my understanding, Idea 1 seems safe and preferable to me because the users data never leaves the device, even to be decrypted. Idea 2 seems safe but I can't think of any advantages over using the stored key approach in idea 1.

Is my understanding correct or should I be using some other method in order to safely encrypt this data?


The client will have full access to any unencrypted data before it is encrypted, or after the server permits the client to decrypt it.

This means that you will need to trust the client to not export the unencrypted data. It's impossible to fully lock the device down - someone could, for example, simply point a video camera at their monitor to "export" the unencrypted data. However, you can take steps to make it difficult for the user to install their own extraction software on the device.

All you need to do is store a per-vendor secret key on the server, keep a different per-vendor secret key known only to the client, and combine them both to get the actual encryption/decryption key. You can simply XOR them both together to derive the key.

Now, when it's time to access the data, the client logs in to the server and asks for a copy of the server's key, and decrypts the content by combining the server's key with the client's key.

If you want to be more fine grained, you can have a client and server key per unit of data that is stored. Therefore, the server can log access to different units of data for a particular vendor, and so can limit the rate at which the client requests access. Now, the client will have to request multiple keys from the server according to the data that needs accessing. The client can use HKDF to generate many per-unit client keys, but the server keys should be independently generated so that the client has to ask for each server key for each unit that needs to be decrypted.

Since the client has to be trusted, the client can then immediately discard the server's key and the derived encryption/decryption key as soon as it has finished decrypting/re-encrypting the modified vendor data. You'd probably make the client software automatically time out after a certain period, and require the client to log back in to the server to continue working on the vendor data.

Note that this is effectively a secret-sharing approach, which can be extended to require assistance from multiple parties. You can use Shamir's Secret Sharing to require a threshold of parties to grant access to the data on the client.

Comments (5)

  • +0 – Ok, this sounds like a perfectly reasonable approach. Many thanks. — Aug 24, 2022 at 05:50  
  • +0 – Are there any risks with the client doing the encryption? — Aug 24, 2022 at 10:05  
  • +0 – @webnoob The client has the unencrypted data that is being entered/modified, so the client has to be the one that encrypts it so that the server can't see it. Your best defence is to be granular, so that the client only retains in memory the specific units of data it needs to see, and cannot easily request that the server sends bulk data to it that it does not need. The focus should be on auditing/logging/rate limited restrictions on the server side, to prevent a rogue client from exporting lots of data it should not normally need to see. — Aug 24, 2022 at 14:13  
  • +0 – That was my thinking too but I've read so many articles saying never to do encryption in the browser that it had be questioning myself. That was the main reason for me posting this question in the first place. Given the client will be typing any information it, it all makes sense to me. Thanks for the sense check. — Aug 25, 2022 at 08:42  
  • +1 – @webnoob if it's critical that the server can't see the unencrypted information, then you have no choice other than to have the client do the encryption (even if the client is a browser), or to use some kind of trusted intermediate server that will only store client encryption keys and do the encryption on behalf of the web browser connecting to it. — Aug 25, 2022 at 13:32  

External Links

External links referenced by this document: