解密 AES 加密文件,导致崩溃的函数
Posted
技术标签:
【中文标题】解密 AES 加密文件,导致崩溃的函数【英文标题】:Decrypt AES-encrypted file, function causing a crash 【发布时间】:2015-01-12 00:10:12 【问题描述】:我正在尝试编写一个 C++ 程序来加密和解密某些文件。加密工作正常,但是当我来解密该函数时似乎导致程序崩溃。
这是我用来解密的函数:
CString DecryptFile(CString csSourceFile)
CString csDecryptedFile = csSourceFile;
CRijndael aesEncryption;
int nPos = csDecryptedFile.ReverseFind('.');
if(nPos == -1)
//Log("ERROR:: file name not proper");
return CString("");
csDecryptedFile = csDecryptedFile.Left(nPos);
csDecryptedFile += ".wav";
FILE *fIn = fopen(csSourceFile.GetBuffer(0),"rb");
FILE *fOut = fopen(csDecryptedFile.GetBuffer(0),"wb");
if(!fIn || !fOut)
//Log("ERROR:: failed to Open File for encryption");
return CString("");
int nlen = -1;
aesEncryption.MakeKey(AM_ENC_KEY,AM_NULL_KEY,BLOCK_SIZE,BLOCK_SIZE);
int nRead = 0;
while (true)
char szBlockIn[EBLOCK_SIZE+1] = 0;
char szBlockOut[BLOCK_SIZE+1] = 0;
char szBlockDec[BLOCK_SIZE+1] = 0;
memset(szBlockIn,0,sizeof(char)*(EBLOCK_SIZE+1));
memset(szBlockOut,0,sizeof(char)*(BLOCK_SIZE+1));
memset(szBlockDec,0,sizeof(char)*(BLOCK_SIZE+1));
int nRead = fread(szBlockIn,sizeof(char),EBLOCK_SIZE, fIn);
if(nRead <= 0)
break;
nlen = EBLOCK_SIZE;
Decode(szBlockIn,szBlockOut,nlen);
aesEncryption.DecryptBlock((char *)szBlockOut,szBlockDec);
fwrite(szBlockDec,sizeof(char),BLOCK_SIZE,fOut);
fclose(fIn);
fclose(fOut);
RemoveEndTag(csDecryptedFile.GetBuffer(0));
AfxMessageBox(csDecryptedFile);
AfxMessageBox(_T("returning"));
return csDecryptedFile;
这是调用函数的代码sn-p:
CString strTest = DecryptFile(m_DecompressedTempFile);
AfxMessageBox(strTest);
奇怪的是解密执行得非常好 - 如果我到达文件位置,我可以看到解密的文件并访问它。不幸的是,该程序必须做更多的事情,并且该程序只是紧随其后而挂起。特别奇怪的是,如您所见,我在函数结束之前调用了AfxMessageBox(_T("returning"));
:这会产生一个消息框。但是当我从运行该函数的代码中调用AfxMessageBox(strTest);
时,不会生成任何消息框。
通过调试,问题似乎以某种方式在我用于加密文件的while循环中路由,所以我想知道我是否没有关闭我应该关闭的东西或其他东西?虽然不太确定从哪里开始,所以任何可以提供任何帮助的人都会很棒!?!
提前致谢。
我忘了提到这已经工作多年了——最近唯一的变化是我们现在在 Windows 7 上编译代码,而之前是在 Windows XP 上。这会对函数中的任何内容产生影响吗?
更新: 值得注意的是,如果我删除 while 循环并只运行一次循环的内容,就会出现同样的问题。如果我删除aesEncryption.DecryptBlock((char *)szBlockOut,szBlockDec);
,那么问题就会消失,但显然我需要那条线。所以问题是我认为的那个功能。虽然这个函数存在于库中,但我将它包含在下面:
//Decrypt exactly one block of ciphertext.
// in - The ciphertext.
// result - The plaintext generated from a ciphertext using the session key.
void CRijndael::DecryptBlock(char const* in, char* result)
if(false==m_bKeyInit)
throw exception(sm_szErrorMsg1);
if(DEFAULT_BLOCK_SIZE == m_blockSize)
DefDecryptBlock(in, result);
return;
int BC = m_blockSize / 4;
int SC = BC == 4 ? 0 : (BC == 6 ? 1 : 2);
int s1 = sm_shifts[SC][1][1];
int s2 = sm_shifts[SC][2][1];
int s3 = sm_shifts[SC][3][1];
//Temporary Work Arrays
int i;
int tt;
int* pi = t;
for(i=0; i<BC; i++)
*pi = ((unsigned char)*(in++) << 24);
*pi |= ((unsigned char)*(in++) << 16);
*pi |= ((unsigned char)*(in++) << 8);
(*(pi++) |= (unsigned char)*(in++)) ^= m_Kd[0][i];
//Apply Round Transforms
for(int r=1; r<m_iROUNDS; r++)
for(i=0; i<BC; i++)
a[i] = (sm_T5[(t[i] >> 24) & 0xFF] ^
sm_T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
sm_T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^
sm_T8[ t[(i + s3) % BC] & 0xFF]) ^ m_Kd[r][i];
memcpy(t, a, 4*BC);
int j;
//Last Round is Special
for(i=0,j=0; i<BC; i++)
tt = m_Kd[m_iROUNDS][i];
result[j++] = sm_Si[(t[i] >> 24) & 0xFF] ^ (tt >> 24);
result[j++] = sm_Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16);
result[j++] = sm_Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8);
result[j++] = sm_Si[ t[(i + s3) % BC] & 0xFF] ^ tt;
【问题讨论】:
是GetBuffer(0)
打来的电话吗? msdn.microsoft.com/en-us/library/aa314880%28v=vs.60%29.aspx 由于 CString 是引用计数字符串,您可能需要调用 ReleaseBuffer
谢谢你,ReleaseBuffer 肯定应该在那里。不幸的是,这对悬挂问题没有帮助:(
也许你应该使用另一个 AES 库,因为你使用的那个似乎没有经过很多同行评审:codeproject.com/Articles/1380/…
我忘了提到这已经工作了好几年了——最近唯一的变化是我们现在在 Windows 7 上编译代码,而之前是在 Windows XP 上。这会对函数中的任何内容产生影响吗?
有加密++。 cryptopp.com 只需确保您采用现有示例并根据您的需要对其进行定制。该库是稳定的,但往往难以使用(我的看法)。
【参考方案1】:
不清楚您是否只需要 C++ 解决方案;如果您对 Python 解决方案没问题,请尝试 PyCrypto 模块。这是我用来压缩当前目录的内容,除了一些忽略模式,并在 ./bin 下加密 .zip(其中大部分是从 SO 上其他地方复制的代码):
import sys
import os
import zipfile
import getpass
from contextlib import contextmanager
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
def derive_key_and_iv(password, salt, key_length, iv_length):
d = d_i = ''
while len(d) < key_length + iv_length:
d_i = md5(d_i + password + salt).digest()
d += d_i
return d[:key_length], d[key_length:key_length+iv_length]
def encrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = Random.new().read(bs - len('Salted__'))
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
out_file.write('Salted__' + salt)
finished = False
while not finished:
chunk = in_file.read(1024 * bs)
if len(chunk) == 0 or len(chunk) % bs != 0:
padding_length = (bs - len(chunk) % bs) or bs
chunk += padding_length * chr(padding_length)
finished = True
out_file.write(cipher.encrypt(chunk))
def decrypt(in_file, out_file, password, key_length=32):
bs = AES.block_size
salt = in_file.read(bs)[len('Salted__'):]
key, iv = derive_key_and_iv(password, salt, key_length, bs)
cipher = AES.new(key, AES.MODE_CBC, iv)
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
def query_yes_no(question, default="yes", quiet=False):
"""Ask a yes/no question via raw_input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "yes" (the default), "no" or None (meaning
an answer is required of the user).
The "answer" return value is True for "yes" or False for "no".
"""
valid = "yes": True, "y": True, "ye": True,
"no": False, "n": False
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)
while True:
if not quiet:
sys.stdout.write(question + prompt)
if quiet and default is not None:
choice=default
else:
choice = raw_input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "
"(or 'y' or 'n')\n")
dircurr=os.path.abspath('.').replace('\\','/')
dirwithenc=dircurr+'/bin'
if os.path.isfile(dirwithenc+'/bak.enc'):
if query_yes_no("\nDo you want to decrypt '"+dirwithenc+"/bak.enc'?",'no'):
password=getpass.getpass('\nDecryption password: ')
with open(dirwithenc+'/bak.enc', 'rb') as in_file, open(dircurr+'/bak.zip', 'wb') as out_file:
decrypt(in_file,out_file,password)
print("\nFile '"+dircurr+"/bak.enc' decrypted to '"+dircurr+"/bak.zip'")
raw_input("""
***DONE***
Press Enter...""")
exit(0)
else:
print("\nAnswered 'no'")
print("\nEncrypting files will overwrite any preexisting file '"+dirwithenc+"/bak.enc'")
password=getpass.getpass('\nSet an encryption password (will not be saved): ')
print('\nZipping files, excepting certain ignore patterns...')
zf = zipfile.ZipFile(dircurr+'/bak.zip', 'w')
for dirname, subdirs, files in os.walk(os.path.abspath('.')):
#Do not archive system files beginning with period
for filename in files:
if filename[:1]=='.':
files.remove(filename)
#Do not archive the binaries directory, it should just be delivered as is
if 'bin' in subdirs:
subdirs.remove('bin')
#Do not archive any previous archives, or you will just make a giant snowball
if 'bak.zip' in files:
files.remove('bak.zip')
if 'bak.enc' in files:
files.remove('bak.enc')
zf.write(dirname)
for filename in files:
zf.write(os.path.join(dirname, filename))
#If there was a folder 'dat' under the binaries folder, then archive it too,
#so that you have everything you need to rebuild the project
print("Including files under 'bin/dat'")
if os.path.isdir(dircurr+'/bin/dat'):
for dirname, subdirs, files in os.walk(dircurr+'/bin/dat'):
if 'bin' in subdirs:
subdirs.remove('bin')
zf.write(dirname)
for filename in files:
zf.write(os.path.join(dirname, filename))
#If there were any files '*.ico' under the binaries folder, then archive them too,
#so that you have everything you need to rebuild the project
print("Including .ico files under 'bin'")
for dirname, subdirs, files in os.walk(dircurr+'/bin'):
for filename in files:
if filename[-4:]=='.ico':
zf.write(os.path.join(dirname, filename))
zf.close()
print("\nZipped to '"+dircurr+"/bak.zip'")
print("\nEncrypting zipped file and removing unencrypted zipped file...")
with open(dircurr+'/bak.zip', 'rb') as in_file, open(dirwithenc+'/bak.enc', 'wb') as out_file:
encrypt(in_file, out_file, password)
os.remove(dircurr+'/bak.zip')
print("\nEncrypted to '"+dirwithenc+"/bak.enc'")
raw_input("""
***DONE***
Press Enter...""")
exit(0)
【讨论】:
以上是关于解密 AES 加密文件,导致崩溃的函数的主要内容,如果未能解决你的问题,请参考以下文章