【发布时间】:2016-10-13 03:23:30
【问题描述】:
去年我使用 AES 256 GCM 使用 C++ 和 crypto++ 库制作了一个加密程序。今年我想把它升级到 QT 并改变我在文件中读取的方式。旧方法是将整个文件读入 char*,然后对其进行加密并将其写出。我注意到大文件不起作用,所以我需要将其切换到缓冲区。
我将它切换为读取 8kb、加密、写入重复系统,但现在每次循环时,它都会在输出中增加额外的 33 字节,我不知道为什么。这意味着如果文件大小
到目前为止,我能够弄清楚的是它不是加密代码,因为它适用于小于 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,因此这是一个 many-time pad,因为 GCM 基于 CTR,它是一种流模式,它为每个块创建相同的密钥流。可以在不知道密钥的情况下推导出明文。有了这个方案,如果有更多的块,它会变得更容易。您需要设置一次方案,然后您可以传入多个块。
-
感谢您让我知道这一点,那么您是否建议在加密代码之前拆分大文件,这样它可能就像 4 个小文件一样,然后加密。这样每块都会得到不同的iv,我仍然可以加密一个大文件?谢谢。
-
我不知道它在 Crypto++ 中是如何工作的,但我怀疑你会遍历这些块并将它们传递给
ef.ChannelPut("", ...)并将其他所有内容从ef.ChannelMessageEnd("");移到循环后面. AAD 设置也应该在循环之前完成。 -
好的,谢谢,我会研究 ChannelPut。此外,如果我只是在文件加密之前创建自己的文件拆分代码,这是否会以任何方式损害文件。例如,4GB 文件被拆分为 1GB (4x) 文件。每一块都用单独的 IV 加密。
-
只要您确保使用唯一的 IV(随机数)并且不要忘记添加所有 4 个身份验证标签,就不会妥协。问题是拆分会带来更高的管理开销,因为您需要以某种方式写入一个块结束和下一个块开始的文件格式。如果您在单个块中加密,则根本不必这样做。此外,GCM 使用一对密钥 + IV 可确保高达 68GB 的安全。
标签: c++ qt file encryption crypto++