在 fread/fwrite 期间去除 AES 填充
Strip AES padding during fread/fwrite
我正在使用 libgcrypt 来加密和解密文件。当我使用 fread 获取适当数量的字节时,我需要用 16-n 字节填充它以便它被 gcry_cipher_encrypt
正确加密。然而,解密后,空 bytes/padding 仍然存在。有什么办法可以读写16字节的块,还去掉最后的padding吗?
#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;
}
cry_cipher 似乎没有提到填充,但如果要加密的数据并不总是块大小的倍数,则填充是必要的。有点愚蠢的举动更不用说填充了,也许它总是 PKCS#7 填充。
如果有填充,通常的填充是PKCS#7(PKCS#5 是基本相同的东西)。 PKCS#7 总是至少向块大小的填充字节添加一个,填充值是数字或填充字节。解密时删除填充。
这意味着如果要加密的输入数据是块大小(AES 为 16 字节)的精确倍数,将添加一个填充块。所以加密输出中的 16 字节将是 32 字节。
这是我的评论,但我怀疑您需要改为调用 gcry_cipher_encrypt(handle, encBuffer, bytes, NULL, 0);
。 fread
returns 它从文件中读取的字节数。如果你不断地用 bufSize
字节加密,那么当你到达循环中的文件末尾时,将会有额外的字节被提供给加密函数,这些字节不是文件的一部分,除非文件是16 字节的完美倍数。加密库不知道什么是文件数据,什么不是,它会正确地加密和解密 encBuffer
缓冲区中末尾的额外 NULL。我的猜测是用户不必担心填充自己的数据,您使用的库应该在后台处理这些问题。
快速检查是用 0x20 初始化 encBuffer,然后查看您的解密文件末尾是否有额外空格
分组密码有多种操作模式。有些要求输入数据长度是块大小的倍数,因此本质上需要明文填充;有些没有。查看更多相关信息 here。
如果必须使用需要填充的模式,则必须将明文长度与加密数据一起保存。最简单的方法是最后将它写入一个附加块(也加密该块!)。还有其他更复杂的方案,并不总是需要添加一个块;参见 this。
我能够通过正确存储填充量然后稍后检查它来修复它,zaph. I used 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;
}
当您写入文件并到达最后一个块时,就像您的答案一样,将其填充到完整的 16 个字节。您缺少的是,如果文件恰好以完整的 16 字节块结尾,则添加 另一个 完整的填充块——填充大小为 16——值的 16 字节连续16个。
现在当你读文件的时候,读到最后。最后一个块将是完整的 16 个字节。只看最后一个字节。无论最后一个字节是什么值,这就是您的填充。从最后一个块的末尾删除那么多字节。如果值为 16,则删除整个最后一个块。
如果不添加完整的 16 字节填充,您将永远不知道文件末尾的最后三个字节 3, 3, 3 是填充,还是重要数据。它看起来完全一样。
我正在使用 libgcrypt 来加密和解密文件。当我使用 fread 获取适当数量的字节时,我需要用 16-n 字节填充它以便它被 gcry_cipher_encrypt
正确加密。然而,解密后,空 bytes/padding 仍然存在。有什么办法可以读写16字节的块,还去掉最后的padding吗?
#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;
}
cry_cipher 似乎没有提到填充,但如果要加密的数据并不总是块大小的倍数,则填充是必要的。有点愚蠢的举动更不用说填充了,也许它总是 PKCS#7 填充。
如果有填充,通常的填充是PKCS#7(PKCS#5 是基本相同的东西)。 PKCS#7 总是至少向块大小的填充字节添加一个,填充值是数字或填充字节。解密时删除填充。
这意味着如果要加密的输入数据是块大小(AES 为 16 字节)的精确倍数,将添加一个填充块。所以加密输出中的 16 字节将是 32 字节。
这是我的评论,但我怀疑您需要改为调用 gcry_cipher_encrypt(handle, encBuffer, bytes, NULL, 0);
。 fread
returns 它从文件中读取的字节数。如果你不断地用 bufSize
字节加密,那么当你到达循环中的文件末尾时,将会有额外的字节被提供给加密函数,这些字节不是文件的一部分,除非文件是16 字节的完美倍数。加密库不知道什么是文件数据,什么不是,它会正确地加密和解密 encBuffer
缓冲区中末尾的额外 NULL。我的猜测是用户不必担心填充自己的数据,您使用的库应该在后台处理这些问题。
快速检查是用 0x20 初始化 encBuffer,然后查看您的解密文件末尾是否有额外空格
分组密码有多种操作模式。有些要求输入数据长度是块大小的倍数,因此本质上需要明文填充;有些没有。查看更多相关信息 here。
如果必须使用需要填充的模式,则必须将明文长度与加密数据一起保存。最简单的方法是最后将它写入一个附加块(也加密该块!)。还有其他更复杂的方案,并不总是需要添加一个块;参见 this。
我能够通过正确存储填充量然后稍后检查它来修复它,zaph. I used 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;
}
当您写入文件并到达最后一个块时,就像您的答案一样,将其填充到完整的 16 个字节。您缺少的是,如果文件恰好以完整的 16 字节块结尾,则添加 另一个 完整的填充块——填充大小为 16——值的 16 字节连续16个。
现在当你读文件的时候,读到最后。最后一个块将是完整的 16 个字节。只看最后一个字节。无论最后一个字节是什么值,这就是您的填充。从最后一个块的末尾删除那么多字节。如果值为 16,则删除整个最后一个块。
如果不添加完整的 16 字节填充,您将永远不知道文件末尾的最后三个字节 3, 3, 3 是填充,还是重要数据。它看起来完全一样。