web-dev-qa-db-ja.com

OpensslでAESを使用して暗号化を行う方法

Opensslを使用してAES暗号化を行うサンプルプログラムを作成しようとしています。私はOpensslのドキュメントを調べてみました(苦痛です)、あまり理解できませんでした。私はコードを調べて、APIの使用法を見つけました。これを使用して、以下のような小さなプログラムを作成しました(行番号は省略してください)。暗号化が行われていません...何かが欠けていますか?

PS:コンパイル時にエラーは発生しません。

  1 #include <stdio.h> 
  2 #include <openssl/aes.h>   
  3 
  4 static const unsigned char key[] = {
  5   0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
  6     0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
  7       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
  8         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
  9         };
 10 
 11 void main()
 12 {
 13     unsigned char text[]="virident";
 14     unsigned char out[10]; 
 15     unsigned char decout[10];
 16 
 17     AES_KEY wctx;
 18 
 19     AES_set_encrypt_key(key, 128, &wctx);
 20     AES_encrypt(text, out, &wctx);  
 21 
 22     printf("encryp data = %s\n", out);
 23     
 24     AES_decrypt(out, decout, &wctx);
 25     printf(" Decrypted o/p: %s \n", decout);
 26 
 27 
 28 }

これを理解するのを助けてください...

36
pkumarn

EVP APIを使用してAES256CBCを使用してデータを暗号化/復号化するサンプルコードがあるこのリンクを確認してください。

https://github.com/saju/misc/blob/master/misc/openssl_aes.c

また、 https://github.com/llubu/mpro で私が開発した詳細なオープンソースプロジェクトでAES256 CBCの使用を確認できます。

コードはコメント付きで十分に詳細であり、API自体についての説明が必要な場合は、この本を確認することをお勧めしますViega/Messier/ChandraによるOpenSSLによるネットワークセキュリティ(このPDFを簡単に見つけることができます。)EVP APIを使用した対称暗号に固有の第6章を読んでください。これは、さまざまな関数を使用する理由を実際に理解するのに大いに役立ちましたおよびEVPの構造。

openssl暗号ライブラリの詳細を知りたい場合は、openssl Webサイト(マシンにインストールされているバージョン)からコードをダウンロードすることをお勧めします。 EVPの実装とaeh apiの実装を調べてください。

上記で投稿したコードからのもう1つの提案は、aes.hのapiを使用しているのを見る代わりにEVPを使用していることをここで確認してください 対称SSL 私が尋ねた質問の一つでダニエルがうまく説明しました。

33
abhi

Opensslを使用してAES暗号化を行うサンプルプログラムを作成しようとしています。

この答えは一般的なものなので、OpenSSLがいくつかの操作モードを追加してくれるので、おそらく最新のものを提供します。

まず、AES_encryptAES_decryptを使用しないでください。それらは低レベルであり、使いにくいです。さらに、ソフトウェアのみのルーチンであり、neverAES-のようなハードウェアアクセラレーションを使用しますNI。最後に、いくつかの不明瞭なプラットフォームでのエンディアンネスの問題の対象。

代わりに、EVP_*インターフェイスを使用します。 EVP_*関数は、利用可能な場合、AES-NIなどのハードウェアアクセラレーションを使用します。また、不明瞭なプラットフォームではエンディアンネスの問題は発生しません。

第二に、CBCのようなモードを使用できますが、暗号文には整合性と信頼性の保証がありません。そのため、通常はEAX、CCM、GCMなどのモードが必要です。 (または、暗号化後に別のキーで手動でHMACを適用する必要があります。)

3番目に、OpenSSLには、おそらく興味があるWikiページがあります。 EVP Authenticated Encryption and Decryption 。 GCMモードを使用します。

最後に、AES/GCMを使用して暗号化するプログラムを示します。 OpenSSL wikiの例はそれに基づいています。

#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/err.h>
#include <string.h>   

int main(int arc, char *argv[])
{
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();     

    /* Set up the key and iv. Do I need to say to not hard code these in a real application? :-) */

    /* A 256 bit key */
    static const unsigned char key[] = "01234567890123456789012345678901";

    /* A 128 bit IV */
    static const unsigned char iv[] = "0123456789012345";

    /* Message to be encrypted */
    unsigned char plaintext[] = "The quick brown fox jumps over the lazy dog";

    /* Some additional data to be authenticated */
    static const unsigned char aad[] = "Some AAD data";

    /* Buffer for ciphertext. Ensure the buffer is long enough for the
     * ciphertext which may be longer than the plaintext, dependant on the
     * algorithm and mode
     */
    unsigned char ciphertext[128];

    /* Buffer for the decrypted text */
    unsigned char decryptedtext[128];

    /* Buffer for the tag */
    unsigned char tag[16];

    int decryptedtext_len = 0, ciphertext_len = 0;

    /* Encrypt the plaintext */
    ciphertext_len = encrypt(plaintext, strlen(plaintext), aad, strlen(aad), key, iv, ciphertext, tag);

    /* Do something useful with the ciphertext here */
    printf("Ciphertext is:\n");
    BIO_dump_fp(stdout, ciphertext, ciphertext_len);
    printf("Tag is:\n");
    BIO_dump_fp(stdout, tag, 14);

    /* Mess with stuff */
    /* ciphertext[0] ^= 1; */
    /* tag[0] ^= 1; */

    /* Decrypt the ciphertext */
    decryptedtext_len = decrypt(ciphertext, ciphertext_len, aad, strlen(aad), tag, key, iv, decryptedtext);

    if(decryptedtext_len < 0)
    {
        /* Verify error */
        printf("Decrypted text failed to verify\n");
    }
    else
    {
        /* Add a NULL terminator. We are expecting printable text */
        decryptedtext[decryptedtext_len] = '\0';

        /* Show the decrypted text */
        printf("Decrypted text is:\n");
        printf("%s\n", decryptedtext);
    }

    /* Remove error strings */
    ERR_free_strings();

    return 0;
}

void handleErrors(void)
{
    unsigned long errCode;

    printf("An error occurred\n");
    while(errCode = ERR_get_error())
    {
        char *err = ERR_error_string(errCode, NULL);
        printf("%s\n", err);
    }
    abort();
}

int encrypt(unsigned char *plaintext, int plaintext_len, unsigned char *aad,
            int aad_len, unsigned char *key, unsigned char *iv,
            unsigned char *ciphertext, unsigned char *tag)
{
    EVP_CIPHER_CTX *ctx = NULL;
    int len = 0, ciphertext_len = 0;

    /* Create and initialise the context */
    if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

    /* Initialise the encryption operation. */
    if(1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
        handleErrors();

    /* Set IV length if default 12 bytes (96 bits) is not appropriate */
    if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
        handleErrors();

    /* Initialise key and IV */
    if(1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();

    /* Provide any AAD data. This can be called zero or more times as
     * required
     */
    if(aad && aad_len > 0)
    {
        if(1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
            handleErrors();
    }

    /* Provide the message to be encrypted, and obtain the encrypted output.
     * EVP_EncryptUpdate can be called multiple times if necessary
     */
    if(plaintext)
    {
        if(1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
            handleErrors();

        ciphertext_len = len;
    }

    /* Finalise the encryption. Normally ciphertext bytes may be written at
     * this stage, but this does not occur in GCM mode
     */
    if(1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
    ciphertext_len += len;

    /* Get the tag */
    if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
        handleErrors();

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);

    return ciphertext_len;
}

int decrypt(unsigned char *ciphertext, int ciphertext_len, unsigned char *aad,
            int aad_len, unsigned char *tag, unsigned char *key, unsigned char *iv,
            unsigned char *plaintext)
{
    EVP_CIPHER_CTX *ctx = NULL;
    int len = 0, plaintext_len = 0, ret;

    /* Create and initialise the context */
    if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();

    /* Initialise the decryption operation. */
    if(!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
        handleErrors();

    /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
    if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
        handleErrors();

    /* Initialise key and IV */
    if(!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();

    /* Provide any AAD data. This can be called zero or more times as
     * required
     */
    if(aad && aad_len > 0)
    {
        if(!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
            handleErrors();
    }

    /* Provide the message to be decrypted, and obtain the plaintext output.
     * EVP_DecryptUpdate can be called multiple times if necessary
     */
    if(ciphertext)
    {
        if(!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
            handleErrors();

        plaintext_len = len;
    }

    /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
    if(!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tag))
        handleErrors();

    /* Finalise the decryption. A positive return value indicates success,
     * anything else is a failure - the plaintext is not trustworthy.
     */
    ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);

    /* Clean up */
    EVP_CIPHER_CTX_free(ctx);

    if(ret > 0)
    {
        /* Success */
        plaintext_len += len;
        return plaintext_len;
    }
    else
    {
        /* Verify failed */
        return -1;
    }
}
26
jww

何が問題なのかはわかりませんが、確かなことは、メッセージを復号化する前にAES_set_decrypt_key()を呼び出す必要があることです。また、暗号化されたメッセージはもはやASCII文字で構成されていないため、%sとして出力しようとしないでください。例:

static const unsigned char key[] = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};

int main()
{
    unsigned char text[]="hello world!";
    unsigned char enc_out[80];
    unsigned char dec_out[80];

    AES_KEY enc_key, dec_key;

    AES_set_encrypt_key(key, 128, &enc_key);
    AES_encrypt(text, enc_out, &enc_key);      

    AES_set_decrypt_key(key,128,&dec_key);
    AES_decrypt(enc_out, dec_out, &dec_key);

    int i;

    printf("original:\t");
    for(i=0;*(text+i)!=0x00;i++)
        printf("%X ",*(text+i));
    printf("\nencrypted:\t");
    for(i=0;*(enc_out+i)!=0x00;i++)
        printf("%X ",*(enc_out+i));
    printf("\ndecrypted:\t");
    for(i=0;*(dec_out+i)!=0x00;i++)
        printf("%X ",*(dec_out+i));
    printf("\n");

    return 0;
} 

U1:鍵は192ビットですよね...

20
user975612

私の提案は実行することです

openssl enc -aes-256-cbc -in plain.txt -out encrypted.bin

デバッガの下で、それが何をしているかを正確に確認してください。 openssl.cは、OpenSSLが持っている唯一の実際のチュートリアル/入門/リファレンスガイドです。他のすべてのドキュメントは、単なるAPIリファレンスです。

U1:私の推測では、動作モード(パディング)のような他の必須オプションを設定していないのでしょう。

U2:これはおそらくこの質問の複製です: AES CTR 256 OpenSSL の操作の暗号化モードとそこにある答えがおそらく役立つでしょう。

8
MK.