问题
I have a single-threaded client/server application that needs to do both encryption and decryption of their network communication. I plan on using OpenSSL's EVP API and AES-256-CBC.
Some sample pseudo-code I found from a few examples:
// key is 256 bits (32 bytes) when using EVP_aes_256_*()
// I think iv is the same size as the block size, 128 bits (16 bytes)...is it?
1: EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
2: EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv, 1); //0=decrypt, 1=encrypt
3: EVP_CipherUpdate(ctx, outbuf, &outlen, inbuf, inlen);
4: EVP_CipherFinal_ex(ctx, outbuf + outlen, &tmplen));
5: outlen += tmplen;
6: EVP_CIPHER_CTX_cleanup(ctx);
7: EVP_CIPHER_CTX_free(ctx);
The problem is from all these examples, I'm not sure what needs to be done at every encryption/decryption, and what I should only do once on startup.
Specifically:
- At line 1, do I create this
EVP_CIPHER_CTX
just once and keep re-using it until the application ends? - Also at line 1, can I re-use the same
EVP_CIPHER_CTX
for both encryption and decryption, or am I supposed to create 2 of them? - At line 2, should the IV be re-set at every packet I'm encrypting? Or do I set the IV just once, and then let it continue forever?
- What if I'm encrypting UDP packets, where a packet can easily go missing or be received out-of-order: am I correct in thinking CBC wont work, or is this where I need to reset the IV at the start of every packet I send out?
回答1:
I have a single-threaded client/server application that needs to do both encryption and decryption of their network communication. I plan on using OpenSSL's EVP API and AES-256-CBC.
If you are using the SSL_*
functions from libssl
, then you will likely never touch the EVP_*
APIs.
At line 1, do I create this EVP_CIPHER_CTX just once and keep re-using it until the application ends?
You create it once per use. That is, as you need to encrypt, you use the same context. If you need to encrypt a second stream, you would use a second context. If you needed to decrypt a third stream, you would use a third context.
Also at line 1, can I re-use the same EVP_CIPHER_CTX for both encryption and decryption, or am I supposed to create 2 of them?
No, see above.
The ciphers will have different states.
At line 2, should the IV be re-set at every packet I'm encrypting? Or do I set the IV just once, and then let it continue forever?
No. You set the IV once and then forget about it. That's part of the state the context object manages for the cipher.
What if I'm encrypting UDP packets, where a packet can easily go missing or be received out-of-order: am I correct in thinking CBC wont work...
If you are using UDP, its up to you to detect these sorts of problems. You'll probably end up reinventing TCP.
Encryption alone is usually not enough. You also need to ensure authenticity and integrity. You don't operate on data that's not authentic. That's what keeps getting SST/TLS and SSH in trouble.
For example, here's the guy who wrote the seminal paper on authenticated encryption with respect to IPSec, SSL/TLS and SSH weighing in on the Authenticate-Then-Encrypt (EtA) scheme used by SSL/TLS: Last Call: (Encrypt-then-MAC for TLS and DTLS) to Proposed Standard:
The technical results in my 2001 paper are correct but the conclusion regarding SSL/TLS is wrong. I assumed that TLS was using fresh IVs and that the MAC was computed on the encoded plaintext, i.e. Encode-Mac-Encrypt while TLS is doing Mac-Encode-Encrypt which is exactly what my theoretical example shows is insecure.
For authenticity, you should forgo CBC mode and switch to GCM mode. GCM is an authenticated encryption mode, and it combines confidentiality and authenticity into one mode so you don't have to combine primitives (like AES/CBC with an HMAC).
or is this where I need to reset the IV at the start of every packet I send out?
No, you set the IV once and then forget about it.
The problem is from all these examples, I'm not sure what needs to be done at every encryption/decryption, and what I should only do once on startup.
- Create this once:
EVP_CIPHER_CTX
- Call this once for setup:
EVP_CipherInit
- Call this as many times as you'd like:
EVP_CipherUpdate
- Call this once for cleanup:
EVP_CipherFinal
The OpenSSL wiki has quite a few examples of using the EVP_*
interfaces. See EVP Symmetric Encryption and Decryption, EVP Authenticated Encryption and Decryption and EVP Signing and Verifying.
All the examples use the same pattern: Init
, Update
and then Final
. It does not matter if its encryption or hashing.
Related: this should be of interest to you: EVP Authenticated Encryption and Decryption. Its sample code from the OpenSSL wiki.
Related: you can find copies of Viega, Messier and Chandra's Network Security with OpenSSL online. You might consider hunting down a copy and getting familiar with some of its concepts.
回答2:
Sorry for reviving an old thread, but I noticed the following error in the accepted answer:
At line 1, do I create this EVP_CIPHER_CTX just once and keep re-using it until the application ends?
You create it once per use. That is, as you need to encrypt, you use the same context. If you need to encrypt a second stream, you would use a second context. If you needed to decrypt a third stream, you would use a third context.
Also at line 1, can I re-use the same EVP_CIPHER_CTX for both encryption and decryption, or am I supposed to create 2 of them?
No, see above.
This is not necessary. From the man page for OpenSSL:
New code should use EVP_EncryptInit_ex(), EVP_EncryptFinal_ex(), EVP_DecryptInit_ex(), EVP_DecryptFinal_ex(), EVP_CipherInit_ex() and EVP_CipherFinal_ex() because they can reuse an existing context without allocating and freeing it up on each call.
In other words, you need to re-initialize the context each time before you use it, but you can certainly use the same context over and over again without creating (allocating) a new one.
回答3:
Sorry for reviving an old thread too, but LazerSharks asked twice about evp cipher context in comments. I don't have enough reputation points here to add some comments that's why I'll take an answer here. (Google search even now doesn't show needful information) From the "Network Security with OpenSSL" book by Pravir Chandra, Matt Messier, John Viega:
Before we can begin encrypting or decrypting, we must allocate and initialize a cipher context. The cipher context is a data structure that keeps track of all relevant state for the purposes of encrypting or decrypting data over a period of time. For example, we can have multiple streams of data encrypted in CBC mode. The cipher context will keep track of the key associated with each stream and the internal state that needs to be kept between messages for CBC mode. Additionally, when encrypting with a block-based cipher mode, the context object buffers data that doesn't exactly align to the block size until more data arrives, or until the buffer is explicitly flushed, at which point the data is usually padded as appropriate.
The generic cipher context type is EVP_CIPHER_CTX. We can initialize one, whether it was allocated dynamically or statically, by calling EVP_CIPHER_CTX_init, like so:
EVP_CIPHER_CTX *x = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)); EVP_CIPHER_CTX_init(x);
来源:https://stackoverflow.com/questions/24365646/do-i-need-multiple-evp-cipher-ctx-structures