プログラムの1つを作成する過程で、テキストの暗号化および暗号化解除のライブラリを処理する必要がありました。 私はそれを理解し、今、私の経験と知識をコミュニティと共有したいと思っています。
この記事では、libgcryptライブラリーに焦点を当てます。
まえがき
Linuxでプログラムを書いています。 そのため、このOSのライブラリも検索しました。 私は何十ものライブラリーを見つけようとはしませんでした。 私は自分のニーズに合ったものを選びました-かなり有名な製品で使用されるもの-2つ。
libgcryptライブラリ自体について
このライブラリは、低レベル暗号化メカニズムへの高レベルインターフェイスを提供します。 簡単に言えば、必要な暗号化メカニズムを選択し、パスワードを考え出し、選択したアルゴリズムの仕組みを詳しく調べることなくテキストをエンコードします。
ライブラリはGnuPGプロジェクトの一部として作成され、LGPLライセンスの下で配布されています。
暗号化プロセス
暗号化プロセスでは、次の機能を使用します(使用順に示されています)。
- gcry_cipher_open-コンテキストハンドルを作成する
- gcry_cipher_setkey-パスワードを設定します
- gcry_cipher_setiv-初期化ベクトルを設定する
- gcry_cipher_encrypt-テキスト暗号化関数
- gcry_cipher_close-コンテキスト記述子を閉じる
各機能について詳しく説明します。
gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *hd, int algo, int mode, unsigned int flags)
この関数は、さらなる暗号化関数に必要なコンテキスト記述子を作成し、「hd」で記述子を返します。 エラーの場合、ゼロ以外のエラーコードが返されます。
hdは、将来のコンテキスト記述子へのポインタです。
algoは、テキストの暗号化に使用するアルゴリズムです。 例:
GCRY_CIPHER_IDEA-IDEAアルゴリズム。 選択できますが、機能しません。 このアルゴリズムは特許を取得しているため、無料のライブラリには実装されていません。
GCRY_CIPHER_3DES-(EDEとして3つのキーを持つトリプルDES)対称ブロック暗号。
GCRY_CIPHER_BLOWFISH-Blowfishアルゴリズム。 現在の実装では、128ビットキーのみが許可されています。
RIJNDAEL、TWOFISH、AES、SERPENTなどもあります。
(アルゴリズムの全リストはここにあります )
モード -次のオプションのいずれか:
- GCRY_CIPHER_MODE_NONE-修飾子を使用しません。 (このキーの使用は避けてください。)
- GCRY_CIPHER_MODE_ECB-電子コードブックモード。 ( 電子コードブック )
- GCRY_CIPHER_MODE_CFB-暗号文フィードバックモード。 ( 暗号フィードバック )
- GCRY_CIPHER_MODE_CBC-暗号文のブロックを結合するモード。 ( 暗号ブロック連鎖 )
- GCRY_CIPHER_MODE_STREAM-ストリームアルゴリズムでのみ使用するモード。 たとえば、GCRY_CIPHER_MODE_STREAM。
- GCRY_CIPHER_MODE_OFB-出力フィードバックモード。 ( 出力フィードバック )
- GCRY_CIPHER_MODE_CTR-カウンターモード。 ( カウンター )
flags -0(ゼロ)、または以下のフラグの組み合わせを指定できます:
- GCRY_CIPHER_SECURE-すべての操作は保護されたメモリにあります。
- GCRY_CIPHER_ENABLE_SYNC-このフラグは、CFB同期モードを有効にします。
- GCRY_CIPHER_CBC_CTS-CBCモードのCTS(暗号化テキストのスチール)を有効にします。 (flanはGCRY_CIPHER_CBC_MACと同時に使用できません。)CTSモードでは、任意のサイズのデータ変更を行うことができます。
- GCRY_CIPHER_CBC_MAC-CBC MACキー付きチェックサムを計算します。 (GCRY_CIPHER_CBC_CTSと同時にフランを使用することはできません。)
例
gcryError = gcry_cipher_open( &gcryCipherHd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS);
記述子の操作を続行するには、まずgcry_cipher_setkey関数を使用してキーを設定する必要があります。
gcry_error_t gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *k, size_t l)
hd-以前に受信した記述子
kはキーであり、パスワード(文字列)でもあります
l-キーの長さ(strlen(k))
ほとんどの暗号化モードには初期化ベクトルが必要です。初期化ベクトルは通常、ソルトとして機能する非秘密のランダム文字列です。 CTRモードの場合、「salt」の値にも類似したカウンターを指定する必要があります。 これらの値を設定するには、関数を使用します。
- gcry_cipher_setiv (gcry_cipher_hd_t h、const void * k、size_t l)
暗号化または暗号化解除に使用される初期化ベクトルを設定します。 ベクトルは、長さlバイトのバッファーkとして送信され、内部データ構造にコピーされます。 この関数は、ベクトルが特定のアルゴリズム(アルゴリズム)およびモード(モード)に必要な要件を満たしているかどうかもチェックします。 - gcry_cipher_setctr (gcry_cipher_hd_t h、const void * c、size_t l)
暗号化または暗号化解除に使用されるウィンドカウンターを設定します。 カウンターは、長さlバイトのバッファーkとして送信され、内部データ構造にコピーされます。 この関数は、ベクトルが特定のアルゴリズムに必要な要件を満たしているかどうかも確認します(つまり、ベクトルはブロックサイズと同じサイズでなければなりません)。
それで、私たちはキーポイントに来ます-暗号化。 暗号化プロセス自体は、gcry_cipher_encrypt関数によって実行されます。
gcry_error_t gcry_cipher_encrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
この関数は、1つまたは2つのバッファーで機能します。 in値がNULLとして渡され、inlenが0(ゼロ)の場合、1つのバッファーを使用した暗号化が実行されます。 簡単に言えば、関数を呼び出す前に暗号化されていないテキストが含まれているoutバッファーは、関数を終了するときに新しい暗号化されたテキストで書き換えられます。 in値がNULL以外として渡される場合、inlenバイトは暗号化され、outバッファーに配置されます(少なくともinlenのサイズでなければなりません)。 outsizeは、出力用に十分なスペースがあるかどうかを関数が確認できるように、outバッファーに割り当てられたメモリチャンクのサイズを表示する必要があります。 (バッファーの重複は許可されていません。)
選択したアルゴリズムと暗号化モードに応じて、バッファーの長さはブロックサイズの倍数にする必要があります。
暗号化が成功した場合、戻りコードは0(ゼロ)です。 それ以外の場合、エラーコードが返されます。
メモリと記述子を解放するには、gcry_cipher_close関数を使用します。
void gcry_cipher_close (gcry_cipher_hd_t h)
この関数は、gcry_cipher_openによって実行時に作成されたコンテキストを解放します。 また、この関数は、h記述子内で作成された脆弱な情報をすべてゼロにします。
暗号化解除プロセス
暗号化解除プロセスは、呼び出す必要がある関数の暗号化プロセスに似ています。 すなわち(使用順に示されています):
- gcry_cipher_open-コンテキストハンドルを作成する
- gcry_cipher_setkey-パスワードを設定します
- gcry_cipher_setiv-初期化ベクトルを設定する
- gcry_cipher_decrypt-テキスト暗号化解除関数
- gcry_cipher_close-コンテキスト記述子を閉じる
すべての関数(gcry_cipher_decryptを除く)は類似しているため、暗号化解除関数自体のみを考慮します。
gcry_error_t gcry_cipher_decrypt (gcry_cipher_hd_t h, unsigned char *out, size_t outsize, const unsigned char *in, size_t inlen)
h-コンテキストハンドル
out-結果の(復号化された)テキストが配置されるバッファー
outsize-出力バッファに割り当てられたメモリのサイズ
in-暗号文
inlen-暗号文のサイズ
暗号化機能と同様に、暗号化解除機能は単一のバッファーで実行できます。 これを行うには、inおよびinlenの代わりにゼロを渡します。 これらのパラメーターがゼロでない場合、inlenバイトは復号化され、少なくともinlenに等しいはずのoutバッファーに入れられます。 関数が結果に十分なスペースがあることを確認できるように、outsizeはoutバッファーに割り当てられたバイト数に設定する必要があります。 (バッファーとの重複は許可されていません。)
選択したアルゴリズムと暗号化モードに応じて、バッファーの長さはブロックサイズの倍数にする必要があります。
成功した場合、関数は0を返します。それ以外の場合、エラーコードが返されます。
良い例
#include <stdio.h> #include <gcrypt.h> #define ENCR 1 #define DECR 0 void myCrypt(int encdec, const char * pass, const char * salt, const char * text) { gcry_error_t gcryError; gcry_cipher_hd_t hd; size_t i; size_t passLength = strlen(pass); size_t saltLength = strlen(salt); size_t textLength = strlen(text)+encdec; char * outBuffer = (char*)malloc(textLength); printf("%scryption...\n", encdec?"En":"De"); printf("passLength = %d\n", passLength); printf("saltLength = %d\n", saltLength); printf("textLength = %d\n", textLength); printf(" pass = %s\n", pass); printf(" salt = %s\n", salt); printf(" text = %s\n", encdec?text:"<null>"); // - GCRY_CIPHER_AES128 // // GCRY_CIPHER_CBC_CTS, gcryError = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CBC, GCRY_CIPHER_CBC_CTS); if (gcryError) { printf("gcry_cipher_open failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; } gcryError = gcry_cipher_setkey(hd, pass, passLength); if (gcryError) { printf("gcry_cipher_setkey failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; } gcryError = gcry_cipher_setiv(hd, salt, saltLength); if (gcryError) { printf("gcry_cipher_setiv failed: %s/%s\n", gcry_strsource(gcryError),gcry_strerror(gcryError)); return; } switch (encdec) { case ENCR: gcryError = gcry_cipher_encrypt(hd, outBuffer, textLength, text, textLength); break; case DECR: gcryError = gcry_cipher_decrypt(hd, outBuffer, textLength, text, textLength); } if (gcryError) { printf("gcry_cipher_encrypt failed: %s/%s\n", gcry_strsource(gcryError), gcry_strerror(gcryError)); return; } switch (encdec) { case ENCR: printf("Ecnrypted text = "); for (i = 0; i<textLength; i++) printf("%02X", (unsigned char)outBuffer[i]); printf("\n"); break; case DECR: printf("Original text = %s\n", outBuffer); } gcry_cipher_close(hd); free(outBuffer); } int main(int argc, char **argv) { if ( argc != 4 ) { printf("usage: %s <-e|-d> \"<password>\" \"<salt>\"\n", argv[0]); return 1; } int encdec = ENCR; char line[1024]; printf("Enter text: "); fgets(line, sizeof(line), stdin); if ( !strcmp(argv[1], "-d") ) { // 16 int i = 0; char a[3] = {"00"}; for (; i<strlen(line); i+=2) { sprintf(a, "%c%c", line[i], line[i+1]); line[i/2] = strtol(a, NULL, 16); } line[i/2-1] = '\0'; encdec = DECR; } myCrypt(encdec, argv[2], argv[3], line); return 0; }
Linux用のコンパイル
gcc -o crypto main.c -lgcrypt
打ち上げ
[serge@magnum enc]$ ./crypto -e "This's my passwd" "It is kinda salt"
Enter text:
Encryption...
passLength = 16
saltLength = 16
textLength = 65
pass = This's my passwd
salt = It is kinda salt
text =
Ecnrypted text = 7DA4C2CB7088BC7432E243B1B1ACAE2A4301CE92D5884404B5AFF181EC4C1B17D3B0565FD82BD88D78916506048BA20E87FA5DDE39288FCC32CA3EF02647F7B140
[serge@magnum enc]$ ./crypto -d "This's my passwd" "It is kinda salt"
Enter text: 7DA4C2CB7088BC7432E243B1B1ACAE2A4301CE92D5884404B5AFF181EC4C1B17D3B0565FD82BD88D78916506048BA20E87FA5DDE39288FCC32CA3EF02647F7B140
Decryption...
passLength = 16
saltLength = 16
textLength = 65
pass = This's my passwd
salt = It is kinda salt
text =
Original text =
ライブラリおよびインターフェイス関数の詳細な説明は、 http : //www.gnupg.org/documentation/manuals/gcryptにあります。