暗号化と復号のテクニックを解明しようパート3(Deciphering the Technique of Encryption and Decryption Part III)
本日はヴァーナム暗号を紹介致します。ヴァーナム暗号(Vernam Cipher)、「ワンタイムパッド」とも呼ばれるこの暗号は、正しく使用されると絶対的なセキュリティを提供する対称鍵暗号方式です。この暗号は1917年にGilbert S. Vernamによって開発され、ランダムで秘密の鍵(メッセージと同じ長さであることが重要)を使用してデータを暗号化および復号化する原則に基づいています。
ヴァーナム暗号の動作方法:
鍵の生成:ランダム鍵、またの名を「ワンタイムパッド」とも呼ばれる鍵が生成されます。この鍵はランダムな文字のシーケンス(通常は文字や数字)であり、暗号化されるメッセージと同じ長さである必要があります。
暗号化:メッセージを暗号化するために、平文内の各文字が「ワンタイムパッド」と対応する文字とビットごとのXOR(排他的論理和)演算を使用して結合されます。これにより暗号文が生成されます。XOR演算は逆操作が可能であり、ランダムに見える結果が得られるため使用されます。
復号:暗号文を復号するために、同じ「ワンタイムパッド」が使用され、XORを使用して暗号文の文字と結合されます。この操作は暗号化を元に戻し、元の平文を明らかにします。
ヴァーナム暗号の主な特徴:
完全な秘匿性:真にランダムな鍵が一度だけ使用される(その名前が「ワンタイムパッド」である理由)場合、ヴァーナム暗号は完全な秘匿性を提供します。つまり、暗号文と鍵が与えられても、元の平文を特定する方法はありません。このセキュリティレベルは破られることはありません。
鍵配布:ヴァーナム暗号の主な課題は、安全な鍵の配布です。鍵はメッセージと同じ長さで、ランダムに生成され、秘密に保持される必要があります。鍵が危険にさらされたり、複数回使用されたりすると、この暗号のセキュリティが危険にさらされます。
効率性:ヴァーナム暗号は理論的には安全ですが、真にランダムで長い鍵を生成し、安全に配布する難しさのために、実際のアプリケーションでの使用は稀です。
ヴァーナム暗号は真にランダムなワンタイム鍵と共に使用する場合、理論的には完璧な暗号方式です。ただし、実際の応用では、このような鍵を生成し、安全に配布することが難しいため、現実世界での使用は制限されています。
ヴァーナム暗号の動作方法例 :
1. 平文 : "HELLO"
2. キーの生成: ランダムなキー "MONEY" が生成されます。このキーは平文メッセージと同じ長さである必要があります。
3. バイナリ変換: 平文 "HELLO" とキー "MONEY" をバイナリ値に変換します。
H (平文): 01001000
E (平文): 01000101
L (平文): 01001100
L (平文): 01001100
O (平文): 01001111
M (キー): 01001101
O (キー): 01001111
N (キー): 01001110
E (キー): 01000101
Y (キー): 01011001
4. 暗号化 (XOR 操作): 平文とキーのバイナリ値をXOR操作で結合します。
H XOR M = 00000101 (バイナリ) = 5 (10進数)
E XOR O = 00001010 (バイナリ) = 10 (10進数)
L XOR N = 00000010 (バイナリ) = 2 (10進数)
L XOR E = 00001001 (バイナリ) = 9 (10進数)
O XOR Y = 00010110 (バイナリ) = 22 (10進数)
5. 暗号文: XOR操作の結果がバイナリ形式の暗号文です。
バイナリ形式の暗号文: 00000101 00001010 00000010 00001001 00010110
6. 10進数形式の暗号文:
第1部分: 00000101 = 5
第2部分: 00001010 = 10
第3部分: 00000010 = 2
第4部分: 00001001 = 9
第5部分: 00010110 = 22
10進数形式の暗号文: 5 10 2 9 22
それではヴァーナム暗号を理解したと思います。ヴァーナム暗号を使用して簡単なC++プログラムを作成して見ましょう。
#include <iostream>
#include <string>
#include <bitset>
#include <limits>
using namespace std;
// 関数: Vernam暗号を使用して平文を暗号化
string encryptVernam(const string& plaintext, const string& key)
{
if (plaintext.length() != key.length())
{
throw invalid_argument("平文と鍵は同じ長さである必要があります");
}
string encryptedText = "";
for (size_t i = 0; i < plaintext.length(); i++)
{
// Vernam暗号化: 平文の各文字と鍵の各文字をXOR演算
encryptedText += (char)(plaintext[i] ^ key[i]);
}
return encryptedText;
}
// 関数: Vernam暗号を使用して暗号文を復号化
string decryptVernam(const string& ciphertext, const string& key)
{
if (ciphertext.length() != key.length())
{
throw invalid_argument("暗号文と鍵は同じ長さである必要があります");
}
string decryptedText = "";
for (size_t i = 0; i < ciphertext.length(); i++)
{
// Vernam復号: 暗号文の各文字と鍵の各文字をXOR演算
decryptedText += (char)(ciphertext[i] ^ key[i]);
}
return decryptedText;
}
int main()
{
char eOrd;
string plaintext, key, ciphertext;
char bOrd;
int ciphertextInt;
int ciphertextLength;
string binaryString;
// ユーザーに暗号化または復号化の選択を尋ねる
std::cout << "暗号化する場合は 'e' または 'E' を、復号化する場合は 'd' または 'D' を入力してください:";
std::cin >> eOrd;
if (eOrd == 'e' || eOrd == 'E')
{
try
{
// 平文の入力を促す
std::cout << "平文を入力してください:";
std::cin >> plaintext;
// 鍵の入力を促す(平文と同じ長さで設定することが必要)
std::cout << "キーを入力してください: 平文と同じ長さに設定してください";
std::cin >> key;
// Vernam暗号を使用して平文を暗号化
string encryptedText = encryptVernam(plaintext, key);
std::cout << "暗号化されたテキスト(表示出来ない値になる可能性があります。): " << encryptedText<<endl;
// 暗号化されたテキストの各文字の10進数値を表示
std::cout << "暗号化されたテキストの10進数値: ";
for (char c : encryptedText)
{
int decimalValue = static_cast<int>(c);
std::cout << decimalValue << " ";
}
std::cout << endl;
// 暗号化されたテキストの各文字の2進数値を表示
std::cout << "暗号化されたテキストの2進数値: ";
for (char c : encryptedText)
{
bitset<8> binaryValue(c);
std::cout << binaryValue << " ";
}
std::cout << endl;
}
catch (const invalid_argument& e)
{
// エラーメッセージを表示
std::cerr << "エラー: " << e.what() << endl;
}
}
else if (eOrd == 'd' || eOrd == 'D')
{
try
{
// 暗号文の長さの入力を促す
std::cout << "暗号文の長さを入力してください";
std::cin >> ciphertextLength;
// 暗号文が2進数か10進数かを選択させる
std::cout << "2進数暗号文の場合は 'b'、10進数暗号文の場合は 'd' を入力してください";
std::cin >> bOrd;
if(bOrd=='d' || bOrd=='D')
{
// 10進数暗号文の入力
for(int i=0;i<ciphertextLength;i++)
{
std::cout<<"文字 "<<i<<" を入力してください:";
std::cin>>ciphertextInt;
std::cin.ignore(numeric_limits<streamsize>::max(), '\n');
ciphertext+=static_cast<char>(ciphertextInt);
}
}
else if(bOrd=='b' || bOrd=='B')
{
// 2進数暗号文の入力
for(int i=0;i<ciphertextLength;i++)
{
std::cout<<"文字 "<<i<<" を入力してください:";
std::cin>>binaryString;
std::cin.ignore(numeric_limits<streamsize>::max(), '\n');
std::bitset<8> binaryValue(binaryString);
ciphertextInt = binaryValue.to_ulong();
ciphertext+=static_cast<char>(ciphertextInt);
}
}
// 復号の鍵の入力を促す
std::cout << "キーを入力してください:";
std::cin >> key;
// Vernam暗号を使用して暗号文を復号化
string decryptedText = decryptVernam(ciphertext, key);
std::cout << "復号されたテキスト: " << decryptedText << endl;
}
catch (const invalid_argument& e)
{
// エラーメッセージを表示
std::cerr << "エラー: " << e.what() << endl;
}
}
else
{
// 無効な入力の場合
std::cout << "無効な入力です";
}
return 0;
}
説明 :
ユーザーには、暗号化('e'または'E')と復号化('d'または'D')の選択を求めるプロンプトが表示されます。
ユーザーが暗号化を選択した場合、平文と鍵を入力するように促されます。その後、プログラムはVernam暗号を使用して平文を暗号化し、暗号化されたテキストを10進数および2進数の値で表示します。
ユーザーが復号化を選択した場合、暗号文の長さを入力し、2進数('b'または'B')または10進数('d'または'D')の暗号文入力の選択を求められます。その後、暗号文と鍵を提供するように促されます。プログラムはVernam暗号を使用して暗号文を復号化し、復号されたテキストを表示します。
ユーザーが無効な選択をした場合、エラーメッセージを受け取ります。
プログラムを実行して暗号化と復号化のテストを行った後、他のヴァーナム暗号アルゴリズムと比較してみてください。ヴァーナム暗号も面白かったでしょうか。次は非対称RSA暗号を紹介致しますので、ぜひお読みいただければ嬉しいです。
エンジニアファーストの会社 株式会社CRE-CO
su_myat_phyu
参考
1. RaspberryPiFoundation: Introduction to Encryption and Cryptography
https://www.edx.org/learn/computer-programming/raspberry-pi-foundation-introduction-to-encryption-and-cryptography
2. https://isaaccomputerscience.org/concepts/data_encrypt_vernam?examBoard=all&stage=all