This is a set of scripts for manual secure communication. They're built around OpenSSL, and are basically just wrappers around it because I can't seem to remember the incantations. Remember, the most secure secure thing you can do is use a one-time pad. Just remember to watch out for those Byzantine Generals :)
You need to have OpenSSL installed, which you probably already do. If not, go get it using your distro's package manager.
Install openssl via homebrew or macports etc.
Likewise, you'll need OpenSSL. For example, if you have choclatey, you can install via admin PowerShell:
$ choco install OpenSSL.light
The basic idea here is that you'll derive a key either using RSA or ECDH, then use that key to communicate using a secure block cipher (AES). You should treat this key as an ephemeral session key. Do not reuse it!
To perform hybrid encryption with RSA, first generate a long pseudo-random key for AES. You can plug in whatever key length you'd like; 128 is the minimum acceptable key length recommended by NIST, but since performance is of little concern here, why not use 256.
$ ./gen-aes-key 256 > aes-key.hex
Encrypt this random key using ./rsa-encrypt.sh
. You first need an RSA key-pair (use a key length of at least 2048):
$ ./gen-rsa-key.sh 2048
This produces files called private.pem
and public.pem
.
$ ./rsa-encrypt.sh aes-key.hex public.pem > ciphertext
This is someone else's public key (the target of the communication). Do not use RSA for encrypted communication of long files directly (use a hybrid approach like we're doing here).
They can then decrypt as follows using their private key:
$ ./rsa-decrypt.sh ciphertext private.pem > aes-key.hex
We now have a shared secret AES key (aes-key.hex
) which we can use for symmetric encryption.
We can start encrypting with AES using the "with key" utility. Below we encrypt a file (plaintext
)
using our shared key and using the "Counter" encryption mode for AES (you can also use cfb, ofb, or whatever). Don't
use ECB or CBC.
$ ./aes-encrypt-with-key.sh plaintext aes-key.hex ctr > ciphertext
You can now send this ciphertext along however you'd like on an untrusted medium, e.g.
$ cat ciphertext | nc termbin.com 9999
On the other end they can decrypt with:
$ curl -fL https://termbin.com/XXXX > cipihertext
$ ./aes-decrypt-with-key.sh ciphertext aes-key.hex ctr > plaintext
Or go nuts and hide it in a JPG with steghide or or digital ink toolkit or something.
The tools here use the Elliptic Curve Diffie-Hellman key exchange paired with
256-bit AES in CBC mode. The default curve over the 256-bit prime field (secp256k1
).
Unfortunately this setup is more of a pain, because unlike with RSA, we don't generally advertise a static public key. We will generate a shared secret for every session, then use that to encrypt. Annoying in a manual setup, but more secure. The general flow is the following:
- Receiver (
B
) generates temporary EC keypair - Receiver (
B
) sends temporary public key to sender. This can be over an insecure channel. - Sender (
A
) generates temporary EC keypair - Sender (
A
) derives shared secret fromB
's public key. - Sender (
A
) encrypts message with AES256, using shared secret to derive IV and symmetric key for AES. - Sender (
A
) generates an HMAC of encrypted message using shared ECDH secret. - Sender (
A
) sends HMAC, encrypted message, and temporary public key to (B
). - Receiver (
B
) derives shared secret fromA
's temporary public key. - Receiver (
B
) checks authenticity and integrity of encrypted message by generating his own HMAC from shared secret and received message, and comparing with A's HMAC. - Receiver (
B
) decrypts payload using shared secret to derive IV and symmetric key for AES.
We assume that A has already received B
's public key, here called recv-pub.pem
. A
can
then do the following to generate ciphertext:
$ ./ecdh-encrypt.sh plaintext.txt output.enc recv-pub.pem
Generating temporary ECDH keypair...
....tmppriv.pem
....tmppub.pem
Deriving shared secret...
....secret.send
Encrypting...
....output.enc
Generating HMAC...
....hmac.send
Send your public key (tmppub.pem), the encrypted data (output.enc), and the HMAC (hmac.send) to the recipient.
Now on the receive side, B
can do the following to get back the plaintext:
$ ./ecdh-decrypt.sh recv-priv.pem output.enc foo.out tmppub.pem hmac.send
Deriving shared secret...
...secret.recv
Checking HMAC...
....HMAC OK: Match
Decrypting...
....foo.out
TODO
You can convert an existing ssh key into a public key using convert-ssh.sh
. While the private key formats
are the same with SSH and OpenSSL, the public keys vary. OpenSSL uses PKCS8.