2010-06-29 322 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);调用无效,因为缓冲区从未分配过。我意识到你的代码只是一个简单的测试,但是......好吧,我们是程序员,并不能帮助自己。