12,925
回編集
840行目: | 840行目: | ||
==== サンプルコード : CFBモード ==== | ==== サンプルコード : CFBモード ==== | ||
CFBモードは、ブロック暗号をストリーム暗号のように使用できるモードであり、データを1[byte]ずつ暗号化できる特徴がある。<br> | |||
<br> | |||
CFBモードの特徴を以下に示す。<br> | |||
* ストリーム暗号のように動作するため、データを1バイトずつ暗号化できる。 | |||
* 暗号文の一部が破損しても、その影響は限定的である。 (エラーの伝播が少ない) | |||
* 並列化が難しいため、大量のデータを高速に処理する必要がある場合は他のモードを検討する必要がある。 | |||
<br> | |||
以下の例では、CFBモードを使用して、暗号化および復号を行っている。<br> | 以下の例では、CFBモードを使用して、暗号化および復号を行っている。<br> | ||
<br> | |||
ECBモードやCBCモードとは異なり、データ長をAESのブロックサイズの倍数にする必要は無い。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
// SecureAESCrypto.hファイル | |||
#include <QCoreApplication> | |||
#include <QString> | |||
#include <QByteArray> | |||
#include <QSettings> | |||
#include <QFile> | |||
#include <QDebug> | |||
// | #include <cryptopp/aes.h> | ||
#include <cryptopp/modes.h> | |||
#include <cryptopp/filters.h> | |||
#include <cryptopp/osrng.h> | |||
#include <cryptopp/hex.h> | |||
class SecureAESCrypto { | |||
public: | |||
// 安全な鍵の生成 | |||
static QByteArray generateKey() | |||
{ | |||
QByteArray key; | |||
key.resize(CryptoPP::AES::DEFAULT_KEYLENGTH); | |||
CryptoPP::AutoSeededRandomPool prng; | |||
prng.GenerateBlock(reinterpret_cast<byte*>(key.data()), key.size()); | |||
return key; | |||
} | |||
// | // 安全な初期化ベクトル (IV) の生成 | ||
CFB_Mode<AES>::Decryption | static QByteArray generateIV() | ||
{ | |||
QByteArray iv; | |||
iv.resize(CryptoPP::AES::BLOCKSIZE); | |||
CryptoPP::AutoSeededRandomPool prng; | |||
prng.GenerateBlock(reinterpret_cast<byte*>(iv.data()), iv.size()); | |||
return iv; | |||
} | |||
// 暗号化 | |||
static bool encryptFile(const QString &inputFilename, const QString &outputFilename, const QByteArray &key, QByteArray &iv) | |||
{ | |||
try { | |||
CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption encryptor; | |||
encryptor.SetKeyWithIV(reinterpret_cast<const byte*>(key.constData()), key.size(), reinterpret_cast<const byte*>(iv.constData())); | |||
CryptoPP::FileSource(inputFilename.toStdString().c_str(), true, | |||
new CryptoPP::StreamTransformationFilter(encryptor, new CryptoPP::FileSink(outputFilename.toStdString().c_str()))); | |||
return true; | |||
} | |||
catch (const CryptoPP::Exception& e) { | |||
qCritical() << "File encryption error:" << e.what(); | |||
return false; | |||
} | |||
} | |||
// 復号 | |||
static bool decryptFile(const QString &inputFilename, const QString &outputFilename, const QByteArray &key, const QByteArray &iv) | |||
{ | |||
try { | |||
CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption decryptor; | |||
decryptor.SetKeyWithIV(reinterpret_cast<const byte*>(key.constData()), key.size(), reinterpret_cast<const byte*>(iv.constData())); | |||
CryptoPP::FileSource(inputFilename.toStdString().c_str(), true, | |||
new CryptoPP::StreamTransformationFilter(decryptor, new CryptoPP::FileSink(outputFilename.toStdString().c_str()))); | |||
return true; | |||
} | |||
catch (const CryptoPP::Exception& e) { | |||
qCritical() << "File decryption error:" << e.what(); | |||
return false; | |||
} | |||
} | |||
// 初期化ベクトル (IV) の保存 | |||
static bool saveIV(const QString &filename, const QByteArray &iv) | |||
{ | |||
QFile file(filename); | |||
if (!file.open(QIODevice::WriteOnly)) { | |||
qCritical() << "Unable to open file for writing IV:" << filename; | |||
return false; | |||
} | |||
QDataStream out(&file); | |||
out << iv; | |||
file.close(); | |||
return true; | |||
} | |||
// 初期化ベクトル (IV) の読み込み | |||
static bool loadIV(const QString &filename, QByteArray &iv) | |||
{ | |||
QFile file(filename); | |||
if (!file.open(QIODevice::ReadOnly)) { | |||
qCritical() << "Unable to open file for reading IV:" << filename; | |||
return false; | |||
} | |||
QDataStream in(&file); | |||
in >> iv; | |||
file.close(); | |||
return true; | |||
} | |||
}; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
// main.cppファイル | |||
#include "SecureAESCrypto.h" | |||
QByteArray getOrCreateKey() | |||
{ | |||
QSettings settings("MyCompany", "SecureAESApp"); | |||
if (settings.contains("encryption/key")) { | |||
return QByteArray::fromHex(settings.value("encryption/key").toString().toLatin1()); | |||
} | |||
else { | |||
QByteArray key = SecureAESCrypto::generateKey(); | |||
settings.setValue("encryption/key", QString(key.toHex())); | |||
return key; | |||
} | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
// 鍵の取得または生成 | |||
QByteArray key = getOrCreateKey(); | |||
qDebug() << "Encryption key:" << key.toHex(); | |||
// 入力ファイルと出力ファイルの設定 | |||
QString inputFilename = "input.txt"; | |||
QString encryptedFilename = "encrypted.bin"; | |||
QString decryptedFilename = "decrypted.txt"; | |||
QString ivFilename = "iv.bin"; | |||
// 暗号化 | |||
QByteArray iv = SecureAESCrypto::generateIV(); | |||
if (SecureAESCrypto::encryptFile(inputFilename, encryptedFilename, key, iv)) { | |||
qDebug() << "File encrypted successfully."; | |||
// 初期化ベクトル (IV) の保存 | |||
if (SecureAESCrypto::saveIV(ivFilename, iv)) { | |||
qDebug() << "IV saved successfully."; | |||
// 初期化ベクトル (IV) の読み込み | |||
QByteArray loadedIV; | |||
if (SecureAESCrypto::loadIV(ivFilename, loadedIV)) { | |||
qDebug() << "IV loaded successfully."; | |||
// 復号 | |||
if (SecureAESCrypto::decryptFile(encryptedFilename, decryptedFilename, key, loadedIV)) { | |||
qDebug() << "File decrypted successfully."; | |||
} | |||
else { | |||
qDebug() << "File decryption failed."; | |||
} | |||
} | |||
else { | |||
qDebug() << "Failed to load IV."; | |||
} | |||
} | |||
else { | |||
qDebug() << "Failed to save IV."; | |||
} | |||
} | |||
else { | |||
qDebug() << "File encryption failed."; | |||
} | |||
return a.exec(); | |||
} | |||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | |||
<u>※注意</u><br> | |||
<u>CFBモードは、CBCモードと比較してエラーの伝播が少ないというメリットがあるが、並列処理には向いていない。</u><br> | |||
<u>大きなファイルを暗号化・復号する場合は、進捗状況を表示する機能を追加することを推奨する。</u><br> | |||
<br><br> | <br><br> | ||