None of these answers really constitute reasonable encryption.
What you really want to do is use a certain form of authenticated encryption and some form of secure key decryption algorithm. My personal recommendation is libsodium . It provides very good default settings and an API that is relatively difficult to make mistakes.
There are several ways to do this:
All these features are integrated into libsodium and are implemented with relative ease.
The following code examples are taken directly from the libsodium documentation .
For 1:
#define MESSAGE ((const unsigned char *) "test") #define MESSAGE_LEN 4 #define CIPHERTEXT_LEN (crypto_secretbox_MACBYTES + MESSAGE_LEN) unsigned char nonce[crypto_secretbox_NONCEBYTES]; unsigned char key[crypto_secretbox_KEYBYTES]; unsigned char ciphertext[CIPHERTEXT_LEN]; randombytes_buf(nonce, sizeof nonce); randombytes_buf(key, sizeof key); crypto_secretbox_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce, key); unsigned char decrypted[MESSAGE_LEN]; if (crypto_secretbox_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, key) != 0) { }
For 2: (Getting the key from the password)
#define PASSWORD "Correct Horse Battery Staple" #define KEY_LEN crypto_secretbox_KEYBYTES unsigned char salt[crypto_pwhash_SALTBYTES]; unsigned char key[KEY_LEN]; randombytes_buf(salt, sizeof salt); if (crypto_pwhash (key, sizeof key, PASSWORD, strlen(PASSWORD), salt, crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE, crypto_pwhash_ALG_DEFAULT) != 0) { }
The array key now contains a key that is suitable for use in the code example above. Instead of randombytes_buf(key, sizeof key)
to generate a random key, we generated the key obtained from the user password and used it for encryption.
3 is the "most difficult" of the 3 types. This is what you use if you have two sides to communication. Each side creates a “key pair” that contains the public and private key. With these key pairs, they can jointly agree on a “shared key" that they can use to encrypt (and sign) the data for each other:
#define MESSAGE (const unsigned char *) "test" #define MESSAGE_LEN 4 #define CIPHERTEXT_LEN (crypto_box_MACBYTES + MESSAGE_LEN) unsigned char alice_publickey[crypto_box_PUBLICKEYBYTES]; unsigned char alice_secretkey[crypto_box_SECRETKEYBYTES]; crypto_box_keypair(alice_publickey, alice_secretkey); unsigned char bob_publickey[crypto_box_PUBLICKEYBYTES]; unsigned char bob_secretkey[crypto_box_SECRETKEYBYTES]; crypto_box_keypair(bob_publickey, bob_secretkey); unsigned char nonce[crypto_box_NONCEBYTES]; unsigned char ciphertext[CIPHERTEXT_LEN]; randombytes_buf(nonce, sizeof nonce); if (crypto_box_easy(ciphertext, MESSAGE, MESSAGE_LEN, nonce, bob_publickey, alice_secretkey) != 0) { } unsigned char decrypted[MESSAGE_LEN]; if (crypto_box_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, alice_publickey, bob_secretkey) != 0) { }
This code first generates both keys (this usually happens on the bob and alice machines separately, and they will send each other their public key, keeping the secret key, well, secret).
Then a random exception is thrown, and a call to crypto_box_easy(...)
encrypts the message from alice to bob (using the bob public key for encryption and the alice private key to create the signature).
Then (after potential sending the message over the network), the crypto_box_open_easy(...)
) call is used by bob to decrypt the message (using its own private key to decrypt and the alice public key to verify the signature). If, for any reason, the message check failed (someone tried to intervene in it), this is indicated by a non-zero return code.