Trusted Design

OpenSSLでAES暗号処理

概要

OpenSSLが提供しているC言語のAPIを使って共通鍵暗号方式のAES暗号を実行します。OpenSSLのAPIは抽象化されているので,共通鍵暗号処理なら引数を入れ替えるだけでDESなど他のアルゴリズムでも利用できます。

利用するAPI

暗号/復号を実行するためのAPIを使います。

  • EVP_CipherInit_ex()
    暗号処理を初期化します。
  • EVP_CipherUpdate()
    暗号処理を実行します。
  • EVP_CipherFinal_ex()
    暗号処理を終了させます。

基本的にこのAPIを順番にコールしていけばOKです。
平文の長さがブロック長と同じで,パディングをしない場合には EVP_CipherFinal_ex() を呼び出さなくても大丈夫なのですが,その場合,復号時に EVP_CipherFinal_ex() を呼び出すとエラーになります。

サンプルプログラム

暗号/復号処理の関数

30行目のEVP_CipherInit_ex()関数で第二引数を「EVP_aes_128_cbc()」と指定しています。これを例えば「EVP_des_ede3_cbc()」と指定すると3キーの3DESでの処理になります。DESは古い暗号方式なので,使わないほうが良いですが。


/*
 128bit AES CBCモードで暗号化を実行する関数
 modeに1を設定すると暗号化,0を設定すると復号を実行する
 */
int Cipher_AES_CBC(unsigned char* inbuf, int inbufLen, unsigned char* outbuf, int* outbufLen, int mode)
{
    int enclen = 0;
    EVP_CIPHER_CTX* ctx;
    unsigned char* ptr = outbuf;

    /* 出力バッファ長が入力バッファ長よりも小さい場合は
     * 暗号データを書き込めないためエラー
     * EVP_MAX_BLOCK_LENGTHはパディング分
     */
    if (*outbufLen < (inbufLen + EVP_MAX_BLOCK_LENGTH)) {
        // Length不正
        printf("Error: Invalid length.\n");
        return 0;
    }

    /*
     * 鍵とIVはダミー
     * 通常はセキュアな領域から取得するので,関数の外部から与えられても良い
     */
    unsigned char key[] = "ABCDEFGHIJKLMNOP";
    unsigned char iv[] = "0123456789ABCDEF";

    /* 128bit AES CBCモードで初期化する */
    ctx = EVP_CIPHER_CTX_new();
    EVP_CipherInit_ex(ctx, EVP_aes_128_cbc(), NULL, NULL, NULL, mode);

    OPENSSL_assert(EVP_CIPHER_CTX_key_length(ctx) == 16);
    OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == 16);

    /* 鍵とIVを設定する */
    EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, mode);

    /* 暗号処理を実行する */
    if (!EVP_CipherUpdate(ctx, outbuf, &enclen, inbuf, inbufLen)) {
        // Error
        printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
        EVP_CIPHER_CTX_free(ctx);
        return 0;
    }

    /* バッファのポインタを進める */
    ptr += enclen;
    *outbufLen = enclen;

    /* 残りの部分の暗号処理 */
    if (!EVP_CipherFinal_ex(ctx, ptr, &enclen)) {
        // Error
        printf("Error: %s\n", ERR_reason_error_string(ERR_get_error()));
        EVP_CIPHER_CTX_free(ctx);
        return 0;
    }
    *outbufLen += enclen;

    /* 解放処理 */
    EVP_CIPHER_CTX_free(ctx);
    return 1;
}

呼び出し側

入力する平文は「My Sample program, Encrypt Test.」です。暗号/復号を同じ関数で実行することができます。


#include "openssl/evp.h"
#include "openssl/aes.h"
#include "openssl/err.h"

#define MODE_ENCRYPTO 1       // 暗号処理
#define MODE_DECRYPTO 0       // 復号処理

int main()
{
    unsigned char input[] = "My Sample program, Encrypt Test.";
    unsigned char outbuf[256];
    int outbufLen = 256;
    unsigned char decbuf[256];
    int decbufLen = 256;
    if (!Cipher_AES_CBC(input, sizeof(input), outbuf, &outbufLen, MODE_ENCRYPTO)) {
        printf("Error\n");
        return 0;
    }

    if (!Cipher_AES_CBC(outbuf, outbufLen, decbuf, &decbufLen, MODE_DECRYPTO)) {
        printf("Error\n");
        return 0;
    }

    printf("Plain text : [%s]\n", decbuf);

    return 1;
}

実行結果

当たり前ですが,inputで設定した文字列が復号後に表示されます。


Plain text : [My Sample program, Encrypt Test.]