12,925
回編集
(→OFBモード) |
|||
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++ == |