解密 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 加密文件,导致崩溃的函数的主要内容,如果未能解决你的问题,请参考以下文章

使用 AES 加密/解密

CryptoJS<AES-CBC加密和解密>

前端aes解密实战小结

python3.6 AES 加密解密

为啥windows下aes解密android上的加密文件失败

Android 加密/解密问题 (AES)