AES による暗号化・復号化

2021年4月18日

はじめに

AES による暗号化・復号化について。

C による暗号化

tiny-AES-c というものが使える。

#include 

#include "aes.h"

#define FILE_PATH 128
#define LINE_SIZE 4096
#define BUFFER_SIZE 4096


int main(int argc, char *argv[])
{
    FILE *fp_in, *fp_out;
    char *filename, filename_out[FILE_PATH];
    char *key_str;
    unsigned char key[AES_KEYLEN];
    unsigned char iv[AES_BLOCKLEN];
    struct AES_ctx ctx;
    int i;
    
    if(argc < 3){
        printf("usage: encrypt <filename> <key>\n");
        return 0;
    }
    
    filename = argv[1];
    key_str = argv[2];
    
    fp_in = fopen(filename, "r");

    if(fp_in == NULL){
        printf("error: Can't open file: %s", filename);
        return 1;
    }
    
    if(strlen(key_str) != AES_KEYLEN){
        printf("error: key length must be %d\n", AES_KEYLEN);
        return 0;
    }
    
    sprintf(filename_out, "%s-encrypted", filename);
    
    fp_out = fopen(filename_out, "wb");

    if(fp_out == NULL){
        printf("error: Can't open file: %s", filename_out);
        fclose(fp_in);
        return 1;
    }
    
    for(i = 0; i < AES_KEYLEN; i++){
        key[i] = key_str[i];
    }
    
    srand(time(NULL));
    
    for(i = 0; i < AES_BLOCKLEN; i++){
        iv[i] = rand();
    }
    
    AES_init_ctx_iv(&ctx, key, iv);
    fwrite(iv, sizeof(unsigned char), AES_BLOCKLEN, fp_out);
    
    {
        unsigned char line[LINE_SIZE];
        unsigned char res[LINE_SIZE];
        unsigned char buffer[BUFFER_SIZE];
        int rlen0 = 0;
        
        while(fgets(line, sizeof(line), fp_in)){
            const int len = strlen(line);
            
            /* If the previous rest exists, add it to the buffer. */
            if(rlen0 > 0){
                strncpy(buffer, res, rlen0);
                buffer[rlen0] = '\0';
            }
            
            /* If the sum length does not reach the block size */
            if(rlen0 + len < BUFFER_SIZE){
                strncpy(&res[rlen0], line, len);
                res[rlen0 + len] = '\0';
                rlen0 += len;
            }else{
                int rlen = (rlen0 + len) % BUFFER_SIZE;
                int blen;
                
                /* If the current rest exists, save it. */
                if(rlen > 0){
                    strncpy(res, &line[len - rlen], rlen);
                    res[rlen] = '\0';
                    line[len - rlen] = '\0';
                }
                
                /* write buffer */
                strncpy(&buffer[rlen0], line, len - rlen);
                blen = rlen0 + len - rlen;
                buffer[blen] = '\0';
                AES_CBC_encrypt_buffer(&ctx, buffer, blen);
                fwrite(buffer, sizeof(unsigned char), blen, fp_out);
                rlen0 = rlen;
            }
        }
        
        if(rlen0 > 0){
            const int plen = (BUFFER_SIZE - rlen0) % AES_BLOCKLEN;
            int blen;
            strncpy(buffer, res, rlen0);
            buffer[rlen0] = '\0';
            blen = rlen0 + plen;
            
            /* padding */
            if(plen > 0)
            {
                const unsigned char pad = plen;
                for(i = rlen0; i < blen; i++){
                    buffer[i] = pad;
                }
            }
            
            AES_CBC_encrypt_buffer(&ctx, buffer, blen);
            fwrite(buffer, sizeof(unsigned char), blen, fp_out);
        }
    }
    
    fclose(fp_out);
    fclose(fp_in);

    return 0;
}

まず、key を任意で決める。iv (initialization vector) を乱数で決めて、iv および暗号化したデータをファイルに書き出す。

参考: Good AES Initialization Vector practice (stackoverflow)

C による復号化

#include 

#include "aes.h"

#define FILE_PATH 128
#define BUFFER_SIZE 4096


int main(int argc, char *argv[])
{
    FILE *fp_in, *fp_out;
    char *filename, filename_out[FILE_PATH];
    char *key_str;
    unsigned char key[AES_KEYLEN];
    unsigned char iv[AES_BLOCKLEN];
    unsigned char buffer[BUFFER_SIZE];
    struct AES_ctx ctx;
    int len;
    int i;
    
    if(argc < 3){
        printf("usage: decrypt <filename> <key>\n");
        return 0;
    }
    
    filename = argv[1];
    key_str = argv[2];
    
    fp_in = fopen(filename, "rb");

    if(fp_in == NULL){
        printf("error: Can't open file: %s", filename);
        return 1;
    }
    
    if(strlen(key_str) != AES_KEYLEN){
        printf("error: key length must be %d\n", AES_KEYLEN);
        return 0;
    }
    
    sprintf(filename_out, "%s-decrypted", filename);
    
    fp_out = fopen(filename_out, "w");

    if(fp_out == NULL){
        printf("error: Can't open file: %s", filename_out);
        fclose(fp_in);
        return 1;
    }
    
    for(i = 0; i < AES_KEYLEN; i++){
        key[i] = key_str[i];
    }
    
    fread(iv, sizeof(unsigned char), AES_BLOCKLEN, fp_in);
    AES_init_ctx_iv(&ctx, key, iv);
    
    while(len = fread(buffer, sizeof(unsigned char), BUFFER_SIZE, fp_in)){
        AES_CBC_decrypt_buffer(&ctx, buffer, BUFFER_SIZE);
        
        for(i = 0; i < len; i++){
            if(isprint(buffer[i])){
                fprintf(fp_out, "%c", buffer[i]);
            }else if(buffer[i] == '\n'){
                fprintf(fp_out, "\n");
            }
        }
    }
    
    fclose(fp_out);
    fclose(fp_in);

    return 0;
}

ファイルを暗号化したときの key を受け取る。ファイルからまず iv を読み込み、暗号化データを読み込みつつ復号化する。