I want to do a test application which uses libsodium to communicate from a client to a server.
There are many ports for many languages: C#, PHP,...
and there is always an example with "bob" and "alice". Thats fine, but they never show how to exchange the public keys over the network in a secure way.
So how is the recommend way to exchange the public keys for "alice/client" and "bob/server".
They always use the same file or the same machine to generate the key pairs.
Here is an extract from the libsodium-php extension:
$alice_kp = crypto_box_keypair();
$alice_secretkey = crypto_box_secretkey($alice_kp);
$alice_publickey = crypto_box_publickey($alice_kp);
$bob_kp = crypto_box_keypair();
$bob_secretkey = crypto_box_secretkey($bob_kp);
$bob_publickey = crypto_box_publickey($bob_kp);
$alice_to_bob_kp = crypto_box_keypair_from_secretkey_and_publickey
($alice_secretkey, $bob_publickey);
$bob_to_alice_kp = crypto_box_keypair_from_secretkey_and_publickey
($bob_secretkey, $alice_publickey);
$alice_to_bob_message_nonce = randombytes_buf(CRYPTO_BOX_NONCEBYTES);
$alice_to_bob_ciphertext = crypto_box('Hi, this is Alice',
$alice_to_bob_message_nonce,
$alice_to_bob_kp);
$alice_message_decrypted_by_bob = crypto_box_open($alice_to_bob_ciphertext,
$alice_to_bob_message_nonce,
$bob_to_alice_kp);
$bob_to_alice_message_nonce = randombytes_buf(CRYPTO_BOX_NONCEBYTES);
$bob_to_alice_ciphertext = crypto_box('Hi Alice! This is Bob',
$bob_to_alice_message_nonce,
$bob_to_alice_kp);
$bob_message_decrypted_by_alice = crypto_box_open($bob_to_alice_ciphertext,
$bob_to_alice_message_nonce,
$alice_to_bob_kp);
Libsodium does not provide any direct key exchange methods. You could securely do key exchange in a few different ways:
Through tamper-proof "out-of-band communication" deliver a public key or if the com-channel is safe from listeners, a shared secret. (ex. BittorrentSync, a QR code containing a shared secret that is scanned from device to device)
Have a trusted third party sign the public keys before they are sent unencrypted. (ex. Certificate Authorities in SSL/TLS)
Certificate pinning (Ex. Google Chrome for Google URLs)
Since you probably don't want to re-implement SSL/TLS the first or third option would probably be best. However, the first is kinda hard in a client / server format since the PHP server probably only has the internet to communicate over.
However if you control the client you can do certificate pinning. That is, embed Bob's public key in Alice's executable. Alice will then only encrypt message with Bob's public key.
For example, Alice uses Bob's Public key and her own private key to encrypt some data and sends it (along with the unique nonce and Alice's Public Key) to Bob.
Bob uses the public key he received and his own private key to decrypt and verify the encrypted packet. It would be safe for Bob to use any of the data inside the since only he and Alice can decrypt it.
Well, Eve could block Alice's messages and send Eve's public key instead but she'd be working blind since Alice will only send messages encrypted with Alice's private key and Bob's public key.
In Bob's eyes she'd be just another client. In Alice's eyes she'd be frustrated because Bob doesn't seem to be responding.
If Eve sends Alice messages they would always be marked as invalid since Alice uses Alice's private key and Bob's public key to try to decrypt them which will fail since Eve does not have Bob's private key.
If Alice does not have a verified executable Eve could have altered it as she downloaded it (and inserted Eve's public key instead of Bob's). Verification can be done via her package manager, app store (most use some for of code signing & SSL/TLS for downloads) or signed executable / source tarball.
For a note about leaked private keys see the last section of this answer.
You could serve a QR code on a website over HTTPS after the user of the client has authenticated itself somehow (with some user specific password or maybe in the future by using SQRL) a QR code is shown that the client can scan or a link with a custom protocol through registering itself with the OS as a handler for that uri protocol.
A less complicated alternative to the above QR code / uri method would be to just have the user copy-paste a shared secret from the above mentioned HTTPS site that will be used for initial public key exchange using sodium's crypto_secretbox() method.
Note that the shared secret / public key probably should be base64, base32 or hex encoded during transit since some bytes in keys might get mangled by differing character encodings.
If you want forward secrecy in case of a private key leak then the initial public/private key-pair should only be used to exchange a second round of public keys that are never saved to a permanent storage medium (in essence, no disk or database). Since your case is just a test application Forward Secrecy might not be very important.
The second round of keys are discarded every so often (like every 24h) decreasing risks for past data to become vulnerable due to bugs such as OpenSSL's Heartbleed.
In a traditional webserver use of PHP this might be hard to do since all variables are reset for each HTTP request. A properly configured memcached instance might be an option but have risks of leaking temporary private keys.