使用 C++ 和 OpenSSL 攻击 AES-CTR 加密





5.00/5 (1投票)
使用 C++ (CygWin GCC) 和 OpenSSL 代码攻击 AES-CTR 加密
引言
我在我的 LinkedIn 个人资料中写了一篇文章,所以我也想把它发布在 Code Project 上。 这个攻击最初是由 Prof Bill Buchanan OBE 在以下链接中编写的,
https://billatnapier.medium.com/can-i-break-aes-encryption-yes-31bdf539aba0
它详细解释了这种攻击是如何工作的。 他最初是通过命令行使用 OpenSSL。
我使用 C++ (CygWin GCC with Eclipse over Windows OS) OpenSSL 库自动完成了攻击。
背景
1. 它使用基于密码的密钥派生函数从密码生成密钥。
2. 使用密钥,它使用 AES 计数器模式加密消息“Pay Bob 1 dollar”。
3. 第 9 个字节包含金额,通过简单的位运算,我修改了密文上的金额。
4. 解密密文会得到修改后的消息“Pay Bob 9 dollar”。
这就是中间人在双方之间通过捕获数据并修改数据来发起攻击的方式,而无需知道密钥且无需解密,这种情况发生在 2 和 3 之间。
代码的输出如下。
工作原理
AES 计数器模式下数据加密的过程如下图所示。
AES 计数器模式下数据解密的过程如下图所示。
加密和解密的过程几乎相同。 它获取计数器并使用密钥对其进行加密以获得加密计数器 [EC]。 关于 AES-CTR 如何工作的文章在互联网上有很多。
它们都执行计数器的加密并获得加密的计数器 [EC]。 明文与 [EC] 执行 XOR 运算以在加密过程中获得密文,或者密文与 [EC] 执行 XOR 运算以在解密过程中获得密文,如图所示。
示例
假设,如上面的例子中,当 Alice 向银行发送交易时,Bob 雇佣的攻击者 Eve 能够破译密文。 交易显示为“Pay Bob 1 dollar”,但已加密,如下所示。 再次假设攻击者根据推测知道第九个字节包含数字 1。在交易到达银行之前,攻击者 Eve 想要仅使用密文将金额从 1 更改为 9。
引用0x7b 0xfe 0x7c 0x81 0x60 0x3e 0x86 0xe3 - 0x12 0x82 0x8e 0x1e 0x1e 0xc2 0xd6 0x42
令 Pi 为在 ith 位置的单字节明文(由攻击者确定),Ci 为在 ith 位置的单字节密文,ECi 为在 ith 位置的单字节加密计数器。 因此,通过在 AES 计数器模式下加密数据,密文,
Ci = ECi xor Pi
考虑到 ECi 是未知的,而 Ci 和 Pi 是已知的。 令 NPi 表示必须在攻击者确定的 ith 位置进行更改的新明文。
攻击者的目标是使用 Ci 来确定新的密文 NCi,以便一旦密文被银行解密,明文应该是新的明文 NPi。 也就是说,基于上图。
NCi = ECi xor NPi
令,
Ci=ECi xor Pi, 两边都与 Pi 进行 xor 运算。
Ci xor Pi = (ECi xor Pi xor Pi)。 由于 (Pi xor Pi)= 0,因此,
Ci xor Pi = ECi。
两边都与 NPi 进行 XOR 运算,所以,
Ci xor Pi xor NPi = ECi xor NPi = NCi, 从上面的定义中。
因此,
NCi = Ci xor Pi xor NPi。
从上面的例子中
NCi = 0x12 xor 0x01 xor 0x09 = 0x1a。
因此,新的密码是,
引用0x7b 0xfe 0x7c 0x81 0x60 0x3e 0x86 0xe3 - 0x1a 0x82 0x8e 0x1e 0x1e 0xc2 0xd6 0x42
使用 AES 计数器模式解密上面的密文将导致
引用Pay Bob 9 dollar
使用代码
代码如下,并且是不言自明的。
#include <string.h>
#include <iostream>
using namespace std;
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/bioerr.h>
#include <openssl/evp.h>
#include <openssl/evperr.h>
int main()
{
unsigned char szPass[] = { "strongestpassword" };
cout << "Password:" << endl;
BIO_dump_fp(stdout, (const char*)szPass, strlen((const char*)szPass));
cout << endl << endl;
unsigned char szKey[16];
PKCS5_PBKDF2_HMAC((const char*)szPass, strlen((const char*)szPass), NULL, 0, 5, EVP_md5(), 16, szKey);
cout << "Key derived from password using MD5:" << endl;
BIO_dump_fp(stdout, (const char*)szKey, 16);
cout << endl << endl;
unsigned char szMessage[] = { "Pay Bob 1 dollar" };
cout << "Transaction message:" << endl;
BIO_dump_fp(stdout, (const char*)szMessage, strlen((const char*)szMessage));
cout << endl << endl;
unsigned char szCipher[16];
int nCipherLen = 0;
int nTotCipherLen = 0;
EVP_CIPHER_CTX* pAES128Ctx = EVP_CIPHER_CTX_new();;
EVP_EncryptInit(pAES128Ctx, EVP_aes_128_ctr(), szKey, NULL);
EVP_EncryptUpdate(pAES128Ctx, szCipher, &nCipherLen, szMessage, strlen((const char*)szMessage));
nTotCipherLen = nCipherLen;
nCipherLen = 0;
EVP_EncryptFinal(pAES128Ctx, szCipher, &nCipherLen);
nTotCipherLen += nCipherLen;
EVP_CIPHER_CTX_free(pAES128Ctx);
cout << "Encrypted message:" << endl;
BIO_dump_fp(stdout, (const char*)szCipher, nTotCipherLen);
cout << "If you pass this cipher over the network," << endl << "attacker Bob (Man-In-The-Middle) may capture and modify 9th encrypted byte and send the encrypted transaction!" << endl;
cout << endl << endl;
// MAN-IN-THE-MIDDLE ATTACK mofified the 9th byte and increase the value.
szCipher[8] ^= 0x01; // Remove One dollar.
szCipher[8] ^= 0x09; // Add Nine dollar.
cout << "Modified Encrypted message:" << endl;
BIO_dump_fp(stdout, (const char*)szCipher, nTotCipherLen);
cout << endl << endl;
unsigned char szDycMsg[16];
int nDycMsgLen = 0;
int nTotDycMsgLen = 0;
EVP_CIPHER_CTX* pMSG128Ctx = EVP_CIPHER_CTX_new();
EVP_DecryptInit(pMSG128Ctx, EVP_aes_128_ctr(), szKey, NULL);
EVP_DecryptUpdate(pMSG128Ctx, szDycMsg, &nDycMsgLen, szCipher, nTotCipherLen);
nTotDycMsgLen = nDycMsgLen;
nDycMsgLen = 0;
EVP_DecryptFinal(pMSG128Ctx, szDycMsg, &nDycMsgLen);
nTotDycMsgLen += nDycMsgLen;
EVP_CIPHER_CTX_free(pMSG128Ctx);
cout << "Modified derypted message the bancker receives:" << endl;
BIO_dump_fp(stdout, (const char*)szDycMsg, nTotDycMsgLen);
cout << endl << endl;
return 0;
}
结论
有很多可用的加密算法,但并非所有算法都受到推荐,例如 AES-CTR,但还有其他强烈推荐的计数器模式,例如 GCM 和 CCM。