「Qtの応用 - AES」の版間の差分

ナビゲーションに移動 検索に移動
840行目: 840行目:


==== サンプルコード : CFBモード ====
==== サンプルコード : CFBモード ====
CFBモードは、ブロック暗号をストリーム暗号のように使用できるモードであり、データを1[byte]ずつ暗号化できる特徴がある。<br>
<br>
CFBモードの特徴を以下に示す。<br>
* ストリーム暗号のように動作するため、データを1バイトずつ暗号化できる。
* 暗号文の一部が破損しても、その影響は限定的である。 (エラーの伝播が少ない)
* 並列化が難しいため、大量のデータを高速に処理する必要がある場合は他のモードを検討する必要がある。
<br>
以下の例では、CFBモードを使用して、暗号化および復号を行っている。<br>
以下の例では、CFBモードを使用して、暗号化および復号を行っている。<br>
ECBやCBCではないため、データ長をAESのブロックサイズの倍数にする必要は無い。<br>
<br>
ECBモードやCBCモードとは異なり、データ長をAESのブロックサイズの倍数にする必要は無い。<br>
<br>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
  AutoSeededRandomPool rnd;
  // SecureAESCrypto.hファイル
   
   
  // Generate a random key
  #include <QCoreApplication>
  SecByteBlock key(0x00, AES::DEFAULT_KEYLENGTH);
  #include <QString>
  rnd.GenerateBlock(key, key.size());
  #include <QByteArray>
#include <QSettings>
#include <QFile>
#include <QDebug>
   
   
  // Generate a random IV
  #include <cryptopp/aes.h>
  SecByteBlock iv(AES::BLOCKSIZE);
#include <cryptopp/modes.h>
  rnd.GenerateBlock(iv, iv.size());
  #include <cryptopp/filters.h>
  #include <cryptopp/osrng.h>
#include <cryptopp/hex.h>
   
   
  byte plainText[] = "Hello! How are you.";
  class SecureAESCrypto {
size_tmessageLen = std::strlen((char*)plainText) + 1;
public:
    // 安全な鍵の生成
    static QByteArray generateKey()
    {
      QByteArray key;
      key.resize(CryptoPP::AES::DEFAULT_KEYLENGTH);
      CryptoPP::AutoSeededRandomPool prng;
      prng.GenerateBlock(reinterpret_cast<byte*>(key.data()), key.size());
   
   
// Encrypt
      return key;
CFB_Mode<AES>::Encryption cfbEncryption(key, key.size(), iv);
    }
cfbEncryption.ProcessData(plainText, plainText, messageLen);
   
   
  // Decrypt
    // 安全な初期化ベクトル (IV) の生成
  CFB_Mode<AES>::Decryption cfbDecryption(key, key.size(), iv);
    static QByteArray generateIV()
  cfbDecryption.ProcessData(plainText, plainText, messageLen);
    {
      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>
ここでは、外部で生成された鍵と初期化ベクトル(IV)を使用して、暗号化器と復号器を取得する例を記述する。<br>
その後の処理は、上記の例と同様に記述する。<br>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
  SecByteBlock aes_key(16);
  // main.cppファイル
  SecByteBlock iv(16);
#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";
   
   
// stub for how you really get it, e.g. reading it from a file, off of a
    // 暗号化
// network socket encrypted with an asymmetric cipher, or whatever
    QByteArray iv = SecureAESCrypto::generateIV();
read_key(aes_key, aes_key.size());
    if (SecureAESCrypto::encryptFile(inputFilename, encryptedFilename, key, iv)) {
      qDebug() << "File encrypted successfully.";
   
   
// stub for how you really get it, e.g. filling it with random bytes or
      // 初期化ベクトル (IV) の保存
//  reading it from the other side of the socket since both sides have
      if (SecureAESCrypto::saveIV(ivFilename, iv)) {
//  to use the same IV as well as the same key
          qDebug() << "IV saved successfully.";
read_initialization_vector(iv);
   
   
// the final argument is specific to CFB mode, and specifies the refeeding size in bytes.
          // 初期化ベクトル (IV) の読み込み
// This invocation corresponds to Java's Cipher.getInstance("AES/CFB8/NoPadding")
          QByteArray loadedIV;
auto enc = new CFB_Mode<AES>::Encryption(aes_key, sizeof(aes_key), iv, 1);
          if (SecureAESCrypto::loadIV(ivFilename, loadedIV)) {
            qDebug() << "IV loaded successfully.";
   
   
// the final argument is specific to CFB mode, and specifies the refeeding size in bytes.
            // 復号
// This invocation corresponds to Java's Cipher.getInstance("AES/CFB8/NoPadding")
            if (SecureAESCrypto::decryptFile(encryptedFilename, decryptedFilename, key, loadedIV)) {
  auto dec = new CFB_Mode<AES>::Decryption(aes_key, sizeof(aes_key), iv, 1);
                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>


案内メニュー