📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

 
(同じ利用者による、間の17版が非表示)
61行目: 61行目:
CBCモードは、暗号化がランダムアクセスに適さないため、暗号化は先頭から順次行う必要がある。<br>
CBCモードは、暗号化がランダムアクセスに適さないため、暗号化は先頭から順次行う必要がある。<br>
また、復号がランダムアクセスに適しており、復号はランダムに選択した着目ブロックに対して操作できる。(1つ前の暗号文ブロックは必要である)<br>
また、復号がランダムアクセスに適しており、復号はランダムに選択した着目ブロックに対して操作できる。(1つ前の暗号文ブロックは必要である)<br>
<br>
==== CFBモード ====
CFBモード (Cipher Feedback Mode) は、AES暗号化アルゴリズムで使用されるブロック暗号の運用モードの1つである。<br>
このモードは、ストリーム暗号のように機能して、平文を任意のビット長で暗号化することができる。<br>
<br>
CFBモードの主な特徴は、暗号文の生成時に、前の暗号ブロックの一部を使用することである。<br>
これにより、各ブロックの暗号化が前のブロックに依存するため、暗号文にパターンが現れにくくなる。<br>
<br>
動作の流れを以下に示す。<br>
# まず、初期化ベクトル (IV) を暗号化する。<br>その結果の一部と平文の最初の部分をXOR演算して、最初の暗号文ブロックを生成する。
# 次に、この暗号文ブロックを暗号化して、その結果と次の平文部分をXOR演算して次の暗号文ブロックを作る。
# この過程を繰り返して、全ての平文を暗号化する。
<br>
CFBモードのメリットは、エラーの伝播が限定的であることが挙げられる。<br>
一部の暗号文が破損しても、その影響は限られた範囲にとどまる。<br>
<br>
また、復号時に平文の長さが事前にわからなくても、リアルタイムで復号を行えるという特徴がある。<br>
<br>
一方で、CFBモードにはいくつかの注意点もある。<br>
例えば、初期化ベクトル (IV) の選択が重要で、同じ初期化ベクトル (IV) を再利用すると安全性が低下する可能性がある。<br>
また、並列処理が困難であるため、大量のデータを高速に処理する必要がある場合には適していない。<br>
<br>
CFBモードは、ネットワーク通信やストリーミングデータの暗号化等、リアルタイム性が求められる場面で特に有効である。<br>
<br>
ただし、使用する場合は適切な初期化ベクトル (IV) の管理や必要に応じて認証機能の追加等のセキュリティ上の考慮が必要である。<br>
<br>
<br>
==== OFBモード ====
==== OFBモード ====
79行目: 104行目:
<br>
<br>
カウンタモードであれば、安全性が若干損なう可能性があるが、暗号化も復号もランダムに行えて便利である。<br>
カウンタモードであれば、安全性が若干損なう可能性があるが、暗号化も復号もランダムに行えて便利である。<br>
<br>
==== CTRモード ====
CTRモード (Counter Mode) は、AES暗号化アルゴリズムで使用される別のブロック暗号の運用モードである。<br>
このモードは、ブロック暗号をストリーム暗号のように機能させて、高速で並列処理が可能な暗号化を実現する。<br>
<br>
CTRモードの基本的な動作原理は、カウンタ値を暗号化して、その結果と平文をXOR演算することで暗号文を生成するというものである。
# まず、初期値 (ノンス) とカウンタ値を組み合わせた値を暗号化する。<br>この暗号化された値は、キーストリームと呼ばれる。
# 次に、このキーストリームと平文をXOR演算することで暗号文を生成する。<br>カウンタ値は各ブロックごとに増加して、これを繰り返して全ての平文を暗号化する。
<br>
CTRモードの大きな特徴の1つは、暗号化と復号が同じ操作で行えることである。<br>
つまり、暗号化アルゴリズムのみを実装するだけでよく、ハードウェア実装が容易になる。<br>
また、各ブロックの暗号化が他のブロックに依存しないため、並列処理が可能であり、高速な暗号化・復号を実現できる。<br>
<br>
さらに、CTRモードはランダムアクセスが可能である。<br>
つまり、暗号文の特定の部分だけを復号することができる。<br>
これは大きなファイルの一部だけを復号する必要がある場合等に有効である。<br>
<br>
ただし、CTRモードにも注意点がある。<br>
例えば、同じ鍵とノンスの組み合わせを再利用する場合、セキュリティが著しく低下する可能性がある。<br>
また、暗号文の改竄が容易であるため、認証機能を別途追加する必要がある。<br>
<br>
CTRモードは、その高速性と並列処理の特性から、大量のデータを扱うディスク暗号化やネットワークプロトコルでの利用に適している。<br>
例えば、IPsecやSSL / TLS等のセキュリティプロトコルで使用されている。<br>
<br>
CTRモードを安全に使用するためには、適切な鍵管理とノンスの生成が重要である。<br>
また、必要に応じて、GCM (Galois / Counter Mode) モードのような認証付き暗号モードを使用することにより、機密性と完全性を同時に確保することができる。<br>
<br>
==== GCMモード ====
GCM (Galois / Counter Mode) は、AESで使用される高速で安全な認証付き暗号化モードの1つである。<br>
このモードは、データの機密性と完全性を同時に提供する効率的な方法として広く採用されている。<br>
<br>
GCMモードの主な特徴は、暗号化と認証を同時に行えることである。<br>
これにより、従来の方式に比べてパフォーマンスが向上して、実装も簡素化される。<br>
<br>
GCMの動作原理を以下に示す。<br>
# まず、CTRモードでデータを暗号化する。
# その後、暗号文とAAD (追加認証データ) を使用して認証タグを生成する。<br>この過程でガロアフィールド上の乗算が利用されるため、ガロアの名前が付いている。
<br>
セキュリティの観点から、GCMモードは非常に堅牢とされている。<br>
ただし、使用する場合は1つのキーで大量のデータを暗号化しないこと、初期化ベクトル (IV) の再利用を避けることが重要である。<br>
<br>
GCMのメリットとして、並列処理が可能であることが挙げられる。<br>
これにより、ハードウェア実装での高速化が容易になる。<br>
また、パディングが不要なため、データの長さが変わらないという特徴もある。<br>
<br>
GCMモードにも課題はあり、例えば、実装が複雑になる可能性があることや一部の実装では副チャネル攻撃に対して脆弱性を持つ可能性があること等が挙げられる。<br>
<br>
GCMモードは多くの暗号プロトコルで採用されている。<br>
例えば、TLS 1.2以降のバージョンでサポートされており、安全な通信に広く使用されている。<br>
<br><br>
<br><br>


== : CBCモード ==
== 初期化ベクトル ==
==== カウンタモード (CTR) ====
CTRモードでは、カウンタ値を初期化ベクトルとして使用する。<br>
各ブロックごとにカウンタをインクリメントすることにより、一意の値を生成する。<br>
<br>
==== GCMモード ====
GCMモード等では、ナンス (使い捨ての数値) とカウンタを組み合わせて初期化ベクトルを生成する。<br>
<br>
==== 初期化ベクトル生成時の注意点 ====
* 予測可能性
*: 自動生成されたIV (初期化ベクトル) が予測可能であってはいけない。
*: 例えば、単純なインクリメント方式は避けるべきである。
*: <br>
* 一意性
*: 同じキーで同じIV (初期化ベクトル) を再利用しないことが重要である。
*: 特にストリーム暗号的なモード (CTRモード, GCMモード) では致命的な脆弱性につながる。
*: <br>
* ランダム性
*: 可能な限り、暗号学的に安全な乱数生成器を使用してIV (初期化ベクトル) を生成すべきである。
*: <br>
* 通信
*: IV (初期化ベクトル) は、一般的に、暗号文と一緒に送信する必要がある。
*: 多くの場合、暗号文の先頭に付加される。
*: <br>
* 長さ
*: IV (初期化ベクトル) の長さは、使用する暗号化モードに応じて適切に設定する必要がある。
*: 多くの場合、ブロックサイズと同じである。
<br>
自動生成されたIV (初期化ベクトル) を使用することにより、同じ平文でも毎回異なる暗号文が生成されて、セキュリティが向上する。<br>
<br><br>
 
== QCrypto ==
==== サンプルコード : CBCモード ====
以下の例では、<code>QCrypto</code>クラスを使用して、CBCモードでの暗号化と復号を行っている。<br>
以下の例では、<code>QCrypto</code>クラスを使用して、CBCモードでの暗号化と復号を行っている。<br>
<br>
<br>
94行目: 200行目:
* パディング
* パディング
*: PKCS7パディングを使用しており、復号時にパディングの妥当性を確認する。
*: PKCS7パディングを使用しており、復号時にパディングの妥当性を確認する。
 
<br>
 
<syntaxhighlight lang="cmake">
# CMakeLists.txtファイル
# ...略
# Pkg-configの使用
find_package(PkgConfig REQUIRED)
# Qtライブラリの検索
find_package(QT NAMES Qt6 Qt5 COMPONENTS Core REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core REQUIRED)
# OpenSSLライブラリの検索
find_package(OpenSSL REQUIRED)
# ...略
# Qtライブラリのリンク
target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Core)
# OpenSSLライブラリのリンク
target_link_libraries(${PROJECT_NAME} PRIVATE OpenSSL::SSL OpenSSL::Crypto)
# OpenSSLのインクルードディレクトリ
target_include_directories(${PROJECT_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR})
# C++コンパイラフラグの設定
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
    target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -pedantic)
elseif(MSVC)
    target_compile_options(${PROJECT_NAME} PRIVATE /W4)
endif()
</syntaxhighlight>
<br>
<br>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
302行目: 440行目:
<br>
<br>
  <syntaxhighlight lang="c++">
  <syntaxhighlight lang="c++">
// main.cppファイル
  #include <QCoreApplication>
  #include <QCoreApplication>
  #include <QDebug>
  #include <QDebug>
343行目: 483行目:
* パスワードからキーを生成しているが、実務ではより安全なキー管理方法を使用すべきである。
* パスワードからキーを生成しているが、実務ではより安全なキー管理方法を使用すべきである。
* エラーハンドリングにおいて、入出力の検証を追加することを推奨する。
* エラーハンドリングにおいて、入出力の検証を追加することを推奨する。
<br>
==== パスワード / キーの管理 ====
パスワード / キーの管理は非常に重要なセキュリティ上の問題である。<br>
ハードコーディングされたパスワードは避けるべきであり、より安全な方法でパスワードを取得・管理する必要がある。<br>
<br>
以下に、いくつかの一般的なアプローチと推奨事項を示す。<br>
<br>
===== ユーザ入力 =====
最も簡単な方法は、ユーザに直接パスワードを入力してもらうことである。<br>
<br>
<syntaxhighlight lang="c++">
QString getPasswordFromUser()
{
    QInputDialog dialog;
    dialog.setInputMode(QInputDialog::TextInput);
    dialog.setTextEchoMode(QLineEdit::Password);
    dialog.setWindowTitle("パスワードの入力");
    dialog.setLabelText("暗号化パスワードを入力してください: ");
    if (dialog.exec() == QDialog::Accepted) {
      return dialog.textValue();
    }
    return QString();
}
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// 使用例
QString password = getPasswordFromUser();
if (!password.isEmpty()) {
    aes.setKey(password);
}
</syntaxhighlight>
<br>
===== 環境変数の使用 =====
セキュリティ上のリスクはあるが、開発環境やテスト環境では環境変数を使用することがある。<br>
<br>
<syntaxhighlight lang="c++">
QString getPasswordFromEnv()
{
    return qgetenv("MY_APP_ENCRYPTION_KEY");
}
</syntaxhighlight>
<br>
===== 暗号化された設定ファイル =====
暗号化された設定ファイルにパスワードを保存する方法もある。<br>
ただし、この方法を使用する場合は、設定ファイル自体を安全に保護する必要がある。<br>
<br>
<syntaxhighlight lang="c++">
QString getPasswordFromConfig()
{
    QSettings settings("MyCompany", "MyApp");
    return settings.value("EncryptionKey").toString();
}
</syntaxhighlight>
<br>
===== キーストア / セキュアエンクレーブ =====
OSのセキュアなキーストアを使用する方法である。<br>
これは最も安全な方法の1つであるが、実装が複雑になる可能性がある。<br>
<br>
以下に示すAPIを使用する場合は、プラットフォーム固有のコードが必要になる。<br>
* Windows
*: Data Protection API (DPAPI)
* MacOS
*: Keychain
* Linux
*: Secret Service API
<br>
===== ハードウェアセキュリティモジュール (HSM) =====
高度なセキュリティが必要な場合、HSMを使用してキーを管理することができる。<br>
<br>
===== キーの派生 =====
ユーザのパスワードから安全にキーを派生させる方法もある。<br>
これには、PBKDF2やscrypt等のキー派生関数を使用する。<br>
<br>
<syntaxhighlight lang="c++">
QByteArray deriveKey(const QString &password, const QByteArray &salt)
{
    // OpenSSLを使用してPBKDF2でキーを派生させる例
    QByteArray key(32, 0); // 256ビットキー
    PKCS5_PBKDF2_HMAC(password.toUtf8().constData(), password.length(),
                      reinterpret_cast<const unsigned char*>(salt.constData()),
                      salt.length(), 10000, EVP_sha256(),
                      key.size(), reinterpret_cast<unsigned char*>(key.data()));
    return key;
}
</syntaxhighlight>
<br>
===== 推奨事項 =====
* パスワードを平文で保存しないこと。
* 可能な限り、ユーザに直接パスワードを入力してもらう方法を選択すること。
* パスワードの強度要件を設定して、ユーザに強力なパスワードの使用を促す。
* パスワード / キーの転送時は、安全な通信チャネル (例: HTTPS) を使用すること。
* 必要に応じて、多要素認証を実装することを検討する。
* 定期的なパスワード変更を促すポリシーを実装することを検討する。
<br>
実務では、これらの方法を組み合わせて使用することが一般的である。<br>
例えば、ユーザが入力したパスワードからキーを派生させて、派生したキーをセキュアなキーストアに保存するという方法がある。<br>
<br><br>
<br><br>


391行目: 630行目:
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<br>
==== サンプルコード ====
==== サンプルコード : 使用する鍵長の最小値、最大値、標準値の表示 ====
以下の例では、AESが使用する鍵長の最小値、最大値、標準値をダンプしている。(2番目と3番目はパイプラインのフィルターを使用している)<br>
以下の例では、AESが使用する鍵長の最小値、最大値、標準値をダンプしている。(2番目と3番目はパイプラインのフィルターを使用している)<br>
パイプラインは高レベルの抽象化であり、入力のバッファリング、出力のバッファリング、パディングを処理する。<br>
パイプラインは高レベルの抽象化であり、入力のバッファリング、出力のバッファリング、パディングを処理する。<br>
407行目: 646行目:
  ブロックサイズ : 16
  ブロックサイズ : 16
  </syntaxhighlight>
  </syntaxhighlight>
<br>
==== サンプルコード : CBCモード ====
以下の例では、CBCモードを使用して、暗号化および復号を行っている。<br>
使用する鍵はINIファイルでを保存して、アプリケーション再起動時にも使用できるようにしている。<br>
<br>
また、メモリ使用量を考慮して、ストリーミング暗号化および復号を行っている。<br>
<br>
以下の例では、16バイト (128ビット) の鍵を使用しているが、AESは192ビットや256ビットのキーもサポートしている。<br>
暗号化されたデータを保存または送信する場合は、初期化ベクトル (IV) も一緒に保存または送信する必要がある。<br>
<br>
<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) の生成
    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::CBC_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::CBC_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;
      }
    }
    // 平文を暗号化
    static QByteArray encrypt(const QByteArray &plaintext, const QByteArray &key, QByteArray &iv)
    {
      try {
          CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encryptor;
          encryptor.SetKeyWithIV(reinterpret_cast<const byte*>(key.constData()), key.size(), reinterpret_cast<const byte*>(iv.constData()));
          QByteArray ciphertext;
          CryptoPP::StringSource(reinterpret_cast<const byte*>(plaintext.constData()), plaintext.size(), true,
                                new CryptoPP::StreamTransformationFilter(encryptor, new CryptoPP::StringSink(ciphertext)));
          return ciphertext;
      }
      catch (const CryptoPP::Exception& e) {
          qCritical() << "Encryption error:" << e.what();
          return QByteArray();
      }
    }
    // 暗号文を復号
    static QByteArray decrypt(const QByteArray &ciphertext, const QByteArray &key, const QByteArray &iv)
    {
      try {
          CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decryptor;
          decryptor.SetKeyWithIV(reinterpret_cast<const byte*>(key.constData()), key.size(), reinterpret_cast<const byte*>(iv.constData()));
          QByteArray plaintext;
          CryptoPP::StringSource(reinterpret_cast<const byte*>(ciphertext.constData()), ciphertext.size(), true,
                                new CryptoPP::StreamTransformationFilter(decryptor, new CryptoPP::StringSink(plaintext)));
          return plaintext;
      }
      catch (const CryptoPP::Exception& e) {
          qCritical() << "Decryption error:" << e.what();
          return QByteArray();
      }
    }
    // 暗号文と初期化ベクトル (IV) をファイルに保存
    static bool saveEncryptedData(const QString &filename, const QByteArray &data, const QByteArray &iv)
    {
      QFile file(filename);
      if (!file.open(QIODevice::WriteOnly)) {
          qCritical() << "Unable to open file for writing:" << filename;
          return false;
      }
      QDataStream out(&file);
      out << iv << data;
      file.close();
      return true;
    }
    // ファイルから暗号文と初期化ベクトル (IV) を読み込む
    static bool loadEncryptedData(const QString &filename, QByteArray &data, QByteArray &iv)
    {
      QFile file(filename);
      if (!file.open(QIODevice::ReadOnly)) {
          qCritical() << "Unable to open file for reading:" << filename;
          return false;
      }
      QDataStream in(&file);
      in >> iv >> data;
      file.close();
      return true;
    }
    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;
    }
    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>
<br>
<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";                // 初期化ベクトル (IV) ファイル
    // 平文を暗号化
    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>
<br>
<u>※注意</u><br>
<u>鍵の保存において、実務ではより安全な方法 (例: OSのキーチェーンやセキュアな暗号化ストレージ) を使用することを推奨する。</u><br>
<u>また、ユーザ認証やアクセス制御等の追加のセキュリティ層を実装することを推奨する。</u><br>
<br>
<u>ファイルの暗号化や大量のデータの暗号化を行う場合は、メモリ使用量を考慮してストリーミング暗号化を行う。</u><br>
<br>
==== サンプルコード : CFBモード ====
CFBモードは、ブロック暗号をストリーム暗号のように使用できるモードであり、データを1[byte]ずつ暗号化できる特徴がある。<br>
<br>
CFBモードの特徴を以下に示す。<br>
* ストリーム暗号のように動作するため、データを1バイトずつ暗号化できる。
* 暗号文の一部が破損しても、その影響は限定的である。 (エラーの伝播が少ない)
* 並列化が難しいため、大量のデータを高速に処理する必要がある場合は他のモードを検討する必要がある。
<br>
<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ファイル
#include <QCoreApplication>
#include <QString>
#include <QByteArray>
#include <QSettings>
#include <QFile>
#include <QDebug>
   
   
  // Generate a random key
  #include <cryptopp/aes.h>
  SecByteBlock key(0x00, AES::DEFAULT_KEYLENGTH);
#include <cryptopp/modes.h>
  rnd.GenerateBlock(key, key.size());
#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) の生成
    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;
    }
   
   
// Generate a random IV
    // 初期化ベクトル (IV) の読み込み
SecByteBlock iv(AES::BLOCKSIZE);
    static bool loadIV(const QString &filename, QByteArray &iv)
rnd.GenerateBlock(iv, iv.size());
    {
      QFile file(filename);
      if (!file.open(QIODevice::ReadOnly)) {
          qCritical() << "Unable to open file for reading IV:" << filename;
          return false;
      }
   
   
byte plainText[] = "Hello! How are you.";
      QDataStream in(&file);
size_tmessageLen = std::strlen((char*)plainText) + 1;
      in >> iv;
   
   
// Encrypt
      file.close();
CFB_Mode<AES>::Encryption cfbEncryption(key, key.size(), iv);
cfbEncryption.ProcessData(plainText, plainText, messageLen);
   
   
// Decrypt
      return true;
CFB_Mode<AES>::Decryption cfbDecryption(key, key.size(), iv);
    }
  cfbDecryption.ProcessData(plainText, plainText, messageLen);
  };
  </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()));
   
   
// stub for how you really get it, e.g. reading it from a file, off of a
      return key;
// network socket encrypted with an asymmetric cipher, or whatever
    }
  read_key(aes_key, aes_key.size());
  }
   
   
  // stub for how you really get it, e.g. filling it with random bytes or
  int main(int argc, char *argv[])
  //  reading it from the other side of the socket since both sides have
  {
//  to use the same IV as well as the same key
    QCoreApplication a(argc, argv);
read_initialization_vector(iv);
   
   
// 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")
    QByteArray key = getOrCreateKey();
auto enc = new CFB_Mode<AES>::Encryption(aes_key, sizeof(aes_key), iv, 1);
    qDebug() << "Encryption key:" << key.toHex();
   
   
  // 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")
    QString inputFilename = "input.txt";
auto dec = new CFB_Mode<AES>::Decryption(aes_key, sizeof(aes_key), iv, 1);
    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>


468行目: 1,122行目:
そのため、Qtの実行バイナリまたはQtプロジェクトにおいて、Qt AESライブラリを同梱して配布してもよい。<br>
そのため、Qtの実行バイナリまたはQtプロジェクトにおいて、Qt AESライブラリを同梱して配布してもよい。<br>
<br>
<br>
==== Qt AESライブラリのダウンロード ====
==== Qt AESライブラリとは ====
Qt向けのポータブルなAES暗号化クラスである。<br>
<br>
全ての鍵長 (128/192/256ビット) をサポートしている。<br>
また、全ての鍵長に対応したECB、CBC、CFB、OFBモードおよび部分的なAES-NIをサポートしている。<br>
<br>
デフォルトのパディング方法はISOであるが、ZERO、PKCS7もサポートしている。<br>
<br>
==== Qt AESライブラリのインストール ====
Qt AESライブラリのビルドに必要なライブラリをインストールする。<br>
Qt AESライブラリのビルドに必要なライブラリをインストールする。<br>
  # SUSE
  # SUSE
544行目: 1,206行目:
<br><br>
<br><br>


{{#seo:
|title={{PAGENAME}} : Exploring Electronics and SUSE Linux | MochiuWiki
|keywords=MochiuWiki,Mochiu,Wiki,Mochiu Wiki,Electric Circuit,Electric,pcb,Mathematics,AVR,TI,STMicro,AVR,ATmega,MSP430,STM,Arduino,Xilinx,FPGA,Verilog,HDL,PinePhone,Pine Phone,Raspberry,Raspberry Pi,C,C++,C#,Qt,Qml,MFC,Shell,Bash,Zsh,Fish,SUSE,SLE,Suse Enterprise,Suse Linux,openSUSE,open SUSE,Leap,Linux,uCLnux,Podman,電気回路,電子回路,基板,プリント基板
|description={{PAGENAME}} - 電子回路とSUSE Linuxに関する情報 | This page is {{PAGENAME}} in our wiki about electronic circuits and SUSE Linux
|image=/resources/assets/MochiuLogo_Single_Blue.png
}}


__FORCETOC__
__FORCETOC__
[[カテゴリ:Qt]]
[[カテゴリ:Qt]]