去年,我用C++和crypto ++ lib制作了一个使用AES 256 GCM的加密程序。今年我想升级到QT并改变我在文件中阅读的方式。旧的方法是将整个文件读入char *,然后加密并写出。我注意到大文件不起作用,所以我需要将它切换到缓冲区。使用文件缓冲区循环加密文件
我将它切换为读取8kb,加密,写入重复系统,但现在每次循环时,它都会向输出添加额外的33bytes,我不知道为什么。这意味着如果文件大小< 8KB有效,如果文件大小在8KB和16KB之间,则输出会增加额外的33字节,如果文件大小在16KB和24KB之间,则输出会增加额外的66字节等。
我有什么到目前为止能够弄清楚它不是加密代码,因为它对小于8KB的文件起作用,并且它不是文件循环代码,因为我用简单的复制文件代码替换了加密代码,并且它复制了文件正确。
我认为问题是我没有重置一个变量,它在某种程度上搞乱了每个循环的加密代码的数据馈送。
这里是我的代码
void encryptfile(double progressbarfilecount, bool& threadstatus) {
// variables for file data
int buffersize = 8192;
string fullfilename;
string filepath;
string filename;
char memblock[8192];
streampos size;
double filesize;
double encryptedfilesize;
string datastring;
CryptoPP::SecByteBlock initializationvector(32);
string initializationvectorstring;
string cipher;
string encoded;
QMessageBox msgBox;
// encrypt the file
// get the filepath and filename
fullfilename = listbox1->item(progressbarfilecount)->text().toUtf8().constData();
size_t found = fullfilename.find_last_of("/\\");
filepath = fullfilename.substr(0,found);
filename = fullfilename.substr(found + 1);
// get the file size
//QFile myFile(QString::fromStdString(fullfilename));
//filesize = myFile.size();
//myFile.close();
filesize = getfilesize(fullfilename);
qDebug() << "filesize:" << QString::number(filesize);
// setup the file data
ifstream originalfile(fullfilename, ios::in | ios::binary | ios::ate);
ofstream encryptedfile(fullfilename + ".txt", ios::app);
// get random initializationvector
randomnumber.GenerateBlock(initializationvector, initializationvector.size());
// convert it to a string for the text filee
initializationvectorstring = string((char *)initializationvector.begin(),32);
// check if we should get the checksum of the original file
if (testencryptiontogglebuttonguisetting == "On") {
originalfilechecksum << checksum(fullfilename);
}
// here is the loop where the problem maybe
// encrypt the file 8KB at a time
for (encryptedfilesize = 0; encryptedfilesize < filesize; encryptedfilesize+= buffersize) {
// check if the data left to write is less than the buffer size
if (filesize - encryptedfilesize < buffersize) {
buffersize = filesize - encryptedfilesize;
qDebug() << "new buffersize:" << QString::number(buffersize);
}
// read the file into a memory block
originalfile.seekg(encryptedfilesize);
originalfile.read(memblock, buffersize);
// convert the memoryblock to readable hexadecimal
datastring = stringtohexadecimal(string(memblock, buffersize), true);
// encrypt
try
{
GCM<AES>::Encryption e;
e.SetKeyWithIV(key, sizeof(key), initializationvector,initializationvector.size());
// Not required for GCM mode (but required for CCM mode)
// e.SpecifyDataLengths(adata.size(), pdata.size(), 0);
AuthenticatedEncryptionFilter ef(e,new StringSink(cipher), false, TAG_SIZE); // AuthenticatedEncryptionFilter
// AuthenticatedEncryptionFilter::ChannelPut
// defines two channels: "" (empty) and "AAD"
// channel "" is encrypted and authenticated
// channel "AAD" is authenticated
ef.ChannelPut("AAD", (const byte*)adata.data(), adata.size());
ef.ChannelMessageEnd("AAD");
// Authenticated data *must* be pushed before
// Confidential/Authenticated data. Otherwise
// we must catch the BadState exception
ef.ChannelPut("", (const byte*)datastring.data(), datastring.size());
ef.ChannelMessageEnd("");
// Pretty print
StringSource(cipher, true,new HexEncoder(new StringSink(encoded), true, 16, " "));
}
catch (CryptoPP::BufferedTransformation::NoChannelSupport&)
{
// The tag must go in to the default channel:
// "unknown: this object doesn't support multiple channels"
if (operatingsystem() == "Linux") {
system("error_message_encrypt_file_error.sh");
}
if (operatingsystem() == "Windows") {
ShellExecute(0, L"open", L"error_message_encrypt_file_error.vbs", 0, 0, SW_NORMAL);
}
//msgBox.setText("No Channel Support");
//msgBox.exec();
return;
}
catch (CryptoPP::AuthenticatedSymmetricCipher::BadState&)
{
// Pushing PDATA before ADATA results in:
// "GMC/AES: Update was called before State_IVSet"
if (operatingsystem() == "Linux") {
system("error_message_encrypt_file_error.sh");
}
if (operatingsystem() == "Windows") {
ShellExecute(0, L"open", L"error_message_encrypt_file_error.vbs", 0, 0, SW_NORMAL);
}
//msgBox.setText("Data was read before adata");
//msgBox.exec();
return;
}
catch (CryptoPP::InvalidArgument&)
{
if (operatingsystem() == "Linux") {
system("error_message_encrypt_file_invalid.sh");
}
if (operatingsystem() == "Windows") {
ShellExecute(0, L"open", L"error_message_encrypt_file_invalid.vbs", 0, 0, SW_NORMAL);
}
//msgBox.setText("Invalid Argument");
//msgBox.exec();
return;
}
// convert the cipher to hexadecimal string
cipher = stringtohexadecimal(cipher, true);
// write the encrypted file to a text file with the original file extension
// check to see if we need to write the initialization vector
if (encryptedfilesize == 0) {
initializationvectorstring = stringtohexadecimal(initializationvectorstring, true);
encryptedfile << initializationvectorstring;
qDebug() << "wrote the initilization vector";
}
encryptedfile << encoded;
qDebug() << "encrypted filesize:" << QString::number(encryptedfilesize);
// clear the variables
encoded = "";
cipher = "";
initializationvectorstring = "";
keys = "";
}
// close the file data
originalfile.close();
encryptedfile.close();
如果有人可以帮助我弄清楚什么是错的代码,我将不胜感激。
请不要分别加密8KB块。你正在重复使用每个块的IV,因此这是一个[多次填充](http://crypto.stackexchange.com/questions/2249/how-does-one-attack-a-two-time-pad因为GCM基于点播模式,它为每个块创建相同的密钥流。在不知道密钥的情况下推导出明文是可能的。有了这个方案,如果有更多的块,它会变得更容易。您需要设置一次该方案,然后您可以传入多个区块。 –
感谢您让我知道这一点,所以您会建议在加密代码之前将大文件分割开来,因此它可能只有4个较小的文件,然后对其进行加密。这样每个部分会得到不同的iv,我仍然可以加密一个大文件?谢谢。 –
我不确切知道它在Crypto ++中的工作方式,但我怀疑你会遍历块并将它们传递给'ef.ChannelPut(“”,...)'并将所有其他内容从'ef.ChannelMessageEnd (“”);'在循环后面。 AAD设置也应该在循环之前完成。 –