user-0 提问: [求助]AES加密后再解密时出现乱码

回复:
user: user-1
content: 楼主,是否遇到中文字加密解密乱码的问题

user: user-0
content: 搞定了，把strcat和strcpy全改为memcpy，strlen改为实际长度之后，排除字符串中'\0'的干扰，加解密正常。

user: user-0
content: 引用 3 楼 zxh707wk 的回复:是不是接收字符串数组的长度短了？  应该不是这个问题，因为消息的最大长度为600，现在600时AES加解密，BASE64编解码都是正常的，只有消息长度小于600时才会出现异常。

user: user-0
content: 引用 2 楼 zytsonny 的回复:首先，楼主得贴一下代码了，不然不好锁定问题位置 然后，根据楼主所描述的，部分解密正确，是不是解密模式不统一？ 其次还得确定TCP是否正确传输，还有就是1楼说的，BASE64是否正确，都排除之后再回来看AES  AES解密模式是一样的，同样的密钥。并且有些解密正常，而有些不正常。如果模式不一样，那应该所有的解密都会异常吧。 TCP传输也是正确的。因为不经过AES加解密，只用BASE64编解码是正常的。

user: user-0
content: 引用 1 楼 cs290473786 的回复:测试下先用BASE64编码，然后直接用BASE64解码看看逻辑是否正确先。  对以下模型测试了下： 1、客户端BASE64编码，消息长度都是448，TCP发送，服务端BASE64解码，正常。 2、客户端BASE64编码，长度为448，再次BASE64编码，长度变为600，TCP发送，服务端BASE64解码，再次BASE64解码，正常。 3、客户端BASE64编码，AES加密，再次BASE64编码，长度就不确定了，但都不会超过600，TCP发送，服务端BASE64解码，AES解密，再次BASE64解码，当消息长度为600时正常，长度为其它时异常。  也就是说在客户端AES加密后，消息长度不确定，导致二次BASE64编码时，消息长度也不确定，从而导致服务端异常。但是原因为什么呢？

user: user-2
content: 用strncpy或者memcpy     要记的，在加解密的时候，经常会出现中间为0的情况

user: user-3
content: 不会吧，这么低级的错误，居然用strcpy?

user: user-0
content: 引用 11 楼 zhao4zhong1 的回复:[quote=引用 10 楼 Echizen13 的回复:] 原因找出来了，客户端AES加密的时候，对明文进行分块，每块16个字符，分别对每一块进行加密时，某一个中间字符结果为0，导致加密结果字符串在0处截断，后面字符串丢弃，所以解密的时候出现异常。但是如何解决这个问题呢？ 要么勒令对方改正，要么和对方同流合污。[/quote]  怎么改正？

user: user-4
content: 引用 10 楼 Echizen13 的回复:原因找出来了，客户端AES加密的时候，对明文进行分块，每块16个字符，分别对每一块进行加密时，某一个中间字符结果为0，导致加密结果字符串在0处截断，后面字符串丢弃，所以解密的时候出现异常。但是如何解决这个问题呢？ 要么勒令对方改正，要么和对方同流合污。

user: user-0
content: 原因找出来了，客户端AES加密的时候，对明文进行分块，每块16个字符，分别对每一块进行加密时，某一个中间字符结果为0，导致加密结果字符串在0处截断，后面字符串丢弃，所以解密的时候出现异常。但是如何解决这个问题呢？

user: user-4
content: 再供参考#pragma comment(lib,"crypt32") #include <windows.h> #include <stdio.h> #include <tchar.h>  //+------------------------------------------------------------------------- // convert formatted string to binary // If cchString is 0, then pszString is NULL terminated and // cchString is obtained via strlen() + 1. // dwFlags defines string format // if pbBinary is NULL, *pcbBinary returns the size of required memory // *pdwSkip returns the character count of skipped strings, optional // *pdwFlags returns the actual format used in the conversion, optional //-------------------------------------------------------------------------- BOOL WINAPI CryptStringToBinaryA(     IN     LPCSTR  pszString,     IN     DWORD     cchString,     IN     DWORD     dwFlags,     IN     BYTE     *pbBinary,     IN OUT DWORD    *pcbBinary,     OUT    DWORD    *pdwSkip,    //OPTIONAL     OUT    DWORD    *pdwFlags    //OPTIONAL     ); //+------------------------------------------------------------------------- // convert formatted string to binary // If cchString is 0, then pszString is NULL terminated and // cchString is obtained via strlen() + 1. // dwFlags defines string format // if pbBinary is NULL, *pcbBinary returns the size of required memory // *pdwSkip returns the character count of skipped strings, optional // *pdwFlags returns the actual format used in the conversion, optional //-------------------------------------------------------------------------- BOOL WINAPI CryptStringToBinaryW(     IN     LPCWSTR  pszString,     IN     DWORD     cchString,     IN     DWORD     dwFlags,     IN     BYTE     *pbBinary,     IN OUT DWORD    *pcbBinary,     OUT    DWORD    *pdwSkip,    //OPTIONAL     OUT    DWORD    *pdwFlags    //OPTIONAL     ); #ifdef UNICODE #define CryptStringToBinary  CryptStringToBinaryW #else #define CryptStringToBinary  CryptStringToBinaryA #endif // !UNICODE  //+------------------------------------------------------------------------- // convert binary to formatted string // dwFlags defines string format // if pszString is NULL, *pcchString returns the size of required memory in byte //-------------------------------------------------------------------------- BOOL WINAPI CryptBinaryToStringA(     IN     CONST BYTE  *pbBinary,     IN     DWORD        cbBinary,     IN     DWORD        dwFlags,     IN     LPSTR      pszString,     IN OUT DWORD       *pcchString     ); //+------------------------------------------------------------------------- // convert binary to formatted string // dwFlags defines string format // if pszString is NULL, *pcchString returns the size of required memory in byte //-------------------------------------------------------------------------- BOOL WINAPI CryptBinaryToStringW(     IN     CONST BYTE  *pbBinary,     IN     DWORD        cbBinary,     IN     DWORD        dwFlags,     IN     LPWSTR      pszString,     IN OUT DWORD       *pcchString     ); #ifdef UNICODE #define CryptBinaryToString  CryptBinaryToStringW #else #define CryptBinaryToString  CryptBinaryToStringA #endif // !UNICODE  // dwFlags has the following defines #define CRYPT_STRING_BASE64HEADER           0x00000000 #define CRYPT_STRING_BASE64                 0x00000001 #define CRYPT_STRING_BINARY                 0x00000002 #define CRYPT_STRING_BASE64REQUESTHEADER    0x00000003 #define CRYPT_STRING_HEX                    0x00000004 #define CRYPT_STRING_HEXASCII               0x00000005 #define CRYPT_STRING_BASE64_ANY             0x00000006 #define CRYPT_STRING_ANY                    0x00000007 #define CRYPT_STRING_HEX_ANY                0x00000008 #define CRYPT_STRING_BASE64X509CRLHEADER    0x00000009 #define CRYPT_STRING_HEXADDR                0x0000000a #define CRYPT_STRING_HEXASCIIADDR           0x0000000b  #define CRYPT_STRING_NOCR                   0x80000000  // CryptBinaryToString uses the following flags // CRYPT_STRING_BASE64HEADER - base64 format with certificate begin //                             and end headers // CRYPT_STRING_BASE64 - only base64 without headers // CRYPT_STRING_BINARY - pure binary copy // CRYPT_STRING_BASE64REQUESTHEADER - base64 format with request begin //                                    and end headers // CRYPT_STRING_BASE64X509CRLHEADER - base64 format with x509 crl begin //                                    and end headers // CRYPT_STRING_HEX - only hex format // CRYPT_STRING_HEXASCII - hex format with ascii char display // CRYPT_STRING_HEXADDR - hex format with address display // CRYPT_STRING_HEXASCIIADDR - hex format with ascii char and address display // // CryptBinaryToString accepts CRYPT_STRING_NOCR or'd into one of the above. // When set, line breaks contain only LF, instead of CR-LF pairs.  // CryptStringToBinary uses the following flags // CRYPT_STRING_BASE64_ANY tries the following, in order: //    CRYPT_STRING_BASE64HEADER //    CRYPT_STRING_BASE64 // CRYPT_STRING_ANY tries the following, in order: //    CRYPT_STRING_BASE64_ANY //    CRYPT_STRING_BINARY -- should always succeed // CRYPT_STRING_HEX_ANY tries the following, in order: //    CRYPT_STRING_HEXADDR //    CRYPT_STRING_HEXASCIIADDR //    CRYPT_STRING_HEXASCII //    CRYPT_STRING_HEX   char *flags[12]={ "CRYPT_STRING_BASE64HEADER", "CRYPT_STRING_BASE64", "CRYPT_STRING_BINARY", "CRYPT_STRING_BASE64REQUESTHEADER", "CRYPT_STRING_HEX", "CRYPT_STRING_HEXASCII", "CRYPT_STRING_BASE64_ANY", "CRYPT_STRING_ANY", "CRYPT_STRING_HEX_ANY", "CRYPT_STRING_BASE64X509CRLHEADER", "CRYPT_STRING_HEXADDR", "CRYPT_STRING_HEXASCIIADDR", }; #define MAXC 1024 BYTE b[22]={ 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F, 0x41,0x42,0xB0,0xA1,0x4A,0x55, }; BOOL r; DWORD len,dwFlags; TCHAR s[MAXC]; int _tmain() {     _tprintf(_T("API CryptBinaryToString in crypt32.dll Demonstration:\n"));     for (dwFlags=0;dwFlags<12;dwFlags++) {         if (dwFlags==2          || dwFlags==6          || dwFlags==7          || dwFlags==8) continue;         r=CryptBinaryToString(b,22,dwFlags,NULL,&len);         if (!r) {             _tprintf(_T("CryptBinaryToString error!\n"));             return 1;         }         if (len>MAXC) {             _tprintf(_T("%d==len>MAXC==%d!\n"),len,MAXC);             return 2;         }         r=CryptBinaryToString(b,22,dwFlags,s,&len);         if (!r) {             _tprintf(_T("CryptBinaryToString error!\n"));             return 3;         }         _tprintf(_T("\n%s:[\n%s]\n"),flags[dwFlags],s);     }     return 0; } //API CryptBinaryToString in crypt32.dll Demonstration: // //CRYPT_STRING_BASE64HEADER:[ //-----BEGIN CERTIFICATE----- //AAECAwQFBgcICQoLDA0OD0FCsKFKVQ== //-----END CERTIFICATE----- //] // //CRYPT_STRING_BASE64:[ //AAECAwQFBgcICQoLDA0OD0FCsKFKVQ== //] // //CRYPT_STRING_BASE64REQUESTHEADER:[ //-----BEGIN NEW CERTIFICATE REQUEST----- //AAECAwQFBgcICQoLDA0OD0FCsKFKVQ== //-----END NEW CERTIFICATE REQUEST----- //] // //CRYPT_STRING_HEX:[ //        00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f //        41 42 b0 a1 4a 55 //] // //CRYPT_STRING_HEXASCII:[ //        00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f   ................ //        41 42 b0 a1 4a 55                                  AB..JU //] // //CRYPT_STRING_BASE64X509CRLHEADER:[ //-----BEGIN X509 CRL----- //AAECAwQFBgcICQoLDA0OD0FCsKFKVQ== //-----END X509 CRL----- //] // //CRYPT_STRING_HEXADDR:[ //0000    00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f //0010    41 42 b0 a1 4a 55 //] // //CRYPT_STRING_HEXASCIIADDR:[ //0000    00 01 02 03 04 05 06 07  08 09 0a 0b 0c 0d 0e 0f   ................ //0010    41 42 b0 a1 4a 55                                  AB..JU //] //

user: user-4
content: 仅供参考#pragma comment(lib, "crypt32.lib") #pragma comment(lib, "advapi32.lib") #define _WIN32_WINNT 0x0400 #include <stdio.h> #include <windows.h> #include <wincrypt.h> #define MY_ENCODING_TYPE  (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING) #define KEYLENGTH  0x00800000 void HandleError(char *s); //-------------------------------------------------------------------- //  These additional #define statements are required. #define ENCRYPT_ALGORITHM CALG_RC4 #define ENCRYPT_BLOCK_SIZE 8 //   Declare the function EncryptFile. The function definition //   follows main. BOOL EncryptFile(     PCHAR szSource,     PCHAR szDestination,     PCHAR szPassword); //-------------------------------------------------------------------- //   Begin main. void main(void) {     CHAR szSource[100];     CHAR szDestination[100];     CHAR szPassword[100];     printf("Encrypt a file. \n\n");     printf("Enter the name of the file to be encrypted: ");     scanf("%s",szSource);     printf("Enter the name of the output file: ");     scanf("%s",szDestination);     printf("Enter the password:");     scanf("%s",szPassword);     //--------------------------------------------------------------------     // Call EncryptFile to do the actual encryption.     if(EncryptFile(szSource, szDestination, szPassword)) {         printf("Encryption of the file %s was a success. \n", szSource);         printf("The encrypted data is in file %s.\n",szDestination);     } else {         HandleError("Error encrypting file!");     } } // End of main //-------------------------------------------------------------------- //   Code for the function EncryptFile called by main. static BOOL EncryptFile(     PCHAR szSource,     PCHAR szDestination,     PCHAR szPassword) //-------------------------------------------------------------------- //   Parameters passed are: //     szSource, the name of the input, a plaintext file. //     szDestination, the name of the output, an encrypted file to be //         created. //     szPassword, the password. {     //--------------------------------------------------------------------     //   Declare and initialize local variables.     FILE *hSource;     FILE *hDestination;     HCRYPTPROV hCryptProv;     HCRYPTKEY hKey;     HCRYPTHASH hHash;     PBYTE pbBuffer;     DWORD dwBlockLen;     DWORD dwBufferLen;     DWORD dwCount;     //--------------------------------------------------------------------     // Open source file.     if(hSource = fopen(szSource,"rb")) {         printf("The source plaintext file, %s, is open. \n", szSource);     } else {         HandleError("Error opening source plaintext file!");     }     //--------------------------------------------------------------------     // Open destination file.     if(hDestination = fopen(szDestination,"wb")) {         printf("Destination file %s is open. \n", szDestination);     } else {         HandleError("Error opening destination ciphertext file!");     }     //以下获得一个CSP句柄     if(CryptAcquireContext(                 &hCryptProv,                 NULL,               //NULL表示使用默认密钥容器，默认密钥容器名                 //为用户登陆名                 NULL,                 PROV_RSA_FULL,                 0)) {         printf("A cryptographic provider has been acquired. \n");     } else {         if(CryptAcquireContext(                     &hCryptProv,                     NULL,                     NULL,                     PROV_RSA_FULL,                     CRYPT_NEWKEYSET))//创建密钥容器         {             //创建密钥容器成功，并得到CSP句柄             printf("A new key container has been created.\n");         } else {             HandleError("Could not create a new key container.\n");         }     }     //--------------------------------------------------------------------     // 创建一个会话密钥（session key）     // 会话密钥也叫对称密钥，用于对称加密算法。     // （注: 一个Session是指从调用函数CryptAcquireContext到调用函数     //   CryptReleaseContext 期间的阶段。会话密钥只能存在于一个会话过程）     //--------------------------------------------------------------------     // Create a hash object.     if(CryptCreateHash(                 hCryptProv,                 CALG_MD5,                 0,                 0,                 &hHash)) {         printf("A hash object has been created. \n");     } else {         HandleError("Error during CryptCreateHash!\n");     }     //--------------------------------------------------------------------     // 用输入的密码产生一个散列     if(CryptHashData(                 hHash,                 (BYTE *)szPassword,                 strlen(szPassword),                 0)) {         printf("The password has been added to the hash. \n");     } else {         HandleError("Error during CryptHashData. \n");     }     //--------------------------------------------------------------------     // 通过散列生成会话密钥     if(CryptDeriveKey(                 hCryptProv,                 ENCRYPT_ALGORITHM,                 hHash,                 KEYLENGTH,                 &hKey)) {         printf("An encryption key is derived from the password hash. \n");     } else {         HandleError("Error during CryptDeriveKey!\n");     }     //--------------------------------------------------------------------     // Destroy the hash object.     CryptDestroyHash(hHash);     hHash = NULL;     //--------------------------------------------------------------------     //  The session key is now ready.     //--------------------------------------------------------------------     // 因为加密算法是按ENCRYPT_BLOCK_SIZE 大小的块加密的，所以被加密的     // 数据长度必须是ENCRYPT_BLOCK_SIZE 的整数倍。下面计算一次加密的     // 数据长度。     dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;     //--------------------------------------------------------------------     // Determine the block size. If a block cipher is used,     // it must have room for an extra block.     if(ENCRYPT_BLOCK_SIZE > 1)         dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;     else         dwBufferLen = dwBlockLen;     //--------------------------------------------------------------------     // Allocate memory.     if(pbBuffer = (BYTE *)malloc(dwBufferLen)) {         printf("Memory has been allocated for the buffer. \n");     } else {         HandleError("Out of memory. \n");     }     //--------------------------------------------------------------------     // In a do loop, encrypt the source file and write to the source file.     do {         //--------------------------------------------------------------------         // Read up to dwBlockLen bytes from the source file.         dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);         if(ferror(hSource)) {             HandleError("Error reading plaintext!\n");         }         //--------------------------------------------------------------------         // 加密数据         if(!CryptEncrypt(                     hKey,           //密钥                     0,              //如果数据同时进行散列和加密，这里传入一个                     //散列对象                     feof(hSource),  //如果是最后一个被加密的块，输入TRUE.如果不是输.                     //入FALSE这里通过判断是否到文件尾来决定是否为                     //最后一块。                     0,              //保留                     pbBuffer,       //输入被加密数据，输出加密后的数据                     &dwCount,       //输入被加密数据实际长度，输出加密后数据长度                     dwBufferLen))   //pbBuffer的大小。         {             HandleError("Error during CryptEncrypt. \n");         }         //--------------------------------------------------------------------         // Write data to the destination file.         fwrite(pbBuffer, 1, dwCount, hDestination);         if(ferror(hDestination)) {             HandleError("Error writing ciphertext.");         }     } while(!feof(hSource));     //--------------------------------------------------------------------     //  End the do loop when the last block of the source file has been     //  read, encrypted, and written to the destination file.     //--------------------------------------------------------------------     // Close files.     if(hSource)         fclose(hSource);     if(hDestination)         fclose(hDestination);     //--------------------------------------------------------------------     // Free memory.     if(pbBuffer)         free(pbBuffer);     //--------------------------------------------------------------------     // Destroy session key.     if(hKey)         CryptDestroyKey(hKey);     //--------------------------------------------------------------------     // Destroy hash object.     if(hHash)         CryptDestroyHash(hHash);     //--------------------------------------------------------------------     // Release provider handle.     if(hCryptProv)         CryptReleaseContext(hCryptProv, 0);     return(TRUE); } // End of Encryptfile //-------------------------------------------------------------------- //  This example uses the function HandleError, a simple error //  handling function, to print an error message to the standard error //  (stderr) file and exit the program. //  For most applications, replace this function with one //  that does more extensive error reporting. void HandleError(char *s) {     fprintf(stderr,"An error occurred in running the program. \n");     fprintf(stderr,"%s\n",s);     fprintf(stderr, "Error number %x.\n", GetLastError());     fprintf(stderr, "Program terminating. \n");     exit(1); } // End of HandleError

user: user-5
content: 只能围观了。。。

user: user-5
content: 是不是接收字符串数组的长度短了？

user: user-6
content: 首先，楼主得贴一下代码了，不然不好锁定问题位置 然后，根据楼主所描述的，部分解密正确，是不是解密模式不统一？ 其次还得确定TCP是否正确传输，还有就是1楼说的，BASE64是否正确，都排除之后再回来看AES

user: user-7
content: 测试下先用BASE64编码，然后直接用BASE64解码看看逻辑是否正确先。