2017-09-22 70 views
-1

有沒有辦法從CryptoAPI獲得非確定性輸出?換句話說,加密字符串時會輸出不同的字符串。總是得到與CryptoAPI相同的加密字符串

例如,獲得與「密碼」密碼加密密鑰和字符串加密的「A」在使用CALG_AES_256,它總是返回「SnÆwÞ¢大號\ X1E?6FÏLþw」

我有點的n00b使用CryptoAPI,所以任何幫助表示讚賞。

編輯: 下面是來自微軟的示例代碼密碼系統的代碼decrypteencrypt這是相同的代碼,只是縮短/壓實。此代碼作爲Win32控制檯應用程序在VS 2017中編譯。 pszSource和pszDest是C:\ temp文件夾中的兩個文件。 source.txt有我們試圖加密的信件。

時遇到的問題是從的CryptoAPI 這隱窩/解密代碼不允許某些字符串來進行加密,然後解密後的(即,n,T,L,P,AA,AB,AC,AD ,ae等)。如果有人能告訴我爲什麼,那會非常有幫助。

#include <windows.h> 
#include <tchar.h> 
#include <wincrypt.h> 

#define KEYLENGTH 0x00800000 
#define ENCRYPT_BLOCK_SIZE 8 

bool MyDecryptFile(LPTSTR szSource,LPTSTR szDestination,LPTSTR szPassword); 
bool MyEncryptFile(LPTSTR szSource,LPTSTR szDestination,LPTSTR szPassword); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 

    LPTSTR pszSource = L"c:\\temp\\source.txt"; 
    LPTSTR pszDestination = L"c:\\temp\\dest.txt"; 
    LPTSTR pszPassword = L"t"; 

    if (MyEncryptFile(pszSource, pszDestination, pszPassword)) 
     { 
     _tprintf(TEXT("Encryption of the file %s was successful. \n"),pszSource); 
     _tprintf(TEXT("The encrypted data is in file %s.\n"),pszDestination); 
     } 

    if (MyDecryptFile(pszSource, pszDestination, pszPassword)) 
     { 
     _tprintf(TEXT("Encryption of the file %s was successful. \n"),pszSource); 
     _tprintf(TEXT("The encrypted data is in file %s.\n"),pszDestination); 
     } 

    return 0; 
} 

bool MyEncryptFile(LPTSTR pszSourceFile,LPTSTR pszDestinationFile,LPTSTR pszPassword) 
{ 
    bool fReturn = false; 
    HANDLE hSourceFile = INVALID_HANDLE_VALUE, hDestinationFile = INVALID_HANDLE_VALUE; 
    HCRYPTPROV hCryptProv = NULL; 
    HCRYPTKEY hKey = NULL, hXchgKey = NULL; 
    HCRYPTHASH hHash = NULL; 
    PBYTE pbBuffer = NULL; 
    DWORD dwBlockLen, dwBufferLen, dwCount; 

    hSourceFile = CreateFile(pszSourceFile,FILE_READ_DATA,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 
    if (INVALID_HANDLE_VALUE == hSourceFile) 
     goto Exit_MyEncryptFile; 

    hDestinationFile = CreateFile(pszDestinationFile,FILE_WRITE_DATA,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); 
    if (INVALID_HANDLE_VALUE == hDestinationFile) 
     goto Exit_MyEncryptFile; 

    CryptAcquireContext(&hCryptProv,NULL,MS_ENH_RSA_AES_PROV,PROV_RSA_AES,0); 
    CryptCreateHash(hCryptProv,CALG_SHA_256,0,0,&hHash); 
    CryptHashData(hHash,(BYTE *)pszPassword,lstrlen(pszPassword),0); 
    CryptDeriveKey(hCryptProv,CALG_AES_256,hHash,CRYPT_EXPORTABLE,&hKey); 

    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE; 
    if (ENCRYPT_BLOCK_SIZE > 1) 
     dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE; 
    else 
     dwBufferLen = dwBlockLen; 
    pbBuffer = (BYTE *)malloc(dwBufferLen); 

    bool fEOF = FALSE; 
    do 
    { 
     if (ReadFile(hSourceFile,pbBuffer,dwBlockLen,&dwCount,NULL)) 
      { 
      if (dwCount < dwBlockLen) 
       fEOF = TRUE; 
      if (CryptEncrypt(hKey,NULL,fEOF,0,pbBuffer,&dwCount,dwBufferLen)) 
       WriteFile(hDestinationFile,pbBuffer,dwCount,&dwCount,NULL); 
      } 
    } 
    while (!fEOF); 

    fReturn = true; 

Exit_MyEncryptFile: 
    if (hSourceFile) CloseHandle(hSourceFile); 
    if (hDestinationFile) CloseHandle(hDestinationFile); 
    if (pbBuffer) free(pbBuffer); 
    if (hHash) {CryptDestroyHash(hHash);hHash = NULL;} 
    if (hKey) CryptDestroyKey(hKey); 
    if (hCryptProv) CryptReleaseContext(hCryptProv, 0); 

    return fReturn; 
} 


bool MyDecryptFile(LPTSTR pszSourceFile,LPTSTR pszDestinationFile,LPTSTR pszPassword) 
{ 
    bool fReturn = false; 
    HANDLE hSourceFile = INVALID_HANDLE_VALUE, hDestinationFile = INVALID_HANDLE_VALUE; 
    HCRYPTKEY hKey = NULL; 
    HCRYPTHASH hHash = NULL; 
    HCRYPTPROV hCryptProv = NULL; 
    PBYTE pbBuffer = NULL; 
    DWORD dwCount, dwBlockLen, dwBufferLen; 

    hSourceFile = CreateFile(pszDestinationFile,FILE_READ_DATA,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); 
    if (INVALID_HANDLE_VALUE == hSourceFile) 
     goto Exit_MyDecryptFile; 

    hDestinationFile = CreateFile(pszSourceFile,FILE_WRITE_DATA,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 
    if (INVALID_HANDLE_VALUE == hDestinationFile) 
     goto Exit_MyDecryptFile; 

    CryptAcquireContext(&hCryptProv,NULL,MS_ENH_RSA_AES_PROV,PROV_RSA_AES,0); 
    CryptCreateHash(hCryptProv,CALG_SHA_256,0,0,&hHash); 
    CryptHashData(hHash,(BYTE *)pszPassword,lstrlen(pszPassword),0); 
    CryptDeriveKey(hCryptProv,CALG_AES_256,hHash,CRYPT_EXPORTABLE,&hKey); 

    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE; 
    dwBufferLen = dwBlockLen; 

    pbBuffer = (PBYTE)malloc(dwBufferLen); 
    bool fEOF = false; 
    do 
    { 
     if (!ReadFile(hSourceFile,pbBuffer,dwBlockLen,&dwCount,NULL)) 
      goto Exit_MyDecryptFile; 

     if (dwCount <= dwBlockLen) 
      fEOF = TRUE; 

     LONG rv = CryptDecrypt(hKey,0,fEOF,0,pbBuffer,&dwCount); 
     if (rv==0) 
      { 
      DWORD dwErr = GetLastError();  // <--- fails if password and string are n, t, L, p, aa, ab, ac, ad , ae 
      goto Exit_MyDecryptFile; 
      } 

     if (!WriteFile(hDestinationFile,pbBuffer,dwCount,&dwCount,NULL)) 
      goto Exit_MyDecryptFile; 
    } 
    while (!fEOF); 

    fReturn = true; 

Exit_MyDecryptFile: 

    if (pbBuffer) free(pbBuffer); 
    if (hSourceFile) CloseHandle(hSourceFile); 
    if (hDestinationFile) CloseHandle(hDestinationFile); 
    if (hHash) {CryptDestroyHash(hHash);hHash = NULL;} 
    if (hKey) CryptDestroyKey(hKey); 
    if (hCryptProv) CryptReleaseContext(hCryptProv, 0); 

    return fReturn; 
} 

如何使用它來獲得KP_IV選項?

BOOL bRV; 
bRV = CryptAcquireContextW(&hCryptProv, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 0); 
bRV = CryptGenKey(hCryptProv, CALG_AES_256,0,&hKey); 
DWORD dwMode = CRYPT_MODE_CBC; 
bRV = CryptSetKeyParam(hKey,KP_MODE,(BYTE*)&dwMode,0); 
BYTE pbData[16]; 
memcpy(pbData,"n",sizeof("n")); // <--- Hard coded password 
bRV = CryptSetKeyParam(hKey,KP_IV,pbData,0); 
enter code here 
+1

當保存密碼驗證程序只是使用哈希函數是不夠的,只是添加鹽對提高安全性沒有多大作用。相反,用隨機鹽迭代HMAC約100ms持續時間,然後用散列表保存鹽。更好的是使用諸如'PBKDF2','Rfc2898DeriveBytes','password_hash','Bcrypt','passlib.hash'或類似函數的函數。關鍵是要讓攻擊者花費大量時間通過暴力破解密碼。 – zaph

+1

散列函數是確定性的,這是它們的一個主要屬性,爲了獲得一個隨機輸出,你需要提供一個通常用密碼安全僞隨機數生成器[CSPRNG]獲得的隨機輸入(https://en.wikipedia。組織/維基/ Cryptographically_secure_pseudorandom_number_generator)。 – zaph

+0

謝謝。舊的capicom.dll文件做了256個並且每次都給出了不同的字符串,但每次都正確解密。 – JeffR

回答

0

如果你想用相同的密鑰加密同一明文時獲得不同的cypertext,你必須使用操作的CBC模式: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

爲了與CBC正確加密,您需要每次生成不同的隨機初始化向量(IV)。 爲了解密,您需要知道加密過程中使用的IV。因此,IV必須與密文相關聯(清楚地)。

關於你例如,調用CryptDeriveKey功能時,CBC是默認模式,但它使用的IV設置爲零,並且該無效的CBC操作模式的效用: https://msdn.microsoft.com/en-us/library/windows/desktop/aa379916(v=vs.85).aspx

爲了設置你需要調用CryptSetKeyParam功能,接受KP_IV PARAM隨機IV: https://msdn.microsoft.com/en-us/library/windows/desktop/aa380272(v=vs.85).aspx

再見 喬瓦尼

+0

我已經把CryptSetKeyParam的問題中的示例代碼。該示例代碼是使用CryptSetKeyParam的正確方法嗎?它正確地加密,但是當我在CryptDecrypt之前使用相同的代碼時,CryptDecrypt將GetLastEror設置爲NTE_BAD_DATA。 – JeffR

+0

使用零IV製作的CBC並不安全,但在隱藏模式方面優於ECB模式,請參閱[ECB模式](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_Codebook_.28ECB.29),向下滾動至企鵝。除了CBC之外,還有其他的模式使用隨機的IV或IV替代品。 – zaph

+0

我可以用零IV來加密它,但我無法解密它。你能幫助我嗎? – JeffR

相關問題