2010-06-29 320 views
17

對於OpenSSL來說,我還有什麼新意,任何人都可以告訴我如何從C文件初始化AES CTR模式。我知道這是該方法的簽名,但我遇到了參數問題,沒有多少文檔,也沒有一個清晰的例子來說明如何進行簡單的加密。如果有人能夠舉例說明這種方法,我將不勝感激。提前致謝!AES CTR 256在OpenSSL上的加密操作模式

void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out, 
    const unsigned long length, const AES_KEY *key, 
    unsigned char ivec[AES_BLOCK_SIZE], 
    unsigned char ecount_buf[AES_BLOCK_SIZE], 
    unsigned int *num); 

嗨咖啡館我真的很感激你快速回答它已經真正有用的,並且defenetly最好的例子我已經在網上找到。我試圖打開一個長度不確定的文件,對它進行加密並用生成的密文寫入另一個文件,然後打開加密文件並恢復明文。我需要使用大量MB的文件,因爲我想基準測試CPU的性能。不過,Im在解密時仍然有問題。不知何故,當解密一個相當大的txt文件(1504KB)時,它不會解密完成,而我以明文形式得到一半,另一半仍然被加密。我想這可能與iv的大小或我打電話給櫃檯的方式有關。以下是我迄今爲止:

#include <openssl/aes.h> 
#include <stdio.h> 
#include <string.h> 

struct ctr_state { 
    unsigned char ivec[16]; 
    unsigned int num; 
    unsigned char ecount[16]; 
}; 

FILE *fp; 
FILE *rp; 
FILE *op; 
size_t count; 
char * buffer; 
AES_KEY key; 

int bytes_read, bytes_written; 
unsigned char indata[AES_BLOCK_SIZE]; 
unsigned char outdata[AES_BLOCK_SIZE]; 
unsigned char ckey[] = "thiskeyisverybad"; // It is 128bits though.. 
unsigned char iv[8] = {0};//This should be generated by RAND_Bytes I will take into consideration your previous post 
struct ctr_state state; 

int init_ctr(struct ctr_state *state, const unsigned char iv[8]){  
    state->num = 0; 
    memset(state->ecount, 0, 16);  
    memset(state->ivec + 8, 0, 8); 
    memcpy(state->ivec, iv, 8); 
} 

void encrypt(){ 
    //Opening files where text plain text is read and ciphertext stored  
    fp=fopen("input.txt","a+b"); 
    op=fopen("output.txt","w"); 
    if (fp==NULL) {fputs ("File error",stderr); exit (1);} 
    if (op==NULL) {fputs ("File error",stderr); exit (1);}  

    //Initializing the encryption KEY 
    AES_set_encrypt_key(ckey, 128, &key); 

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext 
while (1) {  
    init_ctr(&state, iv); //Counter call 
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, fp); 
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num);  
    bytes_written = fwrite(outdata, 1, bytes_read, op); 
    if (bytes_read < AES_BLOCK_SIZE) 
    break; 
    } 

    fclose (fp); 
    fclose (op); 
    free (buffer); 
} 

void decrypt(){ 
    //Opening files where text cipher text is read and the plaintext recovered   
    rp=fopen("recovered.txt","w"); 
    op=fopen("output.txt","a+b"); 
    if (rp==NULL) {fputs ("File error",stderr); exit (1);} 
    if (op==NULL) {fputs ("File error",stderr); exit (1);} 

    //Initializing the encryption KEY 
    AES_set_encrypt_key(ckey, 128, &key); 

    //Encrypting Blocks of 16 bytes and writing the output.txt with ciphertext 
    while (1) {  
    init_ctr(&state, iv);//Counter call 
    bytes_read = fread(indata, 1, AES_BLOCK_SIZE, op); 
    AES_ctr128_encrypt(indata, outdata, bytes_read, &key, state.ivec, state.ecount, &state.num); 
    bytes_written = fwrite(outdata, 1, bytes_read, rp); 
    if (bytes_read < AES_BLOCK_SIZE) 
    break; 
    } 
    fclose (rp); 
    fclose (op); 
    free (buffer); 
} 

int main(int argc, char *argv[]){ 
    encrypt(); 
    //decrypt(); 
    system("PAUSE"); 
    return 0; 
} 

每個加密和解密功能,被稱爲在不同的運行,從而一切都擁有相同的值總是初始化。再次感謝您提前給我提示的提示& Regards !!!

+2

你的問題是你在每塊後重新初始化計數器。這是錯誤的 - 在加密和解密時,將'init_ctr()'調用移到'while()'循環之外。 'indata'和'outdata'也不一定是'AES_BLOCK_SIZE'的長度 - 它們可以大得多。 – caf 2010-08-06 00:24:55

+1

你應該*不*使用'AES_encrypt'和朋友。這是一個純軟件實現,所以你不會喜歡硬件支持,比如AES-NI。您應該使用'EVP_ *'功能。請參閱OpenSSL wiki上的[EVP Symmetric Encryption and Decryption](http://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption)。事實上,您應該使用經過身份驗證的加密,因爲它提供了*機密性和真實性。請參閱OpenSSL wiki上的[EVP Authenticated Encryption and Decryption](http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption)。 – jww 2016-05-28 13:15:46

+0

如果使用'EVP_ *'函數,那麼感興趣的密碼是'EVP_aes_128_ctr','EVP_aes_192_ctr'和'EVP_aes_256_ctr'。 – jww 2016-05-28 13:27:08

回答

26

通常情況下,您將打算重複呼叫AES_ctr128_encrypt()以發送具有相同密鑰和IV以及遞增計數器的多個消息。這意味着你需要跟蹤的「IVEC」,「民」和通話之間「ecount價值觀的 - 所以創建一個struct持有這些和初始化函數:

struct ctr_state { 
    unsigned char ivec[16]; /* ivec[0..7] is the IV, ivec[8..15] is the big-endian counter */ 
    unsigned int num; 
    unsigned char ecount[16]; 
}; 

int init_ctr(struct ctr_state *state, const unsigned char iv[8]) 
{ 
    /* aes_ctr128_encrypt requires 'num' and 'ecount' set to zero on the 
    * first call. */ 
    state->num = 0; 
    memset(state->ecount, 0, 16); 

    /* Initialise counter in 'ivec' to 0 */ 
    memset(state->ivec + 8, 0, 8); 

    /* Copy IV into 'ivec' */ 
    memcpy(state->ivec, iv, 8); 
} 

現在,當你開始通信與目標,你需要生成一個IV使用和初始化計數器:

unsigned char iv[8]; 
struct ctr_state state; 

if (!RAND_bytes(iv, 8)) 
    /* Handle the error */; 

init_ctr(&state, iv); 

然後,您將需要8個字節的IV發送到目的地。您還需要從您的原始密鑰字節初始化一個AES_KEY

AES_KEY aes_key; 

if (!AES_set_encrypt_key(key, 128, &aes_key)) 
    /* Handle the error */; 

您現在可以開始加密數據,並將其發送到目的地,重複調用AES_ctr128_encrypt()這樣的:

if (!AES_ctr128_encrypt(msg_in, msg_out, msg_len, &aes_key, state->ivec, state->ecount, &state->num)) 
    /* Handle the error */; 

msg_in是指向包含明文消息的緩衝區的指針,msg_out是指向加密消息應去的緩衝區的指針,而msg_len是消息長度)。

解密是完全一樣的,除了你不生成與RAND_bytes() IV - 相反,你取對方給你的價值。

重要:

  1. 對呼叫多次init_ctr()更在加密過程。在開始加密之前,計數器和IV必須初始化一次

  2. 在加密端,任何情況下都不會試圖從除RAND_bytes()以外的任何地方獲得IV。不要將其設置爲固定值;不要使用散列函數;不要使用收件人的姓名;不要從磁盤讀取它。用RAND_bytes()生成併發送到目的地。每當你開始一個零計數器,你必須必須開始一個全新的,你從來沒有用過的IV。

  3. 如果完全可能發送2 ** 64個字節而不更改IV和/或密鑰,則需要測試計數器是否溢出。

  4. 不要忽略錯誤檢查。如果一個功能失敗並且你忽略了它,那麼很可能(甚至可能)你的系統看起來正常運行,但實際上卻完全不安全。

+3

讓我添加一個詳細信息,當我使用這個函數時,我逃脫了:num參數是多少個字節到一個塊中,而不是計數器。如果您正在加密數據包(例如),請始終將state-> num設置爲零,並將您的計數器置於iv的高位字節中。 – 2010-11-09 18:51:02

+0

@Mike Elkins:的確 - 您可以將'num'和'ecount'視爲OpenSSL CTR實現的不透明內部狀態。在大多數情況下,不需要直接修改它們。 – caf 2010-11-09 21:35:55

2

看起來您的測試程序的基本問題是fopen調用的模式值不正確。我認爲你需要加密改變你的fopen調用此:

fp=fopen("input.txt","rb"); 
op=fopen("output.txt","wb"); 

而且在解密那些到:

rp=fopen("recovered.txt","wb"); 
op=fopen("output.txt","rb"); 

值得指出的另一件事是,ckey也許應該被聲明爲32字節(256位)緩衝區。確實,128位加密僅使用來自密鑰的16個字節的數據。但是OpenSSL函數AES_set_encrypt_key(至少在我使用的版本中)從該緩衝區中讀取了32個字節。它只使用適當數量的字節,但讀取確實發生。這意味着,如果緩衝區只有16個字節,並且發生在與內存中不可讀頁面相鄰的頁面末尾,則會導致訪問衝突。

噢 - 我剛剛注意到那裏有一個無關緊要的電話freefree(buffer);調用無效,因爲緩衝區從未分配過。我意識到你的代碼只是一個簡單的測試,但是......好吧,我們是程序員,並不能幫助自己。