C 中的 fwrite 在文件末尾添加额外的二进制字符

fwrite in C add extra binary characters at the end of file

要求:

我是一名开发人员,但直到一周前我对 C 的经验几乎为零。对于一个项目,我需要 encrypt/decrypt source-code/text 个文件并且需要为此使用 C。

我做了什么?

我正在使用 Kokke/tony-AES-c library for it and implementing it with pkcs7 padding as explained in this Gist。我写的代码是:

main.c

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <stdlib.h>

#define CBC 1
#define DECRYPT 0
#define ENCRYPT 1

#include "aes.h"
#include "pkcs7_padding.h"

const void my_decrypt(char *report, char *output_file, long dlen) {
    FILE *op_file;

    //Initialization Vector
    uint8_t iv[]  = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };

    uint8_t i;
    char* key = "thisIstheKey";
//    int dlen = strlen(report);
    int klen = strlen(key);

    printf("THE encrypted TEXT STRING = ");
    for (i=0; i<dlen;i++){
        printf("%c", report[i]);
    }
    printf("\n");

    //Proper Length of report
    int dlenu = dlen;
//    if (dlen % 16) {
//        dlenu += 16 - (dlen % 16);
//        printf("The original length of the STRING = %d and the length of the padded STRING = %d\n", dlen, dlenu);
//    }

    //Proper length of key
    int klenu = klen;
    if (klen % 16) {
        klenu += 16 - (klen % 16);
        printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
    }

    // Make the uint8_t arrays
    uint8_t hexarray[dlenu];
    uint8_t kexarray[klenu];

    // Initialize them with zeros
    memset( hexarray, 0, dlenu );
    memset( kexarray, 0, klenu );

    // Fill the uint8_t arrays
    for (int i=0;i<dlen;i++) {
        hexarray[i] = (uint8_t)report[i];
    }
    for (int i=0;i<klen;i++) {
        kexarray[i] = (uint8_t)key[i];
    }

    int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
    int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );

    printf("The padded STRING in hex is = ");
    for (i=0; i<dlenu;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");

    printf("The padded key in hex is = ");
    for (i=0; i<klenu;i++){
        printf("%02x",kexarray[i]);
    }
    printf("\n");

    // In case you want to check if the padding is valid
    int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
    int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
    printf("Is the pkcs7 padding valid  report = %d  |  key = %d\n", valid, valid2);

    //start the decryption
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, kexarray, iv);

    // decrypt
    AES_CBC_decrypt_buffer(&ctx, hexarray, dlenu);

    // ks
    size_t actualDataLength = pkcs7_padding_data_length( hexarray, dlenu, 16);
    printf("The actual data length (without the padding) = %ld\n", actualDataLength);

    printf("the decrypted STRING in hex = ");
    for (i=0; i<actualDataLength;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");

    printf("Printing message from variables1\n");
    char* decrypted_message = printf((char*) hexarray);
    printf("Printing message from variables2\n");
//    printf("Decrypted string (decrypted_message) = %s", decrypted_message);
    printf("Decrypted string (hexarray) = %s", (char*)hexarray);

    // Write file
    printf("Writing contents to %s\n", output_file);
    op_file = fopen(output_file, "wb");
    if (!output_file) {
        /* Unable to open temp file for writing */
        fprintf(stderr, "ERROR: fread error: %s\n", strerror(errno));
    }
    int out_len = sizeof (hexarray);
    fwrite(hexarray, sizeof(unsigned char), out_len, op_file);

    fclose(op_file);
}

const void my_encrypt(char *report, char *output_file, long dlen) {
    FILE *op_file;

    //Initialization Vector
    uint8_t iv[]  = { 0x75, 0x52, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x21, 0x21 };

    uint8_t i;
    char* key = "thisIstheKey";
//    int dlen = strlen(report);
    int klen = strlen(key);

    printf("THE PLAIN TEXT STRING = ");
    for (i=0; i<dlen;i++){
        printf("%c", report[i]);
    }
    printf("\n");

    //Proper Length of report
    int dlenu = dlen;
    if (dlen % 16) {
        dlenu += 16 - (dlen % 16);
        printf("The original length of the STRING = %l and the length of the padded STRING = %l\n", dlen, dlenu);
    }

    //Proper length of key
    int klenu = klen;
    if (klen % 16) {
        klenu += 16 - (klen % 16);
        printf("The original length of the KEY = %d and the length of the padded KEY = %d\n", klen, klenu);
    }

    // Make the uint8_t arrays
    uint8_t hexarray[dlenu];
    uint8_t kexarray[klenu];

    // Initialize them with zeros
    memset( hexarray, 0, dlenu );
    memset( kexarray, 0, klenu );

    // Fill the uint8_t arrays
    for (int i=0;i<dlen;i++) {
        hexarray[i] = (uint8_t)report[i];
    }
    for (int i=0;i<klen;i++) {
        kexarray[i] = (uint8_t)key[i];
    }

    int reportPad = pkcs7_padding_pad_buffer( hexarray, dlen, sizeof(hexarray), 16 );
    int keyPad = pkcs7_padding_pad_buffer( kexarray, klen, sizeof(kexarray), 16 );

    printf("The padded STRING in hex is = ");
    for (i=0; i<dlenu;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");

    printf("The padded key in hex is = ");
    for (i=0; i<klenu;i++){
        printf("%02x",kexarray[i]);
    }
    printf("\n");

    // In case you want to check if the padding is valid
    int valid = pkcs7_padding_valid( hexarray, dlen, sizeof(hexarray), 16 );
    int valid2 = pkcs7_padding_valid( kexarray, klen, sizeof(kexarray), 16 );
    printf("Is the pkcs7 padding valid  report = %d  |  key = %d\n", valid, valid2);

    //start the encryption
    struct AES_ctx ctx;
    AES_init_ctx_iv(&ctx, kexarray, iv);

    // encrypt
    AES_CBC_encrypt_buffer(&ctx, hexarray, dlenu);

    printf("the encrypted STRING = ");
    for (i=0; i<dlenu;i++){
        printf("%02x",hexarray[i]);
    }
    printf("\n");

    // Write file
    printf("Writing contents to %s\n", output_file);
    op_file = fopen(output_file, "wb");
    if (!output_file) {
        /* Unable to open temp file for writing */
        fprintf(stderr, "ERROR: fread error: %s\n", strerror(errno));
    }
    int out_len = sizeof (hexarray);
    fwrite(hexarray, sizeof(unsigned char), out_len, op_file);
    fclose(op_file);
}

int main(int argc, char *argv[]) {
    // File pointer
    FILE *input_file, *output_file;

    int operation;

    char * input_file_buffer = 0, output_file_buffer = 0;
    long input_file_length;

    // ////////////////////////////////////////////////////////
    // Read command line arguments
    // ////////////////////////////////////////////////////////

    // Make sure proper command params are supplied
    if (argc != 3) {
        printf("Usage: %s encrypt/decrypt filename\n", argv[0]);
        return -1;
    }

    // Define operation type
    if (strcmp(argv[1], "encrypt") == 0) {
        operation = ENCRYPT;
    } else if (strcmp(argv[1], "decrypt") == 0) {
        operation = DECRYPT;
    }

    // ////////////////////////////////////////////////////////
    // Open File contents
    // ////////////////////////////////////////////////////////

    // Open input_file to encrypt/decrypt.
    input_file = fopen(argv[2], "rb");
    if (!input_file) {
        fprintf(stderr, "ERROR: '%s' file fopen error: %s\n", argv[2], strerror(errno));
        return errno;
    }

    // Read contents of file in buffer
    fseek(input_file, 0, SEEK_END);
    input_file_length = ftell (input_file);
    fseek(input_file, 0, SEEK_SET);
    input_file_buffer = malloc (input_file_length);
    if (input_file_buffer)
    {
        fread(input_file_buffer, 1, input_file_length, input_file);
    }

    // Close input_file
    fclose(input_file);

    // We have contents of file in input_file_buffer, let's print them.
    printf("File contents:\n-------\n%s\n", input_file_buffer);

    if (operation == ENCRYPT) {
        // Let's encrypt input_file_buffer
        my_encrypt(input_file_buffer, argv[2], input_file_length);
    } else if (operation == DECRYPT) {
        my_decrypt(input_file_buffer, argv[2], input_file_length);
    }

    return 0;
}

生成文件

# Compiler
CC       = gcc
CFLAGS   = -Wall

# Library options (Needed for OpenSSL)
LDLIBS   = -lcrypto

# Define input/output files
SOURCES  = aes.c pkcs7_padding.c main.c
OBJFILES = aes.o pkcs7_padding.o main.o
TARGET   = encdec

# Generated files
ENCFILE  = temp_encrypted_file
DECFILE  = temp_decrypted_file

# Start building
all: $(TARGET)

$(TARGET): $(OBJFILES)
    $(CC) $(CFLAGS) -o $(TARGET) $(OBJFILES) $(LDLIBS)

# Start clean up
clean:
    rm -f $(OBJFILES) $(TARGET) $(ENCFILE) $(DECFILE)

编译后执行的命令

编译(make all)后,我运行命令./encdec encrypt test.php,加密test.php文件。

原版test.php

<?php

// This is comment to check if comment can also be retrieved after decryption
echo "Hello world!";

// One empty lime after the end (Without closing ?> tag) to check if empty line is retrieved after decryption

在 运行ning 命令之后,一种可能的输出是:

加密test.php

cat test.php
��D�zt�5�W�L�QP�8B�n`-`����݇Eֶr��˘�`\��k
��pDZ��8NU��e�T�P|�W�{2�,]��=�'��i:���E��fϓQt�P"���_���,����?�M�����3b3��Al�����|q2������/��
���8�,�˦�VV��q�����a�]�]��#��i5]?����<a�Z����q�I0�?-S�9?%

解密./encdev decrypt test.php后,在test.php

中得到如下内容

解密test.php(签入vim)

<?php

// This is comment to check if comment can also be retrieved after decryption
echo "Hello world!";

// One empty lime after the end (Without closing ?> tag) to check if empty line is retrieved after decryption










问题

最后,我得到了 10 行空行,在原始文件中,只有一个。

谁能帮我理解我犯了什么错误,我怎样才能去掉解密文件中额外的 9 换行符?

my_decrypt()中,当将解密数据写入文件时,不使用由pkcs7_padding_data_length()确定的长度:int out_len = sizeof(hexarray)必须替换为int out_len = actualDataLength。这使得解密在我的机器上工作。

此外,my_decrypt()中密文的填充是没有意义的。它在这里也没有效果,因为 pkcs7_padding_pad_buffer() 由于 data_length + pad_byte > buffer_size 而没有填充(并且 pkcs7_padding_valid() 表示无效填充)。

另外,key不要被填充。如果密钥没有为 AES 定义的长度,最好显示一条错误消息。如果要使用密码而不是密钥,应该使用像PBKDF2这样可靠的密钥推导函数。

最后,出于安全原因,请勿应用静态 IV。