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

 
(同じ利用者による、間の10版が非表示)
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>


112行目: 186行目:
<br><br>
<br><br>


== 使用例: CBCモード ==
== QCrypto ==
==== サンプルコード ====
==== サンプルコード : CBCモード ====
以下の例では、<code>QCrypto</code>クラスを使用して、CBCモードでの暗号化と復号を行っている。<br>
以下の例では、<code>QCrypto</code>クラスを使用して、CBCモードでの暗号化と復号を行っている。<br>
<br>
<br>
577行目: 651行目:
使用する鍵はINIファイルでを保存して、アプリケーション再起動時にも使用できるようにしている。<br>
使用する鍵はINIファイルでを保存して、アプリケーション再起動時にも使用できるようにしている。<br>
<br>
<br>
16バイト (128ビット) の鍵を使用しているが、AESは192ビットや256ビットのキーもサポートしている。<br>
また、メモリ使用量を考慮して、ストリーミング暗号化および復号を行っている。<br>
<br>
以下の例では、16バイト (128ビット) の鍵を使用しているが、AESは192ビットや256ビットのキーもサポートしている。<br>
暗号化されたデータを保存または送信する場合は、初期化ベクトル (IV) も一緒に保存または送信する必要がある。<br>
暗号化されたデータを保存または送信する場合は、初期化ベクトル (IV) も一緒に保存または送信する必要がある。<br>
<br>
<br>
618行目: 694行目:
   
   
       return iv;
       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;
      }
     }
     }
   
   
686行目: 795行目:
       QDataStream in(&file);
       QDataStream in(&file);
       in >> iv >> data;
       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();
       file.close();
721行目: 862行目:
     qDebug() << "Encryption key:" << key.toHex();
     qDebug() << "Encryption key:" << key.toHex();
   
   
     // テスト用のデータ
     // 入力ファイルと出力ファイルの設定
     QByteArray plaintext = "Hello, World! This is a secret message.";
     QString inputFilename = "input.txt";         // 暗号化するファイル
     qDebug() << "Original text:" << plaintext;
     QString encryptedFilename = "encrypted.bin";  // 暗号化されたファイル
    QString decryptedFilename = "decrypted.txt";  // 復号されたファイル
    QString ivFilename = "iv.bin";               // 初期化ベクトル (IV) ファイル
   
   
     // 暗号化
     // 平文を暗号化
     QByteArray iv = SecureAESCrypto::generateIV();
     QByteArray iv = SecureAESCrypto::generateIV();
     QByteArray ciphertext = SecureAESCrypto::encrypt(plaintext, key, iv);
     if (SecureAESCrypto::encryptFile(inputFilename, encryptedFilename, key, iv)) {
    if (!ciphertext.isEmpty()) {
       qDebug() << "File encrypted successfully.";
       qDebug() << "Encrypted text:" << ciphertext.toHex();
   
   
       // 暗号化されたデータとIVの保存
       // 初期化ベクトル (IV) の保存
       if (SecureAESCrypto::saveEncryptedData("encrypted_data.bin", ciphertext, iv)) {
       if (SecureAESCrypto::saveIV(ivFilename, iv)) {
           qDebug() << "Encrypted data and IV saved successfully.";
           qDebug() << "IV saved successfully.";
   
   
           // 暗号化されたデータとIVの読み込み
           // 初期化ベクトル (IV) の読み込み
           QByteArray loadedCiphertext, loadedIV;
           QByteArray loadedIV;
           if (SecureAESCrypto::loadEncryptedData("encrypted_data.bin", loadedCiphertext, loadedIV)) {
           if (SecureAESCrypto::loadIV(ivFilename, loadedIV)) {
             qDebug() << "Encrypted data and IV loaded successfully.";
             qDebug() << "IV loaded successfully.";
   
   
             // 復号
             // 復号
             QByteArray decryptedText = SecureAESCrypto::decrypt(loadedCiphertext, key, loadedIV);
             if (SecureAESCrypto::decryptFile(encryptedFilename, decryptedFilename, key, loadedIV)) {
            if (!decryptedText.isEmpty()) {
                 qDebug() << "File decrypted successfully.";
                 qDebug() << "Decrypted text:" << decryptedText;
             }
             }
             else {
             else {
                 qDebug() << "Decryption failed.";
                 qDebug() << "File decryption failed.";
             }
             }
           }
           }
           else {
           else {
             qDebug() << "Failed to load encrypted data and IV.";
             qDebug() << "Failed to load IV.";
           }
           }
       }
       }
       else {
       else {
           qDebug() << "Failed to save encrypted data and IV.";
           qDebug() << "Failed to save IV.";
       }
       }
     }
     }
     else {
     else {
        qDebug() << "Encryption failed.";
      qDebug() << "File encryption failed.";
     }
     }
   
   
773行目: 914行目:


==== サンプルコード : 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";
    // 暗号化
    QByteArray iv = SecureAESCrypto::generateIV();
    if (SecureAESCrypto::encryptFile(inputFilename, encryptedFilename, key, iv)) {
      qDebug() << "File encrypted successfully.";
   
   
// stub for how you really get it, e.g. reading it from a file, off of a
      // 初期化ベクトル (IV) の保存
// network socket encrypted with an asymmetric cipher, or whatever
      if (SecureAESCrypto::saveIV(ivFilename, iv)) {
read_key(aes_key, aes_key.size());
          qDebug() << "IV saved 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
          QByteArray loadedIV;
//  to use the same IV as well as the same key
          if (SecureAESCrypto::loadIV(ivFilename, loadedIV)) {
read_initialization_vector(iv);
            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 enc = new CFB_Mode<AES>::Encryption(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.";
    }
   
   
// the final argument is specific to CFB mode, and specifies the refeeding size in bytes.
    return a.exec();
// This invocation corresponds to Java's Cipher.getInstance("AES/CFB8/NoPadding")
  }
  auto dec = new CFB_Mode<AES>::Decryption(aes_key, sizeof(aes_key), iv, 1);
  </syntaxhighlight>
  </syntaxhighlight>
<br>
<u>※注意</u><br>
<u>CFBモードは、CBCモードと比較してエラーの伝播が少ないというメリットがあるが、並列処理には向いていない。</u><br>
<u>大きなファイルを暗号化・復号する場合は、進捗状況を表示する機能を追加することを推奨する。</u><br>
<br><br>
<br><br>


833行目: 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
909行目: 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]]