Encrypting and Decrypting The EVP Public Key Interface

209 Specifies the number of bytes contained in the data buffer. Once all of the data has been passed into the context along with EVP_VerifyUpdate , EVP_VerifyFinal must be called to perform the actual verification of the signature. Its signature matches that of EVP_SignFinal , but its arguments and return value are interpreted differently. int EVP_VerifyFinalEVP_MD_CTX ctx, unsigned char sigbuf, unsigned int siglen, EVP_PKEY pkey; ctx The initialized context that contains the data to be signed. sigbuf A buffer containing the signature to be verified. siglen Specifies the number of bytes contained in the signature buffer. pkey The EVP_PKEY object that will be used to verify the signature. It must contain either a DSA or an RSA public key. The key should match the private key that was used to create the signature. EVP_VerifyFinal will return -1 if an error occurs in verifying the signature. If the signature does not match the data, the return value will be 0, and if the signature is valid, the return value will be 1.

8.5.2 Encrypting and Decrypting

The EVP interface also provides an interface for enveloping data using RSA keys. Data enveloping is the process of encrypting a chunk of data with RSA, typically for securely sending it to a recipient. Initially, it may seem that enveloping is equivalent to using RSA to encrypt all of our data, but this is not correct. Because public key algorithms are inappropriate for encrypting large amounts of data, enveloping requires the sender to generate a random key, also called a session key, and encrypt that key using the public key of the intended recipient. The actual data is then encrypted with a symmetric cipher using the session key. Incorrectly implementing this process often causes bugs or vulnerabilities in programs. To avoid this, OpenSSL provides features in the EVP interface referred to as the envelope encryptiondecryption interface that handles all of the subtleties correctly. One of the features offered by the EVP interface for data encryption is the ability to encrypt the same data using several public keys. A single session key is generated and encrypted using each public key that is supplied. The recipients can then use their respective private keys to decrypt the session key, and thus decrypt the data using the appropriate symmetric cipher. The support for this feature is wholly contained by the context initialization function, which means the interface for encrypting for multiple recipients is the same as encrypting for a single recipient. The act of encrypting data using a public key through the EVP interface is called sealing. A set of functions, EVP_SealInit , EVP_SealUpdate , and EVP_SealFinal , are provided to encrypt an arbitrary amount of data. Each of these functions requires an EVP_CIPHER_CTX object to maintain state. This object can be allocated either dynamically or statically and must be initialized TE AM FL Y Team-Fly ® 210 prior to use. Initialization of the context object is achieved by calling EVP_SealInit . Unlike the other EVP context initialization functions that weve discussed, EVP_SealInit has not been deprecated in the engine release or Version 0.9.7 of OpenSSL. int EVP_SealInitEVP_CIPHER_CTX ctx, EVP_CIPHER type, unsigned char ek, int ekl, unsigned char iv, EVP_PKEY pubk, int npubk; ctx The context to be initialized. type The symmetric cipher used to perform the actual encryption. A large number of symmetric ciphers and variations are supported by OpenSSL; we dont list all of the options here. Chapter 6 discusses the supported ciphers in detail and provides a comprehensive list of suitable options for this argument. ek An array of buffers. There must be as many elements allocated for the array as there are public keys specified for encryption. Each element in the array is a buffer that must be large enough to hold the encrypted session key. The size required for each buffer can be determined by calling EVP_PKEY_size , passing the EVP_PKEY object for each buffer as its only argument. ekl An array that will receive the actual encrypted key length for each public key. Again, there must be as many elements allocated for the array as there are public keys specified for encryption. iv A buffer that contains the initialization vector to use. If not specified as NULL , the initialization vector buffer should contain at least as many bytes as defined by the constant EVP_MAX_IV_LENGTH . Not all ciphers require an initialization vector. For such ciphers, this argument can be specified as NULL . pubk An array of public keys to use to encrypt the randomly generated session key. Each element in the array should be an EVP_PKEY object that contains an RSA public key. npubk Specifies the number of public keys contained in the pubk array. It also determines the size requirements for the ek and ekl arrays. Example 8-1 demonstrates how to call EVP_SealInit . Example 8-1. Calling EVP_SealInit ek = unsigned char mallocsizeofunsigned char npubk; 211 ekl = int mallocsizeofint npubk; pubk = EVP_PKEY mallocsizeofEVP_PKEY npubk; for i = 0; i npubk; i++ { pubk[i] = EVP_PKEY_new; EVP_PKEY_set1_RSApubk[i], rsakey[i]; ek[i] = unsigned char mallocEVP_PKEY_sizepubk[i]; } EVP_SealInitctx, type, ek, ekl, iv, pubk, npubk; Once initialization has been performed, the remainder of the sealing process is very much like encrypting with a symmetric cipher, as described in Chapter 6 . In fact, it is identical, with the exception of the names of the functions that are used. The initialization function takes care of generating the session key and encrypting it using the public key of each recipient. It also prepares the context to perform the encryption of the actual data using the selected symmetric cipher. int EVP_SealUpdateEVP_CIPHER_CTX ctx, unsigned char out, int outl, unsigned char in, int inl; ctx The context previously initialized by EVP_SealInit . out A buffer that will receive the encrypted data. Refer to Chapter 6 for the details of how to compute the size of this buffer. outl Receives the number of bytes written to the encrypted data buffer. in A buffer that contains the data to be encrypted. inl Specifies the number of bytes contained in the unencrypted data buffer. After the data to be encrypted has been fed into EVP_SealUpdate , EVP_SealFinish must be called to finish the job. It will perform any necessary padding and write any remaining encrypted data into its output buffer. Once EVP_SealFinish has been called, EVP_SealInit must be called again before the context can be reused. int EVP_SealFinalEVP_CIPHER_CTX ctx, unsigned char out, int outl; ctx The context previously initialized by EVP_SealInit . Used by EVP_SealUpdate to encrypt data. out A buffer that will receive any final encrypted data. 212 outl Receives the number of bytes written to the encrypted data buffer. When the seal process is complete, the encrypted session key, initialization vector if any, and encrypted data must all be sent to the recipient in order for the recipient to decrypt the data successfully. The recipient can then use the EVP interface to unseal or open decrypt the data. The function EVP_OpenInit initializes a context for decryption. Like EVP_SealInit , it has not been deprecated by the engine release or Version 0.9.7 of OpenSSL. int EVP_OpenInitEVP_CIPHER_CTX ctx, EVP_CIPHER type, unsigned char ek, int ekl, unsigned char iv, EVP_PKEY pkey; ctx The context object that will be initialized. type The symmetric cipher to use to decrypt the data. It should be the same cipher that was used to encrypt the data. ek A buffer containing the public key-encrypted session key. ekl Specifies the number of bytes contained in the encrypted session key buffer. iv A buffer containing the initialization vector that was used with the symmetric cipher to encrypt the data. pkey An EVP_PKEY object that contains an RSA private key that will be used to decrypt the session key. Initializing the context decrypts the session key and prepares the context for decrypting the data using the specified symmetric cipher. The rest of the process is identical to decrypting data that has been encrypted with a symmetric cipher, i.e., the functions EVP_OpenUpdate and EVP_OpenFinal are identical to the functions EVP_DecryptUpdate and EVP_DecryptFinal in every way, except for their names. int EVP_OpenUpdateEVP_CIPHER_CTX ctx, unsigned char out, int outl, unsigned char in, int inl; ctx The initialized context that will be used to decrypt the data. out 213 Specifies a buffer to which decrypted data will be written. It must be as large as the input buffer. outl Receives the number of bytes written to the decrypted data buffer. in Specifies a buffer from which encrypted data will be decrypted. inl Specifies the number of bytes contained in the encrypted data buffer. Once all of the data to be decrypted has been fed into EVP_OpenUpdate , EVP_OpenFinal should be called to complete the job. Remember from our discussion of ciphers in Chapter 6 that when a block cipher is being used, the final block of decrypted data will not be written to the output buffer until EVP_DecryptFinal is called or, in this case, EVP_OpenFinal . Once EVP_OpenFinal is called, the context cannot be reused until EVP_OpenInit is called again. int EVP_OpenFinalEVP_CIPHER_CTX ctx, unsigned char out, int outl; ctx Specifies the context to finalize decryption for. out Specifies a buffer to which decrypted data will be written. outl Receives the number of bytes written to the decrypted data buffer.

8.6 Encoding and Decoding Objects