「Qtの基礎 - ハッシュ値」の版間の差分
(→概要) |
(→概要) |
||
| (同じ利用者による、間の5版が非表示) | |||
| 3行目: | 3行目: | ||
ハッシュ関数と呼ばれる特殊な関数を使用して、入力データからハッシュ値 (ダイジェストとも呼ばれる) を生成する。<br> | ハッシュ関数と呼ばれる特殊な関数を使用して、入力データからハッシュ値 (ダイジェストとも呼ばれる) を生成する。<br> | ||
<br> | <br> | ||
ハッシュの特徴を以下に示す。<br | ハッシュの特徴を以下に示す。<br> | ||
* 一方向性 | * 一方向性 | ||
*: ハッシュ値から元のデータを復元することは計算上非常に困難である。 | *: ハッシュ値から元のデータを復元することは計算上非常に困難である。 | ||
| 43行目: | 43行目: | ||
<br><br> | <br><br> | ||
== ハッシュ化 == | == QCryptographicHashクラス == | ||
==== ハッシュ化 ==== | |||
MD5、SHA-2、SHA-3等のハッシュを求める場合、<code>QCryptographicHash</code>クラスの<code>hash</code>メソッドを使用する。<br> | MD5、SHA-2、SHA-3等のハッシュを求める場合、<code>QCryptographicHash</code>クラスの<code>hash</code>メソッドを使用する。<br> | ||
<br> | <br> | ||
| 110行目: | 111行目: | ||
QString sha3_512HexHash = sha3_512Hash.toHex(); | QString sha3_512HexHash = sha3_512Hash.toHex(); | ||
qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512HexHash; | qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512HexHash; | ||
return a.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== セキュリティ対策を施したハッシュ化の例 ==== | |||
<code>QCryptographicHash</code>クラスは、暗号学的ハッシュ関数を提供するためのクラスであるが、<br> | |||
ソルト値の追加やPBKDF2等の鍵引き伸ばし関数の適用は自動的には行わない。<br> | |||
<br> | |||
そのため、これらの機能を使用する場合は、設計者側で実装する必要がある。<br> | |||
<br> | |||
パスワードのハッシュ化を安全に行う場合は、以下に示すような手順を実装することが推奨される。<br> | |||
* ソルト値の生成 | |||
*: パスワード毎にランダムな一意のソルト値を生成する。 | |||
*: ソルト値は、十分に長く、暗号学的に安全な乱数生成器を使用して生成する必要がある。 | |||
* パスワードとソルト値の結合 | |||
*: ソルト値をパスワードに結合する。 | |||
*: 一般的には、パスワードの前後にソルト値を付加する。 | |||
* 鍵引き伸ばし関数の適用 | |||
*: 結合したパスワードとソルト値に対して、PBKDF2等の鍵引き伸ばし関数を適用する。 | |||
*: 鍵引き伸ばし関数は、計算コストを調整できるパラメータを持ち、ハッシュ計算に時間が掛かるようにすることで、ブルートフォース攻撃を困難にする。 | |||
* ハッシュ値の生成 | |||
*: 鍵引き伸ばし関数により得られた結果を、暗号学的ハッシュ関数(SHA-256等)で処理して、最終的なハッシュ値を生成する。 | |||
* ハッシュ値とソルト値の保存 | |||
*: 生成されたハッシュ値とソルト値をデータベース等に安全に保存する。 | |||
<br> | |||
QtではPBKDF2を直接サポートするクラスは無いが、<code>QMessageAuthenticationCode</code>クラスと<code>QCryptographicHash</code>クラスを組み合わせることにより、PBKDF2を実装することができる。<br> | |||
<br> | |||
以下の例は、PBKDF2-HMAC-SHA256を使用したパスワードをハッシュ化している。<br> | |||
<br> | |||
まず、<code>QRandomGenerator</code>クラスを使用してランダムなソルト値を生成し、次に、パスワードとソルト値を結合してPBKDF2-HMAC-SHA256を適用している。<br> | |||
また、反復回数とキーの長さを指定することにより、計算コストを調整することができる。<br> | |||
ただし、実際の設計では、イテレーション回数を適切に調整して、ハードウェアの進歩に合わせて定期的に更新する必要がある。<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QCryptographicHash> | |||
#include <QMessageAuthenticationCode> | |||
#include <QRandomGenerator> | |||
QByteArray hashPassword(const QString &password, const QByteArray &salt, int iterations, int keyLength) | |||
{ | |||
QMessageAuthenticationCode hmac(QCryptographicHash::Sha256); | |||
QByteArray derivedKey(keyLength, Qt::Uninitialized); | |||
hmac.setKey(password.toUtf8() + salt); | |||
QByteArray U = hmac.result(); | |||
QByteArray T = U; | |||
for (int i = 1; i < iterations; ++i) { | |||
hmac.addData(U); | |||
U = hmac.result(); | |||
T = T.toHex().append(U.toHex()); | |||
} | |||
return QCryptographicHash::hash(T, QCryptographicHash::Sha256).left(keyLength); | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
// ...略 | |||
QString password = "myPassword"; | |||
QByteArray salt = QRandomGenerator::global()->generate(16).toHex(); | |||
int iterations = 10000; | |||
int keyLength = 32; | |||
QByteArray hashedPassword = hashPassword(password, salt, iterations, keyLength); | |||
// ...略 | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
ただし、セキュリティ上重要な用途では、専門家によって監査・検証されたライブラリや実装を使用することが推奨される。<br> | |||
<br><br> | |||
== QMessageAuthenticationCodeクラス == | |||
==== ハッシュ化 ==== | |||
以下の例では、<code>QMessageAuthenticationCode</code>クラスを使用して、MD4、MD5、SHA-2、SHA-3でハッシュ化している。<br> | |||
<br> | |||
各ハッシュアルゴリズムに対して、<code>QMessageAuthenticationCode</code>オブジェクトを作成して、コンストラクタでアルゴリズムを指定する。<br> | |||
<code>addData</code>メソッドを使用して、ハッシュ化する文字列をUTF-8エンコードで追加する。<br> | |||
<code>result</code>メソッドを使用してハッシュ値を取得し、<code>toHex</code>メソッドで16進数表記の文字列に変換する。<br> | |||
<br> | |||
出力結果は、<code>QCryptographicHash</code>クラスを使用した場合と同値となる。<br> | |||
<br> | |||
<code>QMessageAuthenticationCode</code>クラスは、主にメッセージ認証コード (MAC) の計算に使用されるが、このようにハッシュ化にも使用することができる。<br> | |||
<u>ただし、一般的なハッシュ化の用途では、<code>QCryptographicHash</code>クラスを使用する方が簡潔で便利である。</u><br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QCoreApplication> | |||
#include <QMessageAuthenticationCode> | |||
#include <QDebug> | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
// ハッシュ化する文字列 | |||
QString input = "Hello, world!"; | |||
// MD4 | |||
QMessageAuthenticationCode md4(QCryptographicHash::Md4); | |||
md4.addData(input.toUtf8()); | |||
QByteArray md4Hash = md4.result().toHex(); | |||
qDebug() << "MD4 Hash:" << md4Hash; | |||
// MD5 | |||
QMessageAuthenticationCode md5(QCryptographicHash::Md5); | |||
md5.addData(input.toUtf8()); | |||
QByteArray md5Hash = md5.result().toHex(); | |||
qDebug() << "MD5 Hash:" << md5Hash; | |||
// SHA-2 (SHA-224) | |||
QMessageAuthenticationCode sha224(QCryptographicHash::Sha224); | |||
sha224.addData(input.toUtf8()); | |||
QByteArray sha224Hash = sha224.result().toHex(); | |||
qDebug() << "SHA-2 (SHA-224) Hash:" << sha224Hash; | |||
// SHA-2 (SHA-256) | |||
QMessageAuthenticationCode sha256(QCryptographicHash::Sha256); | |||
sha256.addData(input.toUtf8()); | |||
QByteArray sha256Hash = sha256.result().toHex(); | |||
qDebug() << "SHA-2 (SHA-256) Hash:" << sha256Hash; | |||
// SHA-2 (SHA-384) | |||
QMessageAuthenticationCode sha384(QCryptographicHash::Sha384); | |||
sha384.addData(input.toUtf8()); | |||
QByteArray sha384Hash = sha384.result().toHex(); | |||
qDebug() << "SHA-2 (SHA-384) Hash:" << sha384Hash; | |||
// SHA-2 (SHA-512) | |||
QMessageAuthenticationCode sha512(QCryptographicHash::Sha512); | |||
sha512.addData(input.toUtf8()); | |||
QByteArray sha512Hash = sha512.result().toHex(); | |||
qDebug() << "SHA-2 (SHA-512) Hash:" << sha512Hash; | |||
// SHA-3 (SHA3-224) | |||
QMessageAuthenticationCode sha3_224(QCryptographicHash::Sha3_224); | |||
sha3_224.addData(input.toUtf8()); | |||
QByteArray sha3_224Hash = sha3_224.result().toHex(); | |||
qDebug() << "SHA-3 (SHA3-224) Hash:" << sha3_224Hash; | |||
// SHA-3 (SHA3-256) | |||
QMessageAuthenticationCode sha3_256(QCryptographicHash::Sha3_256); | |||
sha3_256.addData(input.toUtf8()); | |||
QByteArray sha3_256Hash = sha3_256.result().toHex(); | |||
qDebug() << "SHA-3 (SHA3-256) Hash:" << sha3_256Hash; | |||
// SHA-3 (SHA3-384) | |||
QMessageAuthenticationCode sha3_384(QCryptographicHash::Sha3_384); | |||
sha3_384.addData(input.toUtf8()); | |||
QByteArray sha3_384Hash = sha3_384.result().toHex(); | |||
qDebug() << "SHA-3 (SHA3-384) Hash:" << sha3_384Hash; | |||
// SHA-3 (SHA3-512) | |||
QMessageAuthenticationCode sha3_512(QCryptographicHash::Sha3_512); | |||
sha3_512.addData(input.toUtf8()); | |||
QByteArray sha3_512Hash = sha3_512.result().toHex(); | |||
qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512Hash; | |||
return a.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== セキュリティ対策を施したハッシュ化の例 ==== | |||
ソルト値はレインボーテーブル攻撃を防ぐために使用され、PBKDF2はブルートフォース攻撃を困難にするために、意図的に計算コストを高くする。<br> | |||
<br> | |||
ただし、実際の設計では、ソルト値とハッシュ値を安全に保存する必要がある。<br> | |||
また、反復回数やキーの長さ等のパラメータは、セキュリティ要件に応じて適切に調整する必要がある。<br> | |||
<br> | |||
セキュリティ上重要な用途では、専門家によって監査・検証されたライブラリや実装を使用することを強く推奨する。<br> | |||
<br> | |||
以下の例では、ソルト値を使用して、PBKDF2アルゴリズムを適用してパスワードをハッシュ化している。<br> | |||
# QRandomGenerator::global()->generate(16)を使用して、16バイトのランダムなソルト値を生成する。 | |||
# pbkdf2関数において、QMessageAuthenticationCodeクラスを使用してPBKDF2アルゴリズムを実装する。 | |||
#* パスワードとソルト値を連結してHMAC(SHA-256)のキーとして設定する。 | |||
#* 指定された反復回数(iterations)だけHMACを繰り返し適用します。 | |||
#* 各反復で得られたハッシュ値をXORして、最終的な派生キーを生成します。 | |||
# pbkdf2関数の引数として、パスワード、ソルト値、反復回数、キーの長さを指定する。 | |||
# 生成されたハッシュ値 (派生キー) を16進数表記の文字列に変換して出力する。 | |||
<syntaxhighlight lang="c++"> | |||
#include <QCoreApplication> | |||
#include <QMessageAuthenticationCode> | |||
#include <QRandomGenerator> | |||
#include <QDebug> | |||
QByteArray pbkdf2(const QString &password, const QByteArray &salt, int iterations, int keyLength) | |||
{ | |||
QMessageAuthenticationCode hmac(QCryptographicHash::Sha256); | |||
QByteArray derivedKey(keyLength, Qt::Uninitialized); | |||
hmac.setKey(password.toUtf8() + salt); | |||
QByteArray U = hmac.result(); | |||
QByteArray T = U; | |||
for (int i = 1; i < iterations; ++i) { | |||
hmac.addData(U); | |||
U = hmac.result(); | |||
for (int j = 0; j < keyLength; ++j) { | |||
T[j] = T[j] ^ U[j]; | |||
} | |||
} | |||
return T; | |||
} | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
// パスワード | |||
QString password = "myPassword"; | |||
// ソルト値の生成 | |||
QByteArray salt = QRandomGenerator::global()->generate(16).toHex(); | |||
// PBKDF2パラメータ | |||
int iterations = 10000; | |||
int keyLength = 32; | |||
// PBKDF2の適用 | |||
QByteArray hashedPassword = pbkdf2(password, salt, iterations, keyLength); | |||
// 結果の出力 | |||
qDebug() << "Password:" << password; | |||
qDebug() << "Salt:" << salt; | |||
qDebug() << "Iterations:" << iterations; | |||
qDebug() << "Key Length:" << keyLength; | |||
qDebug() << "Hashed Password:" << hashedPassword.toHex(); | |||
return a.exec(); | return a.exec(); | ||
| 115行目: | 345行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br><br> | <br><br> | ||
== QSslクラス == | |||
QSslクラスには、<code>hash</code>というスタティックメソッドが存在する。<br> | |||
このメソッドを使用することにより、指定されたアルゴリズムでデータのハッシュ値を計算することができる。<br> | |||
<br> | |||
サポートされているアルゴリズムは、<code>QCryptographicHash</code>クラスと同様である。<br> | |||
<br> | |||
==== ハッシュ化 ==== | |||
以下の例では、<code>QSsl::hash</code>メソッドを使用して、任意の文字列をMD4、MD5、SHA-2、SHA-3でハッシュ化している。<br> | |||
<br> | |||
<code>QSsl::hash</code>メソッドにおいて、第1引数にハッシュ化する文字列をUTF-8エンコードで渡して、第2引数に使用するハッシュアルゴリズムを指定する。<br> | |||
戻り値は、計算されたハッシュ値が<code>QByteArray</code>型である。<br> | |||
<br> | |||
また、<code>toHex</code>メソッドを使用して、ハッシュ値を16進数表記の文字列に変換している。<br> | |||
<br> | |||
<u>出力は、<code>QCryptographicHash</code>クラスや<code>QMessageAuthenticationCode</code>クラスを使用した場合と同値になる。</u><br> | |||
<br> | |||
<code>QSsl::hash</code>メソッドは、<code>QCryptographicHash</code>クラスと同様のインターフェースを提供しているが、<code>QSsl</code>クラスの一部として提供されている。<br> | |||
<code>QSsl</code>クラスは、SSL/TLSの暗号化や認証に関連する機能を提供するクラスであるが、一般的なハッシュ化の用途では、<code>QCryptographicHash</code>クラスを使用するのが一般的である。<br> | |||
<syntaxhighlight lang="c++"> | |||
#include <QCoreApplication> | |||
#include <QSslSocket> | |||
#include <QDebug> | |||
int main(int argc, char *argv[]) | |||
{ | |||
QCoreApplication a(argc, argv); | |||
// ハッシュ化する文字列 | |||
QString input = "Hello, world!"; | |||
// MD4 | |||
QByteArray md4Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Md4).toHex(); | |||
qDebug() << "MD4 Hash:" << md4Hash; | |||
// MD5 | |||
QByteArray md5Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Md5).toHex(); | |||
qDebug() << "MD5 Hash:" << md5Hash; | |||
// SHA-2 (SHA-224) | |||
QByteArray sha224Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha224).toHex(); | |||
qDebug() << "SHA-2 (SHA-224) Hash:" << sha224Hash; | |||
// SHA-2 (SHA-256) | |||
QByteArray sha256Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha256).toHex(); | |||
qDebug() << "SHA-2 (SHA-256) Hash:" << sha256Hash; | |||
// SHA-2 (SHA-384) | |||
QByteArray sha384Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha384).toHex(); | |||
qDebug() << "SHA-2 (SHA-384) Hash:" << sha384Hash; | |||
// SHA-2 (SHA-512) | |||
QByteArray sha512Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha512).toHex(); | |||
qDebug() << "SHA-2 (SHA-512) Hash:" << sha512Hash; | |||
// SHA-3 (SHA3-224) | |||
QByteArray sha3_224Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_224).toHex(); | |||
qDebug() << "SHA-3 (SHA3-224) Hash:" << sha3_224Hash; | |||
// SHA-3 (SHA3-256) | |||
QByteArray sha3_256Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_256).toHex(); | |||
qDebug() << "SHA-3 (SHA3-256) Hash:" << sha3_256Hash; | |||
// SHA-3 (SHA3-384) | |||
QByteArray sha3_384Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_384).toHex(); | |||
qDebug() << "SHA-3 (SHA3-384) Hash:" << sha3_384Hash; | |||
// SHA-3 (SHA3-512) | |||
QByteArray sha3_512Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_512).toHex(); | |||
qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512Hash; | |||
return a.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] | ||
2024年6月20日 (木) 04:33時点における最新版
概要
ハッシュとは、任意のサイズのデータを固定長のデータに変換するアルゴリズムである。
ハッシュ関数と呼ばれる特殊な関数を使用して、入力データからハッシュ値 (ダイジェストとも呼ばれる) を生成する。
ハッシュの特徴を以下に示す。
- 一方向性
- ハッシュ値から元のデータを復元することは計算上非常に困難である。
- 固定長出力
- 入力データのサイズに関係なく、ハッシュ値は常に固定長になる。
- 一意性
- 異なる入力データから同じハッシュ値が生成される可能性は非常に低い。 (衝突耐性)
- 高速性
- ハッシュ関数は効率的で、大量のデータを高速に処理できる。
ハッシュの用途を以下に示す。
- データの整合性検証
- ハッシュ値を比較することで、データが改ざんされていないことを確認できる。
- パスワードの保存
- パスワードをそのまま保存するのではなく、ハッシュ値を保存することでセキュリティを向上させる。
- データの識別
- ハッシュ値を使用してデータを一意に識別できる。
- 署名生成
- デジタル署名においてハッシュ関数が使用される。
代表的なハッシュアルゴリズムを以下に示す。
- MD5
- 古くから使用されてきたが、現在は脆弱性が発見されている。
- SHA-1
- MD5の後継として広く使用されてきたが、現在は衝突耐性に問題があることが判明している。
- SHA-2 (SHA-256, SHA-384, SHA-512)
- 現在広く使用されている安全なハッシュアルゴリズムである。
- SHA-2は現在のコンピューティング能力では十分に安全であり、多くのシステムやプロトコルで広く使用されている。
- SHA-3への移行には、ソフトウェアやインフラストラクチャの更新が必要であり、コストと時間が掛かるため、緊急性がない限り、急激な変更は推奨されない。
- SHA-3
- SHA-2の後継として設計された新しいハッシュアルゴリズムである。
- ただし、新しいシステムを設計する際や、長期的なセキュリティを考慮する必要がある場合には、SHA-3の採用を検討することを推奨する。
- SHA-3は、量子コンピュータによる攻撃に対してより耐性があると考えられており、将来を見据えた選択肢となっている。
ハッシュは、データの完全性検証、パスワードの保護、効率的なデータ検索等、ソフトウェア開発のさまざまな場面で活用されてる。
適切なハッシュアルゴリズムを選択し、ハッシュの特性を理解することが重要である。
QCryptographicHashクラス
ハッシュ化
MD5、SHA-2、SHA-3等のハッシュを求める場合、QCryptographicHashクラスのhashメソッドを使用する。
以下の例では、任意の文字列から、MD4、MD5、SHA-2、SHA-3のハッシュを計算している。
サポートされているハッシュの詳細については、Qtの公式ドキュメントを参照すること。
#include <QCoreApplication>
#include <QCryptographicHash>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// ハッシュ化する文字列
QString input = "Hello, world!";
// MD4
QByteArray md4Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Md4);
QString md4HexHash = md4Hash.toHex();
qDebug() << "MD4 Hash:" << md4HexHash;
// MD5
QByteArray md5Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Md5);
QString md5HexHash = md5Hash.toHex();
qDebug() << "MD5 Hash:" << md5HexHash;
// SHA-2 (SHA-224)
QByteArray sha224Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha224);
QString sha224HexHash = sha224Hash.toHex();
qDebug() << "SHA-2 (SHA-224) Hash:" << sha224HexHash;
// SHA-2 (SHA-256)
QByteArray sha256Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha256);
QString sha256HexHash = sha256Hash.toHex();
qDebug() << "SHA-2 (SHA-256) Hash:" << sha256HexHash;
// SHA-2 (SHA-384)
QByteArray sha384Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha384);
QString sha384HexHash = sha384Hash.toHex();
qDebug() << "SHA-2 (SHA-384) Hash:" << sha384HexHash;
// SHA-2 (SHA-512)
QByteArray sha512Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha512);
QString sha512HexHash = sha512Hash.toHex();
qDebug() << "SHA-2 (SHA-512) Hash:" << sha512HexHash;
// SHA-3 (SHA3-224)
QByteArray sha3_224Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha3_224);
QString sha3_224HexHash = sha3_224Hash.toHex();
qDebug() << "SHA-3 (SHA3-224) Hash:" << sha3_224HexHash;
// SHA-3 (SHA3-256)
QByteArray sha3_256Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha3_256);
QString sha3_256HexHash = sha3_256Hash.toHex();
qDebug() << "SHA-3 (SHA3-256) Hash:" << sha3_256HexHash;
// SHA-3 (SHA3-384)
QByteArray sha3_384Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha3_384);
QString sha3_384HexHash = sha3_384Hash.toHex();
qDebug() << "SHA-3 (SHA3-384) Hash:" << sha3_384HexHash;
// SHA-3 (SHA3-512)
QByteArray sha3_512Hash = QCryptographicHash::hash(input.toUtf8(), QCryptographicHash::Sha3_512);
QString sha3_512HexHash = sha3_512Hash.toHex();
qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512HexHash;
return a.exec();
}
セキュリティ対策を施したハッシュ化の例
QCryptographicHashクラスは、暗号学的ハッシュ関数を提供するためのクラスであるが、
ソルト値の追加やPBKDF2等の鍵引き伸ばし関数の適用は自動的には行わない。
そのため、これらの機能を使用する場合は、設計者側で実装する必要がある。
パスワードのハッシュ化を安全に行う場合は、以下に示すような手順を実装することが推奨される。
- ソルト値の生成
- パスワード毎にランダムな一意のソルト値を生成する。
- ソルト値は、十分に長く、暗号学的に安全な乱数生成器を使用して生成する必要がある。
- パスワードとソルト値の結合
- ソルト値をパスワードに結合する。
- 一般的には、パスワードの前後にソルト値を付加する。
- 鍵引き伸ばし関数の適用
- 結合したパスワードとソルト値に対して、PBKDF2等の鍵引き伸ばし関数を適用する。
- 鍵引き伸ばし関数は、計算コストを調整できるパラメータを持ち、ハッシュ計算に時間が掛かるようにすることで、ブルートフォース攻撃を困難にする。
- ハッシュ値の生成
- 鍵引き伸ばし関数により得られた結果を、暗号学的ハッシュ関数(SHA-256等)で処理して、最終的なハッシュ値を生成する。
- ハッシュ値とソルト値の保存
- 生成されたハッシュ値とソルト値をデータベース等に安全に保存する。
QtではPBKDF2を直接サポートするクラスは無いが、QMessageAuthenticationCodeクラスとQCryptographicHashクラスを組み合わせることにより、PBKDF2を実装することができる。
以下の例は、PBKDF2-HMAC-SHA256を使用したパスワードをハッシュ化している。
まず、QRandomGeneratorクラスを使用してランダムなソルト値を生成し、次に、パスワードとソルト値を結合してPBKDF2-HMAC-SHA256を適用している。
また、反復回数とキーの長さを指定することにより、計算コストを調整することができる。
ただし、実際の設計では、イテレーション回数を適切に調整して、ハードウェアの進歩に合わせて定期的に更新する必要がある。
#include <QCryptographicHash>
#include <QMessageAuthenticationCode>
#include <QRandomGenerator>
QByteArray hashPassword(const QString &password, const QByteArray &salt, int iterations, int keyLength)
{
QMessageAuthenticationCode hmac(QCryptographicHash::Sha256);
QByteArray derivedKey(keyLength, Qt::Uninitialized);
hmac.setKey(password.toUtf8() + salt);
QByteArray U = hmac.result();
QByteArray T = U;
for (int i = 1; i < iterations; ++i) {
hmac.addData(U);
U = hmac.result();
T = T.toHex().append(U.toHex());
}
return QCryptographicHash::hash(T, QCryptographicHash::Sha256).left(keyLength);
}
int main(int argc, char *argv[])
{
// ...略
QString password = "myPassword";
QByteArray salt = QRandomGenerator::global()->generate(16).toHex();
int iterations = 10000;
int keyLength = 32;
QByteArray hashedPassword = hashPassword(password, salt, iterations, keyLength);
// ...略
}
ただし、セキュリティ上重要な用途では、専門家によって監査・検証されたライブラリや実装を使用することが推奨される。
QMessageAuthenticationCodeクラス
ハッシュ化
以下の例では、QMessageAuthenticationCodeクラスを使用して、MD4、MD5、SHA-2、SHA-3でハッシュ化している。
各ハッシュアルゴリズムに対して、QMessageAuthenticationCodeオブジェクトを作成して、コンストラクタでアルゴリズムを指定する。
addDataメソッドを使用して、ハッシュ化する文字列をUTF-8エンコードで追加する。
resultメソッドを使用してハッシュ値を取得し、toHexメソッドで16進数表記の文字列に変換する。
出力結果は、QCryptographicHashクラスを使用した場合と同値となる。
QMessageAuthenticationCodeクラスは、主にメッセージ認証コード (MAC) の計算に使用されるが、このようにハッシュ化にも使用することができる。
ただし、一般的なハッシュ化の用途では、QCryptographicHashクラスを使用する方が簡潔で便利である。
#include <QCoreApplication>
#include <QMessageAuthenticationCode>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// ハッシュ化する文字列
QString input = "Hello, world!";
// MD4
QMessageAuthenticationCode md4(QCryptographicHash::Md4);
md4.addData(input.toUtf8());
QByteArray md4Hash = md4.result().toHex();
qDebug() << "MD4 Hash:" << md4Hash;
// MD5
QMessageAuthenticationCode md5(QCryptographicHash::Md5);
md5.addData(input.toUtf8());
QByteArray md5Hash = md5.result().toHex();
qDebug() << "MD5 Hash:" << md5Hash;
// SHA-2 (SHA-224)
QMessageAuthenticationCode sha224(QCryptographicHash::Sha224);
sha224.addData(input.toUtf8());
QByteArray sha224Hash = sha224.result().toHex();
qDebug() << "SHA-2 (SHA-224) Hash:" << sha224Hash;
// SHA-2 (SHA-256)
QMessageAuthenticationCode sha256(QCryptographicHash::Sha256);
sha256.addData(input.toUtf8());
QByteArray sha256Hash = sha256.result().toHex();
qDebug() << "SHA-2 (SHA-256) Hash:" << sha256Hash;
// SHA-2 (SHA-384)
QMessageAuthenticationCode sha384(QCryptographicHash::Sha384);
sha384.addData(input.toUtf8());
QByteArray sha384Hash = sha384.result().toHex();
qDebug() << "SHA-2 (SHA-384) Hash:" << sha384Hash;
// SHA-2 (SHA-512)
QMessageAuthenticationCode sha512(QCryptographicHash::Sha512);
sha512.addData(input.toUtf8());
QByteArray sha512Hash = sha512.result().toHex();
qDebug() << "SHA-2 (SHA-512) Hash:" << sha512Hash;
// SHA-3 (SHA3-224)
QMessageAuthenticationCode sha3_224(QCryptographicHash::Sha3_224);
sha3_224.addData(input.toUtf8());
QByteArray sha3_224Hash = sha3_224.result().toHex();
qDebug() << "SHA-3 (SHA3-224) Hash:" << sha3_224Hash;
// SHA-3 (SHA3-256)
QMessageAuthenticationCode sha3_256(QCryptographicHash::Sha3_256);
sha3_256.addData(input.toUtf8());
QByteArray sha3_256Hash = sha3_256.result().toHex();
qDebug() << "SHA-3 (SHA3-256) Hash:" << sha3_256Hash;
// SHA-3 (SHA3-384)
QMessageAuthenticationCode sha3_384(QCryptographicHash::Sha3_384);
sha3_384.addData(input.toUtf8());
QByteArray sha3_384Hash = sha3_384.result().toHex();
qDebug() << "SHA-3 (SHA3-384) Hash:" << sha3_384Hash;
// SHA-3 (SHA3-512)
QMessageAuthenticationCode sha3_512(QCryptographicHash::Sha3_512);
sha3_512.addData(input.toUtf8());
QByteArray sha3_512Hash = sha3_512.result().toHex();
qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512Hash;
return a.exec();
}
セキュリティ対策を施したハッシュ化の例
ソルト値はレインボーテーブル攻撃を防ぐために使用され、PBKDF2はブルートフォース攻撃を困難にするために、意図的に計算コストを高くする。
ただし、実際の設計では、ソルト値とハッシュ値を安全に保存する必要がある。
また、反復回数やキーの長さ等のパラメータは、セキュリティ要件に応じて適切に調整する必要がある。
セキュリティ上重要な用途では、専門家によって監査・検証されたライブラリや実装を使用することを強く推奨する。
以下の例では、ソルト値を使用して、PBKDF2アルゴリズムを適用してパスワードをハッシュ化している。
- QRandomGenerator::global()->generate(16)を使用して、16バイトのランダムなソルト値を生成する。
- pbkdf2関数において、QMessageAuthenticationCodeクラスを使用してPBKDF2アルゴリズムを実装する。
- パスワードとソルト値を連結してHMAC(SHA-256)のキーとして設定する。
- 指定された反復回数(iterations)だけHMACを繰り返し適用します。
- 各反復で得られたハッシュ値をXORして、最終的な派生キーを生成します。
- pbkdf2関数の引数として、パスワード、ソルト値、反復回数、キーの長さを指定する。
- 生成されたハッシュ値 (派生キー) を16進数表記の文字列に変換して出力する。
#include <QCoreApplication>
#include <QMessageAuthenticationCode>
#include <QRandomGenerator>
#include <QDebug>
QByteArray pbkdf2(const QString &password, const QByteArray &salt, int iterations, int keyLength)
{
QMessageAuthenticationCode hmac(QCryptographicHash::Sha256);
QByteArray derivedKey(keyLength, Qt::Uninitialized);
hmac.setKey(password.toUtf8() + salt);
QByteArray U = hmac.result();
QByteArray T = U;
for (int i = 1; i < iterations; ++i) {
hmac.addData(U);
U = hmac.result();
for (int j = 0; j < keyLength; ++j) {
T[j] = T[j] ^ U[j];
}
}
return T;
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// パスワード
QString password = "myPassword";
// ソルト値の生成
QByteArray salt = QRandomGenerator::global()->generate(16).toHex();
// PBKDF2パラメータ
int iterations = 10000;
int keyLength = 32;
// PBKDF2の適用
QByteArray hashedPassword = pbkdf2(password, salt, iterations, keyLength);
// 結果の出力
qDebug() << "Password:" << password;
qDebug() << "Salt:" << salt;
qDebug() << "Iterations:" << iterations;
qDebug() << "Key Length:" << keyLength;
qDebug() << "Hashed Password:" << hashedPassword.toHex();
return a.exec();
}
QSslクラス
QSslクラスには、hashというスタティックメソッドが存在する。
このメソッドを使用することにより、指定されたアルゴリズムでデータのハッシュ値を計算することができる。
サポートされているアルゴリズムは、QCryptographicHashクラスと同様である。
ハッシュ化
以下の例では、QSsl::hashメソッドを使用して、任意の文字列をMD4、MD5、SHA-2、SHA-3でハッシュ化している。
QSsl::hashメソッドにおいて、第1引数にハッシュ化する文字列をUTF-8エンコードで渡して、第2引数に使用するハッシュアルゴリズムを指定する。
戻り値は、計算されたハッシュ値がQByteArray型である。
また、toHexメソッドを使用して、ハッシュ値を16進数表記の文字列に変換している。
出力は、QCryptographicHashクラスやQMessageAuthenticationCodeクラスを使用した場合と同値になる。
QSsl::hashメソッドは、QCryptographicHashクラスと同様のインターフェースを提供しているが、QSslクラスの一部として提供されている。
QSslクラスは、SSL/TLSの暗号化や認証に関連する機能を提供するクラスであるが、一般的なハッシュ化の用途では、QCryptographicHashクラスを使用するのが一般的である。
#include <QCoreApplication>
#include <QSslSocket>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// ハッシュ化する文字列
QString input = "Hello, world!";
// MD4
QByteArray md4Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Md4).toHex();
qDebug() << "MD4 Hash:" << md4Hash;
// MD5
QByteArray md5Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Md5).toHex();
qDebug() << "MD5 Hash:" << md5Hash;
// SHA-2 (SHA-224)
QByteArray sha224Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha224).toHex();
qDebug() << "SHA-2 (SHA-224) Hash:" << sha224Hash;
// SHA-2 (SHA-256)
QByteArray sha256Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha256).toHex();
qDebug() << "SHA-2 (SHA-256) Hash:" << sha256Hash;
// SHA-2 (SHA-384)
QByteArray sha384Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha384).toHex();
qDebug() << "SHA-2 (SHA-384) Hash:" << sha384Hash;
// SHA-2 (SHA-512)
QByteArray sha512Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha512).toHex();
qDebug() << "SHA-2 (SHA-512) Hash:" << sha512Hash;
// SHA-3 (SHA3-224)
QByteArray sha3_224Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_224).toHex();
qDebug() << "SHA-3 (SHA3-224) Hash:" << sha3_224Hash;
// SHA-3 (SHA3-256)
QByteArray sha3_256Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_256).toHex();
qDebug() << "SHA-3 (SHA3-256) Hash:" << sha3_256Hash;
// SHA-3 (SHA3-384)
QByteArray sha3_384Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_384).toHex();
qDebug() << "SHA-3 (SHA3-384) Hash:" << sha3_384Hash;
// SHA-3 (SHA3-512)
QByteArray sha3_512Hash = QSsl::hash(input.toUtf8(), QCryptographicHash::Sha3_512).toHex();
qDebug() << "SHA-3 (SHA3-512) Hash:" << sha3_512Hash;
return a.exec();
}