1

I'm working on a system that shares data between a client and server. The end goal of this system is to store server-encrypted data on the client's hard drive, using keys generated by the server. the client needs copies of the keys so that they can decrypt the data when they're offline.

I am hoping to achieve some clarification on best practices and if I'm using the correct techniques.

The data is larger than the typical RSA Key size so using RSA to encrypt everything won't work. I've been using LibSodium's xChaCha-based encryption functions to share data.

For the key exchanges, I'm basically doing this:

client -> server: here's my publicKey

then 

server -> client: here's my publicKey

then

client -> server: sends an encrypted message that was encrypted with the client's private key.  
the server knows what message to expect when it is decrypted with client's public key.
This is how the server confirms that the client's public key is valid and can be used to encrypt/decrypt.

then

server -> client: sends an encrypted message that was encrypted with server's private key.  
the Client knows what message to expect when it is decrypted with server's public key  
This is how the client confirms that the server's public key is valid and can be used to encrypt/decrypt. 

After RSA Key exchange between the client and the server has completed successfully, I exchange LibSodium KX keys (https://doc.libsodium.org/key_exchange) which can be used to generate TX/RX keys for the XChaCha encrypt/decrypt functions (https://doc.libsodium.org/secret-key_cryptography/aead/chacha20-poly1305/xchacha20-poly1305_construction):

These keys can be used by any functions requiring secret keys up to 
crypto_kx_SESSIONKEYBYTES bytes, including crypto_secretbox_*() and crypto_aead_*().

Now both sides have generated RSA & KX key pairs and have each other's public keys (RSA and KX).

All further RSA-encrypted messages are encrypted using the recipient's public key, so that only the recipient can decrypt it with their private key.

I'm sending data between the server and client like this (pseudo-code/JSON-ish):

sender --> receiver: 
{
    "data": base64_encode( Encrypt(message = aReallyLongMessageOrLotsOfData, 
                                   key = TXKey, 
                                   algo = xChaCha20-poly1305,
                                   additionalData,
                                   nonce ) ),
    "nonce": base64_encode( Encrypt(message = nonce, 
                                    key = receiversRSAPublicKey, 
                                    algo = RSA) )
}

In plain english:

  • big chunks of data are encrypted with the TX key (which is derived from the sender's public/private keys + receiver's public key) using the xChaCha algorithm.
  • the nonce for that xChaCha algorithm is encrypted with the recipient's RSA public key.
  • both sides know how to produce additionalData correctly, so they don't need to include it with the message like how the nonce is being sent.

I have implemented all of this encrypted communication between my C++ desktop client and my web server (php) and it all works great.

Let me know if this is the correct way to send encrypted data in a client-server model, or if I'm missing some important detail that defeats all of this effort.

Thanks!!

2
  • is there any reason why you're not using crypto_box_easy or crypto_box_seal? the former requires both parties to hold both public keys (and one secret), while the latter allows any sender to send a msg to the recipient without they themselves being able to decrypt once encrypted .. all of these are compatible in php and many other impls (including browser js), across multiple host os
    – brynk
    Commented Jan 13, 2023 at 6:44
  • ps. if you want the property of "one-way" encryption, eg server -> client without the sender being able to decrypt later, and you also want a strong guarantee that it came from the sender, then you can have the sender sign the ciphertext as well using crypto_sign
    – brynk
    Commented Jan 13, 2023 at 6:47

1 Answer 1

3

Use TLS and don't implement own encryption scheme. This is one of the best practices.

If you use TLS, then all what you have described makes no sense, because all the data sent in both directions are already encrypted.

If you don't use TLS yet, then first try to use TLS. Then you will save a lot of efforts and you will avoid multiple security risks related to own implementation.

If you still have reasons not to use TLS and you are forced to implement an own solution, then try to apply the main ideas behind TLS. The scheme you described is insecure and has following problems (that you would not have if you used TLS):

  • It allows man-in-the-middle attacks.
  • It allows replay attacks.
  • It has no way to prevent usage of key pair in case it was compromised
  • It has no future secrecy. If an attacker obtains a private key, all the previous messages can be decrypted.
5
  • Alternatively, SSH (with public keys) is sometimes better suited than TLS on some use cases, even though SSH is lacking some security features over TLS. But I agree that a REST API over TLS is often the easiest solution to implement.
    – A. Hersean
    Commented Jan 12, 2023 at 13:51
  • I forgot to include that this is built on top of the TLS system, and mainly used to allow the data being transmitted to be stored on the recipient's disk in an encrypted manner. Commented Jan 12, 2023 at 15:19
  • @mentallurg the main problem I'm trying to solve is: storing data on the client's side that came from the server, and the stored data is encrypted with keys that are unique to each client. Also, this is the 2nd question of mine that you've provided a great answer to. do you have a site where you can be contacted for consultant work? Commented Jan 12, 2023 at 19:14
  • @MatkatMusic: If you have a native application (mobile, desktop), then they can receive unencrypted data from the server, encrypt them and save. If you have browser, you can do the same (all major browsers have Web Crypto API), but it might be a bit more complex to implement. In such case client can generate password, send it to the server (it is safe because of TLS), server can encrypt data, client downloads them and stores locally.
    – mentallurg
    Commented Jan 13, 2023 at 1:15
  • @MatkatMusic: You can reach me via [email protected]. If you don't get an answer (may be it was filtered out as spam), you can reach me via this chat, or just write me comment here and we can find some other communication channel.
    – mentallurg
    Commented Jan 13, 2023 at 1:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .