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

ナビゲーションに移動 検索に移動
80行目: 80行目:
カウンタモードであれば、安全性が若干損なう可能性があるが、暗号化も復号もランダムに行えて便利である。<br>
カウンタモードであれば、安全性が若干損なう可能性があるが、暗号化も復号もランダムに行えて便利である。<br>
<br><br>
<br><br>
== 例: CBCモード ==
<syntaxhighlight lang="c++">
// AES.hファイル
#ifndef AES_H
#define AES_H
#include <QObject>
#include <QByteArray>
class AES : public QObject
{
    Q_OBJECT
private:
    QByteArray m_key;
    QString    m_lastError;
    QByteArray generateKey(const QString &password);
    QByteArray generateIV();
    QByteArray padData(const QByteArray &data);
    QByteArray unpadData(const QByteArray &data);
public:
    explicit  AES(QObject *parent = nullptr);
    ~AES();
    bool      setKey(const QString &password);
    QByteArray encrypt(const QByteArray &plaintext);
    QByteArray decrypt(const QByteArray &ciphertext);
    QString    lastError() const;
};
#endif // AES_H
</syntaxhighlight>
<br>
<syntaxhighlight lang="c++">
// AES.cppファイル
#include <QCryptographicHash>
#include <openssl/aes.h>
#include <openssl/rand.h>
#include <openssl/err.h>
#include "AES.h"
AES::AES(QObject *parent) : QObject(parent)
{
    OpenSSL_add_all_algorithms();
}
AES::~AES()
{
    EVP_cleanup();
}
bool AES::setKey(const QString &password)
{
    m_key = generateKey(password);
    return !m_key.isEmpty();
}
QByteArray AES::encrypt(const QByteArray &plaintext)
{
    if (m_key.isEmpty()) {
      m_lastError = "キーがセットされていません";
      return QByteArray();
    }
    QByteArray iv = generateIV();
    QByteArray paddedText = padData(plaintext);
    QByteArray ciphertext(paddedText.size(), 0);
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
      m_lastError = "暗号化コンテキストの生成に失敗";
      return QByteArray();
    }
    if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
                          reinterpret_cast<const unsigned char*>(m_key.constData()),
                          reinterpret_cast<const unsigned char*>(iv.constData())) != 1) {
      m_lastError = "Failed to initialize encryption";
      EVP_CIPHER_CTX_free(ctx);
      return QByteArray();
    }
    int len;
    int ciphertext_len;
    if (EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char*>(ciphertext.data()),
                          &len, reinterpret_cast<const unsigned char*>(paddedText.constData()),
                          paddedText.size()) != 1) {
      m_lastError = "データの暗号化に失敗";
      EVP_CIPHER_CTX_free(ctx);
      return QByteArray();
    }
    ciphertext_len = len;
    if (EVP_EncryptFinal_ex(ctx, reinterpret_cast<unsigned char*>(ciphertext.data()) + len, &len) != 1) {
      m_lastError = "暗号化のファイナライズに失敗";
      EVP_CIPHER_CTX_free(ctx);
      return QByteArray();
    }
    ciphertext_len += len;
    EVP_CIPHER_CTX_free(ctx);
    return iv + ciphertext.left(ciphertext_len);
}
QByteArray AES::decrypt(const QByteArray &ciphertext)
{
    if (m_key.isEmpty()) {
      m_lastError = "キーがセットされていません";
      return QByteArray();
    }
    if (ciphertext.size() < AES_BLOCK_SIZE) {
      m_lastError = "暗号文のサイズが無効";
      return QByteArray();
    }
    QByteArray iv = ciphertext.left(AES_BLOCK_SIZE);
    QByteArray actualCiphertext = ciphertext.mid(AES_BLOCK_SIZE);
    QByteArray plaintext(actualCiphertext.size(), 0);
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
      m_lastError = "暗号化コンテキストの生成に失敗";
      return QByteArray();
    }
    if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL,
                          reinterpret_cast<const unsigned char*>(m_key.constData()),
                          reinterpret_cast<const unsigned char*>(iv.constData())) != 1) {
      m_lastError = "Failed to initialize decryption";
      EVP_CIPHER_CTX_free(ctx);
      return QByteArray();
    }
    int len;
    int plaintext_len;
    if (EVP_DecryptUpdate(ctx, reinterpret_cast<unsigned char*>(plaintext.data()),
                          &len, reinterpret_cast<const unsigned char*>(actualCiphertext.constData()),
                          actualCiphertext.size()) != 1) {
      m_lastError = "データの復号に失敗";
      EVP_CIPHER_CTX_free(ctx);
      return QByteArray();
    }
    plaintext_len = len;
    if (EVP_DecryptFinal_ex(ctx, reinterpret_cast<unsigned char*>(plaintext.data()) + len, &len) != 1) {
      m_lastError = "復号のファイナライズに失敗";
      EVP_CIPHER_CTX_free(ctx);
      return QByteArray();
    }
    plaintext_len += len;
    EVP_CIPHER_CTX_free(ctx);
    return unpadData(plaintext.left(plaintext_len));
}
QString AES::lastError() const
{
    return m_lastError;
}
QByteArray AES::generateKey(const QString &password)
{
    return QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256);
}
QByteArray AES::generateIV()
{
    QByteArray iv(AES_BLOCK_SIZE, 0);
    if (RAND_bytes(reinterpret_cast<unsigned char*>(iv.data()), AES_BLOCK_SIZE) != 1) {
      m_lastError = "初期化ベクトルの生成に失敗";
      return QByteArray();
    }
    return iv;
}
QByteArray AES::padData(const QByteArray &data)
{
    int padding = AES_BLOCK_SIZE - (data.size() % AES_BLOCK_SIZE);
    return data + QByteArray(padding, padding);
}
QByteArray AES::unpadData(const QByteArray &data)
{
    if (data.isEmpty()) {
      return data;
    }
    int padding = data[data.size() - 1];
    if (padding > AES_BLOCK_SIZE || padding < 1) {
      m_lastError = "パディングが無効";
      return QByteArray();
    }
    return data.left(data.size() - padding);
}
</syntaxhighlight>


== Crypto++ ==
== Crypto++ ==

案内メニュー