暗号化とは、データを使用できない形式に変換し、復号化によってのみ使用可能にする方法です。その目的は、データを復号化できる人だけが利用できるようにすることです (つまり、使用可能にする)。通常、データは、不正アクセスが発生した場合に取得できないように暗号化する必要があります。これは、攻撃者が認証システムとアクセス制御を突破した後の最後の防御線です。
これは、すべてのデータを暗号化する必要があるという意味ではありません。多くの場合、認証およびアクセス システムで十分であり、さらに、データの暗号化と復号化にはパフォーマンスの低下が伴うからです。データをいつ暗号化するかは、アプリケーションの計画とリスク評価の問題であり、HIPAA や GDPR などの規制要件である場合もあります。
データは、ディスク上などの保存時に暗号化することも、インターネット経由で通信する 2 者間などの転送中に暗号化することもできます。
ここでは、対称暗号化とも呼ばれるパスワードを使用してデータを暗号化および復号化する方法を学びます。このパスワードは、情報を交換する双方が知っている必要があります。
暗号化を適切かつ安全に使用するには、説明する必要がある概念がいくつかあります。
暗号とは、暗号化に使用されるアルゴリズムです。たとえば、AES256 は暗号です。暗号化といえば、ほとんどの人が暗号を思い浮かべるでしょう。
ダイジェストは基本的に、暗号で使用される前にパスワード (つまり、暗号化キー) をスクランブルして長くするために使用されるハッシュ関数です。なぜこれを行うのでしょうか。1 つは、暗号化に適した、よくランダム化された均一な長さのキーのハッシュを作成するためです。また、次に説明する「ソルティング」にも非常に適しています。
「ソルト」は、いわゆる「レインボー」テーブルを破る方法です。攻撃者は、元のハッシュ値が同じであれば、2 つのハッシュ値もまったく同じに見えることを知っています。しかし、ハッシュにソルト値を追加すると、同じではなくなります。ソルトはキーと混ぜて異なるものを生成するため、「ソルト」と呼ばれます。
ここで、レインボー テーブルは、パスワードを推測するために、既知のハッシュ値と事前に計算されたデータを照合しようとします。
通常、ソルトは各キーに対してランダムに生成され、キーとともに保存されます。既知のハッシュと一致させるために、攻撃者は多数のランダムな値のレインボー テーブルを事前に計算する必要がありますが、これは通常実現不可能です。
暗号化では「反復」という言葉をよく耳にするでしょう。反復とは、キーとソルトを混合してキーの推測を困難にする単一のサイクルのことです。攻撃者がキーを逆推測するのを計算的に困難にするために、これを何度も実行します。これが「反復」(複数形) です。通常、反復の最小必要回数は 1000 回ですが、これと異なる場合があります。非常に強力なパスワードから始める場合は、通常、必要な回数は少なくなります。
IV (または「初期化ベクトル」) は、通常、各メッセージの暗号化に使用されるランダムな値です。ソルトは、パスワードに基づいてキーを生成するために使用されます。IV は、すでにキーがあり、メッセージを暗号化しているときに使用されます。IV の目的は、同じメッセージを暗号化したときに異なるように見せることです。IV には連続したコンポーネントが含まれる場合もあり、ランダムな文字列と常に増加するシーケンスで構成されます。
これにより、「リプレイ」攻撃が困難になります。この攻撃では、攻撃者はメッセージを解読する必要がありません。むしろ、暗号化されたメッセージが「スニッフィング」され (つまり、送信者と受信者の間で傍受され)、その後、すでに実行されたアクションを繰り返すことを期待してリプレイされます。ただし、実際には、ほとんどの高レベル プロトコルにはすでにシーケンスが配置されており、各メッセージには、その一部として増加するパケット番号があるため、ほとんどの場合、IV は必要ありません。
この例ではGliimlyフレームワークを使用します。まずこれをインストールします。
ここでの例を実行するには、独自のディレクトリにアプリケーション「enc」を作成します (Gliimly のプログラム マネージャーの詳細については、 mgrg を参照してください)。
mkdir enc_example cd enc_example gg -k enc
データを暗号化するには、 encrypt-dataステートメントを使用します。最も単純な形式は、ヌルで終了する文字列を暗号化することです。ファイル「encrypt.gliim」を作成し、これをコピーします。
begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str p-out dec_str @ end-handler
encrypt-data とdecrypt-dataの基本的な使用法を確認できます。データ (オリジナルまたは暗号化済み) とパスワードを入力すると、実行できます。データは暗号化され、その後復号化されて、オリジナルが戻されます。
ソース コードでは、文字列変数「enc_str」(「char *」として作成されます) に「これには秘密のコードが含まれています。それは Open Sesame!」の暗号化されたバージョンが含まれ、「dec_str」は完全に同じであるはずの復号化されたデータになります。
このコードをコマンドラインから実行するには、まずアプリケーションを作成します。
gg -q
次に、Gliimly に実行するための bash コードを生成させます。リクエスト パスは "/encrypt" で、この場合はソース ファイル "encrypt.gliim" で定義されている関数 "void encrypt()" によって処理されます。Gliimly では、これらの名前は常に一致するため、コードの記述、読み取り、実行が簡単になります。ggで"-r" オプションを使用してリクエスト パスを指定し、プログラムの実行に必要なコードを取得します。
gg -r --req="/encrypt" --silent-header --exec
次のような応答が返されます。
72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9 This contains a secret code, which is Open Sesame!
ここにあるのは暗号化されたデータです。この暗号化されたデータは、同じパスワードを使用して復号化されます。当然ながら、結果は最初に暗号化した文字列と一致します。
デフォルトでは、encrypt-data は人間が判読できる 16 進数形式で暗号化された値を生成します。つまり、16 進数文字の「0」から「9」および「a」から「f」で構成されます。この方法では、暗号化されたデータを通常の文字列に保存できます。たとえば、JSON ドキュメント、データベースの VARCHAR 列、または他のほぼすべての場所に保存できます。ただし、バイナリ暗号化データを生成することもできます。これについては後で詳しく説明します。
前の例では、結果として得られる暗号化データは、人間が判読できる 16 進形式です。人間が判読できる文字列ではなく、より短いバイナリ暗号化データを作成することもできます。そのためには、「binary」句を使用します。「encrypt.gliim」のコードを次のコードに置き換えます。
begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" binary // Save the encrypted data to a file write-file "encrypted_data" from enc_str get-app directory to app_dir @Encrypted data written to file <<p-out app_dir>>/encrypted_data // Decrypt data decrypt-data enc_str password "my_password" binary to dec_str p-out dec_str @ end-handler
バイナリ暗号化データを取得する場合は、その長さもバイト単位で取得する必要があります。そうしないと、ヌル バイトが含まれる可能性があるため、データの終了位置がわかりません。そのためには、「output-length」句を使用します。このコードでは、変数「enc_str」の暗号化データがファイル「encrypted_data」に書き込まれ、書き込まれる長さは「outlen」バイトです。
パスなしでファイルが書き込まれる場合、そのファイルは常にアプリケーションのホーム ディレクトリ (ディレクトリを参照) に書き込まれるため、そのディレクトリを取得するにはget-app を使用します。
データを復号化するときは、「input-length」句の使用に注意してください。これは、暗号化されたデータのバイト数を示します。明らかに、暗号化データの長さを格納する「outlen」変数から取得できます。暗号化と復号化が分離されている場合、つまり別のプログラムで実行されている場合は、この長さが使用可能であることを確認します。
また、データが「バイナリ」として暗号化されている場合(バイナリ出力を生成することを意味します)、復号化でも同じものを使用する必要があることに注意してください。
アプリケーションを作成します。
gg -q
前と同じように実行します。
gg -r --req="/encrypt" --silent-header --exec
結果は次のとおりです。
Encrypted data written to file /var/lib/gg/enc/app/encrypted_data This contains a secret code, which is Open Sesame!
復号化されたデータは元のデータとまったく同じです。
「octal dump」(「od」) Linux ユーティリティを使用すると、ファイルに書き込まれた実際の暗号化データを確認できます。
od -c /var/lib/gg/enc/app/encrypted_data
結果は次のようになります:
$ od -c /var/lib/gg/enc/app/encrypted_data 0000000 r 335 324 L 020 351 i ; 346 254 w 312 253 306 N 005 0000020 370 \t ) \n 020 235 367 317 305 t \0 224 214 270 210 315 0000040 # 307 351 216 025 274 362 033 % 253 023 7 335 306 320 0000060 224 # ! 021 252 242 325 H 300 217 # \vm V 351 0000100
これで完了です。データがバイナリであり、実際には null バイトが含まれていることがわかります。
これらの例で暗号化するデータは文字列、つまり null で区切られたものです。バイナリ データは、データ全体を指定するか (Gliimly はバイト数を追跡するため)、"input-length" 句で長さを指定することで簡単に暗号化できます。たとえば、これを "encrypt.gliim" にコピーします。
begin-handler /encrypt public set-string str = "This c\000ontains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" input-length 12 p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str // Output binary data; present null byte as octal \000 string-length dec_str to res_len start-loop repeat res_len use i start-with 0 if-true dec_str[i] equal 0 p-out "\\000" else-if pf-out "%c", dec_str[i] end-if end-loop @ end-handler
これにより、ヌル バイトに関係なく、メモリ位置 "enc_str" の 12 バイトが暗号化されます。この場合、"This c" の後にヌル バイトが続き、その後に "ontain" 文字列が続きますが、JPG ファイルの内容など、任意の種類のバイナリ データにすることができます。
復号化側では、「出力長さ」句で復号化されたバイト数を取得します。最後に、復号化されたデータは元のデータとまったく同じであることが示され、ヌル バイトは一般的な 8 進表現で表示されます。
アプリケーションを作成します。
gg -q
前と同じように実行します。
gg -r --req="/encrypt" --silent-header --exec
結果は次のとおりです。
6bea45c2f901c0913c87fccb9b347d0a This c\000ontai
この場合もデータが短いため暗号化された値は短くなり、結果は元の値と完全に一致します。
デフォルトで使用される暗号化は、標準OpenSSLライブラリのAES256およびSHA256ハッシュです。どちらも暗号化で広く使用されています。ただし、OpenSSL でサポートされている任意の暗号とダイジェスト (つまりハッシュ) を使用できます (カスタムのものも含む)。
使用可能なアルゴリズムを確認するには、コマンド ラインで次の操作を実行します。
#get list of cipher providers openssl list -cipher-algorithms #get list of digest providers openssl list -digest-algorithms
これら 2 つは、暗号とダイジェスト (ハッシュ) アルゴリズムのリストを提供します。これらのアルゴリズムの一部は、Gliimly が選択したデフォルトのアルゴリズムよりも弱い可能性があり、他のアルゴリズムは古いシステムとの下位互換性のためだけに用意されている可能性があります。また、他のアルゴリズムは非常に新しく、必要な程度まで検証する時間が十分になかった可能性があります。
したがって、これらのアルゴリズムを選択するときは注意し、デフォルトのアルゴリズムを変更する理由を必ず理解してください。ここでは、Camellia-256 (つまり、「CAMELLIA-256-CFB1」) 暗号化を「SHA3-512」ダイジェストで使用する例を示します。「encrypt.gliim」のコードを次のように置き換えます。
begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt data encrypt-data str to enc_str password "my_password" \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out enc_str @ // Decrypt data decrypt-data enc_str password "my_password" to dec_str \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out dec_str @ end-handler
アプリケーションを作成します。
gg -q
実行します:
gg -r --req="/encrypt" --silent-header --exec
この場合、結果は次のようになります。
f4d64d920756f7220516567727cef2c47443973de03449915d50a1d2e5e8558e7e06914532a0b0bf13842f67f0a268c98da6 This contains a secret code, which is Open Sesame!
再び、元のデータを取得します。encrypt-data と decrypt-data の両方で同じ暗号とダイジェストを使用する必要があることに注意してください。
もちろん、「binary」句と「output-length」句を使用して、以前と同じようにバイナリ暗号化された値を生成することもできます。
データを暗号化する外部システムがあり、そのシステムが使用する暗号とダイジェストがわかっている場合は、それらを一致させてコードを相互運用可能にすることができます。Gliimly は標準の OpenSSL ライブラリを使用しているため、他のソフトウェアでも同様に使用できる可能性があります。
暗号化にソルトを追加するには、「salt」句を使用します。random -stringステートメント (または必要に応じてrandom-crypto ) を使用して、ランダムなソルトを生成できます。以下は「encrypt.gliim」のコードです。
begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Get salt random-string to rs length 16 // Encrypt data encrypt-data str to enc_str password "my_password" salt rs @Salt used is <<p-out rs>>, and the encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs to dec_str p-out dec_str @ end-handler
アプリケーションを作成します。
gg -q
数回実行します。
gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec
結果:
Salt used is VA9agPKxL9hf3bMd, and the encrypted string is 3272aa49c9b10cb2edf5d8a5e23803a5aa153c1b124296d318e3b3ad22bc911d1c0889d195d800c2bd92153ef7688e8d1cd368dbca3c5250d456f05c81ce0fdd This contains a secret code, which is Open Sesame! Salt used is FeWcGkBO5hQ1uo1A, and the encrypted string is 48b97314c1bc88952c798dfde7a416180dda6b00361217ea25278791c43b34f9c2e31cab6d9f4f28eea59baa70aadb4e8f1ed0709db81dff19f24cb7677c7371 This contains a secret code, which is Open Sesame! Salt used is nCQClR0NMjdetTEf, and the encrypted string is f19cdd9c1ddec487157ac727b2c8d0cdeb728a4ecaf838ca8585e279447bcdce83f7f95fa53b054775be1bb2de3b95f2e66a8b26b216ea18aa8b47f3d177e917 This contains a secret code, which is Open Sesame!
ご覧のとおり、暗号化ごとにランダムなソルト値 (この場合は 16 バイト長) が生成され、暗号化されるデータは同じであっても、暗号化された値は毎回異なります。これにより、このような暗号化を解読することが困難になります。
もちろん、復号化するには、ソルトを記録し、暗号化時とまったく同じように使用する必要があります。このコードでは、変数「rs」がソルトを保持しています。暗号化された値をデータベースに保存する場合は、ソルトをそのすぐ隣に保存することになります。
実際には、各メッセージに異なるソルト値を使用することはありません。毎回新しいキーが作成され、パフォーマンスが低下する可能性があります。また、実際にその必要はありません。ソルトの使用は、各キー (同じキーであっても) を推測しにくくするためです。一度これを実行してしまえば、再度実行する必要がなくなるか、頻繁に実行する必要がなくなるかもしれません。
代わりに、各メッセージに IV (初期化ベクトル) を使用します。これは通常、同じメッセージが異なるように見えるランダムな文字列であり、パスワードを解読するための計算コストが増加します。以下は、「encrypt.gliim」の新しいコードです。
begin-handler /encrypt public // Get salt random-string to rs length 16 // Encrypt data start-loop repeat 10 use i start-with 0 random-string to iv length 16 encrypt-data "The same message" to enc_str password "my_password" salt rs iterations 2000 init-vector iv cache @The encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs iterations 2000 init-vector iv to dec_str cache p-out dec_str @ end-loop end-handler
アプリケーションを作成します。
gg -q
数回実行します。
gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec
結果は次のようになります:
The encrypted string is 787909d332fd84ba939c594e24c421b00ba46d9c9a776c47d3d0a9ca6fccb1a2 The same message The encrypted string is 7fae887e3ae469b666cff79a68270ea3d11b771dc58a299971d5b49a1f7db1be The same message The encrypted string is 59f95c3e4457d89f611c4f8bd53dd5fa9f8c3bbe748ed7d5aeb939ad633199d7 The same message The encrypted string is 00f218d0bbe7b618a0c2970da0b09e043a47798004502b76bc4a3f6afc626056 The same message The encrypted string is 6819349496b9f573743f5ef65e27ac26f0d64574d39227cc4e85e517f108a5dd The same message The encrypted string is a2833338cf636602881377a024c974906caa16d1f7c47c78d9efdff128918d58 The same message The encrypted string is 04c914cd9338fcba9acb550a79188bebbbb134c34441dfd540473dd8a1e6be40 The same message The encrypted string is 05f0d51561d59edf05befd9fad243e0737e4a98af357a9764cba84bcc55cf4d5 The same message The encrypted string is ae594c4d6e72c05c186383e63c89d93880c8a8a085bf9367bdfd772e3c163458 The same message The encrypted string is 2b28cdf5a67a5a036139fd410112735aa96bc341a170dafb56818dc78efe2e00 The same message
同じメッセージでも、暗号化すると違って見えますが、復号化するとまた同じであることがわかります。もちろん、パスワード、ソルト、反復回数、および初期ベクトルは、暗号化と復号化の両方で同じである必要があります。
encrypt-data と decrypt-data での「cache」句の使用に注意してください。これは、計算されたキー (指定されたパスワード、ソルト、暗号/ダイジェスト アルゴリズム、および反復回数) を効果的にキャッシュするため、ループのたびにキーが計算されることはありません。「cache」を使用すると、キーは 1 回計算され、その後、各メッセージに対して異なる IV (「init-vector」句内) が使用されます。
キーを時々再構築したい場合は、ブール値を提供する「clear-cache」句を使用します。true の場合、キーは再計算され、そうでない場合はそのまま残されます。詳細については、 encrypt-data を参照してください。
Gliimly でさまざまな暗号、ダイジェスト、ソルト、IV 値を使用してデータを暗号化および復号化する方法を学びました。また、人間が判読できる暗号化された値とバイナリ出力を作成したり、文字列とバイナリ値 (ドキュメントなど) の両方を暗号化したりすることもできます。