You need to set up a cipher so that you can perform encryption and/or decryption operations in CBC, CFB, OFB, or ECB mode.
Here are the steps you need to perform for cipher setup in OpenSSL, using their high-level API:
Make sure your code includes openssl/evp.h and
links to libcrypto (-lcrypto
).
Decide which algorithm and mode you want to use, looking up the mode in Table 5-6 to determine which function instantiates an OpenSSL object representing that mode. Note that OpenSSL provides only a CTR mode implementation for AES. See Recipe 5.9 for more on CTR mode.
Instantiate a cipher context (type EVP_CIPHER_CTX
).
Pass a pointer to the cipher context to EVP_CIPHER_CTX_init(
)
to initialize memory properly.
Choose an IV or nonce, if appropriate to the mode (all except ECB).
Initialize the mode by calling EVP_EncryptInit_ex(
)
or EVP_DecryptInit_ex(
)
, as appropriate:
int EVP_EncryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *engine, unsigned char *key, unsigned char *ivornonce); int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *type, ENGINE *engine, unsigned char *key, unsigned char *ivornonce);
If desired, perform any additional configuration the cipher may allow (see Recipe 5.20).
Use the raw OpenSSL API only when absolutely necessary because there is a huge potential for introducing a security vulnerability by accident. For general-purpose use, we recommend a high-level abstraction, such as that discussed in Recipe 5.16.
The OpenSSL EVP API is a reasonably high-level interface to a multitude of cryptographic primitives. It attempts to abstract out most algorithm dependencies, so that algorithms are easy to swap.[14]
The EVP_EncryptInit_ex(
)
and EVP_DecryptInit_ex(
)
functions set up a cipher context object to
be used for further operations. It takes four arguments that provide
all the information necessary before encryption or decryption can
begin. Both take the same arguments:
ctx
Pointer to an EVP_CIPHER_CTX
object, which stores
cipher state across calls.
type
Pointer to an EVP_CIPHER
object, which represents
the cipher configuration to use (see the later discussion).
engine
Pointer to an ENGINE
object representing the
actual implementation to use. For example, if you want to use
hardware acceleration, you can pass in an ENGINE
object that represents your cryptographic accelerator.
key
Pointer to the encryption key to be used.
ivornonce
Pointer to an initialization vector or none, if appropriate (use
NULL
otherwise). For CBC, CFB, and OFB modes, the
initialization vector or nonce is always the same size as the block
size of the cipher, which is often different from the key size of the
cipher.
There are also deprecated versions of these calls,
EVP_EncryptInit(
)
and EVP_DecryptInit(
)
, that are the same except that they do not
take the engine
argument, and they use only the
built-in software implementation.
Calling a function that returns an EVP_CIPHER
object will cause the cipher's implementation to
load dynamically and place information about the algorithm into an
internal table if it has not yet done so. Alternatively, you can load
all possible symmetric ciphers at once with a call to the function
OpenSSL_add_all_ciphers(
)
, or all ciphers and message digest algorithms
with a call to the function OpenSSL_add_all_algorithms(
)
(neither function takes any arguments). For
algorithms that have been loaded, you can retrieve pointers to their
objects by name using the EVP_get_cipherbyname(
)
function, which takes a single parameter of
type char *
, representing the desired cipher
configuration.
Table 5-6 summarizes the possible functions that
can load ciphers (if necessary) and return
EVP_CIPHER
objects. The table also shows the
strings that can be used to look up loaded ciphers.
As noted in Recipe 5.2, we personally recommend AES-based solutions, or (of the ciphers OpenSSL offers) Triple-DES if AES is not appropriate. If you use other algorithms, be sure to research them thoroughly.
Table 5-6. Cipher instantiation reference
Cipher |
Key strength / actual size (if different) |
Cipher mode |
Call for EVP_CIPHER object |
Cipher lookup string |
---|---|---|---|---|
AES |
128 bits |
ECB |
|
aes-128-ecb |
AES |
128 bits |
CBC |
|
aes-128-cbc |
AES |
128 bits |
CFB |
|
aes-128-cfb |
AES |
128 bits |
OFB |
|
aes-128-ofb |
AES |
192 bits |
ECB |
|
aes-192-ecb |
AES |
192 bits |
CBC |
|
aes-192-cbc |
AES |
192 bits |
CFB |
|
aes-192-cfb |
AES |
192 bits |
OFB |
|
aes-192-ofb |
AES |
256 bits |
ECB |
|
aes-256-ecb |
AES |
256 bits |
CBC |
|
aes-256-cbc |
AES |
256 bits |
CFB |
|
aes-256-cfb |
AES |
256 bits |
OFB |
|
aes-256-ofb |
Blowfish |
128 bits |
ECB |
|
bf-ecb |
Blowfish |
128 bits |
CBC |
|
bf-cbc |
Blowfish |
128 bits |
CFB |
|
bf-cfb |
Blowfish |
128 bits |
OFB |
|
bf-ofb |
CAST5 |
128 bits |
ECB |
|
cast-ecb |
CAST5 |
128 bits |
CBC |
|
cast-cbc |
CAST5 |
128 bits |
CFB |
|
cast-cfb |
CAST5 |
128 bits |
OFB |
|
cast-ofb |
DES |
Effective: 56 bitsActual: 64 bits |
ECB |
|
des-ecb |
DES |
Effective: 56 bitsActual: 64 bits |
CBC |
|
des-cbc |
DES |
Effective: 56 bitsActual: 64 bits |
CFB |
|
des-cfb |
DES |
Effective: 56 bitsActual: 64 bits |
OFB |
|
des-ofb |
DESX |
Effective[15]: 120 bitsActual: 128 bits |
CBC |
|
desx |
3-key Triple-DES |
Effective: 112 bitsActual: 192 bits |
ECB |
|
des-ede3 |
3-key Triple-DES |
Effective: 112 bitsActual: 192 bits |
CBC |
|
des-ede3-cbc |
3-key Triple-DES |
Effective: 112 bitsActual: 192 bits |
CFB |
|
des-ede3-cfb |
3-key Triple-DES |
Effective: 112 bitsActual: 192 bits |
OFB |
|
des-ede3-ofb |
2-key Triple-DES |
Effective: 112 bitsActual: 128 bits |
ECB |
|
des-ede |
2-key Triple-DES |
Effective: 112 bitsActual: 128 bits |
CBC |
|
des-ede-cbc |
2-key Triple-DES |
Effective: 112 bitsActual: 128 bits |
CFB |
|
des-ede-cfb |
2-key Triple-DES |
Effective: 112 bitsActual: 128 bits |
OFB |
|
des-ede-ofb |
IDEA |
128 bits |
ECB |
|
idea-ecb |
IDEA |
128 bits |
CBC |
|
idea-cbc |
IDEA |
128 bits |
CFB |
|
idea-cfb |
IDEA |
128 bits |
OFB |
|
idea-ofb |
RC2™ |
128 bits |
ECB |
|
rc2-ecb |
RC2™ |
128 bits |
CBC |
|
rc2-cbc |
RC2™ |
128 bits |
CFB |
|
rc2-cfb |
RC2™ |
128 bits |
OFB |
|
rc2-ofb |
RC4™ |
40 bits |
n/a |
|
rc4-40 |
RC4™ |
128 bits |
n/a |
|
rc4 |
RC5™ |
128 bits |
ECB |
|
rc5-ecb |
RC5™ |
128 bits |
CBC |
|
rc5-cbc |
RC5™ |
128 bits |
CFB |
|
rc5-cfb |
RC5™ |
128 bits |
OFB |
|
rc5-ofb |
[15] There are known plaintext attacks against DESX that reduce the effective strength to 60 bits, but these are generally considered infeasible. |
For stream-based modes (CFB and OFB), encryption and decryption are
identical operations. Therefore, EVP_EncryptInit_ex(
)
and EVP_DecryptInit_ex( )
are
interchangeable in these cases.
While RC4 can be set up using these instructions, you must be very careful to set it up securely. We discuss how to do so in Recipe 5.23.
Here is an example of setting up an encryption context using 128-bit AES in CBC mode:
#include <openssl/evp.h> #include <openssl/rand.h> /* key must be of size EVP_MAX_KEY_LENGTH. * iv must be of size EVP_MAX_IV_LENGTH. */ EVP_CIPHER_CTX *sample_setup(unsigned char *key, unsigned char *iv) { EVP_CIPHER_CTX *ctx; /* This uses the OpenSSL PRNG . See Recipe 11.9 */ RAND_bytes(key, EVP_MAX_KEY_LENGTH); RAND_bytes(iv, EVP_MAX_IV_LENGTH); if (!(ctx = (EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)))) return 0; EVP_CIPHER_CTX_init(ctx); EVP_EncryptInit_ex(ctx, EVP_aes_128_cbc( ), 0, key, iv); return ctx; }
This example selects a key and initialization vector at random. Both of these items need to be communicated to any party that needs to decrypt the data. The caller therefore needs to be able to recover this information. In this example, we handle this by having the caller pass in allocated memory, which we fill with the new key and IV. The caller can then communicate them to the other party in whatever manner is appropriate.
Note that to make replacing algorithms easier, we always create keys and initialization vectors of the maximum possible length, using macros defined in the openssl/evp.h header file.