2017-03-25 10 views
-1

저는 CNG를 처음 사용합니다. 나는 msdn 사이트에서 기본 프로그램으로 놀고있다. aes cbc 암호화 된 출력을 제공하는 다른 웹 사이트를 사용하여 입력 일반 문자열을 수정하고 출력을 테스트했습니다. 불행히도 전반전 만 일치하고 다음 절반은 일치하지 않습니다. 실수가 어디에 있는지 누군가가 나를 가리킬 수 있다면 좋을 것입니다.CNG에서 AES-CBC로 암호화 된 출력이 온라인 도구와 일치하지 않습니다.

msdn의 원본 코드는 here입니다.

다음은 내 코드에서 생성 된 출력입니다 (아래). 입력 문자열을 수정하는 것과 별개로 코드에 차이가 없음에 유의하십시오. 하반기가 일치하지 않음을 한 후 여기에 enter image description here

은 .. 상반기는 "B0의 C4 (29) (18)"로 끝나는 온라인 웹 사이트 (http://aes.online-domain-tools.com/anothersite) enter image description here

의 출력이다.

여기서 코드는 데이터의 길이가 블록 크기 (AES 16 바이트)의 정확한 배수가 아닌 때문에 패딩이 추가되어 있지만

#include <windows.h> 
#include <stdio.h> 
#include <bcrypt.h> 

#pragma comment(lib, "bcrypt.lib") 

#ifndef STATUS_UNSUCCESSFUL 
#define STATUS_UNSUCCESSFUL   ((NTSTATUS)0xC0000001L) 
#endif // !STATUS_UNSUCCESSFUL 

#ifndef NT_SUCCESS 
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) 
#endif 

void 
print_inhex(char *buf, int len) { 
    for (int i = 0; i < len; i++) 
     printf(" %02x", buf[i]); 
    printf("\n"); 
} 

const BYTE rgbPlaintext[] = 
{ 
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 
}; 

static const BYTE rgbIV[] = 
{ 
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 
}; 

static const BYTE rgbAES128Key[] = 
{ 
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f 
}; 


void 
CNG_aes_cbc() 
{ 

    BCRYPT_ALG_HANDLE  hAesAlg = NULL; 
    BCRYPT_KEY_HANDLE  hKey = NULL; 
    NTSTATUS    status = STATUS_UNSUCCESSFUL; 
    DWORD     cbCipherText = 0, 
     cbPlainText = 0, 
     cbData = 0, 
     cbKeyObject = 0, 
     cbBlockLen = 0, 
     cbBlob = 0; 
    PBYTE     pbCipherText = NULL, 
     pbPlainText = NULL, 
     pbKeyObject = NULL, 
     pbIV = NULL, 
     pbBlob = NULL; 

    // Open an algorithm handle. 
    if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptOpenAlgorithmProvider\n", status); 
     goto Cleanup; 
    } 

    // Calculate the size of the buffer to hold the KeyObject. 
    if (!NT_SUCCESS(status = BCryptGetProperty(hAesAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbData, 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status); 
     goto Cleanup; 
    } 

    // Allocate the key object on the heap. 
    pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject); 
    if (NULL == pbKeyObject) { 
     wprintf(L"**** memory allocation failed\n"); 
     goto Cleanup; 
    } 

    // Calculate the block length for the IV. 
    if (!NT_SUCCESS(status = BCryptGetProperty(hAesAlg, BCRYPT_BLOCK_LENGTH, (PBYTE)&cbBlockLen, sizeof(DWORD), &cbData, 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptGetProperty\n", status); 
     goto Cleanup; 
    } 

    // Determine whether the cbBlockLen is not longer than the IV length. 
    if (cbBlockLen > sizeof(rgbIV)) { 
     wprintf(L"**** block length is longer than the provided IV length\n"); 
     goto Cleanup; 
    } 

    // Allocate a buffer for the IV. The buffer is consumed during the 
    // encrypt/decrypt process. 
    pbIV = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlockLen); 
    if (NULL == pbIV) { 
     wprintf(L"**** memory allocation failed\n"); 
     goto Cleanup; 
    } 

    memcpy(pbIV, rgbIV, cbBlockLen); 

    if (!NT_SUCCESS(status = BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptSetProperty\n", status); 
     goto Cleanup; 
    } 

    // Generate the key from supplied input key bytes. 
    if (!NT_SUCCESS(status = BCryptGenerateSymmetricKey(hAesAlg, &hKey, pbKeyObject, cbKeyObject, (PBYTE)rgbAES128Key, sizeof(rgbAES128Key), 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptGenerateSymmetricKey\n", status); 
     goto Cleanup; 
    } 


    // Save another copy of the key for later. 
    if (!NT_SUCCESS(status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, NULL, 0, &cbBlob, 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status); 
     goto Cleanup; 
    } 


    // Allocate the buffer to hold the BLOB. 
    PUCHAR pbBlob_1 = (PUCHAR)malloc(sizeof(PUCHAR) * cbBlob); 
    //pbBlob = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbBlob); 
    if (NULL == pbBlob_1) { 
     wprintf(L"**** memory allocation failed\n"); 
     goto Cleanup; 
    } 

    if (!NT_SUCCESS(status = BCryptExportKey(hKey, NULL, BCRYPT_KEY_DATA_BLOB, pbBlob_1, cbBlob, &cbBlob, 0))) { 
     wprintf(L"**** Error 0x%x returned by BCryptExportKey\n", status); 
     goto Cleanup; 
    } 

    PUCHAR blob = pbBlob_1 + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER); 
    int len = cbBlob - sizeof(BCRYPT_KEY_DATA_BLOB_HEADER); 
    printf("key:"); 
    print_inhex(blob, len); 

    cbPlainText = sizeof(rgbPlaintext); 
    pbPlainText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbPlainText); 
    if (NULL == pbPlainText) { 
     wprintf(L"**** memory allocation failed\n"); 
     goto Cleanup; 
    } 

    /*memcpy(pbPlainText, rgbPlaintext, sizeof(rgbPlaintext));*/ 
    char *test_msg = "This is my test msg"; 
    cbPlainText = strlen(test_msg) + 1; 
    memcpy(pbPlainText, test_msg, cbPlainText); 

    printf("plain text:"); 
    print_inhex(test_msg, strlen(test_msg)); 

    // Get the output buffer size. 
    if (!NT_SUCCESS(status = BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, pbIV, cbBlockLen, NULL, 0, &cbCipherText, BCRYPT_BLOCK_PADDING))) { 
     wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status); 
     goto Cleanup; 
    } 

    pbCipherText = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbCipherText); 
    if (NULL == pbCipherText) { 
     wprintf(L"**** memory allocation failed\n"); 
     goto Cleanup; 
    } 

    // Use the key to encrypt the plaintext buffer. 
    // For block sized messages, block padding will add an extra block. 
    if (!NT_SUCCESS(status = BCryptEncrypt(hKey, pbPlainText, cbPlainText, NULL, pbIV, 
         cbBlockLen, pbCipherText, cbCipherText, &cbData, BCRYPT_BLOCK_PADDING))){ 
     wprintf(L"**** Error 0x%x returned by BCryptEncrypt\n", status); 
     goto Cleanup; 
    } 

    printf("cipher text:"); 
    for (int i = 0; i < cbCipherText; i++) 
     printf(" %02x", pbCipherText[i]); 

    wprintf(L"\nSuccess!\n"); 

Cleanup: 
    if (hAesAlg) 
     BCryptCloseAlgorithmProvider(hAesAlg, 0); 

    if (hKey) 
     BCryptDestroyKey(hKey); 

    if (pbCipherText) 
     HeapFree(GetProcessHeap(), 0, pbCipherText); 

    if (pbKeyObject) 
     HeapFree(GetProcessHeap(), 0, pbKeyObject); 

    if (pbIV) 
     HeapFree(GetProcessHeap(), 0, pbIV); 
} 
+0

IV는 256 비트가 아니며 16 진수입니다. 여기서 각 문자는 4 비트, 32 문자 * 4 비트 = 128 비트를 나타냅니다. 또한 스크린 샷에 입력, 키 및 IV를 제공하지 마십시오. – zaph

+0

하지만 내 프로그램과 온라인 웹 사이트에서도 같은 IV를 사용하고 있습니다. 그들 중 누구도 불평하지 않았습니다. msdn과 동일한 코드이므로 IV가 문제가되지 않을 수도 있습니다. – ABCDEFG

+0

데이터 길이가 블록 크기의 정확한 배수 (AES의 경우 16 바이트)가 아니므로 패딩이 추가되지만. 구현시 데이터를 거부하거나 0x00 (cryptomathic), PKCS # 7 (일반적으로 사용되는 패딩) 또는 junbk가 제공 한 데이터를 메모리에 저장하는 등의 기본 방법으로 패드를 거부합니다. AES의 인스턴스 생성시 패딩을 지정하는 것이 가장 좋습니다. – zaph

답변

2

니펫이다. 구현시 0x00 (cryptomathic), PKCS # 7 (일반적으로 사용되는 패딩) 또는 정크 메일에 메모리의 제공된 데이터와 같은 기본 방법으로 패드가 데이터를 거부하는 경우입니다.

AES Class

SymmetricAlgorithm.Padding Property 주를 사용, BCryptEncrypt를 사용하지 마십시오 : 기본값은 PaddingMode.PKCS7입니다.

AES의 인스턴스 생성시 패딩을 지정하는 것이 가장 좋습니다.

PaddingMode Enumeration 다음 PKCS # 7 패딩 문자열이 추가 패딩 바이트의 총 수와 동일한 각각의 바이트의 시퀀스로 구성 PKCS7
.

수동으로 PKCS 번호 7 패딩에 추가 :

cryptommathic AES :

가 생성됩니다 46CC2228E81B2A05E8E8EBF2B0C42918EC496128D7C45BD0B19BB2D6452A3936

2

당신은 cbPlainText에 대한 값과 일치하지 않습니다를.

잡담 : 당신이 아니라 문자열로 큰로서 보장 된 버퍼)를 통해 문자열을 쓰기 곳

  • 또한 아주 무서운 복사/realloc과 코드가 있습니다.
  • 또한 NT_SUCCESS를 정의하여 실패했는지 여부를 반환합니다. 0은 성공,! 0은 실패입니다.

tst_msg의 strlen까지 16 진수로 인쇄됩니다. 하지만 cbPlainText = strlen(tst_msg) + 1으로 설정했습니다. strlen(tst_msg)으로 설정하면 @ zaph의 대답 (46CC2228E81B2A05E8E8EBF2B0C42918EC496128D7C45BD0B19BB2D6452A3936)이 표시됩니다.

CNCS를 PKCS # 7 패딩과 함께 사용했기 때문에 웹 사이트와 일치하지 않으며 웹 사이트에서 제로 패딩을 사용합니다. 웹 사이트에서 사용 된 패딩은 출력 암호문을 가져 와서 일반 텍스트로 설정 한 다음 해독 할 수 있습니다. 그런 다음 입력 내용은 54686973206973206d792074657374206d736700000000000000000000000000입니다. 또는 웹 사이트에 입력 한 내용에 00 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C을 추가하면 원래 답변을 얻을 수 있습니다. 또는 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D 0D을 추가하면 @ zaph의 답을 얻을 수 있습니다.

그래서,해야할 일 :

  • 는하지 마십시오, 인쇄 하나 개의 변수 (cbPlainText)를 만들고 그것으로 스틱 물건의 길이를 다시 평가합니다.
  • AES는 블록 암호 알고리즘입니다. 모든 블록 암호에는 완전한 블록이 필요하고, 마지막 마지막 블록에는 패딩이 있어야합니다 (그리고 최종 블록에는 또 다른 블록이 필요합니다). 계속하기 전에 이것이 무엇을 의미하는지 알아보십시오. https://en.wikipedia.org/wiki/Padding_(cryptography)#Symmetric_cryptography
  • 암호화에 문제가있는 것으로 보이는 경우 해독 된 출력을 살펴보십시오.
    • "패딩 없음"으로 해독 된 출력은 매우 드러납니다.
  • 메모리를 어떻게 사용하는지 배우십시오. 또는 C#으로 전환하고 덜 가파른 학습 곡선을 유지하십시오.