从CPP客户端解密Python服务器中的文件时丢失数据

46qrfjad  于 2023-03-11  发布在  Python
关注(0)|答案(2)|浏览(137)

我正在尝试将一个文件从用CPP编写的客户机发送到用Python编写的服务器。
我在客户端使用AES加密来加密文件,然后将其发送到服务器,AES密钥保存在服务器上。
python中的加密是使用CPP中的Crypto.Cipher和Crypto++执行的。
然而,当文件到达服务器时,我总是丢失原始文件的前16个字符。
例如:
客户端txt文件内容〉“AAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCC”
到达服务器后的txt文件内容〉“BBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCC”
我不确定这是因为服务器跳过了数据,还是因为客户机跳过了数据。

std::string encryptFile(const std::string& filename, const CryptoPP::SecByteBlock& key) {
// Step 1: Read the file to be encrypted
std::ifstream input_file(filename, std::ios::binary);
std::string plaintext((std::istreambuf_iterator<char>(input_file)),
    std::istreambuf_iterator<char>());

CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE);

CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor(key, key.size(), iv);

// Step 2: Encrypt the file
std::string ciphertext;
CryptoPP::StringSource s(plaintext, true,
    new CryptoPP::StreamTransformationFilter(encryptor,
        new CryptoPP::StringSink(ciphertext)));

return ciphertext;

}
python服务器上的函数:

def decrypt_file(self, encrypted_data, aes_key, file_path):
        key = base64.b64decode(aes_key)
        iv = encrypted_data[:AES.block_size]
        cipher = AES.new(key, AES.MODE_CBC, iv=iv)
        plaintext = cipher.decrypt(encrypted_data[AES.block_size:])
        # Remove PKCS#7 padding
        padding_len = plaintext[-1]
        plaintext = plaintext[:-padding_len]

        with open(file_path, "wb") as f:
            f.write(plaintext)

解决这个问题的最佳方法是什么?

ttygqcqt

ttygqcqt1#

通常,当使用AES和CBC加密时,首先生成随机的16字节IV,然后执行加密,并且然后IV和密文级联。
在解密过程中,根据已知的IV长度将IV与密文分离并进行解密,IV的公开没有问题,因为IV不是秘密的。
请注意,出于安全原因,静态IV的使用不是一个选项。
虽然Python代码遵循这个过程(即分离IV和密文,执行解密),但C代码缺少IV和密文的连接。
因此,在Python代码中,第一个块用作IV,其余的用作密文,结果,在解密的明文中第一个块丢失了(从CBC流程图中可以看出,第一个密文块确实是第二个密文块的IV)。
作为修正,在C中,代码IV和密文必须以iv|ciphertext的顺序连接:

return std::string(iv.begin(), iv.end()) + ciphertext;

此外,C代码中缺少使用随机值初始化IV的功能,这可以通过以下方式实现(参见AutoSeededRandomPool):

#include "osrng.h"
...
CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE);
CryptoPP::AutoSeededRandomPool prng;
prng.GenerateBlock(iv, iv.size());

默认情况下,C代码使用PKCS#7填充(参见StreamTransformationFilter,默认情况下,DEFAULT_PADDING应用于第3个参数,对于块密码为PKCS_PADDING)。
这与Python代码中的手动去填充兼容,但是使用相应的PyCryptodome模块Crypto.Util.Padding去填充会更健壮。

from Crypto.Util.Padding import unpad
...
plaintext = unpad(plaintext, 16)
nnsrf1az

nnsrf1az2#

对我来说,这可能是一个填充问题。你在代码中使用了默认填充,而不是PKCS#7(即某种填充方案)。
试试这个:

std::string encryptFile(const std::string& filename, const CryptoPP::SecByteBlock& key) {
    // Step 1: Read the file to be encrypted
    std::ifstream input_file(filename, std::ios::binary);
    std::string plaintext((std::istreambuf_iterator<char>(input_file)),
        std::istreambuf_iterator<char>());
    
    CryptoPP::SecByteBlock iv(CryptoPP::AES::BLOCKSIZE);
    
    CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor(key, key.size(), iv);
    
    // Step 2: Encrypt the file
    std::string ciphertext;
    CryptoPP::StringSource s(plaintext, true,
        new CryptoPP::StreamTransformationFilter(encryptor,
         new CryptoPP::PKCS7_PADDING,
            new CryptoPP::StringSink(ciphertext)));
    
    return ciphertext;
    }

python服务器上的函数:

def decrypt_file(self, encrypted_data, aes_key, file_path):
        key = base64.b64decode(aes_key)
        iv = encrypted_data[:AES.block_size]
        cipher = AES.new(key, AES.MODE_CBC, iv=iv)
        plaintext = cipher.decrypt(encrypted_data[AES.block_size:])
        # Remove PKCS#7 padding
        padding_len = plaintext[-1]
        plaintext = plaintext[:-padding_len]

        with open(file_path, "wb") as f:
            f.write(plaintext)

相关问题