PBKDF
Password Base Key Derivation Functions パスワードによる鍵導出関数 KDF パスワードから暗号用の鍵やIVなどを生成する手法
PBKDF1 古い方 互換性用途で残っている key と iv 生成に別々で利用する場合が多い
OpenSSLで拡張あり
PBKDF2 出力長制限がなくなった key と iv 一括で生成する場合が多い
ハッシュ関数を使用する
PBKDF1
入力
Hash ハッシュ関数
MD2
salt ソルト PBESでは長さ制限があるがこちらではなし DESに合わせて64bitかもしれない
c 繰り返し回数
10000程度推奨 1もある
password パスワード
dkLen 出力長 最大はHashの出力長
code:PBKDF1
t = Hash(password + salt)
loop (c-1)
t = Hash(t)
return t の dkLen区切り
最長出力はハッシュの出力に依存する
PBKDF1改
OpenSSLで出力の長さ制限をなくした改変
AES-192, AES-256, TripleDESなどのハッシュ長より長い鍵出力にも対応できるがハッシュ関数や繰り返し回数が固定なので強度は弱い
OpenSSLのRSA鍵の暗号化(旧式)やencで使われるようだ
制限
salt たぶん64bit固定
dkLen 制限なし
出力はkeyのみに使用する。IVはパラメータで指定されたものを使用する。
RSA鍵の暗号など BEGIN RSA PRIVATE KEY の Proc-Type: 4,ENCRYPTEDの場合
DEK-Info: の後側がIV(のHEX表記)なのでCBC, GCMなどのIVとしてそのまま渡す
DESの例が多いのでそのままAES対応するとsalt長などで罠あり
ハッシュ関数はMD5固定
salt は 64bit固定のため IV の先頭64bitを使用する (ASN.1などでsaltが最大64bitまで)
c = 1 繰り返し回数 1固定
password パスワード
dkLen 出力長 暗号の鍵長 DES 64bit TDEA 192bit AES 128,192,256bit などをバイト単位で指定
code:PBKDF1改
byte[] salt = ivの先頭64bit
MD md = new MD5();
do {
md.update(password);
byte[] t = md.digest(salt);
// c = 1固定なので省略
// for (int cn = 1; cn < c; cn++) {
// t = md.digest(t);
// }
dk = dk + t;
md.update(t);
} while ( dk.length < dkLen )
key = dkの先頭dkLen
1回目
t = MD5(password + salt)
dk = t
2回目以降
t = MD5(t + password + salt)
dk = dk + t
PBKDF2
出力長が可変になった
入力
Mac / Hash 疑似乱数関数
デフォルト HMAC-SHA1だが古いので非推奨
salt
c
10000程度推奨
OpenSSL では 2000など
password
dkLen
passwordはHMACの鍵としてそのまま利用する。HMACの出力サイズ以上がほしいか。
関数 F( byte[] password, byte[] salt, int c, int i ) はパスワードを持っているHMACを渡してもいい
F( MAC pef, byte[] salt, int c, int i ) としておく HMAC の prf は乱数生成器の意味
prf 疑似乱数関数
password prfとセット
salt prfに最初に渡す
c ストレッチ回数
i カウンタ U_i
出力
ランダムな値 (長さはprfの出力サイズ)
code:F
byte[] f(MAC prf, byte[] salt, int c, int i) {
prf.update(salt);
// i をバイト列にばらして
u1 = (byte)((i >> 16) & 0xff); u2 = (byte)((i >> 8) & 0xff); u = prf.doFinal(u); // U_1 = PRF(password, salt || INT(i))
byte[] f = u; // f はXOR計算用
int len = u.length;
for (int j = 1; j < c; j++) {
u = prf.doFinal(u); // U_c = PRF(password, U_(c-1)) ひとつ前のMAC
for (int k = 0; k < len; k++) { // 出力はU を XOR したもの
}
}
return f;
}
U_1 = PRF(password, salt || INT(i))
OUT = U_1
U_2 = PRF(password, U_1)
OUT ^= U_2
U_...
U_c = PRF(password, U_(c-1))
OUT ^= U_c
結果は U_1 XOR U_2 ... XOR U_c
PBKDF2本体
prf 乱数生成器 HMACなど
password HMACの最小以上がよい
salt
c ストレッチ回数
dkLen 出力サイズ(バイト)
code:PBKDF2
byte[] pbkdf2(MAC prf, byte[] password, byte[] salt, int c, int dkLen) {
int hLen = prf.getMacLength(); // macのハッシュ出力バイト長
// dkLen > (2^32 -1) * hLen の場合、"derived key too long" を出力して停止する (int型なので略)
prf.init(password); // 乱数生成器(HMACなど)をパスワードで初期化
int l = (int)(((long)dkLen + hLen - 1) / hLen); // dkLen に必要なブロック数
for (int i = 1; i <= l; i++) {
// f(prf, salt, c, i) をtmpなど経由でdkに
System.arraycopy(f(prf, salt, c, i), 0, tmp, (i-1) * hLen, hLen);
}
return Arrays.copyOf(tmp, dkLen);
}