使用 DES(數據加密標準)進行加密和解密的基本操作,重點展示了 ECB 和 CBC 模式,并且通過篡改密文的方式來進行攻擊。下面是對每個部分的詳細解析。
1. 結構體 Slip
struct Slip
{char from[16] = { 0 }; // 交易的發起者(例如 A => B)char to[16] = { 0 }; // 交易的接收者(例如 B => A)long long amount = 0; // 交易金額
};
- Slip 結構體存儲了三部分數據:發起者 (from)、接收者 (to) 和交易金額 (amount)。
- 這段數據需要加密并在后續的操作中進行篡改。
2. 全局變量:DES 密鑰和密鑰調度
static const_DES_cblock key = "1234567";
static DES_key_schedule key_sch;
- key 是一個 8 字節的 DES 密鑰,硬編碼為 “1234567”。
- key_sch 是 DES 密鑰調度,它將密鑰轉換為 DES 算法可用的形式。
3. 加密函數 EnSlip
void EnSlip(const Slip& s, unsigned char* out, int &out_size)
{int size = sizeof(s);auto p = (const unsigned char*)&s;auto o = out;DES_set_key(&key, &key_sch);for (int i = 0; i < size; i += 8){DES_ecb_encrypt((const_DES_cblock*)p, (DES_cblock*)o, &key_sch, DES_ENCRYPT);p += 8;o += 8;out_size += 8;}
}
- EnSlip 負責對 Slip 數據進行加密。
- 以 8 字節為單位處理 Slip 結構體的數據(DES 是塊加密算法,每次處理 8 字節)。
- DES_ecb_encrypt 用于在 ECB 模式下加密數據。
- 每次加密后,將輸入數據指針 p 和輸出數據指針 o 移動 8 字節。
4. 解密函數 DeSlip
void DeSlip(const unsigned char* in, int size, Slip& s)
{auto p = (const unsigned char*)in;auto o = (unsigned char*)&s;DES_set_key(&key, &key_sch);for (int i = 0; i < size; i += 8){DES_ecb_encrypt((const_DES_cblock*)p, (DES_cblock*)o, &key_sch, DES_DECRYPT);p += 8;o += 8;}
}
- DeSlip 用于解密加密后的 Slip 數據。
- 它與 EnSlip 類似,不過使用了 DES_DECRYPT 進行解密。
5. 篡改密文函數 AttackSlip
void AttackSlip(unsigned char* out)
{// 修改密文:交換 from 和 tounsigned char tmp[1024] = { 0 };memcpy(tmp, out, 16); // 復制 from 部分memcpy(out, out + 16, 16); // 將 to 部分放到 from 的位置memcpy(out + 16, tmp, 16); // 將原來的 from 部分放到 to 的位置
}
- AttackSlip 是一個簡單的篡改密文的攻擊方法,它交換了加密后的 from 和 to 字段。
- 這種攻擊顯示了 ECB 模式的弱點,因為 ECB 模式在加密相同的數據塊時會生成相同的密文,攻擊者可以直接修改密文。
6. CBC 模式的加密函數 EnSlipCBC
void EnSlipCBC(const Slip& s, unsigned char* out, int& out_size)
{int size = sizeof(s);auto p = (const unsigned char*)&s;auto o = out;DES_set_key(&key, &key_sch);DES_cblock iv = { 0 }; // 初始化向量,設置為全零out_size = size;if (size % 8 != 0){// 補充0到 8 的倍數out_size = size + (8 - size % 8);}DES_cbc_encrypt(p, o, sizeof(s), &key_sch, &iv, DES_ENCRYPT);
}
- EnSlipCBC 使用 CBC 模式對 Slip 數據進行加密。
- 如果數據長度不是 8 的倍數,則會填充零(補充到 8 字節的倍數)。
- 使用 DES_cbc_encrypt 進行加密,其中需要提供初始化向量(IV)。在此處,IV 被初始化為全零。
7. CBC 模式的解密函數 DeSlipCBC
void DeSlipCBC(const unsigned char* in, int size, Slip& s)
{DES_cblock iv = { 0 }; // 初始化向量DES_set_key(&key, &key_sch);DES_cbc_encrypt(in, (unsigned char*)&s, size, &key_sch, &iv, DES_DECRYPT);
}
- DeSlipCBC 用于解密 CBC 模式加密的數據。
- 它使用與加密相同的 IV(此處為全零),并使用 DES_cbc_encrypt 解密數據。
8. 主函數 main
int main(int argc, char* argv[])
{unsigned char out[1024] = { 0 };int out_size = 0;Slip s1 = { "USER_A", "USER_B", 10000 };cout << "s1 from:" << s1.from << endl;cout << "s1 to:" << s1.to << endl;cout << "s1 amount:" << s1.amount << endl;EnSlip(s1, out, out_size);cout << "En: " << out_size << " | " << out << endl;// 攻擊密文AttackSlip(out);Slip s2;DeSlip(out, out_size, s2);cout << "s2 from:" << s2.from << endl;cout << "s2 to:" << s2.to << endl;cout << "s2 amount:" << s2.amount << endl;Slip s3;EnSlipCBC(s1, out, out_size);// 攻擊密文AttackSlip(out);DeSlipCBC(out, out_size, s3);cout << "s3 from:" << s3.from << endl;cout << "s3 to:" << s3.to << endl;cout << "s3 amount:" << s3.amount << endl;getchar();return 0;
}
- 在主函數中,首先創建了一個交易數據 s1(發起者 “USER_A”、接收者 “USER_B”、金額 10000)。
- 對 s1 使用 ECB 模式進行加密,并輸出加密后的密文。
- 然后,調用 AttackSlip 攻擊密文,交換 from 和 to 字段,并解密密文。
- 同樣,對 s1 使用 CBC 模式進行加密,攻擊密文后,再使用 CBC 解密。
9. 攻擊分析
- ECB 模式的漏洞: 因為 ECB 模式對每個數據塊獨立加密,導致相同的數據塊加密后密文相同,攻擊者可以直接修改密文(例如交換 from 和 to 字段),從而篡改交易數據。
- CBC 模式的優勢: 雖然 CBC 模式對每個數據塊進行依賴加密,增強了安全性,但在此代碼中并沒有使用認證機制,攻擊者仍然可以通過篡改密文的一部分(如改變密文中的塊順序)影響解密結果。
完整代碼
#include <iostream>
#include <openssl/des.h>
#include <cstring>
using namespace std;// 交易數據
struct Slip
{char from[16] = {0}; // A=>B 10000char to[16] = {0}; // 篡改為 B=>A 10000long long amount = 0;
};
static const_DES_cblock key = "1234567";
static DES_key_schedule key_sch;void EnSlip(const Slip &s, unsigned char *out, int &out_size)
{int size = sizeof(s);auto p = (const unsigned char *)&s;auto o = out;DES_set_key(&key, &key_sch);for (int i = 0; i < size; i += 8){DES_ecb_encrypt((const_DES_cblock *)p, // 輸入數據(DES_cblock *)o, // 輸出數據&key_sch, // 秘鑰DES_ENCRYPT // 1 加密);p += 8;o += 8;out_size += 8;}// 補充數據。。。
}void DeSlip(const unsigned char *in, int size, Slip &s)
{auto p = (const unsigned char *)in;auto o = (unsigned char *)&s;DES_set_key(&key, &key_sch);for (int i = 0; i < size; i += 8){DES_ecb_encrypt((const_DES_cblock *)p, (DES_cblock *)o, &key_sch, DES_DECRYPT);p += 8;o += 8;}
}void AttackSlip(unsigned char *out)
{// 修改密文 from 和to 對調unsigned char tmp[1024] = {0};// frommemcpy(tmp, out, 16);// to copy frommemcpy(out, out + 16, 16);memcpy(out + 16, tmp, 16);
}void EnSlipCBC(const Slip &s, unsigned char *out, int &out_size)
{int size = sizeof(s);auto p = (const unsigned char *)&s;auto o = out;DES_set_key(&key, &key_sch);DES_cblock iv = {0}; // 初始化向量out_size = size;// 數據如果不是8的倍數,會補0if (size % 8 != 0){// 補充0out_size = size + (8 - size * 8);}DES_cbc_encrypt(p, // 輸入o, // 輸出sizeof(s), // 輸入數據的大小&key_sch, // 秘鑰&iv, // 初始化向量 DES_cbc_encrypt 調用后值不變// DES_ncbc_encrypt 保存上次的值DES_ENCRYPT // 加密);
}
void DeSlipCBC(const unsigned char *in, int size, Slip &s)
{DES_cblock iv = {0}; // 初始化向量DES_set_key(&key, &key_sch);// 如果補0了 解密后無法知道實際大小,需要用戶存儲原數據大小DES_cbc_encrypt(in, (unsigned char *)&s, size, &key_sch, &iv, DES_DECRYPT);
}
int main(int argc, char *argv[])
{unsigned char out[1024] = {0};int out_size = 0;Slip s1 = {"USER_A", "USER_B", 10000};cout << "s1 from:" << s1.from << endl;cout << "s1 to:" << s1.to << endl;cout << "s1 amount:" << s1.amount << endl;EnSlip(s1, out, out_size);cout << "En:" << out_size << "|" << out << endl;// 攻擊密文AttackSlip(out);Slip s2;DeSlip(out, out_size, s2);cout << "s2 from:" << s2.from << endl;cout << "s2 to:" << s2.to << endl;cout << "s2 amount:" << s2.amount << endl;Slip s3;EnSlipCBC(s1, out, out_size);// 攻擊密文AttackSlip(out);DeSlipCBC(out, out_size, s3);cout << "s3 from:" << s3.from << endl;cout << "s3 to:" << s3.to << endl;cout << "s3 amount:" << s3.amount << endl;getchar();return 0;
}