2009-10-09 5 views
1

wincrypt로 파일 조각을 해독하려고하는데이 함수를 올바르게 해독 할 수없는 것처럼 보입니다. 바이트는 C#에서 RC2 구현으로 암호화되며 동일한 암호와 IV를 암호화 및 암호 해독 프로세스 (C#으로 암호화, C++에서 해독)에 모두 공급합니다.Wincrypt : C#에서 암호화 된 파일의 암호를 해독 할 수 없습니다. NTE_BAD_DATA at CryptDecrypt

내 모든 기능은 최종 "CryptDecrypt"기능까지 true를 반환합니다. 더 이상 입력하지 않고 여기에 함수가 있습니다.

static char* DecryptMyFile(char *input, char *password, int size) 
{ 
    HCRYPTPROV provider = NULL; 

    if(CryptAcquireContext(&provider, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL, 0)) 
    {printf("Context acquired.");} 
    else 
    { 
     if (GetLastError() == NTE_BAD_KEYSET) 
     { 
     if(CryptAcquireContext(&provider, 0, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)) 
      {printf("new key made.");} 
      else 
      { 
       printf("Could not acquire context."); 
      } 
     } 
     else 
     {printf("Could not acquire context.");} 
    } 

    HCRYPTKEY key = NULL; 
    HCRYPTHASH hash = NULL; 

    if(CryptCreateHash(provider, CALG_MD5, 0, 0, &hash)) 
    {printf("empty hash created.");} 
    else 
    {printf("could not create hash.");} 

    if(CryptHashData(hash, (BYTE *)password, strlen(password), 0)) 
    {printf("data buffer is added to hash.");} 
    else 
    {printf("error. could not add data buffer to hash.");} 

    if(CryptDeriveKey(provider, CALG_RC2, hash, 0, &key)) 
    {printf("key derived.");} 
    else 
    {printf("Could not derive key.");} 

    DWORD dwKeyLength = 128; 

if(CryptSetKeyParam(key, KP_EFFECTIVE_KEYLEN, reinterpret_cast<BYTE*>(&dwKeyLength), 0)) 
    {printf("success");} 
    else 
    {printf("failed.");} 

    BYTE IV[8] = {0,0,0,0,0,0,0,0}; 

    if(CryptSetKeyParam(key, KP_IV, IV, 0)) 
    {printf("worked");} 
    else 
    {printf("faileD");} 

    DWORD dwCount = size; 
    BYTE *decrypted = new BYTE[dwCount + 1]; 

    memcpy(decrypted, input, dwCount); 
    decrypted[dwCount] = 0; 


    if(CryptDecrypt(key,0, true, 0, decrypted, &dwCount)) 
    {printf("succeeded");} 
    else 
    {printf("failed");} 

return (char *)decrypted; 
} 

입력은 암호화 된 함수로 전달되는 데이터입니다. password는 C#에서 데이터를 암호화하는 데 사용 된 것과 동일한 암호입니다. size는 암호화 된 데이터의 크기입니다.
위의 모든 함수는 CryptDecrypt가 true가 될 때까지 true를 반환합니다. 그 이유는 알 수 없습니다. 동시에, CryptDecrypt 함수가 내 "암호 해독"변수를 편집하는 방법을 잘 모릅니다. 참조를 전달하지 않기 때문입니다.

왜 이것이 작동하지 않는지에 대한 도움이나 조언은 크게 감사하겠습니다. 이것은 wincrypt와 C++을 처음 사용하면서 처음으로 시도한 작업입니다.

public static byte[] EncryptString(byte[] input, string password) 
    { 
     PasswordDeriveBytes pderiver = new PasswordDeriveBytes(password, null); 
     byte[] ivZeros = new byte[8]; 
     byte[] pbeKey = pderiver.CryptDeriveKey("RC2", "MD5", 128, ivZeros); 

     RC2CryptoServiceProvider RC2 = new RC2CryptoServiceProvider(); 

     //using an empty initialization vector for convenience. 
     byte[] IV = new byte[8]; 
     ICryptoTransform encryptor = RC2.CreateEncryptor(pbeKey, IV); 

     MemoryStream msEncrypt = new MemoryStream(); 
     CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write); 
     csEncrypt.Write(input, 0, input.Length); 
     csEncrypt.FlushFinalBlock(); 

     return msEncrypt.ToArray(); 
    } 

내가 ++ C에서 내 해시 값이 만든 C#에서 내 키와 동일 있음을 확인했다 :

는 더 이상 도움이 경우는 물론, 이것은 (C#에서) 내 암호화입니다 PasswordDeriveBytes.CryptDeriveKey

+0

GetLastError()를 호출하여 오류 코드 (NTE_BAD_DATA, NTE_BAD_KEY, NTE_BAD_LEN 등)를 얻으려고합니다. –

+0

올바른 오류입니다. 그리고 나쁜 데이터를 얻었습니다. – Chris

답변

3

첫 번째로 내 의견에서와 같이 GetLastError()를 사용하여 을 알아 냈으므로 그게 무엇이든 실패했습니다. 나는 당신이 NTE_BAD_DATA를 얻는다 고 가정 할 것이고, 다른 모든 오류들은 기본적으로 당신이 API 호출 시퀀스의 일부 단계를 놓친 것을 의미하기 때문에 다루기가 훨씬 더 쉽다.

CryptDecrypt가 NTE_BAD_DATA와 함께 실패하는 일반적인 이유는 블록 암호화 자의 마지막 블록을 해독하려고하고 (사용자의 경우처럼) 해독 된 패딩 바이트가 올바르지 않기 때문입니다. 이것은 입력이 잘 리거나 (암호화 된 모든 바이트가 파일에 저장되지 않은 경우) 키가 올바르지 않은 경우에 발생할 수 있습니다.

나는 많은 곳이 있기 때문에이 단지를 CryptDecrypt 시간에 그 모든 매니페스트를 실패 할 수 있습니다 위치를 조직적으로이 걸릴 제안 :

  1. 는 C#에서 당신은 암호화 파일은 C#으로 해독 할 수 있는지 확인하십시오. 이렇게하면 파일 저장 잘림 문제가 제거됩니다.
  2. 고정 하드 코드 된 키 (암호가 파생되지 않음)를 사용하여 암호화하고 해독하려고하면 키 집합 코드 IV 초기화가 올바른지 확인해야합니다 (패딩 모드와 암호화 체인 모드는 물론).
  3. 암호 파생 프로세스가 동일한 해시에서 작동하는지 확인하십시오. ANSI 대 유니 코드 또는 터미널 0과 같은 것들이 MD5 해시를 피할 수 있으며 분명히 동일한 암호 해시와 크게 다른 키를 생성 할 수 있습니다.
+0

이것은 좋은 전투 계획처럼 들리지만, 감사합니다. 어떻게 이들을위한 핵심 코드를 작성합니까? 내 무지에 대한 사과. 나는이 일을하는 예제를 보지 못했고 이것은이 타입의 일을 처음하는 시간입니다. – Chris

+0

오래되었습니다 ... 내가 올바르게 기억하면 CryptImportKey를 사용하여 알려진 세션 키에서 키 핸들을 생성합니다. 그게 RC2와 어떻게 작동하는지 모르겠다. –

+0

그래도 작동하지 않는다면 적어도 암호문 해시가 일치하는지 확인한 다음 (CryptDuplicateHash로 md5 핸들을 복제 한 다음 해시 값을 추출하십시오). 또한 생성 된 키의 mathc가 C++에서 RC2 키를 추출하기 위해 CryptExportKey (... PLAINTEXTKEYBLOB)를 사용하는지 확인합니다. –

1

일부 사용자는 운영 체제간에 이동할 때 문제점을 발견했습니다. CryptDeriveKey 호출은 선택한 운영 체제 및 알고리즘에 따라 "기본 키 길이"를 사용합니다. RC2의 경우 기본 생성 키 길이는 Windows 2000에서는 40 비트이고 Windows 2003에서는 128 비트입니다. 생성 된 키가 CryptDecrypt 호출에서 사용될 때이 결과는 "잘못된 데이터"반환 코드가됩니다.

아마도 이것은 40 비트 암호화 된 스트림을 해독하기 위해 128 비트 키를 적용한 후 최종 버퍼의 끝에 나타나는 "가비지"와 관련이 있습니다. 오류 코드는 일반적으로 불량 패딩 바이트를 나타냅니다. 근본적인 원인은 키 생성 문제 일 수 있습니다.

40 비트 암호화 키를 생성하려면 CryptDeriveKey 호출의 플래그 필드에 ((40 < < 16))를 사용하십시오.