我有一个单线程客户端/服务器应用程序,需要对其网络通信进行加密和解密。我计划使用OpenSSL的EVP API和AES-256-CBC。
我从一些例子中找到了一些示例伪代码:
// 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);
问题来自所有这些例子,我不确定在每次加密/解密时需要做什么,以及我应该只在启动时做一次。
具体做法是:
EVP_CIPHER_CTX
并继续重复使用它,直到应用程序结束?EVP_CIPHER_CTX
加密和解密,还是我应该创建其中的2个?答案 0 :(得分:10)
很抱歉恢复旧线程,但我注意到接受的答案中出现以下错误:
在第1行,我是否只创建一次EVP_CIPHER_CTX并继续重复使用它直到应用程序结束?
每次使用都会创建一次。也就是说,当您需要加密时,您使用相同的上下文。如果需要加密第二个流,则可以使用第二个上下文。如果您需要解密第三个流,您将使用第三个上下文。
同样在第1行,我是否可以重复使用相同的EVP_CIPHER_CTX加密和解密,或者我应该创建其中的2个?
不,见上文。
这不是必需的。从OpenSSL的手册页:
新代码应该使用EVP_EncryptInit_ex(),EVP_EncryptFinal_ex(),EVP_DecryptInit_ex(),EVP_DecryptFinal_ex(), EVP_CipherInit_ex()和EVP_CipherFinal_ex()因为它们可以重用现有的上下文而无需在每次调用时分配和释放它。
换句话说,您需要在每次使用之前重新初始化上下文,但是您可以反复使用相同的上下文而无需创建(分配)新的上下文。
答案 1 :(得分:8)
我有一个单线程客户端/服务器应用程序,需要对其网络通信进行加密和解密。我计划使用OpenSSL的EVP API和AES-256-CBC。
如果您使用SSL_*
中的libssl
功能,则可能永远不会触及EVP_*
API。
在第1行,我是否只创建一次EVP_CIPHER_CTX并继续重复使用它直到应用程序结束?
每次使用都会创建一次。也就是说,当您需要加密时,您使用相同的上下文。如果需要加密第二个流,则可以使用第二个上下文。如果您需要解密第三个流,您将使用第三个上下文。
同样在第1行,我是否可以重复使用相同的EVP_CIPHER_CTX加密和解密,或者我应该创建其中的2个?
不,见上文。
密码将具有不同的状态。
在第2行,是否应该在每个加密的数据包中重新设置IV?或者我只将IV设置一次,然后让它永远继续下去?
没有。你设置IV一次然后忘记它。上下文对象为密码管理的状态的一部分。
如果我加密UDP数据包,数据包很容易丢失或无序接收,该怎么办?我认为CBC无法正常工作......
如果您使用的是UDP,则由您来检测这些问题。你可能最终会重新发明TCP。
仅靠加密通常是不够的。您还需要确保真实性和完整性。您不会操作不真实的数据。这就是让SST / TLS和SSH陷入困境的原因。
例如,在SSL / TLS使用的Authenticate-Then-Encrypt(EtA)方案中,编写seminal paper on authenticated encryption关于IPSec,SSL / TLS和SSH的人称重: Last Call: (Encrypt-then-MAC for TLS and DTLS) to Proposed Standard:
我2001年论文中的技术成果是正确但结论 关于SSL / TLS是错误的。我认为TLS正在使用新的IV和 MAC是在编码的明文上计算的,即 在TLS进行Mac-Encode-Encrypt时进行Encode-Mac-Encrypt 正是我的理论实例所表明的是不安全。
为了真实性,您应该放弃CBC模式并切换到GCM模式。 GCM是一种经过身份验证的加密模式,它将机密性和真实性结合到一种模式中,因此您不必组合基元(如AES / CBC和HMAC)。
或者我需要在发送的每个数据包开始时重置IV?
不,你设置IV一次然后忘记它。
问题来自所有这些例子,我不确定每次加密/解密需要做什么,以及我应该只在启动时做一次。
EVP_CIPHER_CTX
EVP_CipherInit
EVP_CipherUpdate
EVP_CipherFinal
OpenSSL wiki有很多使用EVP_*
接口的例子。请参阅EVP Symmetric Encryption and Decryption,EVP Authenticated Encryption and Decryption和EVP Signing and Verifying。
所有示例都使用相同的模式:Init
,Update
,然后是Final
。无论是加密还是哈希都没关系。
相关:您应该对此感兴趣:EVP Authenticated Encryption and Decryption。它的示例代码来自OpenSSL wiki。
相关:您可以在线查找Viega,Messier和Chandra的Network Security with OpenSSL的副本。您可以考虑搜索副本并熟悉其中的一些概念。
答案 2 :(得分:2)
也为恢复旧线程而感到抱歉,但是LazerSharks在评论中两次询问了evp密码上下文。我在这里没有足够的声誉积分,无法添加一些评论,这就是我在这里回答的原因。 (即使现在Google搜索也不显示必要的信息) 摘自Pravir Chandra,Matt Messier和John Viega撰写的《使用OpenSSL进行网络安全》一书:
在我们可以开始加密或解密之前,我们必须分配和初始化一个密码上下文。密文上下文是一种数据结构,可跟踪所有相关状态,以便在一段时间内对数据进行加密或解密。例如,我们可以有多个 以CBC模式加密的数据。密码上下文将跟踪与每个密钥关联的密钥 流和在CBC模式的消息之间需要保留的内部状态。此外,在使用基于块的密码模式进行加密时,上下文对象会缓冲与块大小完全不匹配的数据,直到到达更多数据或显式刷新缓冲区为止,此时通常会适当地填充数据。
通用密码上下文类型为EVP_CIPHER_CTX。我们可以通过调用EVP_CIPHER_CTX_init来初始化一个是动态分配的还是静态分配的,就像这样:
EVP_CIPHER_CTX * x =(EVP_CIPHER_CTX *)malloc(sizeof(EVP_CIPHER_CTX)); EVP_CIPHER_CTX_init(x);