去年,我用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設置也應該在循環之前完成。 –