2011-04-14 226 views
4

我正在玩OpenSSL EVP例程以使用AES 128 cbc模式進行解密。OpenSSL上的EVP_DecryptFinal_ex錯誤

我使用在NIST站點指定的測試向量來測試我的程序。

該程序似乎在EVP_DecryptFinal_ex例程失敗。

任何人都可以告訴我有什麼問題嗎?

另外我該如何做錯誤檢查來找出這個例程失敗的原因?

更新:

請檢查下面的代碼。我已經添加了加密和解密部分。加密作品。但是在解密過程中,雖然兩個匹配的結果相同,但密碼的十六進制值似乎是80字節,而不是預期的64字節(在NIST中提到),儘管解密工作並且解密文本與明文匹配! 有人可以澄清?

預期的密文值應爲:

cipher: 0000 76 49 ab ac 81 19 b2 46 ce e9 8e 9b 12 e9 19 7d 
    0010 50 86 cb 9b 50 72 19 ee 95 db 11 3a 91 76 78 b2 
    0020 73 be d6 b8 e3 c1 74 3b 71 16 e6 9e 22 22 95 16 
    0030 3f f1 ca a1 68 1f ac 09 12 0e ca 30 75 86 e1 a7 

這裏是代碼:

#include <string.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <openssl/evp.h> 

int AES_BLOCK_SIZE; 

int main(int argc, char **argv) 
{ 

    EVP_CIPHER_CTX en; 
    EVP_CIPHER_CTX de; 
    EVP_CIPHER_CTX_init(&en); 
    EVP_CIPHER_CTX_init(&de); 
    const EVP_CIPHER *cipher_type; 
    unsigned char *mode; 
    unsigned char *passkey, *passiv, *plaintxt; 
    int vector_len = 0; 
    char *plain; 
    char *plaintext; 
    unsigned char *ciphertext; 
    int olen, len; 
    int i =0; 

    //NIST VALUES TO CHECK 

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

    unsigned char key[] = 
    { 0x2b, 0x7e, 0x15, 0x16, 
     0x28, 0xae, 0xd2, 0xa6, 
     0xab, 0xf7, 0x15, 0x88, 
     0x09, 0xcf, 0x4f, 0x3c , 0 }; 

    unsigned char input[] = 
    { 0x6b, 0xc1, 0xbe, 0xe2, 
     0x2e, 0x40, 0x9f, 0x96, 
     0xe9, 0x3d, 0x7e, 0x11, 
     0x73, 0x93, 0x17, 0x2a, 

     0xae, 0x2d, 0x8a, 0x57, 
     0x1e, 0x03, 0xac, 0x9c, 
     0x9e, 0xb7, 0x6f, 0xac, 
     0x45, 0xaf, 0x8e, 0x51, 

     0x30, 0xc8, 0x1c, 0x46, 
     0xa3, 0x5c, 0xe4, 0x11, 
     0xe5, 0xfb, 0xc1, 0x19, 
     0x1a, 0x0a, 0x52, 0xef, 

     0xf6, 0x9f, 0x24, 0x45, 
     0xdf, 0x4f, 0x9b, 0x17, 
     0xad, 0x2b, 0x41, 0x7b, 
     0xe6, 0x6c, 0x37, 0x10, 0 }; 

    printf("AES ALGORITHM FOR 128 bit CBC MODE\n"); 
    cipher_type = EVP_aes_128_cbc(); 
    AES_BLOCK_SIZE = 128; 
    passkey = key; 
    passiv = iv; 
    plain = input; 

    printf("iv="); 
    for(i = 0; i < sizeof iv; i++){ 
     printf("%02x", iv[i]); 
    } 
    printf("\n"); 
    printf("key="); 
    for(i = 0; i < sizeof key; i++){ 
     printf("%02x", key[i]); 
    } 
    printf("\n"); 

    printf("Initializing AES ALGORITHM FOR CBC MODE..\n"); 

    EVP_EncryptInit_ex(&en, cipher_type, NULL, passkey, passiv); 

    EVP_DecryptInit_ex(&de, cipher_type, NULL, passkey, passiv); 

    olen = len = strlen(input)+1; 
    printf("len value before aes_encrypt \"%d\"\n", len); 

    int c_len = len + AES_BLOCK_SIZE - 1; 
    int f_len = 0; 
    ciphertext = (unsigned char *)malloc(c_len); 

    if(!EVP_EncryptInit_ex(&en, NULL, NULL, NULL, NULL)){ 
     printf("ERROR in EVP_EncryptInit_ex \n"); 
     return NULL; 
    } 

    if(!EVP_EncryptUpdate(&en, ciphertext, &c_len, plain, len)){ 
     printf("ERROR in EVP_EncryptUpdate \n"); 
     return NULL; 
    } 
    printf("strlen value of ciphertext after update \"%d\"\n", strlen(ciphertext)); 
    if(!EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len)){ 
     printf("ERROR in EVP_EncryptFinal_ex \n"); 
     return NULL; 
    } 
    printf("strlen value of ciphertext after final \"%d\"\n", strlen(ciphertext)); 
    EVP_CIPHER_CTX_cleanup(&en); 

    len = c_len + f_len; 
    printf("len value after aes_encrypt \"%d\"\n", len); 

    len = strlen(ciphertext); 

    printf("strlen value of ciphertext after aes_encrypt \"%d\"\n", len); 

    int p_len = len; 
    f_len = 0; 
    plaintext = (unsigned char *)malloc(p_len); 
    //memset(plaintext,0,sizeof(plaintext)); 
    if(!EVP_DecryptInit_ex(&de, NULL, NULL, NULL, NULL)){ 
     printf("ERROR in EVP_DecryptInit_ex \n"); 
     return NULL; 
    } 
    EVP_CIPHER_CTX_set_padding(&de, 0); 

    if(!EVP_DecryptUpdate(&de, plaintext, &p_len, ciphertext, len)){ 
     printf("ERROR in EVP_DecryptUpdate\n"); 
     return NULL; 
    } 

    if(!EVP_DecryptFinal_ex(&de, plaintext+p_len, &f_len)){ 
     printf("ERROR in EVP_DecryptFinal_ex\n"); 
     return NULL; 
    } 
    EVP_CIPHER_CTX_cleanup(&de); 
    len = p_len + f_len; 
    printf("Decrypted value = %s\n", plaintext); 

    printf("len value after aes_decrypt \"%d\"\n", len); 


    if (strncmp(plaintext, input, olen)) 
     printf("FAIL: enc/dec failed for \"%s\"\n", input); 
    else 
     printf("OK: enc/dec ok for \"%s\"\n", plaintext); // \"%s\"\n 

    printf("OK: ciphertext is \"%s\"\n", ciphertext); // \"%s\"\n 
    printf("\n"); 

    unsigned char *s3 = ciphertext; 
    printf("s3 =\n"); 
    int nc = 0; 
    while(*s3 != '\0'){ 
     printf("%02x", *s3); 
     s3++; 
     nC++; 
     if(nc == 16){ 
      printf("\n"); 
      nc = 0; 
     } 

    } 
    printf("\n"); 
    //printf("nc = %d\n", nc); 
    free(ciphertext); 
    free(plaintext); 

    return 0; 
} 

回答

5

就像你需要匹配的密鑰和IV,當你加密和解密,還需要匹配填充設置。 NIST測試沒有填充。下面是來自OpenSSL的documentation的摘錄:

EVP_DecryptInit_ex(), EVP_DecryptUpdate()和 EVP_DecryptFinal_ex()是 相應的解密操作。 EVP_DecryptFinal()將返回一個 錯誤代碼,如果啓用了填充並且 最終的塊不正確 格式爲。的參數和 限制是相同的 加密操作不同之處在於,如果 填充啓用解密數據 緩衝區out傳遞給 EVP_DecryptUpdate()應具有 足夠的空間用於(INL + cipher_block_size)字節除非 密碼塊大小是1,在這種情況下,inl字節就足夠了。

搜索 「填充」 在同一頁面,你會看到的功能EVP_CIPHER_CTX_set_padding

EVP_CIPHER_CTX_set_padding()使 或禁用填充。默認情況下, 使用 標準塊填充填充加密操作,並在 解密時檢查並刪除填充 。如果填充參數爲 零,則不執行填充,則加密的數據總量或解密的總數量必須是塊大小的倍數或將發生錯誤。

因此,在某些時候,你打電話EVP_CIPHER_CTX_init,你開始解密之前,你需要做到這一點後:

EVP_CIPHER_CTX_set_padding(&de, 0); 
+0

填充標準錯誤導致我行:改用EVPerr(EVP_F_EVP_DECRYPTFINAL_EX,EVP_R_WRONG_FINAL_BLOCK_LENGTH);如果我設置填充如上(零)我得到的錯誤是:EVPerr(EVP_F_EVP_DECRYPTFINAL_EX, EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH);但如何糾正這一點?在解密程序中我沒有提到塊長度還是應該? – pimmling 2011-04-15 07:38:24

+0

但根據你的解釋沒有填充設置,因此我不需要設置填充功能,仍然解密應該正常工作?我編輯了代碼。你可以請現在檢查? – pimmling 2011-04-15 12:51:28

+0

@pimmling:EVP_R_WRONG_FINAL_BLOCK_LENGTH表示它正在尋找填充但沒有找到它,所以你解決了這個問題。現在你看到的是AES數據長度必須是16個字節。您不使用以空字符結尾的字符串。你正在處理二進制數據。在所有事情結束時取消空終止符。不要使用strlen(),因爲它不是以空字符結尾的字符串。在你的情況下使用sizeof()。只需給解密器一次16個字節,它就可以工作,並且您必須弄清楚如何將更大的數據塊分成16個字節的塊。 – indiv 2011-04-15 15:03:46

2

要顯示錯誤的OpenSSL的函數調用失敗後,您可以使用:

ERR_print_errors_fp(stderr); 
0

我有與EVP_DecryptFinal_ex例程相同的問題。我發現你不會得到strlen(ciphertext)的密文長度,因爲函數strlen()返回C字符串的長度。

加密後的密文可以包含被認爲是C字符串結尾的'\ 0'字符,所以您不會得到帶有函數strlen()的正確長度的密文。

相反,您應該記住加密後的密文長度。在你的程序中,使用c_lenf_len做到這一點:

if(!EVP_EncryptUpdate(&en, ciphertext, &c_len, plain, len)){ 
    printf("ERROR in EVP_EncryptUpdate \n"); 
    return NULL; 
    }//Here you get length of ciphertext in c_len 

    printf("strlen value of ciphertext after update \"%d\"\n", strlen(ciphertext)); 
    if(!EVP_EncryptFinal_ex(&en, ciphertext+c_len, &f_len)){ 
    printf("ERROR in EVP_EncryptFinal_ex \n"); 
    return NULL; 
    }//Here you get the rest of padded ciphertext in f_len 
    //This printf won't print out the real lengt of ciphertext you should put in (c_len+f_len) 
    printf("strlen value of ciphertext after final \"%d\"\n", strlen(ciphertext)); 
    EVP_CIPHER_CTX_cleanup(&en); 

    len = c_len + f_len;//This is the real length of ciphertext 
    printf("len value after aes_encrypt \"%d\"\n", len); 

    len = strlen(ciphertext);//And here you rewrite it, delete this line and you should get it right 

另一件事,當你什麼打印出來的密文不使用:

printf("OK: ciphertext is \"%s\"\n", ciphertext); 

「%S」也被認爲是爲C字符串,並且可以打印出整個密文的一部分。如果我不設置

int i = 0; 
printf("\nCiphertext:"); 
for(i = 0; i < len; i++)//variable len is length of ciphertext memorized after encryption. 
{printf("%c",ciphertext[i]);} 
+1

對於打印出密文,我建議使用這種方法: 'BIO_dump_fp(stdout,ciphertext,length);' 這樣的格式輸出就像十六進制編輯。 – alvarez 2014-05-06 14:09:09