在fread / fwrite期间剥离AES填充

时间:2016-03-07 03:54:14

标签: c encryption libgcrypt

我正在使用libgcrypt来加密和解密文件。当我使用fread接收适当数量的字节时,我需要用16-n字节填充它,以便gcry_cipher_encrypt正确加密它。但是,在解密时,仍然存在空字节/填充。有没有办法在16字节块中读写,仍然在最后剥去填充?

#include <stdio.h>
#include <gcrypt.h>

#define algo GCRY_CIPHER_AES128
#define mode GCRY_CIPHER_MODE_CBC
#define KEY_LENGTH 16
#define BLOCK_LENGTH 16

int main(){

    char IV[16];
    char *encBuffer = NULL;
    FILE *in, *out, *reopen;
    char *key = "A key goes here!";
    gcry_cipher_hd_t handle;
    int bufSize = 16, bytes;

    memset(IV, 0, 16);

    encBuffer = malloc(bufSize);

    in = fopen("in.txt", "r");
    out = fopen("out.txt", "w");

    gcry_cipher_open(&handle, algo, mode, 0);
    gcry_cipher_setkey(handle, key, KEY_LENGTH);
    gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);

    while(1){
        bytes = fread(encBuffer, 1, bufSize, in);
        if (!bytes) break;
        while(bytes < bufSize)
            encBuffer[bytes++] = 0x0;
        gcry_cipher_encrypt(handle, encBuffer, bufSize, NULL, 0);
        bytes = fwrite(encBuffer, 1, bufSize, out);
    }

    gcry_cipher_close(handle);
    fclose(in);
    fclose(out);

    gcry_cipher_open(&handle, algo, mode, 0);
    gcry_cipher_setkey(handle, key, KEY_LENGTH);
    gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);

    reopen = fopen("out.txt", "r");
    out = fopen("decoded.txt", "w");

    while(1){
        bytes = fread(encBuffer, 1, bufSize, reopen);
        if (!bytes) break;
        gcry_cipher_decrypt(handle, encBuffer, bufSize, NULL, 0);
        bytes = fwrite(encBuffer, 1, bufSize, out);
    }

    gcry_cipher_close(handle);

    free(encBuffer);

    return 0;
}

5 个答案:

答案 0 :(得分:1)

分组密码有许多操作模式。一些要求输入数据长度为块大小的多个,因此基本上需要明文填充;有些人不知道。详细了解此here

如果必须使用需要填充的模式,则必须将明文长度与加密数据一起保存。最简单的方法是在最后将其写入一个附加块(也加密该块!)。还有其他更复杂的方案,并不总是需要添加块;见this

答案 1 :(得分:1)

我能够通过正确存储填充量然后稍后检查它来修复它,由zaph建议。我使用PKCS#7来确定要写入的字节数和类型。我可以编写NULL字节,但是没有区别,所以不妨坚持标准。

#include <stdio.h>
#include <gcrypt.h>

#define algo GCRY_CIPHER_AES128
#define mode GCRY_CIPHER_MODE_CBC
#define KEY_LENGTH 16
#define BLOCK_LENGTH 16

int main(){

    char IV[16], *encBuffer = NULL;
    char *key = "A key goes here!";
    int bufSize = 16, bytes, i=0, padding;

    FILE *in, *out, *reopen;
    gcry_cipher_hd_t handle;

    memset(IV, 0, 16);
    encBuffer = malloc(bufSize);

    // Open in/out for reading and writing
    in = fopen("in.txt", "r");
    out = fopen("out.txt", "w");

    // Set handle for encryption
    gcry_cipher_open(&handle, algo, mode, 0);
    gcry_cipher_setkey(handle, key, KEY_LENGTH);
    gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);

    // Read from in, write encrypted to out
    while(1){
        bytes = fread(encBuffer, 1, bufSize, in);
        if (!bytes) break;

        // If fread grabbed less than 16 bytes, that's our final line
        // Use the byte number for padding and pad N bytes of N
        if ( bytes < BLOCK_LENGTH ){ padding = 16-bytes; }

        while(bytes < bufSize)
            encBuffer[bytes++] = padding;
        gcry_cipher_encrypt(handle, encBuffer, bytes, NULL, 0);
        bytes = fwrite(encBuffer, 1, bufSize, out);
    }

    // Close handle and i/o files
    gcry_cipher_close(handle);
    fclose(in);
    fclose(out);

    // Set handle for decryption
    gcry_cipher_open(&handle, algo, mode, 0);
    gcry_cipher_setkey(handle, key, KEY_LENGTH);
    gcry_cipher_setiv(handle, IV, BLOCK_LENGTH);

    // Reopen outfile, open decoded file
    reopen = fopen("out.txt", "r");
    out = fopen("decoded.txt", "w");

    //Loop until EOF
    while(1){
        i=0;
        bytes = fread(encBuffer, 1, bufSize, reopen);
        if (!bytes) break;
        gcry_cipher_decrypt(handle, encBuffer, bufSize, NULL, 0);
        // Read each block and check for padding
        while ( i++ < BLOCK_LENGTH ){
            // If padding is found write 16-padding bytes
            if ( encBuffer[i] == padding ){
                bytes = fwrite(encBuffer, 1, (16-padding), out);
                return 0;
            }
        }
        // If padding isn't found, write the whole buffer
        bytes = fwrite(encBuffer, 1, bufSize, out);
    }

    // Close the handle and free the buffer
    gcry_cipher_close(handle);
    free(encBuffer);

    return 0;
}

答案 2 :(得分:0)

cry_cipher似乎没有提到填充,但如果要加密的数据并不总是块大小的倍数,则填充是必要的。一种Bozo的举动,更不用说填充,也许它总是PKCS#7填充。

如果有填充,通常的填充是PKCS#7(PKCS#5必不可少)。 PKCS#7总是在填充的块大小字节中添加至少一个,填充值是数字或填充字节。在解密时,填充被删除。

这意味着如果要加密的输入数据是块大小的精确倍数(AES为16字节),则会添加一个填充块。在加密输出中具有16字节的SO将是32字节。

答案 3 :(得分:0)

这是在我的评论中,但我怀疑你需要拨打gcry_cipher_encrypt(handle, encBuffer, bytes, NULL, 0);fread返回从文件中读取的字节数。如果你经常使用bufSize字节进行加密,那么当你在循环中到达文件的末尾时,将会有额外的字节输入到加密函数中,而不是文件的一部分,除非文件是16字节的完美倍数。加密库不知道文件数据是什么,不知道什么,它正确加密和解​​密encBuffer缓冲区末端的额外NULL 。我的猜测是用户不必担心填充他们自己的数据,您使用的库应该负责处理。

快速检查是用0x20初始化encBuffer,然后查看你的解密文件是否在其末尾有额外的空格

答案 4 :(得分:0)

当您编写文件并进入最后一个块时,就像您的答案一样,将其填充到一个完整的16个字节。您缺少的是,如果文件恰好以一个完整的16字节块结束,则添加另一个完整的填充块 - 填充大小为16 - 16字节,值为16行。

现在,当您阅读文件时,只需阅读到最后。最后一个块将是一个完整的16个字节。只看最后一个字节。无论最后一个字节是什么值,那就是你的填充。从最后一个块的末尾删除那么多字节。如果值为16,则删除整个最后一个块。

如果你没有添加一个完整的16字节填充,你永远不会知道文件末尾的3,3,3的最后三个字节是填充还是重要数据。它看起来完全一样。